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