001// Licensed under the Apache License, Version 2.0 (the "License"); 002// you may not use this file except in compliance with the License. 003// You may obtain a copy of the License at 004// 005// http://www.apache.org/licenses/LICENSE-2.0 006// 007// Unless required by applicable law or agreed to in writing, software 008// distributed under the License is distributed on an "AS IS" BASIS, 009// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 010// See the License for the specific language governing permissions and 011// limitations under the License. 012 013package org.apache.tapestry5.internal.services; 014 015import java.lang.reflect.Constructor; 016import java.lang.reflect.Method; 017import java.util.Collections; 018import java.util.HashSet; 019import java.util.Iterator; 020import java.util.List; 021import java.util.Map; 022import java.util.Map.Entry; 023import java.util.Objects; 024import java.util.Set; 025import java.util.stream.Collectors; 026 027import org.apache.tapestry5.ComponentResources; 028import org.apache.tapestry5.SymbolConstants; 029import org.apache.tapestry5.beanmodel.services.PlasticProxyFactoryImpl; 030import org.apache.tapestry5.commons.Location; 031import org.apache.tapestry5.commons.ObjectCreator; 032import org.apache.tapestry5.commons.Resource; 033import org.apache.tapestry5.commons.services.PlasticProxyFactory; 034import org.apache.tapestry5.commons.util.CollectionFactory; 035import org.apache.tapestry5.commons.util.ExceptionUtils; 036import org.apache.tapestry5.http.TapestryHttpSymbolConstants; 037import org.apache.tapestry5.internal.InternalComponentResources; 038import org.apache.tapestry5.internal.InternalConstants; 039import org.apache.tapestry5.internal.model.MutableComponentModelImpl; 040import org.apache.tapestry5.internal.plastic.PlasticInternalUtils; 041import org.apache.tapestry5.internal.services.ComponentDependencyRegistry.DependencyType; 042import org.apache.tapestry5.ioc.Invokable; 043import org.apache.tapestry5.ioc.LoggerSource; 044import org.apache.tapestry5.ioc.OperationTracker; 045import org.apache.tapestry5.ioc.annotations.PostInjection; 046import org.apache.tapestry5.ioc.annotations.Primary; 047import org.apache.tapestry5.ioc.annotations.Symbol; 048import org.apache.tapestry5.ioc.internal.util.ClasspathResource; 049import org.apache.tapestry5.ioc.internal.util.InternalUtils; 050import org.apache.tapestry5.ioc.internal.util.URLChangeTracker; 051import org.apache.tapestry5.ioc.services.Builtin; 052import org.apache.tapestry5.ioc.services.ClasspathURLConverter; 053import org.apache.tapestry5.ioc.services.UpdateListener; 054import org.apache.tapestry5.ioc.services.UpdateListenerHub; 055import org.apache.tapestry5.model.ComponentModel; 056import org.apache.tapestry5.model.MutableComponentModel; 057import org.apache.tapestry5.plastic.ClassInstantiator; 058import org.apache.tapestry5.plastic.ConstructorCallback; 059import org.apache.tapestry5.plastic.InstanceContext; 060import org.apache.tapestry5.plastic.InstructionBuilder; 061import org.apache.tapestry5.plastic.InstructionBuilderCallback; 062import org.apache.tapestry5.plastic.MethodAdvice; 063import org.apache.tapestry5.plastic.MethodDescription; 064import org.apache.tapestry5.plastic.MethodInvocation; 065import org.apache.tapestry5.plastic.PlasticClass; 066import org.apache.tapestry5.plastic.PlasticClassEvent; 067import org.apache.tapestry5.plastic.PlasticClassListener; 068import org.apache.tapestry5.plastic.PlasticClassTransformation; 069import org.apache.tapestry5.plastic.PlasticClassTransformer; 070import org.apache.tapestry5.plastic.PlasticField; 071import org.apache.tapestry5.plastic.PlasticManager; 072import org.apache.tapestry5.plastic.PlasticManager.PlasticManagerBuilder; 073import org.apache.tapestry5.plastic.PlasticManagerDelegate; 074import org.apache.tapestry5.plastic.PlasticMethod; 075import org.apache.tapestry5.plastic.PlasticUtils; 076import org.apache.tapestry5.plastic.TransformationOption; 077import org.apache.tapestry5.runtime.Component; 078import org.apache.tapestry5.runtime.ComponentEvent; 079import org.apache.tapestry5.runtime.ComponentResourcesAware; 080import org.apache.tapestry5.runtime.PageLifecycleListener; 081import org.apache.tapestry5.services.ComponentClassResolver; 082import org.apache.tapestry5.services.ComponentEventHandler; 083import org.apache.tapestry5.services.TransformConstants; 084import org.apache.tapestry5.services.pageload.PageClassLoaderContext; 085import org.apache.tapestry5.services.pageload.PageClassLoaderContextManager; 086import org.apache.tapestry5.services.transform.ComponentClassTransformWorker2; 087import org.apache.tapestry5.services.transform.ControlledPackageType; 088import org.apache.tapestry5.services.transform.TransformationSupport; 089import org.slf4j.Logger; 090 091/** 092 * A wrapper around a {@link PlasticManager} that allows certain classes to be modified as they are loaded. 093 */ 094public final class ComponentInstantiatorSourceImpl implements ComponentInstantiatorSource, UpdateListener, 095 Runnable, PlasticManagerDelegate, PlasticClassListener 096{ 097 private final Set<String> controlledPackageNames = CollectionFactory.newSet(); 098 099 private final URLChangeTracker<ClassName> changeTracker; 100 101 private final ClassLoader parent; 102 103 private final ComponentClassTransformWorker2 transformerChain; 104 105 private final LoggerSource loggerSource; 106 107 private final Logger logger; 108 109 private final OperationTracker tracker; 110 111 private final InternalComponentInvalidationEventHub invalidationHub; 112 113 private final boolean productionMode; 114 115 private final boolean multipleClassLoaders; 116 117 private final ComponentClassResolver resolver; 118 119 private final PageClassLoaderContextManager pageClassLoaderContextManager; 120 121 private PageClassLoaderContext rootPageClassloaderContext; 122 123 private PlasticProxyFactoryProxy plasticProxyFactoryProxy; 124 125 private ComponentDependencyRegistry componentDependencyRegistry; 126 127 private static final ThreadLocal<String> CURRENT_PAGE = ThreadLocal.withInitial(() -> null); 128 129 /** 130 * Map from class name to Instantiator. 131 */ 132 private final Map<String, Instantiator> classToInstantiator = CollectionFactory.newConcurrentMap(); 133 134 private final Map<String, ComponentModel> classToModel = CollectionFactory.newMap(); 135 136 private final MethodDescription GET_COMPONENT_RESOURCES = PlasticUtils.getMethodDescription( 137 ComponentResourcesAware.class, "getComponentResources"); 138 139 private final ConstructorCallback REGISTER_AS_PAGE_LIFECYCLE_LISTENER = new ConstructorCallback() 140 { 141 public void onConstruct(Object instance, InstanceContext context) 142 { 143 InternalComponentResources resources = context.get(InternalComponentResources.class); 144 145 resources.addPageLifecycleListener((PageLifecycleListener) instance); 146 } 147 }; 148 149 public ComponentInstantiatorSourceImpl(Logger logger, 150 151 LoggerSource loggerSource, 152 153 @Builtin 154 PlasticProxyFactory proxyFactory, 155 156 @Primary 157 ComponentClassTransformWorker2 transformerChain, 158 159 ClasspathURLConverter classpathURLConverter, 160 161 OperationTracker tracker, 162 163 Map<String, ControlledPackageType> configuration, 164 165 @Symbol(TapestryHttpSymbolConstants.PRODUCTION_MODE) 166 boolean productionMode, 167 168 @Symbol(SymbolConstants.MULTIPLE_CLASSLOADERS) 169 boolean multipleClassLoaders, 170 171 ComponentClassResolver resolver, 172 173 InternalComponentInvalidationEventHub invalidationHub, 174 175 PageClassLoaderContextManager pageClassLoaderContextManager, 176 177 ComponentDependencyRegistry componentDependencyRegistry 178 ) 179 { 180 this.parent = proxyFactory.getClassLoader(); 181 this.transformerChain = transformerChain; 182 this.logger = logger; 183 this.loggerSource = loggerSource; 184 this.changeTracker = new URLChangeTracker<ClassName>(classpathURLConverter); 185 this.tracker = tracker; 186 this.invalidationHub = invalidationHub; 187 this.productionMode = productionMode; 188 this.multipleClassLoaders = multipleClassLoaders && !productionMode; 189 this.resolver = resolver; 190 this.pageClassLoaderContextManager = pageClassLoaderContextManager; 191 this.componentDependencyRegistry = componentDependencyRegistry; 192 193 // For now, we just need the keys of the configuration. When there are more types of controlled 194 // packages, we'll need to do more. 195 196 controlledPackageNames.addAll(configuration.keySet()); 197 198 initializeService(); 199 200 pageClassLoaderContextManager.initialize( 201 rootPageClassloaderContext, 202 ComponentInstantiatorSourceImpl.this::createPlasticProxyFactory); 203 204 } 205 206 @PostInjection 207 public void listenForUpdates(UpdateListenerHub hub) 208 { 209 invalidationHub.addInvalidationCallback(this::invalidate); 210 hub.addUpdateListener(this); 211 } 212 213 public synchronized void checkForUpdates() 214 { 215 final Set<ClassName> changedResources = changeTracker.getChangedResourcesInfo(); 216 if (!changedResources.isEmpty()) 217 { 218 219 final List<String> classNames = changedResources.stream().map(ClassName::getClassName).collect(Collectors.toList()); 220 221 if (logger.isInfoEnabled()) 222 { 223 logger.info("Component class(es) changed: {}", String.join(", ", classNames)); 224 } 225 226 if (multipleClassLoaders) 227 { 228 229 final Set<String> classesToInvalidate = new HashSet<>(); 230 231 for (String className : classNames) 232 { 233 final PageClassLoaderContext context = rootPageClassloaderContext.findByClassName(className); 234 if (context != rootPageClassloaderContext && context != null) 235 { 236 classesToInvalidate.addAll(pageClassLoaderContextManager.invalidate(context)); 237 } 238 } 239 240 classNames.clear(); 241 classNames.addAll(classesToInvalidate); 242 243 invalidate(classNames); 244 245 invalidationHub.fireInvalidationEvent(classNames); 246 } 247 else 248 { 249 invalidationHub.classInControlledPackageHasChanged(); 250 } 251 252 } 253 } 254 255 private List<String> invalidate(final List<String> classNames) { 256 257 if (classNames.isEmpty()) 258 { 259 clearCaches(); 260 } 261 else 262 { 263 264 final String currentPage = CURRENT_PAGE.get(); 265 266 final Iterator<Entry<String, Instantiator>> classToInstantiatorIterator = classToInstantiator.entrySet().iterator(); 267 while (classToInstantiatorIterator.hasNext()) 268 { 269 final String className = classToInstantiatorIterator.next().getKey(); 270 if (!className.equals(currentPage) && classNames.contains(className)) 271 { 272 classToInstantiatorIterator.remove(); 273 } 274 } 275 276 final Iterator<Entry<String, ComponentModel>> classToModelIterator = classToModel.entrySet().iterator(); 277 while (classToModelIterator.hasNext()) 278 { 279 final String className = classToModelIterator.next().getKey(); 280 if (!className.equals(currentPage) && classNames.contains(className)) 281 { 282 classToModelIterator.remove(); 283 } 284 } 285 286 } 287 288 return Collections.emptyList(); 289 } 290 291 public void forceComponentInvalidation() 292 { 293 clearCaches(); 294 invalidationHub.classInControlledPackageHasChanged(); 295 } 296 297 private void clearCaches() 298 { 299 classToInstantiator.clear(); 300 pageClassLoaderContextManager.clear(); 301 } 302 303 public void run() 304 { 305 changeTracker.clear(); 306 classToInstantiator.clear(); 307 classToModel.clear(); 308 pageClassLoaderContextManager.clear(); 309 initializeService(); 310 } 311 312 /** 313 * Invoked at object creation, or when there are updates to class files (i.e., invalidation), to create a new set of 314 * Javassist class pools and loaders. 315 * Since TAP5-2742, this method is only called once. 316 */ 317 private void initializeService() 318 { 319 320 pageClassLoaderContextManager.clear(); 321 322 if (rootPageClassloaderContext == null) 323 { 324 logger.info("Initializing page pool. Production mode " + (productionMode ? "enabled" : "disabled") + 325 ". Multiple classloaders " + (!productionMode && multipleClassLoaders ? "enabled" : "disabled") + "."); 326 327 pageClassLoaderContextManager.clear(); 328 329 PlasticProxyFactory proxyFactory = createPlasticProxyFactory(parent); 330 rootPageClassloaderContext = new PageClassLoaderContext( 331 "root", null, Collections.emptySet(), proxyFactory, pageClassLoaderContextManager::get); 332 } 333 else 334 { 335 logger.info("Restarting page pool"); 336 } 337 338 classToInstantiator.clear(); 339 classToModel.clear(); 340 } 341 342 private PlasticProxyFactory createPlasticProxyFactory(final ClassLoader parentClassloader) 343 { 344 PlasticManagerBuilder builder = PlasticManager.withClassLoader(parentClassloader) 345 .delegate(this) 346 .packages(controlledPackageNames); 347 if (!productionMode) 348 { 349 builder.enable(TransformationOption.FIELD_WRITEBEHIND); 350 } 351 PlasticManager plasticManager = builder.create(); 352 plasticManager.addPlasticClassListener(this); 353 PlasticProxyFactory proxyFactory = new PlasticProxyFactoryImpl(plasticManager, logger); 354 return proxyFactory; 355 } 356 357 public Instantiator getInstantiator(final String className) 358 { 359 Instantiator instantiator; 360 if (multipleClassLoaders) 361 { 362 instantiator = classToInstantiator.get(className); 363 364 if (instantiator == null) 365 { 366 instantiator = createInstantiatorForClass(className); 367 classToInstantiator.put(className, instantiator); 368 } 369 370 } 371 else 372 { 373 instantiator = classToInstantiator.computeIfAbsent(className, this::createInstantiatorForClass); 374 } 375 return instantiator; 376 } 377 378 private static final ThreadLocal<Set<String>> OPEN_INSTANTIATORS = 379 ThreadLocal.withInitial(HashSet::new); 380 381 private Instantiator createInstantiatorForClass(final String className) 382 { 383 return tracker.invoke(String.format("Creating instantiator for component class %s", className), 384 new Invokable<Instantiator>() 385 { 386 public Instantiator invoke() 387 { 388 389 // Force the creation of the class (and the transformation of the class). This will first 390 // trigger transformations of any base classes. 391 392 OPEN_INSTANTIATORS.get().add(className); 393 394 componentDependencyRegistry.disableInvalidations(); 395 396 // Make sure the dependencies have been processed in case 397 // there was some invalidation going on and they're not there. 398 399 if (multipleClassLoaders) 400 { 401 402 final Set<String> dependencies = new HashSet<>(); 403 dependencies.addAll( 404 componentDependencyRegistry.getDependencies(className, DependencyType.USAGE)); 405 dependencies.addAll( 406 componentDependencyRegistry.getDependencies(className, DependencyType.SUPERCLASS)); 407 for (String dependency : dependencies) 408 { 409 if (!OPEN_INSTANTIATORS.get().contains(dependency)) 410 { 411 if (multipleClassLoaders) 412 { 413 getInstantiator(dependency); 414 } 415 else 416 { 417 createInstantiatorForClass(dependency); 418 } 419 } 420 } 421 } 422 423 PageClassLoaderContext context; 424 try 425 { 426 context = pageClassLoaderContextManager.get(className); 427 } 428 finally 429 { 430 componentDependencyRegistry.enableInvalidations(); 431 } 432 433 ClassInstantiator<Component> plasticInstantiator; 434 try 435 { 436 plasticInstantiator = context.getPlasticManager().getClassInstantiator(className); 437 if (multipleClassLoaders) 438 { 439 context.getPlasticManager().getClassLoader().loadClass(className); 440 } 441 } catch (Exception e) { 442// System.out.println(pageClassLoaderContextManager.getRoot().toRecursiveString()); 443 throw new RuntimeException(e); 444 } 445 final ComponentModel model = classToModel.get(className); 446 447 OPEN_INSTANTIATORS.get().remove(className); 448 449 return new Instantiator() 450 { 451 public Component newInstance(InternalComponentResources resources) 452 { 453 return plasticInstantiator.with(ComponentResources.class, resources) 454 .with(InternalComponentResources.class, resources).newInstance(); 455 } 456 457 public ComponentModel getModel() 458 { 459 return model; 460 } 461 462 @Override 463 public String toString() 464 { 465 return String.format("[Instantiator[%s:%s]", className, context); 466 } 467 }; 468 } 469 }); 470 } 471 472 public boolean exists(String className) 473 { 474 return parent.getResource(PlasticInternalUtils.toClassPath(className)) != null; 475 } 476 477 public PlasticProxyFactory getProxyFactory() 478 { 479 if (plasticProxyFactoryProxy == null) 480 { 481 plasticProxyFactoryProxy = new PlasticProxyFactoryProxy(); 482 } 483 return plasticProxyFactoryProxy; 484 } 485 486 public void transform(final PlasticClass plasticClass) 487 { 488 tracker.run(String.format("Running component class transformations on %s", plasticClass.getClassName()), 489 new Runnable() 490 { 491 public void run() 492 { 493 String className = plasticClass.getClassName(); 494 String parentClassName = plasticClass.getSuperClassName(); 495 496 // The parent model may not exist, if the super class is not in a controlled package. 497 498 ComponentModel parentModel = classToModel.get(parentClassName); 499 500 final boolean isRoot = parentModel == null; 501 502 if (isRoot 503 && !(parentClassName.equals("java.lang.Object") || parentClassName 504 .equals("groovy.lang.GroovyObjectSupport"))) 505 { 506 String suggestedPackageName = buildSuggestedPackageName(className); 507 508 throw new RuntimeException(String.format("Base class %s (super class of %s) is not in a controlled package and is therefore not valid. You should try moving the class to package %s.", parentClassName, className, suggestedPackageName)); 509 } 510 511 // Tapestry 5.2 was more sensitive that the parent class have a public no-args constructor. 512 // Plastic 513 // doesn't care, and we don't have the tools to dig that information out. 514 515 Logger logger = loggerSource.getLogger(className); 516 517 Resource baseResource = new ClasspathResource(parent, PlasticInternalUtils 518 .toClassPath(className)); 519 520 changeTracker.add(baseResource.toURL(), new ClassName(className)); 521 522 if (isRoot) 523 { 524 implementComponentInterface(plasticClass); 525 } 526 527 boolean isPage = resolver.isPage(className); 528 529 boolean superClassImplementsPageLifecycle = plasticClass.isInterfaceImplemented(PageLifecycleListener.class); 530 531 String libraryName = resolver.getLibraryNameForClass(className); 532 533 final MutableComponentModel model = new MutableComponentModelImpl(className, logger, baseResource, 534 parentModel, isPage, libraryName); 535 536 TransformationSupportImpl transformationSupport = new TransformationSupportImpl(plasticClass, isRoot, model); 537 538 transformerChain.transform(plasticClass, transformationSupport, model); 539 540 transformationSupport.commit(); 541 542 if (!superClassImplementsPageLifecycle && plasticClass.isInterfaceImplemented(PageLifecycleListener.class)) 543 { 544 plasticClass.onConstruct(REGISTER_AS_PAGE_LIFECYCLE_LISTENER); 545 } 546 547 classToModel.put(className, model); 548 } 549 }); 550 } 551 552 private void implementComponentInterface(PlasticClass plasticClass) 553 { 554 plasticClass.introduceInterface(Component.class); 555 556 final PlasticField resourcesField = plasticClass.introduceField(InternalComponentResources.class, 557 "internalComponentResources").injectFromInstanceContext(); 558 559 plasticClass.introduceMethod(GET_COMPONENT_RESOURCES, new InstructionBuilderCallback() 560 { 561 public void doBuild(InstructionBuilder builder) 562 { 563 builder.loadThis().getField(resourcesField).returnResult(); 564 } 565 }); 566 } 567 568 public <T> ClassInstantiator<T> configureInstantiator(String className, ClassInstantiator<T> instantiator) 569 { 570 return instantiator; 571 } 572 573 private String buildSuggestedPackageName(String className) 574 { 575 for (String subpackage : InternalConstants.SUBPACKAGES) 576 { 577 String term = "." + subpackage + "."; 578 579 int pos = className.indexOf(term); 580 581 // Keep the leading '.' in the subpackage name and tack on "base". 582 583 if (pos > 0) 584 return className.substring(0, pos + 1) + InternalConstants.BASE_SUBPACKAGE; 585 } 586 587 // Is this even reachable? className should always be in a controlled package and so 588 // some subpackage above should have matched. 589 590 return null; 591 } 592 593 public void classWillLoad(PlasticClassEvent event) 594 { 595 Logger logger = loggerSource.getLogger("tapestry.transformer." + event.getPrimaryClassName()); 596 597 if (logger.isDebugEnabled()) 598 logger.debug(event.getDissasembledBytecode()); 599 } 600 601 private class TransformationSupportImpl implements TransformationSupport 602 { 603 private final PlasticClass plasticClass; 604 605 private final boolean root; 606 607 private final MutableComponentModel model; 608 609 private final List<MethodAdvice> eventHandlerAdvice = CollectionFactory.newList(); 610 611 public TransformationSupportImpl(PlasticClass plasticClass, boolean root, MutableComponentModel model) 612 { 613 this.plasticClass = plasticClass; 614 this.root = root; 615 this.model = model; 616 } 617 618 /** 619 * Commits any stored changes to the PlasticClass; this is used to defer adding advice to the dispatch method. 620 */ 621 public void commit() 622 { 623 if (!eventHandlerAdvice.isEmpty()) 624 { 625 PlasticMethod dispatchMethod = plasticClass.introduceMethod(TransformConstants.DISPATCH_COMPONENT_EVENT_DESCRIPTION); 626 for (MethodAdvice advice : eventHandlerAdvice) 627 { 628 dispatchMethod.addAdvice(advice); 629 } 630 } 631 } 632 633 public Class toClass(String typeName) 634 { 635 try 636 { 637 final PageClassLoaderContext context = pageClassLoaderContextManager.get(typeName); 638 return PlasticInternalUtils.toClass(context.getPlasticManager().getClassLoader(), typeName); 639 } catch (ClassNotFoundException ex) 640 { 641 throw new RuntimeException(String.format( 642 "Unable to convert type '%s' to a Class: %s", typeName, 643 ExceptionUtils.toMessage(ex)), ex); 644 } 645 } 646 647 public boolean isRootTransformation() 648 { 649 return root; 650 } 651 652 public void addEventHandler(final String eventType, final int minContextValues, final String operationDescription, final ComponentEventHandler handler) 653 { 654 assert InternalUtils.isNonBlank(eventType); 655 assert minContextValues >= 0; 656 assert handler != null; 657 658 model.addEventHandler(eventType); 659 660 MethodAdvice advice = new EventMethodAdvice(tracker, eventType, minContextValues, operationDescription, handler); 661 662 // The advice is added at the very end, after the logic provided by the OnEventWorker 663 664 eventHandlerAdvice.add(advice); 665 } 666 } 667 668 private static class EventMethodAdvice implements MethodAdvice 669 { 670 final OperationTracker tracker; 671 final String eventType; 672 final int minContextValues; 673 final String operationDescription; 674 final ComponentEventHandler handler; 675 676 public EventMethodAdvice(OperationTracker tracker, String eventType, int minContextValues, String operationDescription, ComponentEventHandler handler) 677 { 678 this.tracker = tracker; 679 this.eventType = eventType; 680 this.minContextValues = minContextValues; 681 this.operationDescription = operationDescription; 682 this.handler = handler; 683 } 684 685 public void advise(final MethodInvocation invocation) 686 { 687 final ComponentEvent event = (ComponentEvent) invocation.getParameter(0); 688 689 boolean matches = !event.isAborted() && event.matches(eventType, "", minContextValues); 690 691 if (matches) 692 { 693 tracker.run(operationDescription, new Runnable() 694 { 695 public void run() 696 { 697 Component instance = (Component) invocation.getInstance(); 698 699 handler.handleEvent(instance, event); 700 } 701 }); 702 } 703 704 // Order of operations is key here. This logic takes precedence; base class event dispatch and event handler methods 705 // in the class come AFTER. 706 707 invocation.proceed(); 708 709 if (matches) 710 { 711 invocation.setReturnValue(true); 712 } 713 } 714 } 715 716 private static class ClassName implements ClassNameHolder 717 { 718 private String className; 719 720 public ClassName(String className) 721 { 722 super(); 723 this.className = className; 724 } 725 726 @Override 727 public String getClassName() 728 { 729 return className; 730 } 731 732 @Override 733 public int hashCode() { 734 return Objects.hash(className); 735 } 736 737 @Override 738 public boolean equals(Object obj) 739 { 740 if (this == obj) { 741 return true; 742 } 743 if (!(obj instanceof ClassName)) { 744 return false; 745 } 746 ClassName other = (ClassName) obj; 747 return Objects.equals(className, other.className); 748 } 749 750 @Override 751 public String toString() 752 { 753 return className; 754 } 755 756 } 757 758 private class PlasticProxyFactoryProxy implements PlasticProxyFactory 759 { 760 761 @Override 762 public void addPlasticClassListener(PlasticClassListener listener) 763 { 764 throw new UnsupportedOperationException(); 765 } 766 767 @Override 768 public void removePlasticClassListener(PlasticClassListener listener) 769 { 770 throw new UnsupportedOperationException(); 771 } 772 773 @Override 774 public ClassLoader getClassLoader() { 775 return rootPageClassloaderContext.getProxyFactory().getClassLoader(); 776 } 777 778 @Override 779 public <T> ClassInstantiator<T> createProxy(Class<T> interfaceType, PlasticClassTransformer callback) 780 { 781 return getProxyFactory(interfaceType.getName()).createProxy(interfaceType, callback); 782 } 783 784 @Override 785 public <T> ClassInstantiator<T> createProxy(Class<T> interfaceType, 786 Class<? extends T> implementationType, 787 PlasticClassTransformer callback, 788 boolean introduceInterface) 789 { 790 throw new UnsupportedOperationException(); 791 } 792 793 @Override 794 public <T> ClassInstantiator<T> createProxy(Class<T> interfaceType, Class<? extends T> implementationType, PlasticClassTransformer callback) 795 { 796 throw new UnsupportedOperationException(); 797 } 798 799 @Override 800 public <T> PlasticClassTransformation<T> createProxyTransformation(Class<T> interfaceType) 801 { 802 throw new UnsupportedOperationException(); 803 } 804 805 @Override 806 public <T> PlasticClassTransformation<T> createProxyTransformation(Class<T> interfaceType, Class<? extends T> implementationType) 807 { 808 throw new UnsupportedOperationException(); 809 } 810 811 @Override 812 public <T> T createProxy(Class<T> interfaceType, ObjectCreator<T> creator, String description) 813 { 814 throw new UnsupportedOperationException(); 815 } 816 817 @Override 818 public <T> T createProxy(Class<T> interfaceType, Class<? extends T> implementationType, ObjectCreator<T> creator, String description) 819 { 820 throw new UnsupportedOperationException(); 821 } 822 823 @Override 824 public Location getMethodLocation(Method method) { 825 return getProxyFactory(method.getDeclaringClass().getName()).getMethodLocation(method); 826 } 827 828 @Override 829 public Location getConstructorLocation(Constructor constructor) 830 { 831 return getProxyFactory(constructor.getDeclaringClass().getName()).getConstructorLocation(constructor); 832 } 833 834 @Override 835 public void clearCache() { 836 throw new UnsupportedOperationException(); 837 } 838 839 @Override 840 public PlasticManager getPlasticManager() { 841 return rootPageClassloaderContext.getProxyFactory().getPlasticManager(); 842 } 843 844 @Override 845 public PlasticProxyFactory getProxyFactory(String className) 846 { 847 PageClassLoaderContext context = rootPageClassloaderContext.findByClassName(className); 848 if (context == null) 849 { 850 context = pageClassLoaderContextManager.get(className); 851 } 852 return context.getProxyFactory(); 853 } 854 855 } 856 857}