001 // Copyright 2004, 2005 The Apache Software Foundation
002 //
003 // Licensed under the Apache License, Version 2.0 (the "License");
004 // you may not use this file except in compliance with the License.
005 // You may obtain a copy of the License at
006 //
007 // http://www.apache.org/licenses/LICENSE-2.0
008 //
009 // Unless required by applicable law or agreed to in writing, software
010 // distributed under the License is distributed on an "AS IS" BASIS,
011 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012 // See the License for the specific language governing permissions and
013 // limitations under the License.
014
015 package org.apache.hivemind.impl;
016
017 import java.util.Collection;
018 import java.util.HashMap;
019 import java.util.Iterator;
020 import java.util.List;
021 import java.util.Locale;
022 import java.util.Map;
023
024 import org.apache.commons.logging.Log;
025 import org.apache.hivemind.ErrorHandler;
026 import org.apache.hivemind.Location;
027 import org.apache.hivemind.Occurances;
028 import org.apache.hivemind.ShutdownCoordinator;
029 import org.apache.hivemind.conditional.EvaluationContextImpl;
030 import org.apache.hivemind.conditional.Node;
031 import org.apache.hivemind.conditional.Parser;
032 import org.apache.hivemind.internal.ConfigurationPoint;
033 import org.apache.hivemind.internal.Module;
034 import org.apache.hivemind.internal.RegistryInfrastructure;
035 import org.apache.hivemind.internal.ServicePoint;
036 import org.apache.hivemind.parse.ConfigurationPointDescriptor;
037 import org.apache.hivemind.parse.ContributionDescriptor;
038 import org.apache.hivemind.parse.DependencyDescriptor;
039 import org.apache.hivemind.parse.ImplementationDescriptor;
040 import org.apache.hivemind.parse.InstanceBuilder;
041 import org.apache.hivemind.parse.InterceptorDescriptor;
042 import org.apache.hivemind.parse.ModuleDescriptor;
043 import org.apache.hivemind.parse.ServicePointDescriptor;
044 import org.apache.hivemind.schema.Schema;
045 import org.apache.hivemind.schema.impl.SchemaImpl;
046 import org.apache.hivemind.util.IdUtils;
047
048 /**
049 * Fed a series of {@link org.apache.hivemind.parse.ModuleDescriptor}s, this class will assemble
050 * them into a final {@link org.apache.hivemind.internal.RegistryInfrastructure} as well as perform
051 * some validations.
052 * <p>
053 * This class was extracted from {@link org.apache.hivemind.impl.RegistryBuilder}.
054 *
055 * @author Howard M. Lewis Ship
056 * @since 1.1
057 */
058 public class RegistryInfrastructureConstructor
059 {
060 private ErrorHandler _errorHandler;
061
062 private Log _log;
063
064 private RegistryAssembly _assembly;
065
066 /** @since 1.1 */
067
068 private Parser _conditionalExpressionParser;
069
070 public RegistryInfrastructureConstructor(ErrorHandler errorHandler, Log log,
071 RegistryAssembly assembly)
072 {
073 _errorHandler = errorHandler;
074 _log = log;
075 _assembly = assembly;
076 }
077
078 /**
079 * Map of {@link ModuleDescriptor} keyed on module id.
080 */
081
082 private Map _moduleDescriptors = new HashMap();
083
084 /**
085 * Map of {@link ModuleImpl} keyed on module id.
086 */
087 private Map _modules = new HashMap();
088
089 /**
090 * Map of {@link Schema} keyed on fully qualified module id.
091 */
092 private Map _schemas = new HashMap();
093
094 /**
095 * Map of {@link ServicePointImpl} keyed on fully qualified id.
096 */
097
098 private Map _servicePoints = new HashMap();
099
100 /**
101 * Map of {@link ConfigurationPointImpl} keyed on fully qualified id.
102 */
103
104 private Map _configurationPoints = new HashMap();
105
106 /**
107 * Shutdown coordinator shared by all objects.
108 */
109
110 private ShutdownCoordinator _shutdownCoordinator = new ShutdownCoordinatorImpl();
111
112 /**
113 * This class is used to check the dependencies of a ModuleDescriptor. As the checker is run it
114 * will log errors to the ErrorHandler if dependencies don't resolve or the versions dont match.
115 */
116 private class ModuleDependencyChecker implements Runnable
117 {
118 private ModuleDescriptor _source;
119
120 public ModuleDependencyChecker(ModuleDescriptor source)
121 {
122 _source = source;
123 }
124
125 public void run()
126 {
127 List dependencies = _source.getDependencies();
128 int count = size(dependencies);
129
130 for (int i = 0; i < count; i++)
131 {
132 DependencyDescriptor dependency = (DependencyDescriptor) dependencies.get(i);
133 checkDependency(dependency);
134 }
135 }
136
137 private void checkDependency(DependencyDescriptor dependency)
138 {
139 ModuleDescriptor requiredModule = (ModuleDescriptor) _moduleDescriptors.get(dependency
140 .getModuleId());
141
142 if (requiredModule == null)
143 {
144 _errorHandler.error(
145 _log,
146 ImplMessages.dependencyOnUnknownModule(dependency),
147 dependency.getLocation(),
148 null);
149 return;
150 }
151
152 if (dependency.getVersion() != null
153 && !dependency.getVersion().equals(requiredModule.getVersion()))
154 {
155 _errorHandler.error(
156 _log,
157 ImplMessages.dependencyVersionMismatch(dependency),
158 dependency.getLocation(),
159 null);
160 return;
161 }
162 }
163 }
164
165 /**
166 * Constructs the registry infrastructure, based on data collected during the prior calls to
167 * {@link #addModuleDescriptor(ModuleDescriptor)}. Expects that all post-processing of the
168 * {@link RegistryAssembly} has already occured.
169 */
170 public RegistryInfrastructure constructRegistryInfrastructure(Locale locale)
171 {
172 RegistryInfrastructureImpl result = new RegistryInfrastructureImpl(_errorHandler, locale);
173
174 addServiceAndConfigurationPoints(result);
175
176 addImplementationsAndContributions();
177
178 checkForMissingServices();
179
180 checkContributionCounts();
181
182 result.setShutdownCoordinator(_shutdownCoordinator);
183
184 addModulesToRegistry(result);
185
186 // The caller is responsible for invoking startup().
187
188 return result;
189 }
190
191 public void addModuleDescriptor(ModuleDescriptor md)
192 {
193 String id = md.getModuleId();
194
195 if (_log.isDebugEnabled())
196 _log.debug("Processing module " + id);
197
198 if (_modules.containsKey(id))
199 {
200 Module existing = (Module) _modules.get(id);
201
202 _errorHandler.error(_log, ImplMessages.duplicateModuleId(id, existing.getLocation(), md
203 .getLocation()), null, null);
204
205 // Ignore the duplicate module descriptor.
206 return;
207 }
208
209 ModuleImpl module = new ModuleImpl();
210
211 module.setLocation(md.getLocation());
212 module.setModuleId(id);
213 module.setPackageName(md.getPackageName());
214 module.setClassResolver(md.getClassResolver());
215
216 if (size(md.getDependencies()) > 0)
217 _assembly.addPostProcessor(new ModuleDependencyChecker(md));
218
219 for (Iterator schemas = md.getSchemas().iterator(); schemas.hasNext();)
220 {
221 SchemaImpl schema = (SchemaImpl) schemas.next();
222
223 schema.setModule(module);
224
225 _schemas.put(IdUtils.qualify(id, schema.getId()), schema);
226 }
227
228 _modules.put(id, module);
229
230 _moduleDescriptors.put(id, md);
231 }
232
233 private void addServiceAndConfigurationPoints(RegistryInfrastructureImpl infrastructure)
234 {
235 for (Iterator i = _moduleDescriptors.values().iterator(); i.hasNext();)
236 {
237 ModuleDescriptor md = (ModuleDescriptor) i.next();
238
239 String id = md.getModuleId();
240
241 ModuleImpl module = (ModuleImpl) _modules.get(id);
242
243 addServicePoints(infrastructure, module, md);
244
245 addConfigurationPoints(infrastructure, module, md);
246 }
247 }
248
249 private void addServicePoints(RegistryInfrastructureImpl infrastructure, Module module,
250 ModuleDescriptor md)
251 {
252 String moduleId = md.getModuleId();
253 List services = md.getServicePoints();
254 int count = size(services);
255
256 for (int i = 0; i < count; i++)
257 {
258 ServicePointDescriptor sd = (ServicePointDescriptor) services.get(i);
259
260 String pointId = moduleId + "." + sd.getId();
261
262 ServicePoint existingPoint = (ServicePoint) _servicePoints.get(pointId);
263
264 if (existingPoint != null)
265 {
266 _errorHandler.error(_log, ImplMessages.duplicateExtensionPointId(
267 pointId,
268 existingPoint), sd.getLocation(), null);
269 continue;
270 }
271
272 if (_log.isDebugEnabled())
273 _log.debug("Creating service point " + pointId);
274
275 // Choose which class to instantiate based on
276 // whether the service is create-on-first-reference
277 // or create-on-first-use (deferred).
278
279 ServicePointImpl point = new ServicePointImpl();
280
281 point.setExtensionPointId(pointId);
282 point.setLocation(sd.getLocation());
283 point.setModule(module);
284
285 point.setServiceInterfaceName(sd.getInterfaceClassName());
286
287 point.setParametersSchema(findSchema(sd.getParametersSchema(), module, sd
288 .getParametersSchemaId(), point.getLocation()));
289
290 point.setParametersCount(sd.getParametersCount());
291 point.setVisibility(sd.getVisibility());
292
293 point.setShutdownCoordinator(_shutdownCoordinator);
294
295 infrastructure.addServicePoint(point);
296
297 // Save this for the second phase, where contributions
298 // from other modules are applied.
299
300 _servicePoints.put(pointId, point);
301
302 addInternalImplementations(module, pointId, sd);
303 }
304 }
305
306 private void addConfigurationPoints(RegistryInfrastructureImpl registry, Module module,
307 ModuleDescriptor md)
308 {
309 String moduleId = md.getModuleId();
310 List points = md.getConfigurationPoints();
311 int count = size(points);
312
313 for (int i = 0; i < count; i++)
314 {
315 ConfigurationPointDescriptor cpd = (ConfigurationPointDescriptor) points.get(i);
316
317 String pointId = moduleId + "." + cpd.getId();
318
319 ConfigurationPoint existingPoint = (ConfigurationPoint) _configurationPoints
320 .get(pointId);
321
322 if (existingPoint != null)
323 {
324 _errorHandler.error(_log, ImplMessages.duplicateExtensionPointId(
325 pointId,
326 existingPoint), cpd.getLocation(), null);
327 continue;
328 }
329
330 if (_log.isDebugEnabled())
331 _log.debug("Creating configuration point " + pointId);
332
333 ConfigurationPointImpl point = new ConfigurationPointImpl();
334
335 point.setExtensionPointId(pointId);
336 point.setLocation(cpd.getLocation());
337 point.setModule(module);
338 point.setExpectedCount(cpd.getCount());
339
340 point.setContributionsSchema(findSchema(cpd.getContributionsSchema(), module, cpd
341 .getContributionsSchemaId(), cpd.getLocation()));
342
343 point.setVisibility(cpd.getVisibility());
344
345 point.setShutdownCoordinator(_shutdownCoordinator);
346
347 registry.addConfigurationPoint(point);
348
349 // Needed later when we reconcile the rest
350 // of the configuration contributions.
351
352 _configurationPoints.put(pointId, point);
353 }
354 }
355
356 private void addContributionElements(Module sourceModule, ConfigurationPointImpl point,
357 List elements)
358 {
359 if (size(elements) == 0)
360 return;
361
362 if (_log.isDebugEnabled())
363 _log
364 .debug("Adding contributions to configuration point "
365 + point.getExtensionPointId());
366
367 ContributionImpl c = new ContributionImpl();
368 c.setContributingModule(sourceModule);
369 c.addElements(elements);
370
371 point.addContribution(c);
372 }
373
374 private void addModulesToRegistry(RegistryInfrastructureImpl registry)
375 {
376 // Add each module to the registry.
377
378 Iterator i = _modules.values().iterator();
379 while (i.hasNext())
380 {
381 ModuleImpl module = (ModuleImpl) i.next();
382
383 if (_log.isDebugEnabled())
384 _log.debug("Adding module " + module.getModuleId() + " to registry");
385
386 module.setRegistry(registry);
387 }
388 }
389
390 private void addImplementationsAndContributions()
391 {
392 for (Iterator i = _moduleDescriptors.values().iterator(); i.hasNext();)
393 {
394 ModuleDescriptor md = (ModuleDescriptor) i.next();
395
396 if (_log.isDebugEnabled())
397 _log.debug("Adding contributions from module " + md.getModuleId());
398
399 addImplementations(md);
400 addContributions(md);
401 }
402 }
403
404 private void addImplementations(ModuleDescriptor md)
405 {
406 String moduleId = md.getModuleId();
407 Module sourceModule = (Module) _modules.get(moduleId);
408
409 List implementations = md.getImplementations();
410 int count = size(implementations);
411
412 for (int i = 0; i < count; i++)
413 {
414 ImplementationDescriptor impl = (ImplementationDescriptor) implementations.get(i);
415
416 if (!includeContribution(impl.getConditionalExpression(), sourceModule, impl
417 .getLocation()))
418 continue;
419
420 String pointId = impl.getServiceId();
421 String qualifiedId = IdUtils.qualify(moduleId, pointId);
422
423 addImplementations(sourceModule, qualifiedId, impl);
424 }
425
426 }
427
428 private void addContributions(ModuleDescriptor md)
429 {
430 String moduleId = md.getModuleId();
431 Module sourceModule = (Module) _modules.get(moduleId);
432
433 List contributions = md.getContributions();
434 int count = size(contributions);
435
436 for (int i = 0; i < count; i++)
437 {
438 ContributionDescriptor cd = (ContributionDescriptor) contributions.get(i);
439
440 if (!includeContribution(cd.getConditionalExpression(), sourceModule, cd.getLocation()))
441 continue;
442
443 String pointId = cd.getConfigurationId();
444 String qualifiedId = IdUtils.qualify(moduleId, pointId);
445
446 ConfigurationPointImpl point = (ConfigurationPointImpl) _configurationPoints
447 .get(qualifiedId);
448
449 if (point == null)
450 {
451 _errorHandler.error(_log, ImplMessages.unknownConfigurationPoint(moduleId, cd), cd
452 .getLocation(), null);
453
454 continue;
455 }
456
457 if (!point.visibleToModule(sourceModule))
458 {
459 _errorHandler.error(_log, ImplMessages.configurationPointNotVisible(
460 point,
461 sourceModule), cd.getLocation(), null);
462 continue;
463 }
464
465 addContributionElements(sourceModule, point, cd.getElements());
466 }
467 }
468
469 private Schema findSchema(SchemaImpl schema, Module module, String schemaId, Location location)
470 {
471 if (schema != null)
472 {
473 schema.setModule(module);
474 return schema;
475 }
476
477 if (schemaId == null)
478 return null;
479
480 String moduleId = module.getModuleId();
481 String qualifiedId = IdUtils.qualify(moduleId, schemaId);
482
483 return getSchema(qualifiedId, moduleId, location);
484 }
485
486 private Schema getSchema(String schemaId, String referencingModule, Location reference)
487 {
488 Schema schema = (Schema) _schemas.get(schemaId);
489
490 if (schema == null)
491 _errorHandler
492 .error(_log, ImplMessages.unableToResolveSchema(schemaId), reference, null);
493 else if (!schema.visibleToModule(referencingModule))
494 {
495 _errorHandler.error(
496 _log,
497 ImplMessages.schemaNotVisible(schemaId, referencingModule),
498 reference,
499 null);
500 schema = null;
501 }
502
503 return schema;
504 }
505
506 /**
507 * Adds internal service contributions; the contributions provided inplace with the service
508 * definition.
509 */
510 private void addInternalImplementations(Module sourceModule, String pointId,
511 ServicePointDescriptor spd)
512 {
513 InstanceBuilder builder = spd.getInstanceBuilder();
514 List interceptors = spd.getInterceptors();
515
516 if (builder == null && interceptors == null)
517 return;
518
519 if (builder != null)
520 addServiceInstanceBuilder(sourceModule, pointId, builder, true);
521
522 if (interceptors == null)
523 return;
524
525 int count = size(interceptors);
526
527 for (int i = 0; i < count; i++)
528 {
529 InterceptorDescriptor id = (InterceptorDescriptor) interceptors.get(i);
530 addInterceptor(sourceModule, pointId, id);
531 }
532 }
533
534 /**
535 * Adds ordinary service contributions.
536 */
537
538 private void addImplementations(Module sourceModule, String pointId, ImplementationDescriptor id)
539 {
540 InstanceBuilder builder = id.getInstanceBuilder();
541 List interceptors = id.getInterceptors();
542
543 if (builder != null)
544 addServiceInstanceBuilder(sourceModule, pointId, builder, false);
545
546 int count = size(interceptors);
547 for (int i = 0; i < count; i++)
548 {
549 InterceptorDescriptor ind = (InterceptorDescriptor) interceptors.get(i);
550
551 addInterceptor(sourceModule, pointId, ind);
552 }
553 }
554
555 /**
556 * Adds an {@link InstanceBuilder} to a service extension point.
557 */
558 private void addServiceInstanceBuilder(Module sourceModule, String pointId,
559 InstanceBuilder builder, boolean isDefault)
560 {
561 if (_log.isDebugEnabled())
562 _log.debug("Adding " + builder + " to service extension point " + pointId);
563
564 ServicePointImpl point = (ServicePointImpl) _servicePoints.get(pointId);
565
566 if (point == null)
567 {
568 _errorHandler.error(
569 _log,
570 ImplMessages.unknownServicePoint(sourceModule, pointId),
571 builder.getLocation(),
572 null);
573 return;
574 }
575
576 if (!point.visibleToModule(sourceModule))
577 {
578 _errorHandler.error(
579 _log,
580 ImplMessages.servicePointNotVisible(point, sourceModule),
581 builder.getLocation(),
582 null);
583 return;
584 }
585
586 if (point.getServiceConstructor(isDefault) != null)
587 {
588 _errorHandler.error(
589 _log,
590 ImplMessages.duplicateFactory(sourceModule, pointId, point),
591 builder.getLocation(),
592 null);
593
594 return;
595 }
596
597 point.setServiceModel(builder.getServiceModel());
598 point.setServiceConstructor(builder.createConstructor(point, sourceModule), isDefault);
599 }
600
601 private void addInterceptor(Module sourceModule, String pointId, InterceptorDescriptor id)
602 {
603 if (_log.isDebugEnabled())
604 _log.debug("Adding " + id + " to service extension point " + pointId);
605
606 ServicePointImpl point = (ServicePointImpl) _servicePoints.get(pointId);
607
608 String sourceModuleId = sourceModule.getModuleId();
609
610 if (point == null)
611 {
612 _errorHandler.error(_log, ImplMessages.unknownServicePoint(sourceModule, pointId), id
613 .getLocation(), null);
614
615 return;
616 }
617
618 if (!point.visibleToModule(sourceModule))
619 {
620 _errorHandler.error(_log, ImplMessages.servicePointNotVisible(point, sourceModule), id
621 .getLocation(), null);
622 return;
623 }
624
625 ServiceInterceptorContributionImpl sic = new ServiceInterceptorContributionImpl();
626
627 // Allow the factory id to be unqualified, to refer to an interceptor factory
628 // service from within the same module.
629
630 sic.setFactoryServiceId(IdUtils.qualify(sourceModuleId, id.getFactoryServiceId()));
631 sic.setLocation(id.getLocation());
632
633 sic.setFollowingInterceptorIds(IdUtils.qualifyList(sourceModuleId, id.getBefore()));
634 sic.setPrecedingInterceptorIds(IdUtils.qualifyList(sourceModuleId, id.getAfter()));
635 sic.setName(id.getName() != null ? IdUtils.qualify(sourceModuleId, id.getName()) : null);
636 sic.setContributingModule(sourceModule);
637 sic.setParameters(id.getParameters());
638
639 point.addInterceptorContribution(sic);
640 }
641
642 /**
643 * Checks that each service has at service constructor.
644 */
645 private void checkForMissingServices()
646 {
647 Iterator i = _servicePoints.values().iterator();
648 while (i.hasNext())
649 {
650 ServicePointImpl point = (ServicePointImpl) i.next();
651
652 if (point.getServiceConstructor() != null)
653 continue;
654
655 _errorHandler.error(_log, ImplMessages.missingService(point), null, null);
656 }
657 }
658
659 /**
660 * Checks that each configuration extension point has the right number of contributions.
661 */
662
663 private void checkContributionCounts()
664 {
665 Iterator i = _configurationPoints.values().iterator();
666
667 while (i.hasNext())
668 {
669 ConfigurationPointImpl point = (ConfigurationPointImpl) i.next();
670
671 Occurances expected = point.getExpectedCount();
672
673 int actual = point.getContributionCount();
674
675 if (expected.inRange(actual))
676 continue;
677
678 _errorHandler.error(_log, ImplMessages.wrongNumberOfContributions(
679 point,
680 actual,
681 expected), point.getLocation(), null);
682 }
683
684 }
685
686 /**
687 * Filters a contribution based on an expression. Returns true if the expression is null, or
688 * evaluates to true. Returns false if the expression if non-null and evaluates to false, or an
689 * exception occurs evaluating the expression.
690 *
691 * @param expression
692 * to parse and evaluate
693 * @param location
694 * of the expression (used if an error is reported)
695 * @since 1.1
696 */
697
698 private boolean includeContribution(String expression, Module module, Location location)
699 {
700 if (expression == null)
701 return true;
702
703 if (_conditionalExpressionParser == null)
704 _conditionalExpressionParser = new Parser();
705
706 try
707 {
708 Node node = _conditionalExpressionParser.parse(expression);
709
710 return node.evaluate(new EvaluationContextImpl(module.getClassResolver()));
711 }
712 catch (RuntimeException ex)
713 {
714 _errorHandler.error(_log, ex.getMessage(), location, ex);
715
716 return false;
717 }
718 }
719
720 private static int size(Collection c)
721 {
722 return c == null ? 0 : c.size();
723 }
724 }