KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > sape > carbon > core > config > interceptor > DefaultConfigurationInterceptor


1 /*
2  * The contents of this file are subject to the Sapient Public License
3  * Version 1.0 (the "License"); you may not use this file except in compliance
4  * with the License. You may obtain a copy of the License at
5  * http://carbon.sf.net/License.html.
6  *
7  * Software distributed under the License is distributed on an "AS IS" basis,
8  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
9  * the specific language governing rights and limitations under the License.
10  *
11  * The Original Code is The Carbon Component Framework.
12  *
13  * The Initial Developer of the Original Code is Sapient Corporation
14  *
15  * Copyright (C) 2003 Sapient Corporation. All Rights Reserved.
16  */

17
18 package org.sape.carbon.core.config.interceptor;
19
20 import java.lang.reflect.InvocationTargetException JavaDoc;
21 import java.lang.reflect.Method JavaDoc;
22 import java.util.ArrayList JavaDoc;
23 import java.util.Collection JavaDoc;
24
25 import org.apache.commons.logging.Log;
26 import org.apache.commons.logging.LogFactory;
27
28 import org.sape.carbon.core.component.Component;
29 import org.sape.carbon.core.component.ComponentConfiguration;
30 import org.sape.carbon.core.component.lifecycle.LifecycleInterceptor;
31 import org.sape.carbon.core.component.proxy.ComponentProxyInvocationHandler;
32 import org.sape.carbon.core.component.proxy.Decorator;
33 import org.sape.carbon.core.component.proxy.Interceptor;
34 import org.sape.carbon.core.component.proxy.Invocation;
35 import org.sape.carbon.core.config.Config;
36 import org.sape.carbon.core.config.ConfigurationException;
37 import org.sape.carbon.core.config.node.Node;
38 import org.sape.carbon.core.config.node.NodeNotFoundException;
39 import org.sape.carbon.core.config.node.event.NodeEventListener;
40 import org.sape.carbon.core.util.reflection.GenericProxy;
41
42 /**
43  * <p>
44  * The default implementation of ConfigurationInterceptor. This implementation
45  * is a java.lang.reflect.InvocationHandler that can handle method calls
46  * on both the ConfigurationInterceptor interface and the component's
47  * configuration interface. All set requests to the configuration
48  * interface manipulate the working configuration. All get requests to the
49  * configuration interface manipulate the live configuration.
50  * The ConfigurationInterceptor
51  * interface handles the management of the working and live configurations.
52  * </p>
53  *
54  * Copyright 2002 Sapient
55  * @since carbon 1.0
56  * @author Greg Hinkle, January 2002
57  * @version $Revision: 1.6 $($Author: dvoet $ / $Date: 2003/10/27 19:54:41 $)
58  */

59 public class DefaultConfigurationInterceptor
60     extends GenericProxy
61     implements ConfigurationInterceptor, Interceptor, NodeEventListener {
62         
63     private Log log = LogFactory.getLog(this.getClass());
64
65     /** The component's live configuration */
66     protected ComponentConfiguration liveConfiguration;
67     
68     /**
69      * The component's working configuration,
70      * possibly in an interim/invalid state
71      */

72     protected ComponentConfiguration workingConfiguration = null;
73
74     /** The invocation handler. */
75     protected ComponentProxyInvocationHandler proxyInvocationHandler;
76
77     /** Component being proxied. */
78     protected Component thisComponent;
79
80     /** Next interceptor in the chain. */
81     protected Interceptor nextInterceptor;
82
83     private Thread JavaDoc persistingThread = null;
84
85     /**
86      * This constructor is protected to allow extension but not direct
87      * instantiation. Instantiation is handled by the
88      * <code>ConfigurationInterceptorFactory</code> in this package.
89      *
90      * @param componentConfiguration the configuration object this interceptor
91      * will manage
92      * @param proxyInvocationHandler the proxy invocation handler
93      */

94     protected DefaultConfigurationInterceptor(
95         ComponentConfiguration componentConfiguration,
96         ComponentProxyInvocationHandler proxyInvocationHandler,
97         ConfigurationInterceptorConfiguration config) {
98
99         this.liveConfiguration = componentConfiguration;
100         this.proxyInvocationHandler = proxyInvocationHandler;
101         
102         if (config.isAutoRefreshComponent()) {
103             try {
104                 Config.getInstance().addNodeListener(
105                     componentConfiguration.getConfigurationName(),
106                     this);
107             } catch (NodeNotFoundException nnfe) {
108                 if (log.isWarnEnabled()) {
109                     log.warn(
110                         "Could not register component [" +
111                         componentConfiguration.getConfigurationName() +
112                         "] as a listener for its own configuration",
113                         nnfe);
114                 }
115             }
116         }
117     }
118
119     /**
120      * This implementation returns the ConfigurationInterceptor interface
121      * and the component's configuration interface
122      *
123      * @return Class[] an array of interfaces
124      */

125     public Class JavaDoc[] getExposedInterfaces() {
126         return new Class JavaDoc[] {
127             ConfigurationInterceptor.class,
128             this.liveConfiguration.getConfigurationInterface()};
129     }
130
131     /**
132      * This method sets the next interceptor as the next in the chain from the
133      * current interceptor.
134      * @param interceptor the next interceptor in the chain
135      */

136     public void setNextInterceptor(Interceptor interceptor) {
137         this.nextInterceptor = interceptor;
138     }
139
140     /**
141      * @see Interceptor#setComponentReference(Component)
142      */

143     public void setComponentReference(Component component) {
144         this.thisComponent = component;
145     }
146
147     ///////////////////////////////////////////////////////////////////////////
148
// ConfigurationInterceptor interface methods
149
///////////////////////////////////////////////////////////////////////////
150

151     /**
152      * @see ConfigurationInterceptor#persistConfiguration()
153      */

154     public synchronized void persistConfiguration()
155         throws ConfigurationException {
156
157         try {
158             this.persistingThread = Thread.currentThread();
159             
160             Config.getInstance().storeConfiguration(
161                 this.liveConfiguration.getConfigurationName(),
162                 this.liveConfiguration);
163                 
164         } finally {
165             this.persistingThread = null;
166         }
167     }
168
169     /**
170      * @see ConfigurationInterceptor#revertConfiguration()
171      */

172     public synchronized void revertConfiguration() {
173
174         this.workingConfiguration = null;
175
176         configureComponent(
177             (ComponentConfiguration)
178             Config.
179                 getInstance().
180                 fetchWritableConfiguration(
181                     this.liveConfiguration.getConfigurationName()));
182     }
183
184     /**
185      * @see ConfigurationInterceptor#applyConfiguration()
186      */

187     public synchronized void applyConfiguration() {
188         if (this.workingConfiguration != null) {
189             ComponentConfiguration newConfiguration = this.workingConfiguration;
190             this.workingConfiguration = null;
191
192             configureComponent(newConfiguration);
193         }
194     }
195
196     /**
197      * This implementation returns a copy of the component's
198      * working configuration. Changes to this object have not effect on the
199      * actual working configuration.
200      *
201      * @see ConfigurationInterceptor#getWorkingConfiguration()
202      */

203     public synchronized ComponentConfiguration getWorkingConfiguration() {
204         initializeWorkingConfiguration();
205         return (ComponentConfiguration) this.workingConfiguration.clone();
206     }
207
208     /**
209      * This implementation returns a copy of the component's
210      * live configuration. Changes to this object have not effect on the
211      * actual live configuration.
212      *
213      * @see ConfigurationInterceptor#getWorkingConfiguration()
214      */

215     public synchronized ComponentConfiguration getLiveConfiguration() {
216         return (ComponentConfiguration) this.liveConfiguration.clone();
217     }
218
219     /**
220      * @see ConfigurationInterceptor#refreshConfiguration()
221      */

222     public void refreshConfiguration() {
223         try {
224             Config
225                 .getInstance()
226                 .fetchNode(this.liveConfiguration.getConfigurationName())
227                 .refresh();
228
229         } catch (NodeNotFoundException nnfe) {
230             throw new ConfigurationInterceptorException(
231                 this.getClass(),
232                 "Component's configuration node no longer exists",
233                 nnfe);
234         }
235
236         revertConfiguration();
237     }
238
239     ///////////////////////////////////////////////////////////////////////////
240
// ProxyEventListener interface methods
241
///////////////////////////////////////////////////////////////////////////
242

243     /**
244      * This implementation watches for calls to the configure lifecycle
245      * method. When the configure method is called, this method will
246      * replace the live configuration with the configuration passed to
247      * the configure method. (Should it discard the working copy?)
248      *
249      * @param invocation the invocation to execute
250      * @return the results of the invocation's execution
251      * @throws Throwable indicates an error with the configruation
252      */

253     public Object JavaDoc invoke(Invocation invocation) throws Throwable JavaDoc {
254
255         // call the next interceptor first
256
// if an exception occurs, the live configuration will not be
257
// updated. if the exception was fatal the the component,
258
// it will be destroyed, otherwise, it is correct to leave the
259
// liveConfiguration in it's current state.
260
Object JavaDoc returnValue = this.nextInterceptor.invoke(invocation);
261
262         if (CONFIGURE_METHOD.equals(invocation.getMethod())) {
263             ComponentConfiguration newConfig =
264                 (ComponentConfiguration)
265                 invocation.getMethodArguments()[0];
266
267             // set the live configuration to the value passed into the
268
// configureComponent method.
269
// synchronized so that no other thread is modifying the live
270
// configuration
271
synchronized (this) {
272                 // makes a defensive copy using clone because we don't know
273
// who else has access to the incoming configuration object
274
this.liveConfiguration =
275                     (ComponentConfiguration) newConfig.clone();
276             }
277         }
278         
279         return returnValue;
280     }
281
282     /**
283      * This implementation causes the component to be reconfigured if the
284      * interceptor is configured to auto refresh the component. This
285      * implementation will not refresh the component's configuration if the
286      * event is triggered by a call to persistConfiguration because it must
287      * already have been applied.
288      */

289     public synchronized void nodeChanged(Node changedNode) {
290         if (this.persistingThread != Thread.currentThread()) {
291
292             ComponentConfiguration newConfig = (ComponentConfiguration)
293                 Config.getInstance().fetchWritableConfiguration(
294                     this.liveConfiguration.getConfigurationName());
295                     
296             configureComponent(newConfig);
297         }
298     }
299
300     /** This impelementation does nothing when a component's config is removed */
301     public void nodeRemoved(String JavaDoc removedNodeName) {
302         // do nothing
303
}
304
305     ///////////////////////////////////////////////////////////////////////////
306
// GenericProxy abstract methods
307
///////////////////////////////////////////////////////////////////////////
308

309     /**
310      * This implementation delegates to:
311      * <ul>
312      * <li>The live configuration object when get methods are invoked on the
313      * configuration interface</li>
314      * <li>The working configuration object when set methods are invoked on
315      * the configuration interface</li>
316      * <li>This class for methods called on the ConfigurationInterceptor
317      * interface</li>
318      * </ol>
319      *
320      * @param proxy dynamic proxy
321      * @param m method to be invoked
322      * @param args the arguments to pass to the method
323      * @return result of the proxied call
324      * @throws UnsupportedOperationException if method getDataStructure,
325      * clone, or setName is called
326      * @throws ConfigurationInterceptorException if the Method m is not
327      * assignable from ConfigurationInterceptor or the component's
328      * configuration interface
329      * @throws Throwable indicates a generic error in the invocation chain
330      */

331     protected Object JavaDoc handleInvoke(Object JavaDoc proxy, Method JavaDoc m, Object JavaDoc[] args)
332         throws Throwable JavaDoc {
333
334         try {
335             if (UNSUPPORTED_OPERATIONS.contains(m.getName())) {
336                 throw new UnsupportedOperationException JavaDoc(m.toString());
337             }
338
339             if (INVOKE_METHOD.equals(m)) {
340                 return invoke((Invocation) args[0]);
341
342             } else if (ConfigurationInterceptor.class.isAssignableFrom(m.getDeclaringClass())
343                 || Decorator.class.isAssignableFrom(m.getDeclaringClass())) {
344
345                 return m.invoke(this, args);
346
347             } else if (m.getDeclaringClass().isAssignableFrom(
348                     this.liveConfiguration.getConfigurationInterface())) {
349
350                 synchronized (this) {
351
352                     // TODO GH: Perhaps we should just try to edit and if
353
// we fail get a working config.
354

355                     if (m.getName().startsWith(GET)
356                         || m.getName().startsWith(IS)) {
357                         // the method call is to an accessor,
358
// delegate to the liveConfiguration
359
return m.invoke(this.liveConfiguration, args);
360
361                     } else {
362                         // the method call is not an accessor,
363
// delegate to the workingConfiguration
364
initializeWorkingConfiguration();
365                         return m.invoke(this.workingConfiguration, args);
366                     }
367                 }
368
369             } else {
370                 throw new ConfigurationInterceptorException(
371                     this.getClass(),
372                     "method not supported: " + m);
373             }
374
375         } catch (IllegalArgumentException JavaDoc iae) {
376             throw new ConfigurationInterceptorException(
377                 this.getClass(),
378                 "Caught IllegalArgumentException",
379                 iae);
380
381         } catch (IllegalAccessException JavaDoc iae) {
382             throw new ConfigurationInterceptorException(
383                 this.getClass(),
384                 "Caught IllegalAccessException",
385                 iae);
386
387         } catch (InvocationTargetException JavaDoc ite) {
388             // unwrap exception
389
throw ite.getTargetException();
390         }
391
392     }
393
394     /**
395      * This method will create a working configuration if it does not exist
396      */

397     private void initializeWorkingConfiguration() {
398         if (this.workingConfiguration == null) {
399             this.workingConfiguration =
400                 (ComponentConfiguration) this.liveConfiguration.clone();
401         }
402     }
403
404     /**
405      * Calls the lifecycle assistant to configure the component.
406      *
407      * @param newConfiguration the new config to pass to the component
408      */

409     private void configureComponent(ComponentConfiguration newConfiguration) {
410         LifecycleInterceptor lifecycleAssistant =
411             (LifecycleInterceptor) this.thisComponent;
412
413         lifecycleAssistant.configureComponent(newConfiguration);
414     }
415
416     ///////////////////////////////////////////////////////////////////////////
417
// static data
418
///////////////////////////////////////////////////////////////////////////
419

420     /** Set prefix for properties. */
421     private static final String JavaDoc SET = "set";
422
423     /** Get prefix for properties. */
424     private static final String JavaDoc GET = "get";
425
426     /** Is prefix for boolean properties. */
427     private static final String JavaDoc IS = "is";
428
429     /** Name of the clone method. */
430     private static final String JavaDoc CLONE = "clone";
431
432     /** Name of the get data structure method. */
433     private static final String JavaDoc GET_DATA_STRUCTURE = "getDataStructure";
434
435     /** Name of the set configuration name method. */
436     private static final String JavaDoc SET_NAME = "setConfigurationName";
437
438     /** Holds the list of operations not support by the interceptor. */
439     private static final Collection JavaDoc UNSUPPORTED_OPERATIONS = new ArrayList JavaDoc();
440
441     /** Holds a reference to the configureComponent method of the class. */
442     private static final Method JavaDoc CONFIGURE_METHOD;
443
444     /** Holds a reference to the invoke method of the Invocation class. */
445     private static final Method JavaDoc INVOKE_METHOD;
446     static {
447         UNSUPPORTED_OPERATIONS.add(CLONE);
448         UNSUPPORTED_OPERATIONS.add(GET_DATA_STRUCTURE);
449         UNSUPPORTED_OPERATIONS.add(SET_NAME);
450
451         try {
452             CONFIGURE_METHOD =
453                 LifecycleInterceptor.class.getMethod(
454                     "configureComponent",
455                     new Class JavaDoc[] {ComponentConfiguration.class});
456             INVOKE_METHOD =
457                 Interceptor.class.getMethod(
458                     "invoke",
459                     new Class JavaDoc[] {Invocation.class});
460         } catch (NoSuchMethodException JavaDoc nsme) {
461             throw new ConfigurationInterceptorException(
462                 DefaultConfigurationInterceptor.class,
463                 "caught NoSuchMethodException creating Method object "
464                     + "representing LifecycleInterceptor.configureComponent",
465                 nsme);
466         }
467     }
468
469 }
Popular Tags