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;
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        return classToInstantiator.computeIfAbsent(className, this::createInstantiatorForClass);
360    }
361    
362    private static final ThreadLocal<Set<String>> OPEN_INSTANTIATORS = 
363            ThreadLocal.withInitial(HashSet::new);
364
365    private Instantiator createInstantiatorForClass(final String className)
366    {
367        return tracker.invoke(String.format("Creating instantiator for component class %s", className),
368                new Invokable<Instantiator>()
369                {
370                    public Instantiator invoke()
371                    {
372                        
373                        // Force the creation of the class (and the transformation of the class). This will first
374                        // trigger transformations of any base classes.
375                        
376                        OPEN_INSTANTIATORS.get().add(className);
377                        
378                        componentDependencyRegistry.disableInvalidations();
379                        PageClassLoaderContext context;
380                        try
381                        {
382                            context = pageClassLoaderContextManager.get(className);
383                        }
384                        finally
385                        {
386                            componentDependencyRegistry.enableInvalidations();
387                        }
388                        
389                        // Make sure the dependencies have been processed in case
390                        // there was some invalidation going on and they're not there.
391
392                        // TODO: maybe we need superclasses here too?
393                        final Set<String> dependencies = componentDependencyRegistry.getDependencies(className, DependencyType.USAGE);
394                        for (String dependency : dependencies)
395                        {
396                            if (!OPEN_INSTANTIATORS.get().contains(dependency))
397                            {
398                                createInstantiatorForClass(dependency);
399                            }
400                        }
401                        
402                        ClassInstantiator<Component> plasticInstantiator = context.getPlasticManager().getClassInstantiator(className);
403                        final ComponentModel model = classToModel.get(className);
404                        
405                        OPEN_INSTANTIATORS.get().remove(className);
406
407                        return new Instantiator()
408                        {
409                            public Component newInstance(InternalComponentResources resources)
410                            {
411                                return plasticInstantiator.with(ComponentResources.class, resources)
412                                        .with(InternalComponentResources.class, resources).newInstance();
413                            }
414
415                            public ComponentModel getModel()
416                            {
417                                return model;
418                            }
419
420                            @Override
421                            public String toString()
422                            {
423                                return String.format("[Instantiator[%s:%s]", className, context);
424                            }
425                        };
426                    }
427                });
428    }
429
430    public boolean exists(String className)
431    {
432        return parent.getResource(PlasticInternalUtils.toClassPath(className)) != null;
433    }
434
435    public PlasticProxyFactory getProxyFactory()
436    {
437        if (plasticProxyFactoryProxy == null)
438        {
439            plasticProxyFactoryProxy = new PlasticProxyFactoryProxy();
440        }
441        return plasticProxyFactoryProxy;
442    }
443    
444    public void transform(final PlasticClass plasticClass)
445    {
446        tracker.run(String.format("Running component class transformations on %s", plasticClass.getClassName()),
447                new Runnable()
448                {
449                    public void run()
450                    {
451                        String className = plasticClass.getClassName();
452                        String parentClassName = plasticClass.getSuperClassName();
453
454                        // The parent model may not exist, if the super class is not in a controlled package.
455
456                        ComponentModel parentModel = classToModel.get(parentClassName);
457
458                        final boolean isRoot = parentModel == null;
459
460                        if (isRoot
461                                && !(parentClassName.equals("java.lang.Object") || parentClassName
462                                .equals("groovy.lang.GroovyObjectSupport")))
463                        {
464                            String suggestedPackageName = buildSuggestedPackageName(className);
465
466                            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));
467                        }
468
469                        // Tapestry 5.2 was more sensitive that the parent class have a public no-args constructor.
470                        // Plastic
471                        // doesn't care, and we don't have the tools to dig that information out.
472
473                        Logger logger = loggerSource.getLogger(className);
474
475                        Resource baseResource = new ClasspathResource(parent, PlasticInternalUtils
476                                .toClassPath(className));
477
478                        changeTracker.add(baseResource.toURL(), new ClassName(className));
479
480                        if (isRoot)
481                        {
482                            implementComponentInterface(plasticClass);
483                        }
484
485                        boolean isPage = resolver.isPage(className);
486
487                        boolean superClassImplementsPageLifecycle = plasticClass.isInterfaceImplemented(PageLifecycleListener.class);
488
489                        String libraryName = resolver.getLibraryNameForClass(className);
490
491                        final MutableComponentModel model = new MutableComponentModelImpl(className, logger, baseResource,
492                                parentModel, isPage, libraryName);
493
494                        TransformationSupportImpl transformationSupport = new TransformationSupportImpl(plasticClass, isRoot, model);
495
496                        transformerChain.transform(plasticClass, transformationSupport, model);
497
498                        transformationSupport.commit();
499
500                        if (!superClassImplementsPageLifecycle && plasticClass.isInterfaceImplemented(PageLifecycleListener.class))
501                        {
502                            plasticClass.onConstruct(REGISTER_AS_PAGE_LIFECYCLE_LISTENER);
503                        }
504
505                        classToModel.put(className, model);
506                    }
507                });
508    }
509
510    private void implementComponentInterface(PlasticClass plasticClass)
511    {
512        plasticClass.introduceInterface(Component.class);
513
514        final PlasticField resourcesField = plasticClass.introduceField(InternalComponentResources.class,
515                "internalComponentResources").injectFromInstanceContext();
516
517        plasticClass.introduceMethod(GET_COMPONENT_RESOURCES, new InstructionBuilderCallback()
518        {
519            public void doBuild(InstructionBuilder builder)
520            {
521                builder.loadThis().getField(resourcesField).returnResult();
522            }
523        });
524    }
525
526    public <T> ClassInstantiator<T> configureInstantiator(String className, ClassInstantiator<T> instantiator)
527    {
528        return instantiator;
529    }
530
531    private String buildSuggestedPackageName(String className)
532    {
533        for (String subpackage : InternalConstants.SUBPACKAGES)
534        {
535            String term = "." + subpackage + ".";
536
537            int pos = className.indexOf(term);
538
539            // Keep the leading '.' in the subpackage name and tack on "base".
540
541            if (pos > 0)
542                return className.substring(0, pos + 1) + InternalConstants.BASE_SUBPACKAGE;
543        }
544
545        // Is this even reachable? className should always be in a controlled package and so
546        // some subpackage above should have matched.
547
548        return null;
549    }
550
551    public void classWillLoad(PlasticClassEvent event)
552    {
553        Logger logger = loggerSource.getLogger("tapestry.transformer." + event.getPrimaryClassName());
554
555        if (logger.isDebugEnabled())
556            logger.debug(event.getDissasembledBytecode());
557    }
558
559    private class TransformationSupportImpl implements TransformationSupport
560    {
561        private final PlasticClass plasticClass;
562
563        private final boolean root;
564
565        private final MutableComponentModel model;
566
567        private final List<MethodAdvice> eventHandlerAdvice = CollectionFactory.newList();
568
569        public TransformationSupportImpl(PlasticClass plasticClass, boolean root, MutableComponentModel model)
570        {
571            this.plasticClass = plasticClass;
572            this.root = root;
573            this.model = model;
574        }
575
576        /**
577         * Commits any stored changes to the PlasticClass; this is used to defer adding advice to the dispatch method.
578         */
579        public void commit()
580        {
581            if (!eventHandlerAdvice.isEmpty())
582            {
583                PlasticMethod dispatchMethod = plasticClass.introduceMethod(TransformConstants.DISPATCH_COMPONENT_EVENT_DESCRIPTION);
584                for (MethodAdvice advice : eventHandlerAdvice)
585                {
586                    dispatchMethod.addAdvice(advice);
587                }
588            }
589        }
590
591        public Class toClass(String typeName)
592        {
593            try
594            {
595                final PageClassLoaderContext context = pageClassLoaderContextManager.get(typeName);
596                return PlasticInternalUtils.toClass(context.getPlasticManager().getClassLoader(), typeName);
597            } catch (ClassNotFoundException ex)
598            {
599                throw new RuntimeException(String.format(
600                        "Unable to convert type '%s' to a Class: %s", typeName,
601                        ExceptionUtils.toMessage(ex)), ex);
602            }
603        }
604
605        public boolean isRootTransformation()
606        {
607            return root;
608        }
609
610        public void addEventHandler(final String eventType, final int minContextValues, final String operationDescription, final ComponentEventHandler handler)
611        {
612            assert InternalUtils.isNonBlank(eventType);
613            assert minContextValues >= 0;
614            assert handler != null;
615
616            model.addEventHandler(eventType);
617
618            MethodAdvice advice = new EventMethodAdvice(tracker, eventType, minContextValues, operationDescription, handler);
619
620            // The advice is added at the very end, after the logic provided by the OnEventWorker
621
622            eventHandlerAdvice.add(advice);
623        }
624    }
625
626    private static class EventMethodAdvice implements MethodAdvice
627    {
628        final OperationTracker tracker;
629        final String eventType;
630        final int minContextValues;
631        final String operationDescription;
632        final ComponentEventHandler handler;
633
634        public EventMethodAdvice(OperationTracker tracker, String eventType, int minContextValues, String operationDescription, ComponentEventHandler handler)
635        {
636            this.tracker = tracker;
637            this.eventType = eventType;
638            this.minContextValues = minContextValues;
639            this.operationDescription = operationDescription;
640            this.handler = handler;
641        }
642
643        public void advise(final MethodInvocation invocation)
644        {
645            final ComponentEvent event = (ComponentEvent) invocation.getParameter(0);
646
647            boolean matches = !event.isAborted() && event.matches(eventType, "", minContextValues);
648
649            if (matches)
650            {
651                tracker.run(operationDescription, new Runnable()
652                {
653                    public void run()
654                    {
655                        Component instance = (Component) invocation.getInstance();
656
657                        handler.handleEvent(instance, event);
658                    }
659                });
660            }
661
662            // Order of operations is key here. This logic takes precedence; base class event dispatch and event handler methods
663            // in the class come AFTER.
664
665            invocation.proceed();
666
667            if (matches)
668            {
669                invocation.setReturnValue(true);
670            }
671        }
672    }
673    
674    private static class ClassName implements ClassNameHolder
675    {
676        private String className;
677
678        public ClassName(String className) 
679        {
680            super();
681            this.className = className;
682        }
683
684        @Override
685        public String getClassName() 
686        {
687            return className;
688        }
689
690        @Override
691        public int hashCode() {
692            return Objects.hash(className);
693        }
694
695        @Override
696        public boolean equals(Object obj) 
697        {
698            if (this == obj) {
699                return true;
700            }
701            if (!(obj instanceof ClassName)) {
702                return false;
703            }
704            ClassName other = (ClassName) obj;
705            return Objects.equals(className, other.className);
706        }
707
708        @Override
709        public String toString() 
710        {
711            return className;
712        }
713        
714    }
715
716    private class PlasticProxyFactoryProxy implements PlasticProxyFactory
717    {
718
719        @Override
720        public void addPlasticClassListener(PlasticClassListener listener) 
721        {
722            throw new UnsupportedOperationException();
723        }
724
725        @Override
726        public void removePlasticClassListener(PlasticClassListener listener) 
727        {
728            throw new UnsupportedOperationException();
729        }
730
731        @Override
732        public ClassLoader getClassLoader() {
733            return rootPageClassloaderContext.getProxyFactory().getClassLoader();
734        }
735
736        @Override
737        public <T> ClassInstantiator<T> createProxy(Class<T> interfaceType, PlasticClassTransformer callback) 
738        {
739            return getProxyFactory(interfaceType.getName()).createProxy(interfaceType, callback);
740        }
741
742        @Override
743        public <T> ClassInstantiator<T> createProxy(Class<T> interfaceType,
744                Class<? extends T> implementationType,
745                PlasticClassTransformer callback,
746                boolean introduceInterface) 
747        {
748            throw new UnsupportedOperationException();
749        }
750
751        @Override
752        public <T> ClassInstantiator<T> createProxy(Class<T> interfaceType, Class<? extends T> implementationType, PlasticClassTransformer callback) 
753        {
754            throw new UnsupportedOperationException();            
755        }
756
757        @Override
758        public <T> PlasticClassTransformation<T> createProxyTransformation(Class<T> interfaceType) 
759        {
760            throw new UnsupportedOperationException();
761        }
762
763        @Override
764        public <T> PlasticClassTransformation<T> createProxyTransformation(Class<T> interfaceType, Class<? extends T> implementationType) 
765        {
766            throw new UnsupportedOperationException();
767        }
768
769        @Override
770        public <T> T createProxy(Class<T> interfaceType, ObjectCreator<T> creator, String description) 
771        {
772            throw new UnsupportedOperationException();
773        }
774
775        @Override
776        public <T> T createProxy(Class<T> interfaceType, Class<? extends T> implementationType, ObjectCreator<T> creator, String description) 
777        {
778            throw new UnsupportedOperationException();
779        }
780
781        @Override
782        public Location getMethodLocation(Method method) {
783            return getProxyFactory(method.getDeclaringClass().getName()).getMethodLocation(method);
784        }
785
786        @Override
787        public Location getConstructorLocation(Constructor constructor) 
788        {
789            return getProxyFactory(constructor.getDeclaringClass().getName()).getConstructorLocation(constructor);
790        }
791
792        @Override
793        public void clearCache() {
794            throw new UnsupportedOperationException();
795        }
796
797        @Override
798        public PlasticManager getPlasticManager() {
799            return rootPageClassloaderContext.getProxyFactory().getPlasticManager();
800        }
801        
802        @Override
803        public PlasticProxyFactory getProxyFactory(String className)
804        {
805            PageClassLoaderContext context = rootPageClassloaderContext.findByClassName(className);
806            if (context == null)
807            {
808                context = pageClassLoaderContextManager.get(className);
809            }
810            return context.getProxyFactory();
811        }
812        
813    }
814
815}