KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > sape > carbon > core > component > factory > DefaultComponentFactory


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.component.factory;
19
20 import java.lang.reflect.Method JavaDoc;
21 import java.lang.reflect.Proxy JavaDoc;
22 import java.util.Arrays JavaDoc;
23 import java.util.HashSet JavaDoc;
24 import java.util.Iterator JavaDoc;
25 import java.util.Set JavaDoc;
26
27 import org.sape.carbon.core.component.Component;
28 import org.sape.carbon.core.component.ComponentConfiguration;
29 import org.sape.carbon.core.component.ComponentNotFoundException;
30 import org.sape.carbon.core.component.FunctionalInterface;
31 import org.sape.carbon.core.component.lifecycle.LifecycleInterceptor;
32 import org.sape.carbon.core.component.proxy.ComponentProxyInvocationHandler;
33 import org.sape.carbon.core.component.proxy.Decorator;
34 import org.sape.carbon.core.component.proxy.DecoratorFactory;
35 import org.sape.carbon.core.config.Config;
36 import org.sape.carbon.core.config.Configuration;
37 import org.sape.carbon.core.config.ConfigurationNotFoundException;
38 import org.sape.carbon.core.config.ConfigurationRuntimeException;
39 import org.sape.carbon.core.config.InvalidConfigurationException;
40 import org.sape.carbon.core.util.reflection.ClassUtil;
41
42 import org.apache.commons.logging.Log;
43 import org.apache.commons.logging.LogFactory;
44
45
46 /**
47  * <p>This implementation of <code>ComponentFactory</code> constructs the
48  * component by assembling the component's functional implementation with
49  * various <code>ComponentAssistants</code> defined by the factory's
50  * configuration. This implementation uses a <code>DynamicProxy</code>
51  * as the glue to hold these pieces together.</p>
52  *
53  * Copyright 2002 Sapient
54  * @since carbon 1.0
55  * @author Douglas Voet, January 2002
56  * @version $Revision: 1.46 $($Author: dvoet $ / $Date: 2003/10/27 19:53:00 $)
57  */

58 public class DefaultComponentFactory implements ComponentFactory {
59
60     /** @link dependency */
61     /*#ComponentFactoryConfiguration lnkComponentFactoryConfiguration;*/
62
63     /** @link dependency */
64     /*#ComponentTemplateConfiguration lnkComponentTemplateConfiguration;*/
65
66     /**
67      * The handle to Apache-commons logger.
68      */

69     private Log log = LogFactory.getLog(this.getClass());
70
71
72     /**
73      * <p>Builds the component specified by the name parameter.</p>
74      *
75      * @return the Component referred to by the name parameter in the
76      * LifecycleStateEnum.STOPPED state
77      * @param logicalComponentName the component to build */

78     public Component getInstance(String JavaDoc logicalComponentName) {
79
80         if (log.isTraceEnabled()) {
81             log.trace("Building new component: [" + logicalComponentName + "]");
82         }
83
84         // 1) get the component's configuration
85
ComponentConfiguration componentConfiguration =
86             getComponentConfig(logicalComponentName);
87
88         // 2) assemble the component
89
Component requestedComponent =
90             assembleComponent(componentConfiguration);
91
92         // 3) initialize the component
93
initializeComponent(requestedComponent);
94
95         return requestedComponent;
96     }
97
98     /**
99      * <p>Gets the configuration of the component specified by the name.</p>
100      *
101      * @return the component's configuration
102      * @param logicalComponentName the path to the requested logical component
103      */

104     private ComponentConfiguration getComponentConfig(
105             String JavaDoc logicalComponentName) {
106         Configuration config = null;
107         try {
108             config =
109                 Config.getInstance().fetchWritableConfiguration(logicalComponentName);
110
111             return (ComponentConfiguration) config;
112
113         } catch (ConfigurationNotFoundException cre) {
114             throw new ComponentNotFoundException(
115                 this.getClass(),
116                 logicalComponentName,
117                 cre);
118         } catch (ClassCastException JavaDoc cce) {
119             throw new InvalidConfigurationException(
120                 this.getClass(),
121                 logicalComponentName,
122                 "ConfigurationInterface",
123                 "Unable to create component configuration as the "
124                     + "implementing class is not assignable from "
125                     + "ComponentConfiguration",
126                 cce);
127         }
128     }
129
130     /**
131      * <p>This method does the actual work of constructing a component. It
132      * performs the following steps:
133      * <ol>
134      * <li>instantiates the component's functional implementation</li>
135      * <li>builds all component assistants</li>
136      * <li>instantiates the invocation handler for the component</li>
137      * <li>constructs the component proxy</li>
138      * </ol>
139      * The component will be in the LifecycleStateEnum.CREATED state when
140      * this method is complete.
141      * </p>
142      *
143      * @param componentConfiguration the configuration object for the
144      * component being created
145      * @return the component in the LifecycleStateEnum.CREATED state
146      */

147     private Component assembleComponent(
148             ComponentConfiguration componentConfiguration) {
149
150
151         // 1) instantiate the component's functional implementation
152
FunctionalInterface functionalImplementation =
153             buildFunctionalImplementation(componentConfiguration);
154
155         // 2) get the component template configuration
156
// The componentTemplateConfiguration is used to get the list of
157
// assistants that need to be constructed. It is also passed to
158
// the proxy invocation handler.
159
ComponentTemplateConfiguration componentTemplateConfiguration =
160             getComponentTemplateConfiguration(componentConfiguration);
161
162         // 3) instantiate the component proxy invocation handler
163
ComponentProxyInvocationHandler proxy =
164             buildProxyInvocationHandler(componentTemplateConfiguration);
165
166         // 3.1) set the component's name, in this implementation, it is the
167
// same as its configuration name
168
proxy.setComponentName(componentConfiguration.getConfigurationName());
169
170         // 4) Add the functional implementation as a delegate to the proxy.
171
// Be sure to get all interfaces the FunctionalInterface extends
172
Class JavaDoc[] functionalInterface =
173             new Class JavaDoc[] {componentConfiguration.getFunctionalInterface()};
174
175         proxy.setFunctionalImplementation(
176                 ClassUtil.getSuperInterfaces(functionalInterface),
177             functionalImplementation);
178
179         // 5) build all the assistants (delegates) and figure out all the
180
// interfaces they implement. The output of this step is a Set,
181
// assistants, of Assistants
182
// and a Set, componentInterfaces, consisting of all the interfaces
183
// the component proxy needs to implement.
184

185         // used to tell component proxy what interfaces to implement
186
Set JavaDoc componentInterfaces = new HashSet JavaDoc();
187
188         // 5.1) add the functional interface and Component interface
189
// to componentInterfaces
190
componentInterfaces.add(
191             componentConfiguration.getFunctionalInterface());
192         componentInterfaces.add(Component.class);
193
194         // 5.2) build all the assistants and add them to the assistants Set
195
// and their represented interfaces to componentInterfaces
196

197         // Set of Assistants the component proxy will use
198
Set JavaDoc decoratorSet = new HashSet JavaDoc();
199
200         DecoratorConfiguration[] decoratorConfigurations =
201             componentTemplateConfiguration.getDecoratorConfiguration();
202
203         for (int i = 0; i < decoratorConfigurations.length; i++) {
204             // 5.2.1) build the interceptor
205
Decorator decorator =
206                 buildDecorator(
207                     decoratorConfigurations[i],
208                     functionalImplementation,
209                     componentConfiguration,
210                     proxy);
211
212             // 5.2.2) add all represented interfaces to componentInterfaces
213
Class JavaDoc[] decoratorInterfaces = decorator.getExposedInterfaces();
214             for (int j = 0; j < decoratorInterfaces.length; j++) {
215                 componentInterfaces.add(decoratorInterfaces[j]);
216             }
217
218             // 5.2.3) Add the interceptor to the proxy
219
proxy.addDecorator(decorator);
220
221             // 5.2.4) Add the decorator to the Set so that we can initialize
222
// it later.
223
decoratorSet.add(decorator);
224         }
225
226
227         // 7) construct the component
228

229         // check that there are no clashing method names among the interfaces
230
checkAllMethodNames(componentInterfaces, proxy.getComponentName());
231
232         Class JavaDoc[] componentInterfacesArray =
233             (Class JavaDoc[]) componentInterfaces.toArray(
234                 new Class JavaDoc[componentInterfaces.size()]);
235
236         Component requestedComponent =
237             (Component)
238             Proxy.newProxyInstance(
239                 this.getClass().getClassLoader(),
240                 componentInterfacesArray,
241                 proxy);
242
243         // 8) set the Component reference in all the assistants
244
Iterator JavaDoc decoratorIterator = decoratorSet.iterator();
245         while (decoratorIterator.hasNext()) {
246             Decorator decorator = (Decorator) decoratorIterator.next();
247             decorator.setComponentReference(requestedComponent);
248         }
249
250         return requestedComponent;
251     }
252
253     /**
254      * <p>Calls lifecycle methods to initialize the component. Component
255      * should be in LifecycleStateEnum.STOPPED state when complete.</p>
256      *
257      * @param component the component to be initialized
258      */

259     private void initializeComponent(Component component) {
260         ((LifecycleInterceptor) component).initializeComponent(component);
261     }
262
263     /**
264      * <p>Factory method for the components functional implementation.</p>
265      *
266      * @return an instance of the component's functional implementation class
267      * @param componentConfiguration the configuration object for the
268      * component being created */

269     private FunctionalInterface buildFunctionalImplementation(
270             ComponentConfiguration componentConfiguration) {
271
272
273         try {
274             Class JavaDoc functionInterface =
275                 componentConfiguration.getFunctionalInterface();
276
277             if (functionInterface == null) {
278                 throw new InvalidConfigurationException(
279                     this.getClass(),
280                     componentConfiguration.getConfigurationName(),
281                     "FunctionalInterface",
282                     "Component configurations must provide a Functional " +
283                     "Interface class.");
284             }
285
286             Class JavaDoc functionalImplementation =
287                 componentConfiguration.getFunctionalImplementationClass();
288
289             if (functionalImplementation == null) {
290                 throw new InvalidConfigurationException(
291                     this.getClass(),
292                     componentConfiguration.getConfigurationName(),
293                     "FunctionalImplementationClass",
294                     "Component configurations must provide a Functional " +
295                     "Implementation class.");
296             }
297
298             // 1) check that implementation implements configured interface
299
if (!functionInterface
300                 .isAssignableFrom(functionalImplementation)) {
301                 throw new InvalidConfigurationException(
302                     this.getClass(),
303                     componentConfiguration.getConfigurationName(),
304                     "FunctionalImplementationClass",
305                     "The configured functional implementation class ["
306                         + functionalImplementation.getName()
307                         + "] does not implement configured "
308                         + "FunctionalInterface ["
309                         + functionInterface.getName()
310                         + "]");
311             }
312
313             // 2) return a new instance of the
314
return (FunctionalInterface) functionalImplementation.newInstance();
315
316         } catch (ClassCastException JavaDoc cce) {
317             throw new InvalidConfigurationException(
318                 this.getClass(),
319                 componentConfiguration.getConfigurationName(),
320                 "FunctionalInterface",
321                 "The configured functional interface ["
322                     + componentConfiguration.getFunctionalInterface().getName()
323                     + "] does not extend "
324                     + FunctionalInterface.class.getName(),
325                 cce);
326
327         } catch (InstantiationException JavaDoc ie) {
328             throw new InvalidConfigurationException(
329                 this.getClass(),
330                 componentConfiguration.getConfigurationName(),
331                 "FunctionalImplementationClass",
332                 "Could not instantiate functional implementation class ["
333                     + componentConfiguration
334                         .getFunctionalImplementationClass()
335                         .getName()
336                     + "]",
337                 ie);
338
339         } catch (IllegalAccessException JavaDoc iae) {
340             throw new InvalidConfigurationException(
341                 this.getClass(),
342                 componentConfiguration.getConfigurationName(),
343                 "FunctionalImplementationClass",
344                 "Could not instantiate functional implementation class ["
345                     + componentConfiguration
346                         .getFunctionalImplementationClass()
347                         .getName()
348                     + "]",
349                 iae);
350         }
351     }
352
353     /**
354      * <p>Factory method for the component proxy invocation handler.</p>
355      *
356      * @return ComponentProxyInvocationHandler that is the invocation handler
357      * for the requested component
358      * @param componentTemplateConfiguration the component template
359      * configuration that defines the construction of the requested component
360      */

361     private ComponentProxyInvocationHandler buildProxyInvocationHandler(
362             ComponentTemplateConfiguration componentTemplateConfiguration) {
363
364         Class JavaDoc componentProxyInvocationHandlerClass =
365             componentTemplateConfiguration
366                 .getComponentProxyInvocationHandlerClass();
367
368         if (log.isTraceEnabled()) {
369             log.trace("Building component proxy: ["
370                     + componentProxyInvocationHandlerClass.getName()
371                     + "]");
372         }
373
374         try {
375             return
376                 (ComponentProxyInvocationHandler)
377                 componentProxyInvocationHandlerClass.newInstance();
378
379         } catch (ClassCastException JavaDoc cce) {
380             throw new InvalidConfigurationException(
381                 this.getClass(),
382                 componentTemplateConfiguration.getConfigurationName(),
383                 "ComponentProxyInvocationHandlerClass",
384                 "ComponentProxyInvocationHandler not an instance of ["
385                     + ComponentProxyInvocationHandler.class.getName()
386                     + "]",
387                 cce);
388
389         } catch (InstantiationException JavaDoc ie) {
390             throw new InvalidConfigurationException(
391                 this.getClass(),
392                 componentTemplateConfiguration.getConfigurationName(),
393                 "ComponentProxyInvocationHandlerClass",
394                 "Could not instantiate ["
395                     + componentProxyInvocationHandlerClass.getName()
396                     + "]",
397                 ie);
398
399         } catch (IllegalAccessException JavaDoc iae) {
400             throw new InvalidConfigurationException(
401                 this.getClass(),
402                 componentTemplateConfiguration.getConfigurationName(),
403                 "ComponentProxyInvocationHandlerClass",
404                 "Could not instantiate ["
405                     + componentProxyInvocationHandlerClass.getName()
406                     + "]",
407                 iae);
408         }
409     }
410
411     /**
412      * <p>Factory method for component assistants.</p>
413      *
414      * @return the interceptor specifed in decoratorConfiguration
415      * @param proxyInvocationHandler the invocation handler of the component
416      * being built
417      * @param decoratorConfiguration the configuration object containing the
418      * name and DecoratorFactory class object for the decortator
419      * @param functionalImplementation the component's functional implementation
420      * object
421      * @param componentConfiguration the component's configuration
422      */

423     private Decorator buildDecorator(
424         DecoratorConfiguration decoratorConfiguration,
425         FunctionalInterface functionalImplementation,
426         ComponentConfiguration componentConfiguration,
427         ComponentProxyInvocationHandler proxyInvocationHandler) {
428
429
430         Class JavaDoc factoryClass = decoratorConfiguration.getFactory();
431         DecoratorFactory factory;
432
433         try {
434             factory = (DecoratorFactory) factoryClass.newInstance();
435
436         } catch (ClassCastException JavaDoc cce) {
437             throw new InvalidConfigurationException(
438                 this.getClass(),
439                 decoratorConfiguration.getConfigurationName(),
440                 "DecoratorFactory",
441                 "value was not an instance of: ["
442                     + DecoratorFactory.class.getName()
443                     + "]",
444                 cce);
445
446         } catch (InstantiationException JavaDoc ie) {
447             throw new InvalidConfigurationException(
448                 this.getClass(),
449                 decoratorConfiguration.getConfigurationName(),
450                 "DecoratorFactory",
451                 "could not instantiate AssistantFactory: ["
452                     + factoryClass.getName()
453                     + "]",
454                 ie);
455
456         } catch (IllegalAccessException JavaDoc iae) {
457             throw new InvalidConfigurationException(
458                 this.getClass(),
459                 decoratorConfiguration.getConfigurationName(),
460                 "DecoratorFactory",
461                 "could not instantiate AssistantFactory: ["
462                     + factoryClass.getName()
463                     + "]",
464                 iae);
465         }
466
467         if (log.isTraceEnabled()) {
468             log.trace("Building decorator: [" +
469                 factoryClass.getName() + "]");
470         }
471
472         // fetch interceptor's configuration if one is configured
473
Configuration configuration =
474             decoratorConfiguration.getCustomConfiguration();
475
476         // return new interceptor instance
477
return factory.getInstance(
478             functionalImplementation,
479             componentConfiguration,
480             proxyInvocationHandler,
481             configuration);
482     }
483
484     /**
485      * <p>Gets the ComponentTemplateConfiguration for this component.</p>
486      *
487      * <p>This implementation is hardcoded to always look in one spot in
488      * Config ("/core/component/componentTemplate") for the template.</p>
489      *
490      * @param componentConfiguration this parameter is not used in this
491      * implementation. If ComponentConfiguration is modified to have a
492      * getComponentTemplateConfiguration method, this method can be modified
493      * to use the componet specific component template.
494      * @return ComponentTemplateConfiguration for the component
495      */

496     private ComponentTemplateConfiguration getComponentTemplateConfiguration(
497             ComponentConfiguration componentConfiguration) {
498
499         try {
500             return (ComponentTemplateConfiguration) Config
501                 .getInstance()
502                 .fetchConfiguration(
503                 componentConfiguration.getComponentTemplateName());
504
505         } catch (ClassCastException JavaDoc cce) {
506             throw new InvalidConfigurationException(
507                 this.getClass(),
508                 componentConfiguration.getConfigurationName(),
509                 "ComponentTemplateName",
510                 "ComponentTemplateName does not refer to a configuration "
511                     + "that implements "
512                     + ComponentTemplateConfiguration.class.getName(),
513                 cce);
514
515         } catch (ConfigurationNotFoundException cnfe) {
516             throw new InvalidConfigurationException(
517                 this.getClass(),
518                 componentConfiguration.getConfigurationName(),
519                 "ComponentTemplateName",
520                 "Configurattion ["
521                     + componentConfiguration.getComponentTemplateName()
522                     + "] was not found",
523                 cnfe);
524         }
525     }
526
527
528     /**
529      * <p> Checks if duplicate methods are defined in interfaces.
530      * If they are then this method logs a warning in the log
531      * file about the duplicate </p>
532      *
533      * @param interfaces various interfaces being implemented
534      * @param componentName name of the component
535      */

536     private void checkAllMethodNames(Set JavaDoc interfaces, String JavaDoc componentName) {
537
538         Set JavaDoc methodSet = new HashSet JavaDoc();
539         Iterator JavaDoc interfaceIterator = interfaces.iterator();
540
541         // iterate through each interface
542
while (interfaceIterator.hasNext()) {
543             Class JavaDoc thisInterface = (Class JavaDoc) interfaceIterator.next();
544             Method JavaDoc[] methods = thisInterface.getMethods();
545
546             // iterate through each method if the interface
547
for (int i = 0; i < methods.length; i++) {
548                 MethodWrapper nextMethod = new MethodWrapper(methods[i]);
549                 boolean methodConflict = !methodSet.add(nextMethod);
550
551                 if (methodConflict) {
552                     // we have a conflict! Log a warning
553
if (log.isWarnEnabled()) {
554                         log.warn("Component has conflicting methods, method ["
555                                 + nextMethod.toString()
556                                 + "] in component ["
557                                 + componentName
558                                 + "]");
559                     }
560                 }
561             }
562         }
563     }
564
565     /**
566      * <p>This class provides a wrapper for tracking methods on the component
567      * facade, purely through their signature (name and parameters, but not
568      * declaring class). These
569      * objects can be created for each exposed method in a set of interfaces
570      * in order to determine if there are any collisions of name and parameter
571      * type between different methods being exposed on a component facade.</p>
572      *
573      * @since carbon 1.2
574      */

575     protected static class MethodWrapper {
576         /** Holds the method being wrapped. */
577         private Method JavaDoc method;
578
579         /**
580          * Constructs a new wrapper around a method.
581          *
582          * @param method to wrap
583          */

584         public MethodWrapper(Method JavaDoc method) {
585             this.method = method;
586         }
587
588         /**
589          * Returns the Method being wrapped.
590          *
591          * @return the method being wrapped
592          */

593         public Method JavaDoc getMethod() {
594             return this.method;
595         }
596
597         /**
598          * Returns true if obj is a Method and the Method within this wrapper
599          * has the same name and parameters as obj.
600          *
601          * @param obj checks the equality of the method object
602          * @return if the method is equal
603          */

604         public boolean equals(Object JavaDoc obj) {
605             boolean isEquals = false;
606             try {
607                 Method JavaDoc otherMethod = ((MethodWrapper) obj).getMethod();
608
609                 if (otherMethod.getName().equals(this.method.getName())) {
610                     // they have the same names
611
Class JavaDoc[] thisMethodParams = this.method.getParameterTypes();
612                     Class JavaDoc[] otherMethodParams = otherMethod.getParameterTypes();
613
614                     if (Arrays.equals(thisMethodParams, otherMethodParams)) {
615                         // they have the same parameters too
616
isEquals = true;
617                     }
618                 }
619             } catch (ClassCastException JavaDoc cce) {
620                 isEquals = false;
621             }
622             return isEquals;
623
624         }
625
626         /**
627          * @see java.lang.Object#hashCode()
628          */

629         public int hashCode() {
630             return this.method.getName().hashCode()
631                 + this.method.getParameterTypes().length;
632         }
633
634         /**
635          * @see java.lang.Object#toString()
636          */

637         public String JavaDoc toString() {
638             StringBuffer JavaDoc buffer = new StringBuffer JavaDoc(this.method.getName());
639             buffer.append("(");
640
641             Class JavaDoc[] parameterTypes = this.method.getParameterTypes();
642             if (parameterTypes.length > 0) {
643                 buffer.append(getTypeString(parameterTypes[0]));
644                 for (int i = 1; i < parameterTypes.length; i++) {
645                     buffer.append(", ");
646                     buffer.append(getTypeString(parameterTypes[i]));
647                 }
648             }
649             buffer.append(")");
650
651             return buffer.toString();
652         }
653
654         /**
655          * Adjusts the String of the component type as needed.
656          * If the component is an array, it will append a "[]" to
657          * the end of the name.
658          *
659          * @param type class to inspect
660          * @return a new version of the name with type information
661          */

662         private String JavaDoc getTypeString(Class JavaDoc type) {
663             if (type.isArray()) {
664                 return type.getComponentType().getName() + "[]";
665             } else {
666                 return type.getName();
667             }
668         }
669
670     }
671
672
673 }
674
Popular Tags