View Javadoc

1   /*
2    * Copyright [2007] [University Corporation for Advanced Internet Development, Inc.]
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package edu.internet2.middleware.shibboleth.common.config;
18  
19  import java.util.ArrayList;
20  import java.util.Collections;
21  import java.util.List;
22  import java.util.concurrent.locks.Lock;
23  import java.util.concurrent.locks.ReadWriteLock;
24  import java.util.concurrent.locks.ReentrantReadWriteLock;
25  
26  import org.opensaml.util.resource.Resource;
27  import org.slf4j.Logger;
28  import org.slf4j.LoggerFactory;
29  import org.springframework.beans.factory.BeanNameAware;
30  import org.springframework.context.ApplicationContext;
31  import org.springframework.context.ApplicationContextAware;
32  import org.springframework.context.support.GenericApplicationContext;
33  
34  import edu.internet2.middleware.shibboleth.common.service.Service;
35  import edu.internet2.middleware.shibboleth.common.service.ServiceException;
36  
37  /**
38   * A service whose Spring beans are loaded into a service specific {@link ApplicationContext} that is a child of the
39   * context provided in {@link #setApplicationContext(ApplicationContext)}.
40   * 
41   * Services derived from this base class may not be re-initialized after they have been destroyed.
42   */
43  public abstract class BaseService implements Service, ApplicationContextAware, BeanNameAware {
44  
45      /** Class logger. */
46      private final Logger log = LoggerFactory.getLogger(BaseService.class);
47  
48      /** Unique name of this service. */
49      private String serviceName;
50  
51      /** Read/Write lock for the context. */
52      private ReentrantReadWriteLock serviceContextRWLock;
53  
54      /** Application context owning this engine. */
55      private ApplicationContext owningContext;
56  
57      /** Context containing loaded with service content. */
58      private GenericApplicationContext serviceContext;
59  
60      /** List of configuration resources for this service. */
61      private ArrayList<Resource> serviceConfigurations;
62  
63      /** Indicates if the service has been initialized already. */
64      private boolean isInitialized;
65  
66      /** Indicates if the service has been destroyed. */
67      private boolean isDestroyed;
68  
69      /** Constructor. */
70      public BaseService() {
71          serviceContextRWLock = new ReentrantReadWriteLock(true);
72          isInitialized = false;
73      }
74  
75      /** {@inheritDoc} */
76      public void destroy() throws ServiceException {
77          Lock writeLock = getReadWriteLock().writeLock();
78          writeLock.lock();
79          isDestroyed = true;
80          serviceContext = null;
81          serviceConfigurations.clear();
82          setInitialized(false);
83          writeLock.unlock();
84          serviceContextRWLock = null;
85      }
86  
87      /**
88       * Gets the application context that is the parent to this service's context.
89       * 
90       * @return application context that is the parent to this service's context
91       */
92      public ApplicationContext getApplicationContext() {
93          return owningContext;
94      }
95  
96      /** {@inheritDoc} */
97      public String getId() {
98          return serviceName;
99      }
100     
101     /**
102      * Gets the read-write lock guarding the service context.
103      * 
104      * @return read-write lock guarding the service context
105      */
106     protected ReadWriteLock getReadWriteLock() {
107         return serviceContextRWLock;
108     }
109     
110     /**
111      * Gets an unmodifiable list of configurations for this service.
112      * 
113      * @return unmodifiable list of configurations for this service
114      */
115     public List<Resource> getServiceConfigurations(){
116         return Collections.unmodifiableList(serviceConfigurations);
117     }
118 
119     /**
120      * Gets this service's context.
121      * 
122      * @return this service's context
123      */
124     public ApplicationContext getServiceContext() {
125         return serviceContext;
126     }
127 
128     /** {@inheritDoc} */
129     public void initialize() throws ServiceException {
130         if (isDestroyed()) {
131             throw new SecurityException(getId() + " service has been destroyed, it may not be initialized.");
132         }
133 
134         if (isInitialized()) {
135             return;
136         }
137         
138         loadContext();
139     }
140 
141     /** {@inheritDoc} */
142     public boolean isInitialized() {
143         return isInitialized;
144     }
145     
146     /** {@inheritDoc} */
147     public boolean isDestroyed() {
148         return isDestroyed;
149     }
150 
151     /**
152      * Loads the service context.
153      * 
154      * @throws ServiceException thrown if the configuration for this service could not be loaded
155      */
156     protected void loadContext() throws ServiceException {
157         log.info("Loading new configuration for service {}", getId());
158         
159         if(serviceConfigurations == null || serviceConfigurations.isEmpty()){
160             setInitialized(true);
161             return;
162         }
163         
164         GenericApplicationContext newServiceContext = new GenericApplicationContext(getApplicationContext());
165         newServiceContext.setDisplayName("ApplicationContext:" + getId());
166         Lock writeLock = getReadWriteLock().writeLock();
167         writeLock.lock();
168         try {
169             SpringConfigurationUtils.populateRegistry(newServiceContext, getServiceConfigurations());
170             newServiceContext.refresh();
171 
172             GenericApplicationContext replacedServiceContext = serviceContext;
173             onNewContextCreated(newServiceContext);
174             setServiceContext(newServiceContext);
175             setInitialized(true);
176             if(replacedServiceContext != null){
177                 replacedServiceContext.close();
178             }
179             log.info("{} service loaded new configuration", getId());
180         } catch (Exception e) {
181             // Here we catch all the other exceptions thrown by Spring when it starts up the context
182             setInitialized(false);
183             Throwable rootCause = e;
184             while (rootCause.getCause() != null) {
185                 rootCause = rootCause.getCause();
186             }
187             log.error("Configuration was not loaded for " + getId()
188                     + " service, error creating components.  The root cause of this error was: " +
189                     rootCause.getClass().getCanonicalName() + ": " + rootCause.getMessage());
190             log.trace("Full stacktrace is: ", e);
191             throw new ServiceException("Configuration was not loaded for " + getId()
192                     + " service, error creating components.", (Exception) rootCause);
193         }finally{
194             writeLock.unlock();
195         }
196     }
197 
198     /**
199      * Called after a new context has been created but before it set as the service's context. If an exception is thrown
200      * the new context will not be set as the service's context and the current service context will be retained.
201      * 
202      * @param newServiceContext the newly created context for the service
203      * 
204      * @throws ServiceException thrown if there is a problem with the given service context
205      */
206     protected abstract void onNewContextCreated(ApplicationContext newServiceContext) throws ServiceException;
207 
208     /**
209      * Sets the application context that is the parent to this service's context.
210      * 
211      * {@inheritDoc}
212      */
213     public void setApplicationContext(ApplicationContext applicationContext) {
214         owningContext = applicationContext;
215     }
216 
217     /** {@inheritDoc} */
218     public void setBeanName(String name) {
219         serviceName = name;
220     }
221 
222     /**
223      * Sets whether this service has been initialized.
224      * 
225      * @param initialized whether this service has been initialized
226      */
227     protected void setInitialized(boolean initialized) {
228         isInitialized = initialized;
229     }
230 
231     /**
232      * Sets the service's configuration resources.
233      * 
234      * @param configurations configuration resources for the service
235      * @throws IllegalStateException thrown if the service has already been initialized
236      */
237     public void setServiceConfigurations(List<Resource> configurations) {
238         if(isInitialized){
239             throw new IllegalStateException("Service already initialized");
240         }
241         serviceConfigurations = new ArrayList<Resource>(configurations);
242     }
243 
244     /**
245      * Sets this service's context.
246      * 
247      * @param context this service's context
248      */
249     protected void setServiceContext(GenericApplicationContext context) {
250         serviceContext = context;
251     }
252 }