edu.internet2.middleware.shibboleth.common.config.BaseService.java Source code

Java tutorial

Introduction

Here is the source code for edu.internet2.middleware.shibboleth.common.config.BaseService.java

Source

/*
 * Licensed to the University Corporation for Advanced Internet Development, 
 * Inc. (UCAID) under one or more contributor license agreements.  See the 
 * NOTICE file distributed with this work for additional information regarding
 * copyright ownership. The UCAID licenses this file to You under the Apache 
 * License, Version 2.0 (the "License"); you may not use this file except in 
 * compliance with the License.  You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package edu.internet2.middleware.shibboleth.common.config;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

import org.opensaml.util.resource.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.support.GenericApplicationContext;

import edu.internet2.middleware.shibboleth.common.service.Service;
import edu.internet2.middleware.shibboleth.common.service.ServiceException;

/**
 * A service whose Spring beans are loaded into a service specific {@link ApplicationContext} that is a child of the
 * context provided in {@link #setApplicationContext(ApplicationContext)}.
 * 
 * Services derived from this base class may not be re-initialized after they have been destroyed.
 */
public abstract class BaseService implements Service, ApplicationContextAware, BeanNameAware {

    /** Class logger. */
    private final Logger log = LoggerFactory.getLogger(BaseService.class);

    /** Unique name of this service. */
    private String serviceName;

    /** Read/Write lock for the context. */
    private ReentrantReadWriteLock serviceContextRWLock;

    /** Application context owning this engine. */
    private ApplicationContext owningContext;

    /** Context containing loaded with service content. */
    private GenericApplicationContext serviceContext;

    /** List of configuration resources for this service. */
    private ArrayList<Resource> serviceConfigurations;

    /** Indicates if the service has been initialized already. */
    private boolean isInitialized;

    /** Indicates if the service has been destroyed. */
    private boolean isDestroyed;

    /** Constructor. */
    public BaseService() {
        serviceContextRWLock = new ReentrantReadWriteLock(true);
        isInitialized = false;
    }

    /** {@inheritDoc} */
    public void destroy() throws ServiceException {
        Lock writeLock = getReadWriteLock().writeLock();
        writeLock.lock();
        isDestroyed = true;
        serviceContext = null;
        serviceConfigurations.clear();
        setInitialized(false);
        writeLock.unlock();
        serviceContextRWLock = null;
    }

    /**
     * Gets the application context that is the parent to this service's context.
     * 
     * @return application context that is the parent to this service's context
     */
    public ApplicationContext getApplicationContext() {
        return owningContext;
    }

    /** {@inheritDoc} */
    public String getId() {
        return serviceName;
    }

    /**
     * Gets the read-write lock guarding the service context.
     * 
     * @return read-write lock guarding the service context
     */
    protected ReadWriteLock getReadWriteLock() {
        return serviceContextRWLock;
    }

    /**
     * Gets an unmodifiable list of configurations for this service.
     * 
     * @return unmodifiable list of configurations for this service
     */
    public List<Resource> getServiceConfigurations() {
        return Collections.unmodifiableList(serviceConfigurations);
    }

    /**
     * Gets this service's context.
     * 
     * @return this service's context
     */
    public ApplicationContext getServiceContext() {
        return serviceContext;
    }

    /** {@inheritDoc} */
    public void initialize() throws ServiceException {
        if (isDestroyed()) {
            throw new SecurityException(getId() + " service has been destroyed, it may not be initialized.");
        }

        if (isInitialized()) {
            return;
        }

        loadContext();
    }

    /** {@inheritDoc} */
    public boolean isInitialized() {
        return isInitialized;
    }

    /** {@inheritDoc} */
    public boolean isDestroyed() {
        return isDestroyed;
    }

    /**
     * Loads the service context.
     * 
     * @throws ServiceException thrown if the configuration for this service could not be loaded
     */
    protected void loadContext() throws ServiceException {
        log.info("Loading new configuration for service {}", getId());

        if (serviceConfigurations == null || serviceConfigurations.isEmpty()) {
            setInitialized(true);
            return;
        }

        GenericApplicationContext newServiceContext = new GenericApplicationContext(getApplicationContext());
        newServiceContext.setDisplayName("ApplicationContext:" + getId());
        Lock writeLock = getReadWriteLock().writeLock();
        writeLock.lock();
        try {
            SpringConfigurationUtils.populateRegistry(newServiceContext, getServiceConfigurations());
            newServiceContext.refresh();

            GenericApplicationContext replacedServiceContext = serviceContext;
            onNewContextCreated(newServiceContext);
            setServiceContext(newServiceContext);
            setInitialized(true);
            if (replacedServiceContext != null) {
                replacedServiceContext.close();
            }
            log.info("{} service loaded new configuration", getId());
        } catch (Throwable e) {
            // Here we catch all the other exceptions thrown by Spring when it starts up the context
            setInitialized(false);
            Throwable rootCause = e;
            while (rootCause.getCause() != null) {
                rootCause = rootCause.getCause();
            }
            log.error("Configuration was not loaded for " + getId()
                    + " service, error creating components.  The root cause of this error was: "
                    + rootCause.getClass().getCanonicalName() + ": " + rootCause.getMessage());
            log.trace("Full stacktrace is: ", e);
            throw new ServiceException(
                    "Configuration was not loaded for " + getId() + " service, error creating components.",
                    rootCause);
        } finally {
            writeLock.unlock();
        }
    }

    /**
     * Called after a new context has been created but before it set as the service's context. If an exception is thrown
     * the new context will not be set as the service's context and the current service context will be retained.
     * 
     * @param newServiceContext the newly created context for the service
     * 
     * @throws ServiceException thrown if there is a problem with the given service context
     */
    protected abstract void onNewContextCreated(ApplicationContext newServiceContext) throws ServiceException;

    /**
     * Sets the application context that is the parent to this service's context.
     * 
     * {@inheritDoc}
     */
    public void setApplicationContext(ApplicationContext applicationContext) {
        owningContext = applicationContext;
    }

    /** {@inheritDoc} */
    public void setBeanName(String name) {
        serviceName = name;
    }

    /**
     * Sets whether this service has been initialized.
     * 
     * @param initialized whether this service has been initialized
     */
    protected void setInitialized(boolean initialized) {
        isInitialized = initialized;
    }

    /**
     * Sets the service's configuration resources.
     * 
     * @param configurations configuration resources for the service
     */
    public void setServiceConfigurations(List<Resource> configurations) {
        if (isInitialized) {
            throw new IllegalStateException("Service already initialized");
        }
        serviceConfigurations = new ArrayList<Resource>(configurations);
    }

    /**
     * Sets this service's context.
     * 
     * @param context this service's context
     */
    protected void setServiceContext(GenericApplicationContext context) {
        serviceContext = context;
    }
}