You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tapestry.apache.org by hl...@apache.org on 2006/07/30 01:28:42 UTC
svn commit: r426850 - in /tapestry/tapestry5/tapestry-core/trunk: ./
src/main/java/org/apache/tapestry/internal/
src/main/java/org/apache/tapestry/internal/ioc/services/
src/main/java/org/apache/tapestry/internal/services/
src/main/java/org/apache/tape...
Author: hlship
Date: Sat Jul 29 16:28:41 2006
New Revision: 426850
URL: http://svn.apache.org/viewvc?rev=426850&view=rev
Log:
Finish up the ChainBuilder service and tests.
Add some documentation about chain of command and ChainBuilder.
Replace the HiveMind configuration files with Tapestry IoC module classes.
Added:
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/InternalModule.java
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/transform/InternalTransformModule.java
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/transform/TransformModule.java
tapestry/tapestry5/tapestry-core/trunk/src/site/apt/ioc/command.apt
tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/services/ChainBuilderImplTest.java
tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/services/ChainCommand.java
Removed:
tapestry/tapestry5/tapestry-core/trunk/src/main/resources/META-INF/hivemodule.xml
tapestry/tapestry5/tapestry-core/trunk/src/main/resources/META-INF/org.apache.tapestry.internal.services.xml
tapestry/tapestry5/tapestry-core/trunk/src/main/resources/META-INF/org.apache.tapestry.internal.transform.xml
tapestry/tapestry5/tapestry-core/trunk/src/main/resources/META-INF/org.apache.tapestry.transform.xml
Modified:
tapestry/tapestry5/tapestry-core/trunk/pom.xml
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/ChainBuilderImpl.java
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/PerThreadServiceLifecycle.java
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/UpdateListenerHub.java
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/transform/ComponentClassTransformerImpl.java
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/transform/ComponentInstantiatorSourceImpl.java
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/ChainBuilder.java
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/TapestryIOCModule.java
tapestry/tapestry5/tapestry-core/trunk/src/site/apt/ioc/service.apt
tapestry/tapestry5/tapestry-core/trunk/src/site/site.xml
tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/transform/ComponentInstantiatorSourceImplTest.java
Modified: tapestry/tapestry5/tapestry-core/trunk/pom.xml
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/pom.xml?rev=426850&r1=426849&r2=426850&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/pom.xml (original)
+++ tapestry/tapestry5/tapestry-core/trunk/pom.xml Sat Jul 29 16:28:41 2006
@@ -82,7 +82,12 @@
<configuration>
<archive>
<manifestEntries>
- <Tapestry-Module-Classes>org.apache.tapestry.ioc.services.TapestryIOCModule</Tapestry-Module-Classes>
+ <Tapestry-Module-Classes>
+ org.apache.tapestry.ioc.services.TapestryIOCModule,
+ org.apache.tapestry.transform.TransformModule,
+ org.apache.tapestry.internal.InternalModule,
+ org.apache.tapestry.internal.transform.InternalTransformModule
+ </Tapestry-Module-Classes>
</manifestEntries>
</archive>
</configuration>
Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/InternalModule.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/InternalModule.java?rev=426850&view=auto
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/InternalModule.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/InternalModule.java Sat Jul 29 16:28:41 2006
@@ -0,0 +1,55 @@
+// Copyright 2006 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry.internal;
+
+import org.apache.commons.logging.Log;
+import org.apache.tapestry.annotations.Match;
+import org.apache.tapestry.internal.services.UpdateListenerHub;
+import org.apache.tapestry.internal.services.UpdateListenerHubImpl;
+import org.apache.tapestry.ioc.annotations.Before;
+import org.apache.tapestry.ioc.annotations.Id;
+import org.apache.tapestry.ioc.annotations.InjectService;
+import org.apache.tapestry.ioc.services.LoggingDecorator;
+
+/**
+ * @author Howard M. Lewis Ship
+ */
+@Id("tapestry.internal.services")
+public final class InternalModule
+{
+ /**
+ * The UpdateListenerHub is responsible for tracking which listeners are interested when some
+ * part of external storage changes (invalidating cached data).
+ */
+ public UpdateListenerHub buildUpdateListenerHub()
+ {
+ return new UpdateListenerHubImpl();
+ }
+
+ /**
+ * All public services in the tapestry module, and in any sub-module of tapestry will get
+ * logging.
+ */
+ @Match(
+ { "tapestry.*", "tapestry.*.*" })
+ @Before("*")
+ public <T> T decorateWithLogging(Class<T> serviceInterface, T delegate, String serviceId,
+ Log log, @InjectService("tapestry.ioc.LoggingDecorator")
+ LoggingDecorator loggingDecorator)
+ {
+ return loggingDecorator
+ .createLoggingInterceptor(serviceInterface, delegate, serviceId, log);
+ }
+}
Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/ChainBuilderImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/ChainBuilderImpl.java?rev=426850&r1=426849&r2=426850&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/ChainBuilderImpl.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/ChainBuilderImpl.java Sat Jul 29 16:28:41 2006
@@ -1,3 +1,17 @@
+// Copyright 2006 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
package org.apache.tapestry.internal.ioc.services;
import java.lang.reflect.Constructor;
@@ -15,6 +29,8 @@
import org.apache.tapestry.util.BodyBuilder;
import org.apache.tapestry.util.CollectionFactory;
+import static java.lang.String.format;
+
/**
* @author Howard M. Lewis Ship
*/
@@ -28,32 +44,23 @@
_classFactory = classFactory;
}
- public <T> T buildImplementation(Class<T> commandInterface, List<T> commands)
+ public <T> T buildChain(Class<T> commandInterface, List<T> commands)
{
- return null;
+ Class<T> chainClass = findImplementationClass(commandInterface);
+
+ return createInstance(chainClass, commands);
}
/**
* Map, keyed on service interface, of implementation Class.
*/
- private Map<Class, Class> _implementations = CollectionFactory.newMap();
-
- // public Object buildImplementation(Class commandInterface, List commands, String toString)
- // {
- // Defense.notNull(commandInterface, "commandInterface");
- // Defense.notNull(commands, "commands");
- // Defense.notNull(toString, "toString");
- //
- // Class instanceClass = findImplementationClass(commandInterface);
- //
- // return createInstance(instanceClass, commands, toString);
- // }
+ private Map<Class, Class> _cache = CollectionFactory.newMap();
@Synchronized.Read
private Class findImplementationClass(Class commandInterface)
{
- Class result = (Class) _implementations.get(commandInterface);
+ Class result = (Class) _cache.get(commandInterface);
if (result == null)
result = constructImplementationClass(commandInterface);
@@ -76,7 +83,7 @@
// This is the part that really needs synchronization:
- _implementations.put(commandInterface, result);
+ _cache.put(commandInterface, result);
return result;
@@ -85,8 +92,11 @@
// same command interface simultaneously).
}
- void addInfrastructure(ClassFab cf, Class commandInterface)
+ private void addInfrastructure(ClassFab cf, Class commandInterface)
{
+ // Array types are very, very tricky to deal with.
+ // Also, generics don't help (<T> new T[]) is still java.lang.Object[].
+
String arrayClassName = commandInterface.getCanonicalName() + "[]";
String jvmName = org.apache.tapestry.ioc.services.ClassFabUtils
.getJVMClassName(arrayClassName);
@@ -95,7 +105,11 @@
try
{
- array = Class.forName(jvmName, true, commandInterface.getClass().getClassLoader());
+ ClassLoader loader = commandInterface.getClass().getClassLoader();
+ if (loader == null)
+ loader = Thread.currentThread().getContextClassLoader();
+
+ array = Class.forName(jvmName, true, loader);
}
catch (Exception ex)
{
@@ -106,11 +120,10 @@
cf.addField("_commands", array);
BodyBuilder builder = new BodyBuilder();
-
builder.addln("_commands = (%s[]) $1.toArray(new %<s[0]);", commandInterface.getName());
cf.addConstructor(new Class[]
- { List.class, String.class }, null, builder.toString());
+ { List.class }, null, builder.toString());
}
private <T> T createInstance(Class<T> instanceClass, List<T> commands)
@@ -130,7 +143,7 @@
}
- void addMethods(ClassFab cf, Class commandInterface)
+ private void addMethods(ClassFab cf, Class commandInterface)
{
MethodIterator mi = new MethodIterator(commandInterface);
@@ -142,10 +155,10 @@
}
if (!mi.getToString())
- addToString(cf);
+ cf.addToString(format("<Command chain of %s>", commandInterface.getName()));
}
- void addMethod(ClassFab cf, Class commandInterface, MethodSignature sig)
+ private void addMethod(ClassFab cf, Class commandInterface, MethodSignature sig)
{
Class returnType = sig.getReturnType();
@@ -160,17 +173,13 @@
BodyBuilder builder = new BodyBuilder();
builder.begin();
- builder
- .addln(
- "{0} result = {1};",
- ClassFabUtils.getJavaClassName(returnType),
- defaultValue);
+ builder.addln("%s result = %s;", ClassFabUtils.getJavaClassName(returnType), defaultValue);
builder.addln("for (int i = 0; i < _commands.length; i++)");
builder.begin();
- builder.addln("result = _commands[i].{0}($$);", sig.getName());
+ builder.addln("result = _commands[i].%s($$);", sig.getName());
- builder.addln("if (result != {0}) break;", defaultValue);
+ builder.addln("if (result != %s) break;", defaultValue);
builder.end();
@@ -180,7 +189,7 @@
cf.addMethod(Modifier.PUBLIC, sig, builder.toString());
}
- String defaultForReturnType(Class returnType)
+ private String defaultForReturnType(Class returnType)
{
// For all object and array types.
@@ -191,7 +200,8 @@
return "false";
// Assume, then, that it is a numeric type (this method
- // isn't called for void).
+ // isn't called for type void). Javassist seems to be
+ // able to handle 0 for all numeric types.
return "0";
}
@@ -203,18 +213,11 @@
builder.begin();
builder.addln("for (int i = 0; i < _commands.length; i++)");
- builder.addln("_commands[i].{0}($$);", sig.getName());
+ builder.addln("_commands[i].%s($$);", sig.getName());
builder.end();
cf.addMethod(Modifier.PUBLIC, sig, builder.toString());
- }
-
- void addToString(ClassFab cf)
- {
- MethodSignature sig = new MethodSignature(String.class, "toString", null, null);
-
- cf.addMethod(Modifier.PUBLIC, sig, "return _toString;");
}
}
Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/PerThreadServiceLifecycle.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/PerThreadServiceLifecycle.java?rev=426850&r1=426849&r2=426850&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/PerThreadServiceLifecycle.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/PerThreadServiceLifecycle.java Sat Jul 29 16:28:41 2006
@@ -87,9 +87,8 @@
// Constructor takes a ServiceCreator
- // Caution: Javassist needs this to be a block, not just a single statement!
cf.addConstructor(new Class[]
- { ServiceCreator.class }, null, "{ _creator = $1; }");
+ { ServiceCreator.class }, null, "_creator = $1;");
String body = format("return (%s) _creator.createService();", serviceInterface.getName());
Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/UpdateListenerHub.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/UpdateListenerHub.java?rev=426850&r1=426849&r2=426850&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/UpdateListenerHub.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/UpdateListenerHub.java Sat Jul 29 16:28:41 2006
@@ -25,6 +25,7 @@
{
void addUpdateListener(UpdateListener listener);
+ /** For completeness. */
void removeUpdateListener(UpdateListener listener);
void fireUpdateEvent();
Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/transform/ComponentClassTransformerImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/transform/ComponentClassTransformerImpl.java?rev=426850&r1=426849&r2=426850&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/transform/ComponentClassTransformerImpl.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/transform/ComponentClassTransformerImpl.java Sat Jul 29 16:28:41 2006
@@ -41,7 +41,16 @@
/** Map from class name to class transformation. */
private final Map<String, InternalClassTransformation> _nameToClassTransformation = newMap();
- private ComponentClassTransformWorker _workers;
+ private final ComponentClassTransformWorker _workerChain;
+
+ /**
+ * @param workerChain
+ * the ordered list of class transform works as a chain of command instance
+ */
+ public ComponentClassTransformerImpl(ComponentClassTransformWorker workerChain)
+ {
+ _workerChain = workerChain;
+ }
/**
* Clears the cache of {@link InternalClassTransformation} instances whenever the class loader
@@ -90,7 +99,7 @@
MutableComponentModel model = new MutableComponentModelImpl();
- _workers.transform(transformation, model);
+ _workerChain.transform(transformation, model);
transformation.finish();
@@ -109,14 +118,4 @@
return ct.createInstantiator(componentClass);
}
-
- /**
- * For injection. This will usually be an ordered series of
- * {@link ComponentClassTransformWorker}s, as a chain-of-command.
- */
- public final void setWorkers(ComponentClassTransformWorker workers)
- {
- _workers = workers;
- }
-
}
Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/transform/ComponentInstantiatorSourceImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/transform/ComponentInstantiatorSourceImpl.java?rev=426850&r1=426849&r2=426850&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/transform/ComponentInstantiatorSourceImpl.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/transform/ComponentInstantiatorSourceImpl.java Sat Jul 29 16:28:41 2006
@@ -14,9 +14,6 @@
package org.apache.tapestry.internal.transform;
-import static org.apache.tapestry.util.CollectionFactory.newMap;
-import static org.apache.tapestry.util.CollectionFactory.newSet;
-
import java.net.URL;
import java.util.Map;
import java.util.Set;
@@ -31,13 +28,15 @@
import javassist.Translator;
import org.apache.commons.logging.Log;
-import org.apache.hivemind.ClassResolver;
import org.apache.tapestry.events.UpdateEvent;
import org.apache.tapestry.events.UpdateListener;
import org.apache.tapestry.internal.event.InvalidationEventHubImpl;
import org.apache.tapestry.internal.util.URLChangeTracker;
import org.apache.tapestry.util.Defense;
+import static org.apache.tapestry.util.CollectionFactory.newMap;
+import static org.apache.tapestry.util.CollectionFactory.newSet;
+
/**
* A wrapper around a Javassist class loader that allows certain classes to be modified as they are
* loaded.
@@ -57,9 +56,9 @@
private Loader _loader;
- private ComponentClassTransformer _transformer;
+ private final ComponentClassTransformer _transformer;
- private Log _log;
+ private final Log _log;
/** Map from class name to Instantiator. */
private final Map<String, Instantiator> _instantiatorMap = newMap();
@@ -70,14 +69,19 @@
return _loader;
}
- public ComponentInstantiatorSourceImpl(ClassResolver resolver)
+ public ComponentInstantiatorSourceImpl(ComponentClassTransformer transformer, Log log)
{
- this(resolver.getClassLoader());
+ this(Thread.currentThread().getContextClassLoader(), transformer, log);
}
- public ComponentInstantiatorSourceImpl(ClassLoader parent)
+ public ComponentInstantiatorSourceImpl(ClassLoader parent,
+ ComponentClassTransformer transformer, Log log)
{
_parent = parent;
+ _transformer = transformer;
+ _log = log;
+
+ initializeService();
}
private class PackageAwareLoader extends Loader
@@ -118,7 +122,11 @@
fireInvalidationEvent();
}
- public void initializeService()
+ /**
+ * Invoked at object crtation, or when there are updates, to create a new set of Javassist class
+ * pools and loaders.
+ */
+ private void initializeService()
{
_classPool = new ClassPool();
@@ -258,17 +266,4 @@
_controlledPackageNames.add(packageName);
}
-
- /** For injection. */
- public void setTransformer(ComponentClassTransformer transformer)
- {
- _transformer = transformer;
- }
-
- /** For injection. */
- public void setLog(Log log)
- {
- _log = log;
- }
-
}
Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/transform/InternalTransformModule.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/transform/InternalTransformModule.java?rev=426850&view=auto
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/transform/InternalTransformModule.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/transform/InternalTransformModule.java Sat Jul 29 16:28:41 2006
@@ -0,0 +1,70 @@
+// Copyright 2006 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry.internal.transform;
+
+import org.apache.commons.logging.Log;
+import org.apache.tapestry.internal.services.UpdateListenerHub;
+import org.apache.tapestry.internal.transform.worker.RetainWorker;
+import org.apache.tapestry.internal.transform.worker.UnclaimedFieldWorker;
+import org.apache.tapestry.ioc.OrderedConfiguration;
+import org.apache.tapestry.ioc.annotations.Contribute;
+import org.apache.tapestry.ioc.annotations.Id;
+import org.apache.tapestry.ioc.annotations.InjectService;
+import org.apache.tapestry.transform.ComponentClassTransformWorker;
+
+/**
+ * Counter part to {@link org.apache.tapestry.transform.TransformModule} that discusses actual
+ * implementations.
+ *
+ * @author Howard M. Lewis Ship
+ */
+@Id("tapestry.internal.transform")
+public final class InternalTransformModule
+{
+ @Contribute("tapestry.transform.ComponentClassTransformWorker")
+ public void contributeInternalWorkers(
+ OrderedConfiguration<ComponentClassTransformWorker> configuration)
+ {
+ configuration.add("Retain", new RetainWorker());
+ configuration.add("UnclaimedField", "*", null, new UnclaimedFieldWorker());
+ }
+
+ public ComponentClassTransformer buildComponentClassTransformer(
+ @InjectService("tapestry.transform.ComponentClassTransformWorker")
+ ComponentClassTransformWorker workerChain,
+ @InjectService("ComponentInstantiatorSource")
+ ComponentInstantiatorSource cis)
+ {
+ ComponentClassTransformerImpl transformer = new ComponentClassTransformerImpl(workerChain);
+
+ cis.addInvalidationListener(transformer);
+
+ return transformer;
+ }
+
+ public ComponentInstantiatorSource buildComponentInstantiatorSource(
+ @InjectService("ComponentClassTransformer")
+ ComponentClassTransformer transformer,
+ @InjectService("tapestry.internal.services.UpdateListenerHub")
+ UpdateListenerHub hub, Log log)
+ {
+ ComponentInstantiatorSourceImpl source = new ComponentInstantiatorSourceImpl(transformer,
+ log);
+
+ hub.addUpdateListener(source);
+
+ return source;
+ }
+}
Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/ChainBuilder.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/ChainBuilder.java?rev=426850&r1=426849&r2=426850&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/ChainBuilder.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/ChainBuilder.java Sat Jul 29 16:28:41 2006
@@ -22,9 +22,10 @@
* Four Chain Of Command pattern.
* <p>
* For each method in the interface, the chain implementation will call the corresponding method on
- * each command object in turn. If any of the command objects return true, then the chain of command
- * stops and the initial method invocation returns true. Otherwise, the chain of command continues
- * to the next command (and will return false if none of the commands returns true).
+ * each command object in turn (with the order defined by the list). If any of the command objects
+ * return true, then the chain of command stops and the initial method invocation returns true.
+ * Otherwise, the chain of command continues to the next command (and will return false if none of
+ * the commands returns true).
* <p>
* For methods whose return type is not boolean, the chain stops with the first non-null (for object
* types), or non-zero (for numeric types). The chain returns the value that was returned by the
@@ -36,5 +37,9 @@
*/
public interface ChainBuilder
{
- <T> T buildImplementation(Class<T> commandInterface, List<T> commands);
+ /**
+ * Creates a chain instance from a command interface and a list of commands (implementing the
+ * interface).
+ */
+ <T> T buildChain(Class<T> commandInterface, List<T> commands);
}
Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/TapestryIOCModule.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/TapestryIOCModule.java?rev=426850&r1=426849&r2=426850&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/TapestryIOCModule.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/TapestryIOCModule.java Sat Jul 29 16:28:41 2006
@@ -17,6 +17,7 @@
import java.util.Map;
import org.apache.commons.logging.Log;
+import org.apache.tapestry.internal.ioc.services.ChainBuilderImpl;
import org.apache.tapestry.internal.ioc.services.ClassFactoryImpl;
import org.apache.tapestry.internal.ioc.services.LoggingDecoratorImpl;
import org.apache.tapestry.internal.ioc.services.PerThreadServiceLifecycle;
@@ -33,7 +34,7 @@
* @author Howard M. Lewis Ship
*/
@Id("tapestry.ioc")
-public class TapestryIOCModule
+public final class TapestryIOCModule
{
/**
* The ClassFactory service is used to create new classes at runtime.
@@ -90,5 +91,15 @@
configuration.add(
"perthread",
new PerThreadServiceLifecycle(threadCleanupHub, classFactory));
+ }
+
+ /**
+ * A service that implements the chain of command pattern, creating an efficient implementation
+ * of a chain of command for an arbitrary interface.
+ */
+ public ChainBuilder buildChainBuilder(@InjectService("ClassFactory")
+ ClassFactory classFactory)
+ {
+ return new ChainBuilderImpl(classFactory);
}
}
Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/transform/TransformModule.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/transform/TransformModule.java?rev=426850&view=auto
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/transform/TransformModule.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/transform/TransformModule.java Sat Jul 29 16:28:41 2006
@@ -0,0 +1,42 @@
+// Copyright 2006 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry.transform;
+
+import java.util.List;
+
+import org.apache.tapestry.ioc.annotations.Id;
+import org.apache.tapestry.ioc.annotations.InjectService;
+import org.apache.tapestry.ioc.services.ChainBuilder;
+
+/**
+ * Public facing module that defines some services and configuration used when transforming
+ * component classes (as they are loaded into memory).
+ *
+ * @author Howard M. Lewis Ship
+ */
+@Id("tapestry.transform")
+public final class TransformModule
+{
+ /**
+ * Allows the exact steps in the component class transformation process to be defined.
+ */
+ public ComponentClassTransformWorker buildComponentClassTransformWorker(
+ List<ComponentClassTransformWorker> configuration,
+ @InjectService("tapestry.ioc.ChainBuilder")
+ ChainBuilder chainBuilder)
+ {
+ return chainBuilder.buildChain(ComponentClassTransformWorker.class, configuration);
+ }
+}
Added: tapestry/tapestry5/tapestry-core/trunk/src/site/apt/ioc/command.apt
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/site/apt/ioc/command.apt?rev=426850&view=auto
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/site/apt/ioc/command.apt (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/site/apt/ioc/command.apt Sat Jul 29 16:28:41 2006
@@ -0,0 +1,74 @@
+ ----
+ Chain of Command
+ ----
+
+Chain of Command
+
+ One of the most useful of the Gang Of Four Design Patterns is the <<command>> pattern.
+
+ With the command pattern, a complex process is broken down into many individual steps. The
+ steps are the <commands> in the command pattern. A key part of this is that the commands
+ are expected to implement some common interface. The commands are carefully arrainged into
+ a specific order.
+
+ The process operates by working down the list of commands. Each command is given a chance
+ to operate. A command can terminate the process either by throwing an exception, or by
+ returning true.
+
+ The return type of the command method does not have to be boolean: For object types,
+ any non-null value short-circuits the process. For numeric type, any non-zero value.
+ For void methods, only throwing an exception will short circuit the process.
+
+ Often, the command interface consists of a single method. When the command interface
+ has multiple methods, each can be thought of as its own chain.
+
+ This is a useful pattern because it makes it very easy to <extend> a given process,
+ simply by providing new commands and specifying where they fit into the overall
+ process. Most of chain of command is combined with an ordered
+ {{{configuration.html}configuration}} to define what the command are (and in what
+ order they should execute).
+
+ChainBuilder Service
+
+ Because this pattern is used so often inside Tapestry, a built-in service exists
+ to create implementations of the pattern as needed. The
+ {{{../apidocs/org/apache/tapestry/ioc/services/ChainBuilder.html}tapestry.ioc.ChainBuilder}}
+ service takes care of all the work:
+
++----+
+public interface ChainBuilder
+{
+ <T> T buildChain(Class<T> commandInterface, List<T> commands);
+}
++----+
+
+ All that generics parameterization just ensures that the command interface matches
+ the items in the list, and confirms that a single instance of the command interface
+ will be returned.
+
+ Invoking this method returns an object that encapsulates the chain of command for a
+ particular interface and a particular list of commands implementing that interface.
+
+ This can be used inside a service builder method. Nothing says a service builder method
+ just has to just instantiate a class; it is only required to return an appropriate object.
+ We can just let the ChainBuilder service create that object.
+
++----+
+ public MyChainService buildMyChainService(List<MyChainService> commands,
+ @InjectService("tapestry.ioc.ChainBuilder")
+ ChainBuilder chainBuilder)
+ {
+ return chainBuilder.buildChain(MyChainService.class, commands);
+ }
++----+
+
+ Here, the behavior of the MyChainService is defined by its configuration: an ordered
+ list of MyChainService commands that are contributed by many modules.
+
+ Internally, the ChainBuilder creates a new class that implements the service interface.
+ The list of commands is converted into an array, which is used inside the service implementation
+ (for maximum efficiency). Therefore, changing the list after creating the chain
+ instance will not affect the chain instance's behavior.
+
+ ChainBuilder will reuse the fabricated class for any number of chains of the same
+ command interface.
\ No newline at end of file
Modified: tapestry/tapestry5/tapestry-core/trunk/src/site/apt/ioc/service.apt
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/site/apt/ioc/service.apt?rev=426850&r1=426849&r2=426850&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/site/apt/ioc/service.apt (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/site/apt/ioc/service.apt Sat Jul 29 16:28:41 2006
@@ -158,6 +158,15 @@
The important thing is to invoke the cleanupThread() method, to discard any request/thread
specific information, before processing the next request, message, transaction, or
what have you.
+
+ <<Caution:>> A common technique in Tapestry IoC is to have a service builder method
+ registry a core service implementation as an event listener with some event hub service.
+ With non-singleton objects, this can cause a number of problems; the event hub will
+ hold a reference to the per-thread instance, even after that per-thread instance has been
+ cleaned up (discarded by the inner proxy). Simply put, this is a pattern to avoid. For
+ the most part, perthread services should be simple holders of data specific to a thread or
+ a request, and should not have overly complex relationships with the other services
+ in the registry.
Injecting Resources
Modified: tapestry/tapestry5/tapestry-core/trunk/src/site/site.xml
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/site/site.xml?rev=426850&r1=426849&r2=426850&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/site/site.xml (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/site/site.xml Sat Jul 29 16:28:41 2006
@@ -56,6 +56,7 @@
<item name="Services" href="ioc/service.html"/>
<item name="Decorators" href="ioc/decorator.html"/>
<item name="Configuration" href="ioc/configuration.html"/>
+ <item name="Chain Of Command" href="ioc/command.html"/>
</menu>
${reports}
Added: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/services/ChainBuilderImplTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/services/ChainBuilderImplTest.java?rev=426850&view=auto
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/services/ChainBuilderImplTest.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/services/ChainBuilderImplTest.java Sat Jul 29 16:28:41 2006
@@ -0,0 +1,201 @@
+// Copyright 2006 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry.internal.ioc.services;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.tapestry.internal.test.InternalBaseTestCase;
+import org.apache.tapestry.ioc.services.ChainBuilder;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertEquals;
+
+/**
+ * @author Howard M. Lewis Ship
+ */
+public class ChainBuilderImplTest extends InternalBaseTestCase
+{
+ private final ChainBuilder _builder = new ChainBuilderImpl(new ClassFactoryImpl());
+
+ @Test
+ public void simple_void_method()
+ {
+ Runnable r1 = newRunnable();
+ Runnable r2 = newRunnable();
+
+ // Training:
+
+ r1.run();
+ r2.run();
+
+ replay();
+
+ Runnable chain = build(Runnable.class, r1, r2);
+
+ chain.run();
+
+ verify();
+
+ Assert.assertEquals(chain.toString(), "<Command chain of java.lang.Runnable>");
+ }
+
+ @Test
+ public void int_method()
+ {
+ ChainCommand c1 = newChainCommand();
+ ChainCommand c2 = newChainCommand();
+
+ c1.workInt(7);
+ setReturnValue(0);
+
+ c2.workInt(7);
+ setReturnValue(99);
+
+ replay();
+
+ ChainCommand chain = build(ChainCommand.class, c1, c2);
+
+ assertEquals(chain.workInt(7), 99);
+
+ verify();
+ }
+
+ @Test
+ public void int_method_shortcircuits()
+ {
+ ChainCommand c1 = newChainCommand();
+ ChainCommand c2 = newChainCommand();
+
+ c1.workInt(7);
+ setReturnValue(88);
+
+ replay();
+
+ ChainCommand chain = build(ChainCommand.class, c1, c2);
+
+ assertEquals(chain.workInt(7), 88);
+
+ verify();
+ }
+
+ @Test
+ public void boolean_method()
+ {
+ ChainCommand c1 = newChainCommand();
+ ChainCommand c2 = newChainCommand();
+
+ c1.workBoolean(true);
+ setReturnValue(false);
+
+ c2.workBoolean(true);
+ setReturnValue(true);
+
+ replay();
+
+ ChainCommand chain = build(ChainCommand.class, c1, c2);
+
+ assertEquals(chain.workBoolean(true), true);
+
+ verify();
+ }
+
+ @Test
+ public void string_method()
+ {
+ ChainCommand c1 = newChainCommand();
+ ChainCommand c2 = newChainCommand();
+
+ c1.workString("fred");
+ setReturnValue(null);
+
+ c2.workString("fred");
+ setReturnValue("flintstone");
+
+ replay();
+
+ ChainCommand chain = build(ChainCommand.class, c1, c2);
+
+ assertEquals(chain.workString("fred"), "flintstone");
+
+ verify();
+
+ }
+
+ @Test
+ public void double_method()
+ {
+ ChainCommand c1 = newChainCommand();
+ ChainCommand c2 = newChainCommand();
+
+ c1.workDouble(1.2d);
+ setReturnValue(0);
+
+ c2.workDouble(1.2d);
+ setReturnValue(3.14d);
+
+ replay();
+
+ ChainCommand chain = build(ChainCommand.class, c1, c2);
+
+ assertEquals(chain.workDouble(1.2d), 3.14d);
+
+ verify();
+ }
+
+ private ChainCommand newChainCommand()
+ {
+ return newMock(ChainCommand.class);
+ }
+
+ @Test
+ public void fabricated_classes_are_reused()
+ {
+ Runnable r1 = newRunnable();
+ Runnable r2 = newRunnable();
+
+ Runnable chain1 = build(Runnable.class, r1);
+ Runnable chain2 = build(Runnable.class, r2);
+
+ Assert.assertSame(chain1.getClass(), chain2.getClass());
+
+ // Now make sure that the two instances are independent.
+
+ r1.run();
+
+ replay();
+
+ chain1.run();
+
+ verify();
+
+ r2.run();
+
+ replay();
+
+ chain2.run();
+
+ verify();
+ }
+
+ private <T> T build(Class<T> commandInterface, T... commands)
+ {
+ List<T> list = Arrays.asList(commands);
+
+ return _builder.buildChain(commandInterface, list);
+ }
+
+}
Added: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/services/ChainCommand.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/services/ChainCommand.java?rev=426850&view=auto
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/services/ChainCommand.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/services/ChainCommand.java Sat Jul 29 16:28:41 2006
@@ -0,0 +1,31 @@
+// Copyright 2006 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry.internal.ioc.services;
+
+/**
+ * Used with {@link org.apache.tapestry.internal.ioc.services.ChainBuilderImplTest}.
+ *
+ * @author Howard M. Lewis Ship
+ */
+public interface ChainCommand
+{
+ int workInt(int input);
+
+ boolean workBoolean(boolean input);
+
+ double workDouble(double input);
+
+ String workString(String input);
+}
Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/transform/ComponentInstantiatorSourceImplTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/transform/ComponentInstantiatorSourceImplTest.java?rev=426850&r1=426849&r2=426850&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/transform/ComponentInstantiatorSourceImplTest.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/transform/ComponentInstantiatorSourceImplTest.java Sat Jul 29 16:28:41 2006
@@ -14,16 +14,10 @@
package org.apache.tapestry.internal.transform;
-import static java.lang.Thread.currentThread;
-import static org.apache.hivemind.util.PropertyUtils.read;
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertNull;
-
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
-import java.util.Locale;
import java.util.UUID;
import javassist.CannotCompileException;
@@ -34,23 +28,28 @@
import javassist.LoaderClassPath;
import javassist.NotFoundException;
-import org.apache.hivemind.ClassResolver;
-import org.apache.hivemind.ModuleDescriptorProvider;
-import org.apache.hivemind.Registry;
-import org.apache.hivemind.impl.DefaultClassResolver;
-import org.apache.hivemind.impl.RegistryBuilder;
-import org.apache.hivemind.impl.XmlModuleDescriptorProvider;
+import org.apache.commons.logging.Log;
import org.apache.hivemind.util.PropertyUtils;
import org.apache.tapestry.internal.InternalComponentResources;
+import org.apache.tapestry.internal.InternalModule;
import org.apache.tapestry.internal.services.UpdateListenerHub;
import org.apache.tapestry.internal.transform.pages.BasicComponent;
import org.apache.tapestry.internal.transform.pages.BasicSubComponent;
+import org.apache.tapestry.ioc.Registry;
+import org.apache.tapestry.ioc.RegistryBuilder;
+import org.apache.tapestry.ioc.services.TapestryIOCModule;
import org.apache.tapestry.runtime.ComponentLifecycle;
import org.apache.tapestry.test.BaseTestCase;
+import org.apache.tapestry.transform.TransformModule;
import org.testng.Assert;
import org.testng.annotations.Configuration;
import org.testng.annotations.Test;
+import static java.lang.Thread.currentThread;
+import static org.apache.hivemind.util.PropertyUtils.read;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNull;
+
/**
* Tests for {@link org.apache.tapestry.internal.transform.ComponentInstantiatorSourceImpl}. Many
* of these tests are more of the form of integration tests that instantiate the HiveMind Registry.
@@ -61,19 +60,24 @@
{
private static final String SYNTH_COMPONENT_CLASSNAME = "org.apache.tapestry.internal.transform.pages.SynthComponent";
- private final ClassLoader _defaultClassLoader = currentThread().getContextClassLoader();
-
private File _extraClasspath;
private ComponentInstantiatorSource _source;
private Registry _registry;
+ private ClassLoader _originalContextClassLoader = Thread.currentThread()
+ .getContextClassLoader();
+
@Test
public void controlled_packages() throws Exception
{
+ ComponentClassTransformer transformer = newMock(ComponentClassTransformer.class);
+ Log log = newLog();
- ComponentInstantiatorSourceImpl e = new ComponentInstantiatorSourceImpl(_defaultClassLoader);
+ replay();
+
+ ComponentInstantiatorSourceImpl e = new ComponentInstantiatorSourceImpl(transformer, log);
assertEquals(e.inControlledPackage("foo.bar.Baz"), false);
@@ -94,6 +98,8 @@
// Parents of controlled packages are not controlled
assertEquals(e.inControlledPackage("foo.Gloop"), false);
+
+ verify();
}
/** More of an integration test. */
@@ -155,7 +161,7 @@
assertEquals(named.getName(), "Original");
- // Sometimes this code runs so far that the updated file has the same timestamp as
+ // Sometimes this code runs so fast that the updated file has the same timestamp as
// the original; this little blip seems to help.
Thread.sleep(250);
@@ -165,7 +171,7 @@
// Detect the change and clear out the internal caches
UpdateListenerHub hub = (UpdateListenerHub) _registry.getService(
- "org.apache.tapestry.internal.services.UpdateListenerHub",
+ "tapestry.internal.services.UpdateListenerHub",
UpdateListenerHub.class);
hub.fireUpdateEvent();
@@ -241,19 +247,21 @@
URL url = _extraClasspath.toURL();
ClassLoader extraLoader = new URLClassLoader(new URL[]
- { url }, _defaultClassLoader);
-
- ClassResolver resolver = new DefaultClassResolver(extraLoader);
+ { url }, _originalContextClassLoader);
- ModuleDescriptorProvider provider = new XmlModuleDescriptorProvider(resolver);
+ Thread.currentThread().setContextClassLoader(extraLoader);
RegistryBuilder builder = new RegistryBuilder();
- builder.addModuleDescriptorProvider(provider);
- _registry = builder.constructRegistry(Locale.getDefault());
+ builder.add(
+ TapestryIOCModule.class,
+ TransformModule.class,
+ InternalModule.class,
+ InternalTransformModule.class);
- _source = (ComponentInstantiatorSource) _registry
- .getService(ComponentInstantiatorSource.class);
+ _registry = builder.build();
+
+ _source = _registry.getService(ComponentInstantiatorSource.class);
_source.addPackage("org.apache.tapestry.internal.transform.pages");
}
@@ -261,10 +269,12 @@
@Configuration(afterTestClass = true)
public void shutdownRegistry()
{
- _registry.shutdown();
+ // _registry.shutdown();
_registry = null;
_source = null;
+
+ Thread.currentThread().setContextClassLoader(_originalContextClassLoader);
}
protected final InternalComponentResources newInternalComponentResources()