View Javadoc

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