001: // Copyright 2006, 2007 The Apache Software Foundation
002: //
003: // Licensed under the Apache License, Version 2.0 (the "License");
004: // you may not use this file except in compliance with the License.
005: // You may obtain a copy of the License at
006: //
007: // http://www.apache.org/licenses/LICENSE-2.0
008: //
009: // Unless required by applicable law or agreed to in writing, software
010: // distributed under the License is distributed on an "AS IS" BASIS,
011: // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012: // See the License for the specific language governing permissions and
013: // limitations under the License.
014:
015: package org.apache.tapestry.internal.services;
016:
017: import static org.apache.tapestry.ioc.internal.util.CollectionFactory.newConcurrentMap;
018:
019: import java.lang.reflect.Modifier;
020: import java.util.Map;
021:
022: import javassist.CtClass;
023: import javassist.CtConstructor;
024: import javassist.NotFoundException;
025:
026: import org.apache.commons.logging.Log;
027: import org.apache.tapestry.internal.events.InvalidationListener;
028: import org.apache.tapestry.internal.model.MutableComponentModelImpl;
029: import org.apache.tapestry.ioc.LogSource;
030: import org.apache.tapestry.ioc.Resource;
031: import org.apache.tapestry.ioc.internal.util.ClasspathResource;
032: import org.apache.tapestry.model.ComponentModel;
033: import org.apache.tapestry.model.MutableComponentModel;
034: import org.apache.tapestry.services.ComponentClassTransformWorker;
035:
036: /**
037: * Implementation of {@link org.apache.tapestry.internal.services.ComponentClassTransformer}.
038: */
039: public class ComponentClassTransformerImpl implements
040: ComponentClassTransformer, InvalidationListener {
041: /** Map from class name to class transformation. */
042: private final Map<String, InternalClassTransformation> _nameToClassTransformation = newConcurrentMap();
043:
044: private final Map<String, ComponentModel> _nameToComponentModel = newConcurrentMap();
045:
046: private final ComponentClassTransformWorker _workerChain;
047:
048: private final LogSource _logSource;
049:
050: /**
051: * @param workerChain
052: * the ordered list of class transform works as a chain of command instance
053: */
054: public ComponentClassTransformerImpl(
055: ComponentClassTransformWorker workerChain,
056: LogSource logSource) {
057: _workerChain = workerChain;
058: _logSource = logSource;
059: }
060:
061: /**
062: * Clears the cache of {@link InternalClassTransformation} instances whenever the class loader
063: * is invalidated.
064: */
065: public void objectWasInvalidated() {
066: _nameToClassTransformation.clear();
067: _nameToComponentModel.clear();
068: }
069:
070: public void transformComponentClass(CtClass ctClass,
071: ClassLoader classLoader) {
072: String parentClassname;
073:
074: // Component classes must be public
075:
076: if (!Modifier.isPublic(ctClass.getModifiers()))
077: return;
078:
079: try {
080: // And have a public constructor.
081:
082: CtConstructor ctor = ctClass.getConstructor("()V");
083:
084: if (!Modifier.isPublic(ctor.getModifiers()))
085: return;
086: } catch (NotFoundException ex) {
087: return;
088: }
089:
090: // Force the creation of the parent class.
091:
092: try {
093: parentClassname = ctClass.getSuperclass().getName();
094: } catch (NotFoundException ex) {
095: throw new RuntimeException(ex);
096: }
097:
098: String classname = ctClass.getName();
099:
100: Log log = _logSource.getLog(classname);
101:
102: // If the parent class is in a controlled package, it will already have been loaded and
103: // transformed (that is driven by the ComponentInstantiatorSource).
104:
105: InternalClassTransformation parentTransformation = _nameToClassTransformation
106: .get(parentClassname);
107:
108: // TODO: Check that the name is not already in the map. But I think that can't happen,
109: // because the classloader itself is synchronized.
110:
111: Resource baseResource = new ClasspathResource(classname
112: .replace(".", "/")
113: + ".class");
114:
115: ComponentModel parentModel = _nameToComponentModel
116: .get(parentClassname);
117:
118: MutableComponentModel model = new MutableComponentModelImpl(
119: classname, log, baseResource, parentModel);
120:
121: InternalClassTransformation transformation = parentTransformation == null ? new InternalClassTransformationImpl(
122: ctClass, classLoader, log, model)
123: : new InternalClassTransformationImpl(ctClass,
124: parentTransformation, classLoader, log, model);
125:
126: try {
127: _workerChain.transform(transformation, model);
128:
129: transformation.finish();
130: } catch (Throwable ex) {
131: throw new TransformationException(transformation, ex);
132: }
133:
134: if (log.isDebugEnabled())
135: log.debug("Finished class transformation: "
136: + transformation);
137:
138: _nameToClassTransformation.put(classname, transformation);
139: _nameToComponentModel.put(classname, model);
140: }
141:
142: public Instantiator createInstantiator(Class componentClass) {
143: String className = componentClass.getName();
144:
145: InternalClassTransformation ct = _nameToClassTransformation
146: .get(className);
147:
148: if (ct == null)
149: throw new RuntimeException(ServicesMessages
150: .classNotTransformed(className));
151:
152: try {
153: return ct.createInstantiator(componentClass);
154: } catch (Throwable ex) {
155: throw new TransformationException(ct, ex);
156: }
157: }
158: }
|