View Javadoc

1   package org.apache.fulcrum.yaafi.service.reconfiguration;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.io.File;
23  import java.io.InputStream;
24  import java.security.MessageDigest;
25  
26  import org.apache.avalon.framework.activity.Disposable;
27  import org.apache.avalon.framework.activity.Initializable;
28  import org.apache.avalon.framework.activity.Startable;
29  import org.apache.avalon.framework.activity.Suspendable;
30  import org.apache.avalon.framework.configuration.Configuration;
31  import org.apache.avalon.framework.configuration.ConfigurationException;
32  import org.apache.avalon.framework.configuration.DefaultConfigurationBuilder;
33  import org.apache.avalon.framework.configuration.Reconfigurable;
34  import org.apache.avalon.framework.context.Context;
35  import org.apache.avalon.framework.context.ContextException;
36  import org.apache.avalon.framework.context.Contextualizable;
37  import org.apache.avalon.framework.logger.AbstractLogEnabled;
38  import org.apache.avalon.framework.service.ServiceException;
39  import org.apache.avalon.framework.service.ServiceManager;
40  import org.apache.avalon.framework.service.Serviceable;
41  import org.apache.fulcrum.yaafi.framework.container.ServiceLifecycleManager;
42  
43  
44  /**
45   * Monitors the componentConfiguration.xml and triggers a reconfiguration
46   * if the content of the component configuration file  has changed.
47   *
48   * @author <a href="mailto:siegfried.goeschl@it20one.at">Siegfried Goeschl</a>
49   */
50  
51  public class ReconfigurationServiceImpl
52      extends AbstractLogEnabled
53      implements ReconfigurationService, Serviceable, Contextualizable,
54          Reconfigurable, Initializable, Runnable, Startable, Disposable
55  {
56      /** the interval between two checks in ms */
57      private int interval;
58  
59      /** shall the worker thread terminate immediately */
60      private boolean terminateNow;
61  
62      /** the worker thread polling the componentConfiguraton */
63      private Thread workerThread;
64  
65      /** the ServiceManager to use */
66      private ServiceManager serviceManager;
67  
68      /** the application directory */
69      private File applicationDir;
70  
71      /** our list of resources to monitor */
72      private ReconfigurationEntry[] reconfigurationEntryList;
73  
74      /** the interface to reconfigure individual services */
75      private ServiceLifecycleManager serviceLifecycleManager;
76  
77      /////////////////////////////////////////////////////////////////////////
78      // Avalon Service Lifecycle Implementation
79      /////////////////////////////////////////////////////////////////////////
80  
81      /**
82       * Constructor
83       */
84      public ReconfigurationServiceImpl()
85      {
86          this.terminateNow = false;
87      }
88  
89      /**
90       * @see org.apache.avalon.framework.service.Serviceable#service(org.apache.avalon.framework.service.ServiceManager)
91       */
92      public void service(ServiceManager manager) throws ServiceException
93      {
94          this.serviceManager = manager;
95          this.serviceLifecycleManager = (ServiceLifecycleManager) manager;
96      }
97  
98      /**
99       * @see org.apache.avalon.framework.context.Contextualizable#contextualize(org.apache.avalon.framework.context.Context)
100      */
101     public void contextualize(Context context) throws ContextException
102     {
103         this.applicationDir  = (File) context.get("urn:avalon:home");
104     }
105 
106     /**
107      * @see org.apache.avalon.framework.configuration.Configurable#configure(org.apache.avalon.framework.configuration.Configuration)
108      */
109     public void configure(Configuration configuration) throws ConfigurationException
110     {
111         // limit to minimum interval of 1 second
112 
113         this.interval = Math.max( configuration.getAttributeAsInteger("interval",5000), 1000 );
114 
115         this.getLogger().debug( "Monitoring the resources every " + this.interval + " ms" );
116 
117         // parse the resources to monitor
118 
119         Configuration entry = null;
120         Configuration services = null;
121         Configuration[] serviceEntries = null;
122         Configuration[] entryList = configuration.getChildren("entry");
123 
124         String location = null;
125         String serviceName = null;
126         String[] serviceNameList = null;
127         ReconfigurationEntry reconfigurationEntry = null;
128         ReconfigurationEntry[] list = new ReconfigurationEntry[entryList.length];
129 
130         for( int i=0; i<entryList.length; i++ )
131         {
132             entry = entryList[i];
133             location = entry.getChild("location").getValue();
134             services = entry.getChild("services",false);
135 
136             this.getLogger().debug( "Adding the following resource to monitor : " + location );
137 
138             if( services != null )
139             {
140                 serviceEntries = services.getChildren("service");
141                 serviceNameList = new String[serviceEntries.length];
142 
143                 for( int j=0; j<serviceEntries.length; j++ )
144                 {
145                     serviceName = serviceEntries[j].getAttribute("name");
146                     serviceNameList[j] = serviceName;
147                 }
148             }
149 
150             reconfigurationEntry = new ReconfigurationEntry(
151                 this.getLogger(),
152                 this.applicationDir,
153                 location,
154                 serviceNameList
155                 );
156 
157             list[i] = reconfigurationEntry;
158         }
159 
160         this.getLogger().debug( "Monitoring " + list.length + " resources" );
161 
162         this.setReconfigurationEntryList(list);
163     }
164 
165     /**
166      * @see org.apache.avalon.framework.activity.Initializable#initialize()
167      */
168     public void initialize() throws Exception
169     {
170         // request a SHA-1 to make sure that it is supported
171 
172         MessageDigest.getInstance( "SHA1" );
173 
174         // check that the ServiceManager inplements Reconfigurable
175 
176         if( (this.serviceManager instanceof ServiceLifecycleManager) == false )
177         {
178             String msg = "The ServiceManager instance does not implement ServiceLifecycleManager?!";
179             throw new IllegalArgumentException( msg );
180         }
181 
182         // create the worker thread polling the target
183 
184         this.workerThread = new Thread( this, "ReconfigurationService" );
185     }
186 
187     /**
188      * @see org.apache.avalon.framework.activity.Startable#start()
189      */
190     public void start() throws Exception
191     {
192         this.getLogger().debug( "Starting worker thread ..." );
193         this.workerThread.start();
194     }
195 
196     /**
197      * @see org.apache.avalon.framework.activity.Startable#stop()
198      */
199     public void stop() throws Exception
200     {
201         this.getLogger().debug( "Stopping worker thread ..." );
202         this.terminateNow = true;
203         this.workerThread.interrupt();
204         this.workerThread.join( 10000 );
205     }
206 
207     /**
208      * @see org.apache.avalon.framework.activity.Disposable#dispose()
209      */
210     public void dispose()
211     {
212         this.terminateNow = false;
213         this.applicationDir = null;
214         this.workerThread = null;
215         this.serviceManager = null;
216         this.reconfigurationEntryList = null;
217     }
218 
219     /**
220      * @see org.apache.avalon.framework.configuration.Reconfigurable#reconfigure(org.apache.avalon.framework.configuration.Configuration)
221      */
222     public void reconfigure(Configuration configuration)
223         throws ConfigurationException
224     {
225         this.configure(configuration);
226     }
227 
228     /////////////////////////////////////////////////////////////////////////
229     // Service interface implementation
230     /////////////////////////////////////////////////////////////////////////
231 
232     /**
233      * Polls for changes in the confguration to reconfigure either the
234      * whole container or just a list of services.
235      *
236      * @see java.lang.Runnable#run()
237      */
238     public void run()
239     {
240         ReconfigurationEntry reconfigurationEntry = null;
241         ReconfigurationEntry[] list = null;
242 
243         while( this.terminateNow == false )
244         {
245             list = this.getReconfigurationEntryList();
246 
247             try
248             {
249                 for( int i=0; i<list.length; i++ )
250                 {
251                     reconfigurationEntry = list[i];
252 
253                     if( reconfigurationEntry.hasChanged() )
254                     {
255                         this.onReconfigure( reconfigurationEntry );
256                     }
257                 }
258 
259                 Thread.sleep( this.interval );
260             }
261             catch( InterruptedException e )
262             {
263                 continue;
264             }
265             catch(Exception e)
266             {
267                 String msg = "The ReconfigurationService had a problem";
268                 this.getLogger().error(msg,e);
269                 continue;
270             }
271         }
272     }
273 
274     /////////////////////////////////////////////////////////////////////////
275     // Service implementation
276     /////////////////////////////////////////////////////////////////////////
277 
278     /**
279      * Reconfigure either the whole container or a list of services. This
280      * method is called within a seperate worker thred.
281      *
282      * @param reconfigurationEntry the configuration what to reconfigure
283      * @throws Exception the reconfiguration failed
284      */
285     protected void onReconfigure( ReconfigurationEntry reconfigurationEntry )
286         throws Exception
287     {
288         if( reconfigurationEntry.getServiceList() == null )
289         {
290             // reconfigure the whole container using Avalon Lifecycle Spec
291 
292             InputStream is = reconfigurationEntry.locate();
293             DefaultConfigurationBuilder builder = new DefaultConfigurationBuilder();
294             Configuration configuration = builder.build(is);
295             is.close();
296             is = null;
297 
298             this.getLogger().warn( "Starting to reconfigure the container" );
299 
300             if( this.serviceManager instanceof Suspendable)
301             {
302                 this.getLogger().info( "Calling suspend() of the container" );
303                 ((Suspendable) this.serviceManager).suspend();
304             }
305 
306             if( this.serviceManager instanceof Reconfigurable)
307             {
308                 this.getLogger().info( "Calling reconfigure() of the container" );
309                 ((Reconfigurable) this.serviceManager).reconfigure(configuration);
310             }
311 
312             if( this.serviceManager instanceof Suspendable)
313             {
314                 this.getLogger().info( "Calling resume() of the container" );
315                 ((Suspendable) this.serviceManager).resume();
316             }
317 
318             this.getLogger().info( "Reconfiguring the container was successful" );
319         }
320         else
321         {
322             String[] serviceList = reconfigurationEntry.getServiceList();
323             this.getLogger().warn( "Calling reconfigure() on individual services : " + serviceList.length );
324             this.serviceLifecycleManager.reconfigure(serviceList);
325         }
326     }
327 
328     /**
329      * @return Returns the reconfigurationEntryList.
330      */
331     private synchronized ReconfigurationEntry [] getReconfigurationEntryList()
332     {
333         return reconfigurationEntryList;
334     }
335 
336     /**
337      * @param reconfigurationEntryList The reconfigurationEntryList to set.
338      */
339     private synchronized void setReconfigurationEntryList(
340         ReconfigurationEntry [] reconfigurationEntryList)
341     {
342         this.reconfigurationEntryList = reconfigurationEntryList;
343     }
344 }