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 }