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 milliseconds, that the configuration resources are polled.
90       * 
91       * @return frequency, in milliseconds, that the configuration resources are polled
92       */
93      public long getPollingFrequency() {
94          return resourcePollingFrequency;
95      }
96      
97      /**
98       * Sets the frequency, in milliseconds, that the configuration resources are polled.
99       * 
100      * @param frequency the frequency, in milliseconds, 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         log.info("{} service configuration reloaded", getId());
158     }
159 
160     /** {@inheritDoc} */
161     public void destroy() throws ServiceException {
162         pollingTimer.cancel();
163         super.destroy();
164     }
165 
166     /** A listener for policy resource changes that triggers a reloading of the AFP context. */
167     protected class ConfigurationResourceListener implements ResourceChangeListener {
168 
169         /** {@inheritDoc} */
170         public void onResourceCreate(Resource resource) {
171             try {
172                 loadContext();
173             } catch (ServiceException e) {
174                 log.error(
175                         "Error reloading configuration, upon configuration resource creation, for service " + getId(),
176                         e);
177             }
178         }
179 
180         /** {@inheritDoc} */
181         public void onResourceDelete(Resource resource) {
182             try {
183                 loadContext();
184             } catch (ServiceException e) {
185                 log.error(
186                         "Error reloading configuration, upon configuration resource deletion, for service " + getId(),
187                         e);
188             }
189         }
190 
191         /** {@inheritDoc} */
192         public void onResourceUpdate(Resource resource) {
193             try {
194                 loadContext();
195             } catch (ServiceException e) {
196                 log.error("Error reloading configuration, upon configuration resource update, for service " + getId(),
197                         e);
198             }
199         }
200     }
201 }