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.Timer;
20  
21  import org.opensaml.util.resource.Resource;
22  import org.opensaml.util.resource.ResourceChangeListener;
23  import org.opensaml.util.resource.ResourceChangeWatcher;
24  import org.opensaml.util.resource.ResourceException;
25  import org.slf4j.Logger;
26  import org.slf4j.LoggerFactory;
27  
28  import edu.internet2.middleware.shibboleth.common.service.ReloadableService;
29  import edu.internet2.middleware.shibboleth.common.service.ServiceException;
30  
31  /**
32   * An extension to {@link BaseService} that allows the service's context to be reloaded if the underlying configuration
33   * resources are changed.
34   * 
35   * If, at construction time, polling frequency and retry attempt are given then the configuration resources will be
36   * watched for changes. If a change is detected then the current service's context will be dropped and a new one created
37   * from all resource files. If there is a problem loading a configuration resource during this process the existing
38   * service context is kept and an error is logged. The result of this occuring during the initial configuration load is
39   * implementation dependent.
40   * 
41   * <strong>NOTE:</strong> Service implementations must take out a read lock, through {@link #getReadWriteLock()},
42   * whenever reading or operating on information controlled by the service context. This will ensure that if a
43   * configuration change occurs the service context will not be replaced until after all current reads have completed.
44   */
45  public abstract class BaseReloadableService extends BaseService implements ReloadableService {
46  
47      /** Class logger. */
48      private final Logger log = LoggerFactory.getLogger(BaseReloadableService.class);
49  
50      /** Frequency policy resources are polled for updates.*/
51      private long resourcePollingFrequency;
52  
53      /** Number of policy resource polling retry attempts. */
54      private int resourcePollingRetryAttempts;
55  
56      /** Timer used to schedule resource polling tasks. */
57      private Timer pollingTimer;
58  
59      /**
60       * Constructor. Configuration resources are not monitored for changes.
61       * 
62       * Sets polling frequency to 0 and retry attempts to 0.
63       */
64      public BaseReloadableService() {
65          super();
66          setPollingFrequency(0);
67          setPollingRetryAttempts(0);
68      }
69      
70      /**
71       * Gets the timer used to resource polling jobs.
72       * 
73       * @return timer used to resource polling jobs
74       */
75      public Timer getPollingTimer(){
76          return pollingTimer;
77      }
78     
79      /**
80       * Sets the timer used to resource polling jobs.
81       * 
82       * @param timer timer used to resource polling jobs
83       */
84      public void setPollingTimer(Timer timer){
85          pollingTimer = timer;
86      }
87  
88      /**
89       * Gets the frequency, in millseconds, that the configuration resources are polled.
90       * 
91       * @return frequency, in millseconds, that the configuration resources are polled
92       */
93      public long getPollingFrequency() {
94          return resourcePollingFrequency;
95      }
96      
97      /**
98       * Sets the frequency, in millseconds, that the configuration resources are polled.
99       * 
100      * @param frequency the frequency, in millseconds, that the configuration resources are polled
101      */
102     public void setPollingFrequency(long frequency){
103         resourcePollingFrequency = frequency;
104     }
105 
106     /**
107      * Gets the number of times a resource may error out before it is considered permanently invalid.
108      * 
109      * @return number of times a resource may error out before it is considered permanently invalid
110      */
111     public int getPollingRetryAttempts() {
112         return resourcePollingRetryAttempts;
113     }
114 
115     /**
116      * Sets the number of times a resource may error out before it is considered permanently invalid.
117      * 
118      * @param attempts number of times a resource may error out before it is considered permanently invalid
119      */
120     public void setPollingRetryAttempts(int attempts) {
121         resourcePollingRetryAttempts = attempts;
122     }
123 
124     /** {@inheritDoc} */
125     public void initialize() throws ServiceException {
126         if (isDestroyed()) {
127             throw new SecurityException(getId() + " service has been destroyed, it may not be initialized.");
128         }
129 
130         if (isInitialized()) {
131             return;
132         }
133 
134         try {
135             log.debug("Initializing {} service with resources: {}", getId(), getServiceConfigurations());
136             if (resourcePollingFrequency > 0) {
137                 ResourceChangeWatcher changeWatcher;
138                 ResourceChangeListener changeListener = new ConfigurationResourceListener();
139                 for (Resource configurationResournce : getServiceConfigurations()) {
140                     changeWatcher = new ResourceChangeWatcher(configurationResournce, resourcePollingFrequency,
141                             resourcePollingRetryAttempts);
142                     changeWatcher.getResourceListeners().add(changeListener);
143                     pollingTimer.schedule(changeWatcher, resourcePollingFrequency, resourcePollingFrequency);
144                 }
145             }
146             
147             loadContext();
148         } catch (ResourceException e) {
149             throw new ServiceException("Unable to initialize service: " + getId(), e);
150         }
151     }
152 
153     /** {@inheritDoc} */
154     public void reload() throws ServiceException {
155         log.debug("Reloading service {}", getId());
156         loadContext();
157     }
158 
159     /** {@inheritDoc} */
160     public void destroy() throws ServiceException {
161         pollingTimer.cancel();
162         super.destroy();
163     }
164 
165     /** A listener for policy resource changes that triggers a reloading of the AFP context. */
166     protected class ConfigurationResourceListener implements ResourceChangeListener {
167 
168         /** {@inheritDoc} */
169         public void onResourceCreate(Resource resource) {
170             try {
171                 loadContext();
172             } catch (ServiceException e) {
173                 log.error(
174                         "Error reloading configuration, upon configuration resource creation, for service " + getId(),
175                         e);
176             }
177         }
178 
179         /** {@inheritDoc} */
180         public void onResourceDelete(Resource resource) {
181             try {
182                 loadContext();
183             } catch (ServiceException e) {
184                 log.error(
185                         "Error reloading configuration, upon configuration resource deletion, for service " + getId(),
186                         e);
187             }
188         }
189 
190         /** {@inheritDoc} */
191         public void onResourceUpdate(Resource resource) {
192             try {
193                 loadContext();
194             } catch (ServiceException e) {
195                 log.error("Error reloading configuration, upon configuration resource update, for service " + getId(),
196                         e);
197             }
198         }
199     }
200 }