1 package org.apache.fulcrum.yaafi.service.reconfiguration;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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
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
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
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
171
172 MessageDigest.getInstance( "SHA1" );
173
174
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
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
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
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
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 }