KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > hivemind > impl > ConfigurationPointImpl


1 // Copyright 2004, 2005 The Apache Software Foundation
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
// http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14

15 package org.apache.hivemind.impl;
16
17 import java.lang.reflect.Constructor JavaDoc;
18 import java.lang.reflect.Modifier JavaDoc;
19 import java.util.ArrayList JavaDoc;
20 import java.util.Collection JavaDoc;
21 import java.util.HashMap JavaDoc;
22 import java.util.Iterator JavaDoc;
23 import java.util.List JavaDoc;
24 import java.util.Map JavaDoc;
25
26 import org.apache.commons.logging.Log;
27 import org.apache.commons.logging.LogFactory;
28 import org.apache.hivemind.ApplicationRuntimeException;
29 import org.apache.hivemind.ShutdownCoordinator;
30 import org.apache.hivemind.definition.ConfigurationPointDefinition;
31 import org.apache.hivemind.definition.Contribution;
32 import org.apache.hivemind.definition.ContributionContext;
33 import org.apache.hivemind.definition.ContributionDefinition;
34 import org.apache.hivemind.definition.Occurances;
35 import org.apache.hivemind.impl.servicemodel.SingletonInnerProxy;
36 import org.apache.hivemind.internal.AbstractConstructionContext;
37 import org.apache.hivemind.internal.ConfigurationPoint;
38 import org.apache.hivemind.internal.Module;
39 import org.apache.hivemind.service.BodyBuilder;
40 import org.apache.hivemind.service.ClassFab;
41 import org.apache.hivemind.service.MethodSignature;
42 import org.apache.hivemind.util.ToStringBuilder;
43
44 /**
45  * Implementation of the {@link org.apache.hivemind.internal.ConfigurationPoint} interface; a
46  * container for {@link org.apache.hivemind.definition.Contribution}s.
47  *
48  * @author Howard Lewis Ship
49  */

50 public final class ConfigurationPointImpl extends AbstractExtensionPoint implements
51         ConfigurationPoint
52 {
53     private static final Log LOG = LogFactory.getLog(ConfigurationPointImpl.class);
54
55     /**
56      * The cached elements for the extension point (if caching is enabled).
57      */

58     private Object JavaDoc _configuration;
59     
60     private Class JavaDoc _configurationInterface;
61
62     private Object JavaDoc _configurationProxy;
63
64     private Occurances _expectedCount;
65
66     private boolean _building;
67
68     // TODO: use ShutdownCoordinator
69
private ShutdownCoordinator _shutdownCoordinator;
70
71     public ConfigurationPointImpl(Module module, ConfigurationPointDefinition definition)
72     {
73         super(module, definition);
74     }
75
76     public ConfigurationPointDefinition getConfigurationPointDefinition()
77     {
78         return (ConfigurationPointDefinition) super.getDefinition();
79     }
80     
81     protected void extendDescription(ToStringBuilder builder)
82     {
83         builder.append("type", getConfigurationTypeName());
84         builder.append("expectedCount", _expectedCount);
85     }
86     
87     public Collection JavaDoc getContributions()
88     {
89         return getConfigurationPointDefinition().getContributions();
90     }
91
92     /**
93      * Returns the number of contributions; it is expected that each top-level
94      * {@link org.apache.hivemind.Element} in each {@link Contribution} will convert to one element
95      * instance; the value returned is the total number of top-level elements in all contributed
96      * Extensions.
97      */

98     public int getContributionCount()
99     {
100         if (getConfigurationPointDefinition() == null)
101             return 0;
102
103         return getContributions().size();
104     }
105
106     public Occurances getExpectedCount()
107     {
108         return getConfigurationPointDefinition().getExpectedContributions();
109     }
110
111     /**
112      * @return true if configuration should be created lazy, that means a proxy must be created.
113      */

114     public boolean isLazy()
115     {
116         // TODO annotations: make configurable
117
// exclude ServiceModels, otherwise a cycle occurs because the proxy generation
118
// requires the {@link ClassFactory service}
119
return !getExtensionPointId().equals("hivemind.ServiceModels") &&
120           getConfigurationType().isInterface() &&
121           !Modifier.isFinal(getConfigurationType().getModifiers());
122     }
123
124     /**
125      * @see org.apache.hivemind.internal.ConfigurationPoint#getConfiguration()
126      */

127     public synchronized Object JavaDoc getConfiguration()
128     {
129         if (_configuration != null)
130             return _configuration;
131
132         if (isLazy()) {
133             // Configuration is lazy, so return a proxy that generates the configuration
134
// the first time a member method is called
135
if (_configurationProxy == null)
136             {
137                 _configurationProxy = createSingletonProxy();
138             }
139             return _configurationProxy;
140             
141         } else {
142             // construct the container immediately
143
_configuration = constructConfiguration();
144             return _configuration;
145         }
146     }
147
148     /**
149      * Called by the proxy responsible for lazy construction of the configuration when
150      * the first time a method of the container proxy is called.
151      * Generates the real configuration object and stores it in a field.
152      * Must be public so the proxy can access it.
153      */

154     public synchronized Object JavaDoc constructConfiguration()
155     {
156         // It's nice to have this protection, but (unlike services), you
157
// would really have to go out of your way to provoke
158
// a recursive configuration.
159

160         if (_building)
161             throw new ApplicationRuntimeException(ImplMessages
162                     .recursiveConfiguration(getExtensionPointId()));
163
164         try
165         {
166             if (_configuration == null)
167             {
168                 _building = true;
169                 
170                 processContributions();
171             }
172
173             // Now that we have the real list, we don't need the proxy anymore, either.
174

175             _configurationProxy = null;
176
177             return _configuration;
178         }
179         finally
180         {
181             _building = false;
182         }
183     }
184
185     /**
186      * Adds all contributions to the configuration container.
187      */

188     private void processContributions()
189     {
190         if (LOG.isDebugEnabled())
191             LOG.debug("Constructing extension point " + getExtensionPointId());
192
193         Collection JavaDoc contributions = getContributions();
194         
195         if (contributions == null)
196             return;
197
198         try
199         {
200             for (Iterator JavaDoc iterContrib = contributions.iterator(); iterContrib.hasNext();)
201             {
202                 ContributionDefinition cd = (ContributionDefinition) iterContrib.next();
203                 Module definingModule = getModule().getRegistry().getModule(cd.getModuleId());
204                 ContributionContext context = new ContributionContextImpl(definingModule, this);
205                 cd.getContribution().contribute(context);
206             }
207             // For backward compatibility create empty collections if nothing was contributed
208
if (_configuration == null) {
209                 initEmptyCollection();
210             }
211         }
212         catch (Exception JavaDoc ex)
213         {
214             throw new ApplicationRuntimeException(ImplMessages.unableToConstructConfiguration(
215                     getExtensionPointId(),
216                     ex), ex);
217         }
218
219     }
220     
221     /**
222      * Implementation of {@link ContributionContext}.
223      * Currently defined inline since it needs access to private methods of the outer configuration point.
224      */

225     class ContributionContextImpl extends AbstractConstructionContext implements ContributionContext
226     {
227         private ConfigurationPoint _configurationPoint;
228
229         public ContributionContextImpl(Module definingModule, ConfigurationPoint configurationPoint)
230         {
231             super(definingModule);
232             _configurationPoint = configurationPoint;
233         }
234
235         public Object JavaDoc getConfigurationData()
236         {
237             return _configuration;
238         }
239
240         public void mergeContribution(Object JavaDoc contributionData)
241         {
242             ConfigurationPointImpl.this.mergeContribution(contributionData);
243         }
244
245         public void setConfigurationData(Object JavaDoc data)
246         {
247             _configuration = data;
248         }
249
250         public ConfigurationPoint getConfigurationPoint()
251         {
252             return _configurationPoint;
253         }
254     }
255     
256     private void initEmptyCollection()
257     {
258         // TODO: this should be xml specific and maybe realized as initial empty contribution?
259
// But what happens with the contribution count?
260
// Move contribution count to xml?
261
if (List JavaDoc.class.equals(getConfigurationType())) {
262             _configuration = new ArrayList JavaDoc();
263         }
264         else if (Map JavaDoc.class.equals(getConfigurationType())) {
265             _configuration = new HashMap JavaDoc();
266         }
267     }
268
269     /**
270      * Merges a contribution with the configuration data already present in the field _configuration.
271      * TODO: Refactor as configurable service
272      * @param contribution
273      */

274     private void mergeContribution(Object JavaDoc contribution)
275     {
276         if (!getConfigurationType().isAssignableFrom(contribution.getClass())) {
277             throw new ApplicationRuntimeException("contribution of of type " +
278                     contribution.getClass().getName() + " is not compatible to configuration type " +
279                     getConfigurationType().getName());
280         }
281         if (_configuration == null) {
282             _configuration = contribution;
283         } else {
284             if (_configuration instanceof Collection JavaDoc) {
285                 ((Collection JavaDoc) _configuration).addAll((Collection JavaDoc) contribution);
286             }
287             else if (_configuration instanceof Map JavaDoc) {
288                 ((Map JavaDoc) _configuration).putAll((Map JavaDoc) contribution);
289             }
290         }
291         
292         
293     }
294
295     public void setShutdownCoordinator(ShutdownCoordinator coordinator)
296     {
297         _shutdownCoordinator = coordinator;
298     }
299
300     public String JavaDoc getConfigurationTypeName()
301     {
302         return getConfigurationPointDefinition().getConfigurationTypeName();
303     }
304
305     /**
306      * @see org.apache.hivemind.internal.ConfigurationPoint#getConfigurationType()
307      */

308     public Class JavaDoc getConfigurationType()
309     {
310         if (_configurationInterface == null)
311             _configurationInterface = getModule().resolveType(getConfigurationTypeName());
312
313         return _configurationInterface;
314     }
315
316     /**
317      * Creates a proxy class for the service and then construct the class itself.
318      */

319     private Object JavaDoc createSingletonProxy()
320     {
321         if (LOG.isDebugEnabled())
322             LOG.debug("Creating LazyConstructionProxy for configuration "
323                     + getExtensionPointId());
324
325         try
326         {
327
328             // Create the outer proxy, the one visible to client code (including
329
// other services). It is dependent on an inner proxy.
330

331             Class JavaDoc proxyClass = getSingletonProxyClass();
332
333             // Create the inner proxy, whose job is to replace itself
334
// when the first service method is invoked.
335

336             Class JavaDoc innerProxyClass = getInnerProxyClass(proxyClass);
337
338             // Create the outer proxy.
339

340             Constructor JavaDoc co = proxyClass.getConstructor(new Class JavaDoc[]
341             { String JavaDoc.class });
342
343             Object JavaDoc result = co.newInstance(new Object JavaDoc[] { getExtensionPointId() });
344
345             // The inner proxy's construct invokes a method on the
346
// outer proxy to connect the two.
347

348             Constructor JavaDoc ci = innerProxyClass.getConstructor(new Class JavaDoc[]
349             { String JavaDoc.class, proxyClass, getClass() });
350
351             ci.newInstance(new Object JavaDoc[] { getExtensionPointId(), result, this });
352
353             return result;
354         }
355         catch (Exception JavaDoc ex)
356         {
357             throw new ApplicationRuntimeException(ex);
358         }
359
360     }
361     
362     private final static Map JavaDoc SINGLETON_PROXY_CACHE = new HashMap JavaDoc();
363     private final static Map JavaDoc INNER_PROXY_CACHE = new HashMap JavaDoc();
364     
365     private Class JavaDoc getSingletonProxyClass()
366     {
367         Class JavaDoc configurationInterface = getConfigurationType();
368         Class JavaDoc result = (Class JavaDoc) SINGLETON_PROXY_CACHE.get(configurationInterface);
369         if (result == null) {
370           result = createSingletonProxyClass();
371           SINGLETON_PROXY_CACHE.put(configurationInterface, result);
372         }
373         return result;
374     }
375     
376     private Class JavaDoc getInnerProxyClass(Class JavaDoc deferredProxyClass)
377     {
378         Class JavaDoc result = (Class JavaDoc) INNER_PROXY_CACHE.get(deferredProxyClass);
379         if (result == null) {
380           result = createInnerProxyClass(deferredProxyClass);
381           INNER_PROXY_CACHE.put(deferredProxyClass, result);
382         }
383         return result;
384     }
385
386     /**
387      * Creates a class that implements the service interface. Implements a private synchronized
388      * method, _configuration(), that constructs the service as needed, and has each service interface
389      * method re-invoke on _configuration(). Adds a toString() method if the service interface does not
390      * define toString().
391      */

392     private Class JavaDoc createSingletonProxyClass()
393     {
394         ProxyBuilder proxyBuilder = new ProxyBuilder("LazyConstructionProxy", getModule(), getConfigurationType(),
395                 getConfigurationType(), true);
396
397         ClassFab classFab = proxyBuilder.getClassFab();
398
399
400         // This will initally be the inner proxy, then switch over to the
401
// service implementation.
402

403         classFab.addField("_inner", getConfigurationType());
404         classFab.addMethod(
405                 Modifier.PUBLIC | Modifier.SYNCHRONIZED | Modifier.FINAL,
406                 new MethodSignature(void.class, "_setInner", new Class JavaDoc[]
407                 { getConfigurationType() }, null),
408                 "{ _inner = $1; }");
409
410         BodyBuilder builder = new BodyBuilder();
411         builder.begin();
412
413         builder.addln("return _inner;");
414         builder.end();
415
416         classFab.addMethod(Modifier.PRIVATE, new MethodSignature(getConfigurationType(), "_getInner",
417                 null, null), builder.toString());
418
419         proxyBuilder.addServiceMethods("_getInner()", false);
420
421         // The toString calls the toString method of the configuration if it is
422
// created already
423
// TODO: Implement like described
424
// String proxyToStringMessage = "<LazyConstructionProxy for "
425
// + getExtensionPointId() + "(" + configurationInterface.getName() + ")>";
426
builder.clear();
427         builder.begin();
428         builder.addln(" return _inner.toString();");
429         builder.end();
430
431         MethodSignature toStringSignature = new MethodSignature(String JavaDoc.class, "toString", null,
432                 null);
433         if (!classFab.containsMethod(toStringSignature)) {
434             classFab.addMethod(Modifier.PUBLIC, toStringSignature, builder.toString());
435         }
436
437         return classFab.createClass();
438     }
439
440     private Class JavaDoc createInnerProxyClass(Class JavaDoc deferredProxyClass)
441     {
442         ProxyBuilder builder = new ProxyBuilder("InnerProxy", getModule(), getConfigurationType(),
443                 getConfigurationType(), false);
444
445         ClassFab classFab = builder.getClassFab();
446
447         classFab.addField("_deferredProxy", deferredProxyClass);
448         classFab.addField("_configuration", getConfigurationType());
449         classFab.addField("_configurationPoint", ConfigurationPointImpl.class);
450
451         BodyBuilder body = new BodyBuilder();
452
453         // The constructor remembers the outer proxy and registers itself
454
// with the outer proxy.
455

456         body.begin();
457
458         body.addln("this($1);");
459         body.addln("_deferredProxy = $2;");
460         body.addln("_configurationPoint = $3;");
461         body.addln("_deferredProxy._setInner(this);");
462
463         body.end();
464
465         classFab.addConstructor(new Class JavaDoc[]
466         { String JavaDoc.class, deferredProxyClass, ConfigurationPointImpl.class }, null, body.toString());
467
468         // Method _configuration() will look up the configuration,
469
// then update the deferred proxy to go directly to the
470
// configuration, bypassing itself!
471

472         body.clear();
473         body.begin();
474
475         body.add("if (_configuration == null)");
476         body.begin();
477
478         body.add("_configuration = (");
479         body.add(getConfigurationType().getName());
480         body.addln(") _configurationPoint.constructConfiguration();");
481
482         body.add("_deferredProxy._setInner(_configuration);");
483
484         body.end();
485
486         body.add("return _configuration;");
487
488         body.end();
489
490         classFab.addMethod(
491                 Modifier.PRIVATE | Modifier.FINAL | Modifier.SYNCHRONIZED,
492                 new MethodSignature(getConfigurationType(), "_configuration", null, null),
493                 body.toString());
494
495         builder.addServiceMethods("_configuration()");
496
497         // Build the implementation of interface SingletonInnerProxy
498

499         body.clear();
500         body.begin();
501
502         body.add("_configuration();");
503
504         body.end();
505
506         classFab.addMethod(Modifier.PUBLIC | Modifier.FINAL, new MethodSignature(void.class,
507                 "_instantiateServiceImplementation", null, null), body.toString());
508
509         classFab.addInterface(SingletonInnerProxy.class);
510
511         return classFab.createClass();
512     }
513
514 }
Popular Tags