You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tapestry.apache.org by hl...@apache.org on 2010/03/12 06:21:12 UTC

svn commit: r922145 - in /tapestry/tapestry5/trunk: tapestry-core/src/main/java/org/apache/tapestry5/internal/bindings/ tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ tapestry-core/src/main/java/org/apache/tapestry5/internal/util/ ...

Author: hlship
Date: Fri Mar 12 05:21:12 2010
New Revision: 922145

URL: http://svn.apache.org/viewvc?rev=922145&view=rev
Log:
TAP5-1013: Allow a limited set of IoC service implementations to automatically reload

Added:
    tapestry/tapestry5/trunk/tapestry-core/src/test/app1/ReloadDemo.tml
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/ReloadDemo.java
      - copied, changed from r922144, tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/internal/UninstantiableAutobuildServiceModule.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/services/Reloadable.java
      - copied, changed from r922144, tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/internal/UninstantiableAutobuildServiceModule.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/services/ReloadableImpl.java
      - copied, changed from r922144, tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/internal/UninstantiableAutobuildServiceModule.java
    tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/ReloadableObjectCreator.java   (with props)
    tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/ReloadableObjectCreatorSource.java   (with props)
Modified:
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/bindings/AssetBinding.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ComponentInstantiatorSourceImpl.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ServicesMessages.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ValidatorSpecification.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/util/URLChangeTracker.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/Index.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/services/AppModule.java
    tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/ModuleImpl.java
    tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/ServiceBinderImpl.java
    tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/ClassFabUtils.java
    tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/IntegrationTest.java
    tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/internal/MutlipleAutobuildServiceConstructorsModule.java
    tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/internal/UninstantiableAutobuildServiceModule.java

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/bindings/AssetBinding.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/bindings/AssetBinding.java?rev=922145&r1=922144&r2=922145&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/bindings/AssetBinding.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/bindings/AssetBinding.java Fri Mar 12 05:21:12 2010
@@ -4,7 +4,7 @@
 // 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
+// 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,
@@ -25,7 +25,7 @@ public class AssetBinding extends Abstra
 
     private final Asset2 asset;
 
-    AssetBinding(Location location, String description, Asset asset)
+    public AssetBinding(Location location, String description, Asset asset)
     {
         super(location);
 

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ComponentInstantiatorSourceImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ComponentInstantiatorSourceImpl.java?rev=922145&r1=922144&r2=922145&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ComponentInstantiatorSourceImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ComponentInstantiatorSourceImpl.java Fri Mar 12 05:21:12 2010
@@ -1,10 +1,10 @@
-// Copyright 2006, 2007, 2008 The Apache Software Foundation
+// Copyright 2006, 2007, 2008, 2010 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
+// 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,
@@ -23,6 +23,7 @@ import org.apache.tapestry5.ioc.internal
 import org.apache.tapestry5.ioc.internal.services.CtClassSourceImpl;
 import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
 import org.apache.tapestry5.ioc.internal.util.Defense;
+import org.apache.tapestry5.ioc.services.ClassFabUtils;
 import org.apache.tapestry5.ioc.services.ClassFactory;
 import org.apache.tapestry5.ioc.services.ClasspathURLConverter;
 import org.apache.tapestry5.services.InvalidationEventHub;
@@ -37,7 +38,8 @@ import java.util.Set;
 /**
  * A wrapper around a Javassist class loader that allows certain classes to be modified as they are loaded.
  */
-public final class ComponentInstantiatorSourceImpl extends InvalidationEventHubImpl implements Translator, ComponentInstantiatorSource, UpdateListener
+public final class ComponentInstantiatorSourceImpl extends InvalidationEventHubImpl implements Translator,
+        ComponentInstantiatorSource, UpdateListener
 {
     /**
      * Add -Djavassist-write-dir=target/transformed-classes to the command line to force output of transformed classes
@@ -78,10 +80,10 @@ public final class ComponentInstantiator
         }
 
         /**
-         * Determines if the class name represents a component class from a controlled package.  If so,
+         * Determines if the class name represents a component class from a controlled package. If so,
          * super.findClass() will load it and transform it. Returns null if not in a controlled package, allowing the
          * parent class loader to do the work.
-         *
+         * 
          * @param className
          * @return the loaded transformed Class, or null to force a load of the class from the parent class loader
          * @throws ClassNotFoundException
@@ -89,10 +91,7 @@ public final class ComponentInstantiator
         @Override
         protected Class findClass(String className) throws ClassNotFoundException
         {
-            if (inControlledPackage(className))
-            {
-                return super.findClass(className);
-            }
+            if (inControlledPackage(className)) { return super.findClass(className); }
 
             // Returning null forces delegation to the parent class loader.
 
@@ -101,8 +100,7 @@ public final class ComponentInstantiator
     }
 
     public ComponentInstantiatorSourceImpl(Logger logger, ClassLoader parent, ComponentClassTransformer transformer,
-                                           InternalRequestGlobals internalRequestGlobals,
-                                           ClasspathURLConverter classpathURLConverter)
+            InternalRequestGlobals internalRequestGlobals, ClasspathURLConverter classpathURLConverter)
     {
         this.parent = parent;
         this.transformer = transformer;
@@ -115,7 +113,8 @@ public final class ComponentInstantiator
 
     public synchronized void checkForUpdates()
     {
-        if (!changeTracker.containsChanges()) return;
+        if (!changeTracker.containsChanges())
+            return;
 
         changeTracker.clear();
         classNameToInstantiator.clear();
@@ -220,12 +219,14 @@ public final class ComponentInstantiator
 
         logger.debug(String.format("%5s onLoad %s", diag, classname));
 
-        if (failure != null) throw failure;
+        if (failure != null)
+            throw failure;
     }
 
     private void writeClassToFileSystemForHardCoreDebuggingPurposesOnly(CtClass ctClass)
     {
-        if (JAVASSIST_WRITE_DIR == null) return;
+        if (JAVASSIST_WRITE_DIR == null)
+            return;
 
         try
         {
@@ -242,7 +243,7 @@ public final class ComponentInstantiator
 
     private void addClassFileToChangeTracker(String classname)
     {
-        String path = classname.replace('.', '/') + ".class";
+        String path = ClassFabUtils.getPathForClassNamed(classname);
 
         URL url = loader.getResource(path);
 
@@ -277,7 +278,7 @@ public final class ComponentInstantiator
 
             // Note: this is really a create, and in fact, will create a new Class instance
             // (it doesn't cache internally). This code is the only cache, which is why
-            // the method is synchronized.  We could use a ConcurrentBarrier, but I suspect
+            // the method is synchronized. We could use a ConcurrentBarrier, but I suspect
             // that the overhead of that is greater on a typical invocation than
             // the cost of the synchronization and the Map lookup.
 
@@ -312,7 +313,8 @@ public final class ComponentInstantiator
 
         while (packageName != null)
         {
-            if (controlledPackageNames.contains(packageName)) return true;
+            if (controlledPackageNames.contains(packageName))
+                return true;
 
             packageName = stripTail(packageName);
         }
@@ -324,7 +326,8 @@ public final class ComponentInstantiator
     {
         int lastdot = input.lastIndexOf('.');
 
-        if (lastdot < 0) return null;
+        if (lastdot < 0)
+            return null;
 
         return input.substring(0, lastdot);
     }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ServicesMessages.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ServicesMessages.java?rev=922145&r1=922144&r2=922145&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ServicesMessages.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ServicesMessages.java Fri Mar 12 05:21:12 2010
@@ -34,142 +34,142 @@ import java.util.Collection;
 import java.util.List;
 import java.util.Locale;
 
-class ServicesMessages
+public class ServicesMessages
 {
     private static final Messages MESSAGES = MessagesImpl.forClass(ServicesMessages.class);
 
-    static String duplicateContribution(Object conflict, Class contributionType, Object existing)
+    public static String duplicateContribution(Object conflict, Class contributionType, Object existing)
     {
         return MESSAGES.format("duplicate-contribution", conflict, contributionType.getName(), existing);
     }
 
-    static String markupWriterNoCurrentElement()
+    public static String markupWriterNoCurrentElement()
     {
         return MESSAGES.get("markup-writer-no-current-element");
     }
 
-    static String errorAddingMethod(CtClass ctClass, String methodName, Throwable cause)
+    public static String errorAddingMethod(CtClass ctClass, String methodName, Throwable cause)
     {
         return MESSAGES.format("error-adding-method", ctClass.getName(), methodName, cause);
     }
 
-    static String classNotTransformed(String className)
+    public static String classNotTransformed(String className)
     {
         return MESSAGES.format("class-not-transformed", className);
     }
 
-    static String missingTemplateResource(Resource resource)
+    public static String missingTemplateResource(Resource resource)
     {
         return MESSAGES.format("missing-template-resource", resource);
     }
 
-    static String contentInsideBodyNotAllowed(Location location)
+    public static String contentInsideBodyNotAllowed(Location location)
     {
         return MESSAGES.format("content-inside-body-not-allowed", location);
     }
 
-    static String methodCompileError(TransformMethodSignature signature, String methodBody, Throwable cause)
+    public static String methodCompileError(TransformMethodSignature signature, String methodBody, Throwable cause)
     {
         return MESSAGES.format("method-compile-error", signature, methodBody, cause);
     }
 
-    static String renderQueueError(RenderCommand command, Throwable cause)
+    public static String renderQueueError(RenderCommand command, Throwable cause)
     {
         return MESSAGES.format("render-queue-error", command, cause);
     }
 
-    static String readOnlyField(String className, String fieldName)
+    public static String readOnlyField(String className, String fieldName)
     {
         return MESSAGES.format("read-only-field", className, fieldName);
     }
 
-    static String nonPrivateFields(String className, List<String> names)
+    public static String nonPrivateFields(String className, List<String> names)
     {
         return MESSAGES.format("non-private-fields", className, InternalUtils.joinSorted(names));
     }
 
-    static String bindingSourceFailure(String expression, Throwable cause)
+    public static String bindingSourceFailure(String expression, Throwable cause)
     {
         return MESSAGES.format("binding-source-failure", expression, cause);
     }
 
-    static String contextIndexOutOfRange(String methodDescription)
+    public static String contextIndexOutOfRange(String methodDescription)
     {
         return MESSAGES.format("context-index-out-of-range", methodDescription);
     }
 
-    static String pageNameUnresolved(String pageClassName)
+    public static String pageNameUnresolved(String pageClassName)
     {
         return MESSAGES.format("page-name-unresolved", pageClassName);
     }
 
-    static String exceptionInMethodParameter(String methodDescription, int index, Throwable cause)
+    public static String exceptionInMethodParameter(String methodDescription, int index, Throwable cause)
     {
         return MESSAGES.format("exception-in-method-parameter", methodDescription, index + 1, cause);
     }
 
-    static String componentEventIsAborted(String methodDescription)
+    public static String componentEventIsAborted(String methodDescription)
     {
         return MESSAGES.format("component-event-is-aborted", methodDescription);
     }
 
-    static String parameterNameMustBeUnique(String parameterName, String parameterValue)
+    public static String parameterNameMustBeUnique(String parameterName, String parameterValue)
     {
         return MESSAGES.format("parameter-name-must-be-unique", parameterName, parameterValue);
     }
 
-    static String pageIsDirty(Object page)
+    public static String pageIsDirty(Object page)
     {
         return MESSAGES.format("page-is-dirty", page);
     }
 
-    static String componentInstanceIsNotAPage(Component result)
+    public static String componentInstanceIsNotAPage(Component result)
     {
         return MESSAGES.format("component-instance-is-not-a-page", result.getComponentResources().getCompleteId());
     }
 
-    static String failureReadingMessages(Resource url, Throwable cause)
+    public static String failureReadingMessages(Resource url, Throwable cause)
     {
         return MESSAGES.format("failure-reading-messages", url, cause);
     }
 
-    static String unknownAssetPrefix(String path)
+    public static String unknownAssetPrefix(String path)
     {
         return MESSAGES.format("unknown-asset-prefix", path);
     }
 
-    static String assetDoesNotExist(Resource resource)
+    public static String assetDoesNotExist(Resource resource)
     {
         return MESSAGES.format("asset-does-not-exist", resource);
     }
 
-    static String wrongAssetDigest(Resource resource)
+    public static String wrongAssetDigest(Resource resource)
     {
         return MESSAGES.format("wrong-asset-digest", resource.getPath());
     }
 
-    static String unknownValidatorType(String validatorType, List<String> knownValidatorTypes)
+    public static String unknownValidatorType(String validatorType, List<String> knownValidatorTypes)
     {
         return MESSAGES.format("unknown-validator-type", validatorType, InternalUtils.join(knownValidatorTypes));
     }
 
-    static String unknownTranslatorType(String translatorType, List<String> knownTranslatorTypes)
+    public static String unknownTranslatorType(String translatorType, List<String> knownTranslatorTypes)
     {
         return MESSAGES.format("unknown-translator-type", translatorType, InternalUtils.join(knownTranslatorTypes));
     }
 
-    static String validatorSpecificationParseError(int cursor, String specification)
+    public static String validatorSpecificationParseError(int cursor, String specification)
     {
         return MESSAGES.format("validator-specification-parse-error", specification.charAt(cursor), cursor + 1,
                 specification);
     }
 
-    static String mixinsInvalidWithoutIdOrType(String elementName)
+    public static String mixinsInvalidWithoutIdOrType(String elementName)
     {
         return MESSAGES.format("mixins-invalid-without-id-or-type", elementName);
     }
 
-    static String missingFromEnvironment(Class type, Collection<Class> availableTypes)
+    public static String missingFromEnvironment(Class type, Collection<Class> availableTypes)
     {
         List<String> types = CollectionFactory.newList();
 
@@ -179,7 +179,7 @@ class ServicesMessages
         return MESSAGES.format("missing-from-environment", type.getName(), InternalUtils.joinSorted(types));
     }
 
-    static String invalidComponentEventResult(Object result, Collection<Class> configuredResultTypes)
+    public static String invalidComponentEventResult(Object result, Collection<Class> configuredResultTypes)
     {
         List<String> classNames = CollectionFactory.newList();
 
@@ -190,112 +190,113 @@ class ServicesMessages
                 .getClass()), InternalUtils.joinSorted(classNames));
     }
 
-    static String undefinedTapestryAttribute(String elementName, String attributeName, String allowedAttributeName)
+    public static String undefinedTapestryAttribute(String elementName, String attributeName,
+            String allowedAttributeName)
     {
         return MESSAGES.format("undefined-tapestry-attribute", elementName, attributeName, allowedAttributeName);
     }
 
-    static String parameterElementNameRequired()
+    public static String parameterElementNameRequired()
     {
         return MESSAGES.get("parameter-element-name-required");
     }
 
-    static String missingApplicationStatePersistenceStrategy(String name, Collection<String> availableNames)
+    public static String missingApplicationStatePersistenceStrategy(String name, Collection<String> availableNames)
     {
         return MESSAGES.format("missing-application-state-persistence-strategy", name, InternalUtils
                 .joinSorted(availableNames));
     }
 
-    static String methodIsVoid(String methodName, Class inClass, String propertyExpression)
+    public static String methodIsVoid(String methodName, Class inClass, String propertyExpression)
     {
         return MESSAGES.format("method-is-void", methodName, inClass.getName(), propertyExpression);
     }
 
-    static String methodNotFound(String methodName, Class inClass, String propertyExpression)
+    public static String methodNotFound(String methodName, Class inClass, String propertyExpression)
     {
         return MESSAGES.format("method-not-found", methodName, inClass.getName(), propertyExpression);
     }
 
-    static String noSuchProperty(Class targetClass, String propertyName, String propertyExpression,
+    public static String noSuchProperty(Class targetClass, String propertyName, String propertyExpression,
             Collection<String> propertyNames)
     {
         return MESSAGES.format("no-such-property", targetClass.getName(), propertyName, propertyExpression,
                 InternalUtils.joinSorted(propertyNames));
     }
 
-    static String writeOnlyProperty(String propertyName, Class clazz, String propertyExpression)
+    public static String writeOnlyProperty(String propertyName, Class clazz, String propertyExpression)
     {
         return MESSAGES.format("write-only-property", propertyName, clazz.getName(), propertyExpression);
     }
 
-    static String requestException(Throwable cause)
+    public static String requestException(Throwable cause)
     {
         return MESSAGES.format("request-exception", cause);
     }
 
-    static String componentRecursion(String componentClassName)
+    public static String componentRecursion(String componentClassName)
     {
         return MESSAGES.format("component-recursion", componentClassName);
     }
 
-    static String clientStateMustBeSerializable(Object newValue)
+    public static String clientStateMustBeSerializable(Object newValue)
     {
         return MESSAGES.format("client-state-must-be-serializable", newValue);
     }
 
-    static String corruptClientState()
+    public static String corruptClientState()
     {
         return MESSAGES.get("corrupt-client-state");
     }
 
-    static String unclosedAttributeExpression(String expression)
+    public static String unclosedAttributeExpression(String expression)
     {
         return MESSAGES.format("unclosed-attribute-expression", expression);
     }
 
-    static String noDisplayForDataType(String datatype)
+    public static String noDisplayForDataType(String datatype)
     {
         return MESSAGES.format("no-display-for-data-type", datatype);
     }
 
-    static String noEditForDataType(String datatype)
+    public static String noEditForDataType(String datatype)
     {
         return MESSAGES.format("no-edit-for-data-type", datatype);
     }
 
-    static String missingValidatorConstraint(String validatorType, Class type, String perFormMessageKey,
+    public static String missingValidatorConstraint(String validatorType, Class type, String perFormMessageKey,
             String generalMessageKey)
     {
         return MESSAGES.format("missing-validator-constraint", validatorType, type.getName(), perFormMessageKey,
                 generalMessageKey);
     }
 
-    static String resourcesAccessForbidden(String URI)
+    public static String resourcesAccessForbidden(String URI)
     {
         return MESSAGES.format("resource-access-forbidden", URI);
     }
 
-    static String noMarkupFromPageRender(Page page)
+    public static String noMarkupFromPageRender(Page page)
     {
         return MESSAGES.format("no-markup-from-page-render", page.getName());
     }
 
-    static String baseClassInWrongPackage(String parentClassName, String className, String suggestedPackage)
+    public static String baseClassInWrongPackage(String parentClassName, String className, String suggestedPackage)
     {
         return MESSAGES.format("base-class-in-wrong-package", parentClassName, className, suggestedPackage);
     }
 
-    static String invalidId(String messageKey, String idValue)
+    public static String invalidId(String messageKey, String idValue)
     {
         return MESSAGES.format(messageKey, idValue);
     }
 
-    static String attributeNotAllowed(String elementName)
+    public static String attributeNotAllowed(String elementName)
     {
         return MESSAGES.format("attribute-not-allowed", elementName);
     }
 
-    static String pagePoolExausted(String pageName, Locale locale, int hardLimit)
+    public static String pagePoolExausted(String pageName, Locale locale, int hardLimit)
     {
         return MESSAGES.format("page-pool-exausted", pageName, locale.toString(), hardLimit);
     }
@@ -306,27 +307,27 @@ class ServicesMessages
                 .joinSorted(typeNames));
     }
 
-    static String emptyBinding(String parameterName)
+    public static String emptyBinding(String parameterName)
     {
         return MESSAGES.format("parameter-binding-must-not-be-empty", parameterName);
     }
 
-    static String noSuchMethod(Class clazz, String methodName)
+    public static String noSuchMethod(Class clazz, String methodName)
     {
         return MESSAGES.format("no-such-method", ClassFabUtils.toJavaClassName(clazz), methodName);
     }
 
-    static String forbidInstantiateComponentClass(String className)
+    public static String forbidInstantiateComponentClass(String className)
     {
         return MESSAGES.format("forbid-instantiate-component-class", className);
     }
 
-    static String eventNotHandled(ComponentPageElement element, String eventName)
+    public static String eventNotHandled(ComponentPageElement element, String eventName)
     {
         return MESSAGES.format("event-not-handled", eventName, element.getCompleteId());
     }
 
-    static String documentMissingHTMLRoot(String rootElementName)
+    public static String documentMissingHTMLRoot(String rootElementName)
     {
         return MESSAGES.format("document-missing-html-root", rootElementName);
     }
@@ -336,33 +337,34 @@ class ServicesMessages
         return MESSAGES.format("add-new-method-conflict", signature);
     }
 
-    static String parameterElementDoesNotAllowAttributes()
+    public static String parameterElementDoesNotAllowAttributes()
     {
         return MESSAGES.get("parameter-element-does-not-allow-attributes");
     }
 
-    static String invalidPathForLibraryNamespace(String URI)
+    public static String invalidPathForLibraryNamespace(String URI)
     {
         return MESSAGES.format("invalid-path-for-library-namespace", URI);
     }
 
-    static String literalConduitNotUpdateable()
+    public static String literalConduitNotUpdateable()
     {
         return MESSAGES.get("literal-conduit-not-updateable");
     }
 
-    static String requestRewriteReturnedNull()
+    public static String requestRewriteReturnedNull()
     {
         return MESSAGES.get("request-rewrite-returned-null");
     }
 
-    static String linkRewriteReturnedNull()
+    public static String linkRewriteReturnedNull()
     {
         return MESSAGES.get("link-rewrite-returned-null");
     }
 
     public static String markupWriterAttributeNameOrValueOmitted(String element, Object[] namesAndValues)
     {
-        return MESSAGES.format("markup-writer-attribute-name-or-value-omitted", element, InternalUtils.join(Arrays.asList(namesAndValues)));
+        return MESSAGES.format("markup-writer-attribute-name-or-value-omitted", element, InternalUtils.join(Arrays
+                .asList(namesAndValues)));
     }
 }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ValidatorSpecification.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ValidatorSpecification.java?rev=922145&r1=922144&r2=922145&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ValidatorSpecification.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ValidatorSpecification.java Fri Mar 12 05:21:12 2010
@@ -1,10 +1,10 @@
-// Copyright 2006, 2008 The Apache Software Foundation
+// Copyright 2006, 2008, 2010 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
+// 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,
@@ -19,7 +19,7 @@ import org.apache.tapestry5.internal.Tap
 /**
  * Validator type and constraint values parsed from a validator specification.
  */
-class ValidatorSpecification
+public class ValidatorSpecification
 {
     private final String validatorType;
 
@@ -55,11 +55,13 @@ class ValidatorSpecification
     @Override
     public boolean equals(Object other)
     {
-        if (other == null || other.getClass() != getClass()) return false;
+        if (other == null || other.getClass() != getClass())
+            return false;
 
         ValidatorSpecification ov = (ValidatorSpecification) other;
 
-        if (!validatorType.equals(ov.validatorType)) return false;
+        if (!validatorType.equals(ov.validatorType))
+            return false;
 
         return TapestryInternalUtils.isEqual(constraintValue, ov.constraintValue);
     }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/util/URLChangeTracker.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/util/URLChangeTracker.java?rev=922145&r1=922144&r2=922145&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/util/URLChangeTracker.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/util/URLChangeTracker.java Fri Mar 12 05:21:12 2010
@@ -15,11 +15,11 @@
 package org.apache.tapestry5.internal.util;
 
 import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
+import org.apache.tapestry5.ioc.services.ClassFabUtils;
 import org.apache.tapestry5.ioc.services.ClasspathURLConverter;
 
 import java.io.File;
 import java.io.IOException;
-import java.net.URISyntaxException;
 import java.net.URL;
 import java.util.Map;
 
@@ -80,7 +80,7 @@ public class URLChangeTracker
 
         if (!converted.getProtocol().equals("file")) return timestampForNonFileURL(converted);
 
-        File resourceFile = toFile(converted);
+        File resourceFile = ClassFabUtils.toFileFromFileProtocolURL(converted);
 
         if (fileToTimestamp.containsKey(resourceFile)) return fileToTimestamp.get(resourceFile);
 
@@ -119,20 +119,6 @@ public class URLChangeTracker
         return applyGranularity(timestamp);
     }
 
-    private File toFile(URL url)
-    {
-        // http://weblogs.java.net/blog/kohsuke/archive/2007/04/how_to_convert.html
-
-        try
-        {
-            return new File(url.toURI());
-        }
-        catch (URISyntaxException ex)
-        {
-            return new File(url.getPath());
-        }
-    }
-
     /**
      * Clears all URL and timestamp data stored in the tracker.
      */

Added: tapestry/tapestry5/trunk/tapestry-core/src/test/app1/ReloadDemo.tml
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/app1/ReloadDemo.tml?rev=922145&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/app1/ReloadDemo.tml (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/app1/ReloadDemo.tml Fri Mar 12 05:21:12 2010
@@ -0,0 +1,16 @@
+<html t:type="Border" xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd">
+
+  <h1>Service Implementation Reload Demo</h1>
+
+
+  <p>
+    Reloadable service status:
+    <strong id="status">${reloadable.status}</strong>
+  </p>
+
+  <p>
+    <t:pagelink page="reloaddemo">reload page</t:pagelink>
+  </p>
+
+</html>
+

Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/Index.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/Index.java?rev=922145&r1=922144&r2=922145&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/Index.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/Index.java Fri Mar 12 05:21:12 2010
@@ -67,6 +67,9 @@ public class Index
     private static final List<Item> ITEMS = CollectionFactory
             .newList(
 
+                    new Item("ReloadDemo", "Reloadable Service Implementation Demo",
+                            "Used when manually testing service reloads"),
+                    
                     new Item("QueryParameterDemo", "QueryParameter Annotation Demo",
                             "Use of @QueryParameter annotation on event handler method parameters"),
 

Copied: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/ReloadDemo.java (from r922144, tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/internal/UninstantiableAutobuildServiceModule.java)
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/ReloadDemo.java?p2=tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/ReloadDemo.java&p1=tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/internal/UninstantiableAutobuildServiceModule.java&r1=922144&r2=922145&rev=922145&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/internal/UninstantiableAutobuildServiceModule.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/ReloadDemo.java Fri Mar 12 05:21:12 2010
@@ -1,10 +1,10 @@
-// Copyright 2007 The Apache Software Foundation
+// Copyright 2010 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
+// 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,
@@ -12,14 +12,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package org.apache.tapestry5.ioc.internal;
+package org.apache.tapestry5.integration.app1.pages;
 
-import org.apache.tapestry5.ioc.ServiceBinder;
+import org.apache.tapestry5.annotations.Property;
+import org.apache.tapestry5.integration.app1.services.Reloadable;
+import org.apache.tapestry5.ioc.annotations.Inject;
 
-public class UninstantiableAutobuildServiceModule
+public class ReloadDemo
 {
-    public static void bind(ServiceBinder binder)
-    {
-        binder.bind(Runnable.class, RunnableServiceImpl.class);
-    }
+    @Property
+    @Inject
+    private Reloadable reloadable;
 }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/services/AppModule.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/services/AppModule.java?rev=922145&r1=922144&r2=922145&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/services/AppModule.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/services/AppModule.java Fri Mar 12 05:21:12 2010
@@ -4,7 +4,7 @@
 // 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
+// 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,
@@ -22,6 +22,7 @@ import org.apache.tapestry5.internal.ser
 import org.apache.tapestry5.ioc.Configuration;
 import org.apache.tapestry5.ioc.MappedConfiguration;
 import org.apache.tapestry5.ioc.OrderedConfiguration;
+import org.apache.tapestry5.ioc.ServiceBinder;
 import org.apache.tapestry5.ioc.annotations.Marker;
 import org.apache.tapestry5.ioc.annotations.Symbol;
 import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
@@ -50,7 +51,7 @@ public class AppModule
      * interface.
      */
     @Target(
-            { PARAMETER, FIELD })
+    { PARAMETER, FIELD })
     @Retention(RUNTIME)
     @Documented
     public @interface Local
@@ -58,6 +59,11 @@ public class AppModule
 
     }
 
+    public static void bind(ServiceBinder binder)
+    {
+        binder.bind(Reloadable.class);
+    }
+
     public void contributeAlias(Configuration<AliasContribution> configuration)
     {
         BaseURLSource source = new BaseURLSource()
@@ -66,7 +72,7 @@ public class AppModule
             {
                 String protocol = secure ? "https" : "http";
 
-                // This is all a bit jury-rigged together.  This is for running the app
+                // This is all a bit jury-rigged together. This is for running the app
                 // interactively; Selenium doesn't seem to handle the transition to https.
                 int port = secure ? JettyRunner.DEFAULT_SECURE_PORT : JettyRunner.DEFAULT_PORT;
 
@@ -102,7 +108,8 @@ public class AppModule
 
     public void contributeRequestHandler(OrderedConfiguration<RequestFilter> configuration,
 
-                                         @Local RequestFilter filter)
+    @Local
+    RequestFilter filter)
     {
         configuration.add("Timing", filter);
     }
@@ -128,7 +135,7 @@ public class AppModule
         configuration.add(SymbolConstants.SECURE_ENABLED, "true");
 
         configuration.add("app.injected-symbol", "Symbol contributed to ApplicationDefaults");
-        
+
         configuration.add(SymbolConstants.BLACKBIRD_ENABLED, "true");
     }
 
@@ -161,7 +168,8 @@ public class AppModule
             {
                 Track result = idToTrack.get(id);
 
-                if (result != null) return result;
+                if (result != null)
+                    return result;
 
                 throw new IllegalArgumentException(String.format("No track with id #%d.", id));
             }
@@ -179,7 +187,8 @@ public class AppModule
 
                 for (Track t : tracks)
                 {
-                    if (t.getTitle().toLowerCase().contains(titleLower)) result.add(t);
+                    if (t.getTitle().toLowerCase().contains(titleLower))
+                        result.add(t);
                 }
 
                 return result;
@@ -213,8 +222,7 @@ public class AppModule
     }
 
     public static void contributeValueEncoderSource(MappedConfiguration<Class, ValueEncoderFactory> configuration,
-                                                    final MusicLibrary library,
-                                                    final ToDoDatabase todoDatabase)
+            final MusicLibrary library, final ToDoDatabase todoDatabase)
     {
         ValueEncoder<Track> trackEncoder = new ValueEncoder<Track>()
         {
@@ -231,7 +239,6 @@ public class AppModule
             }
         };
 
-
         configuration.add(Track.class, GenericValueEncoderFactory.create(trackEncoder));
 
         ValueEncoder<ToDoItem> todoEncoder = new ValueEncoder<ToDoItem>()
@@ -252,7 +259,6 @@ public class AppModule
         configuration.add(ToDoItem.class, GenericValueEncoderFactory.create(todoEncoder));
     }
 
-
     public static void contributeComponentClassTransformWorker(
             OrderedConfiguration<ComponentClassTransformWorker> configuration)
     {

Copied: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/services/Reloadable.java (from r922144, tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/internal/UninstantiableAutobuildServiceModule.java)
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/services/Reloadable.java?p2=tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/services/Reloadable.java&p1=tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/internal/UninstantiableAutobuildServiceModule.java&r1=922144&r2=922145&rev=922145&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/internal/UninstantiableAutobuildServiceModule.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/services/Reloadable.java Fri Mar 12 05:21:12 2010
@@ -1,10 +1,10 @@
-// Copyright 2007 The Apache Software Foundation
+// Copyright 2010 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
+// 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,
@@ -12,14 +12,9 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package org.apache.tapestry5.ioc.internal;
+package org.apache.tapestry5.integration.app1.services;
 
-import org.apache.tapestry5.ioc.ServiceBinder;
-
-public class UninstantiableAutobuildServiceModule
+public interface Reloadable
 {
-    public static void bind(ServiceBinder binder)
-    {
-        binder.bind(Runnable.class, RunnableServiceImpl.class);
-    }
+    String getStatus();
 }

Copied: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/services/ReloadableImpl.java (from r922144, tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/internal/UninstantiableAutobuildServiceModule.java)
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/services/ReloadableImpl.java?p2=tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/services/ReloadableImpl.java&p1=tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/internal/UninstantiableAutobuildServiceModule.java&r1=922144&r2=922145&rev=922145&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/internal/UninstantiableAutobuildServiceModule.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/services/ReloadableImpl.java Fri Mar 12 05:21:12 2010
@@ -1,10 +1,10 @@
-// Copyright 2007 The Apache Software Foundation
+// Copyright 2010 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
+// 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,
@@ -12,14 +12,13 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package org.apache.tapestry5.ioc.internal;
+package org.apache.tapestry5.integration.app1.services;
 
-import org.apache.tapestry5.ioc.ServiceBinder;
-
-public class UninstantiableAutobuildServiceModule
+public class ReloadableImpl implements Reloadable
 {
-    public static void bind(ServiceBinder binder)
+    public String getStatus()
     {
-        binder.bind(Runnable.class, RunnableServiceImpl.class);
+        return "Initial Value";
     }
+
 }

Modified: tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/ModuleImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/ModuleImpl.java?rev=922145&r1=922144&r2=922145&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/ModuleImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/ModuleImpl.java Fri Mar 12 05:21:12 2010
@@ -4,7 +4,7 @@
 // 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
+// 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,
@@ -70,7 +70,7 @@ public class ModuleImpl implements Modul
     private final Logger logger;
 
     /**
-     * Lazily instantiated.  Access is guarded by BARRIER.
+     * Lazily instantiated. Access is guarded by BARRIER.
      */
     private Object moduleInstance;
 
@@ -93,7 +93,7 @@ public class ModuleImpl implements Modul
     private final static ConcurrentBarrier BARRIER = new ConcurrentBarrier();
 
     public ModuleImpl(InternalRegistry registry, ServiceActivityTracker tracker, ModuleDef moduleDef,
-                      ClassFactory classFactory, Logger logger)
+            ClassFactory classFactory, Logger logger)
     {
         this.registry = registry;
         this.tracker = tracker;
@@ -111,7 +111,6 @@ public class ModuleImpl implements Modul
         }
     }
 
-
     public <T> T getService(String serviceId, Class<T> serviceInterface)
     {
         Defense.notBlank(serviceId, "serviceId");
@@ -135,8 +134,8 @@ public class ModuleImpl implements Modul
             // given that the return type of the method determines
             // the service interface.
 
-            throw new RuntimeException(IOCMessages.serviceWrongInterface(serviceId, def
-                    .getServiceInterface(), serviceInterface));
+            throw new RuntimeException(IOCMessages.serviceWrongInterface(serviceId, def.getServiceInterface(),
+                    serviceInterface));
         }
     }
 
@@ -146,7 +145,8 @@ public class ModuleImpl implements Modul
 
         for (DecoratorDef def : moduleDef.getDecoratorDefs())
         {
-            if (def.matches(serviceDef)) result.add(def);
+            if (def.matches(serviceDef))
+                result.add(def);
         }
 
         return result;
@@ -158,7 +158,8 @@ public class ModuleImpl implements Modul
 
         for (AdvisorDef def : moduleDef.getAdvisorDefs())
         {
-            if (def.matches(serviceDef)) result.add(def);
+            if (def.matches(serviceDef))
+                result.add(def);
         }
 
         return result;
@@ -182,13 +183,14 @@ public class ModuleImpl implements Modul
 
     /**
      * Locates the service proxy for a particular service (from the service definition).
-     *
-     * @param def              defines the service
-     * @param eagerLoadProxies collection into which proxies for eager loaded services are added (or null)
+     * 
+     * @param def
+     *            defines the service
+     * @param eagerLoadProxies
+     *            collection into which proxies for eager loaded services are added (or null)
      * @return the service proxy
      */
-    private Object findOrCreate(final ServiceDef2 def,
-                                final Collection<EagerLoadServiceProxy> eagerLoadProxies)
+    private Object findOrCreate(final ServiceDef2 def, final Collection<EagerLoadServiceProxy> eagerLoadProxies)
     {
         final String key = def.getServiceId();
 
@@ -239,7 +241,8 @@ public class ModuleImpl implements Modul
             {
                 for (ServiceDef2 def : serviceDefs.values())
                 {
-                    if (def.isEagerLoad()) findOrCreate(def, proxies);
+                    if (def.isEagerLoad())
+                        findOrCreate(def, proxies);
                 }
             }
         };
@@ -247,11 +250,11 @@ public class ModuleImpl implements Modul
         registry.run("Eager loading services", work);
     }
 
-
     /**
      * Creates the service and updates the cache of created services.
-     *
-     * @param eagerLoadProxies a list into which any eager loaded proxies should be added
+     * 
+     * @param eagerLoadProxies
+     *            a list into which any eager loaded proxies should be added
      */
     private Object create(final ServiceDef2 def, final Collection<EagerLoadServiceProxy> eagerLoadProxies)
     {
@@ -272,9 +275,8 @@ public class ModuleImpl implements Modul
             {
                 try
                 {
-                    ServiceBuilderResources resources = new ServiceResourcesImpl(registry, module, def,
-                                                                                 classFactory,
-                                                                                 logger);
+                    ServiceBuilderResources resources = new ServiceResourcesImpl(registry, module, def, classFactory,
+                            logger);
 
                     // Build up a stack of operations that will be needed to realize the service
                     // (by the proxy, at a later date).
@@ -285,7 +287,6 @@ public class ModuleImpl implements Modul
 
                     ServiceLifecycle2 lifecycle = registry.getServiceLifecycle(def.getServiceScope());
 
-
                     // For non-proxyable services, we immediately create the service implementation
                     // and return it. There's no interface to proxy, which throws out the possibility of
                     // deferred instantiation, service lifecycles, and decorators.
@@ -293,9 +294,11 @@ public class ModuleImpl implements Modul
                     if (!serviceInterface.isInterface())
                     {
                         if (lifecycle.requiresProxy())
-                            throw new IllegalArgumentException(String.format(
-                                    "Service scope '%s' requires a proxy, but the service does not have a service interface (necessary to create a proxy). Provide a service interface or select a different service scope.",
-                                    def.getServiceScope()));
+                            throw new IllegalArgumentException(
+                                    String
+                                            .format(
+                                                    "Service scope '%s' requires a proxy, but the service does not have a service interface (necessary to create a proxy). Provide a service interface or select a different service scope.",
+                                                    def.getServiceScope()));
 
                         return creator.createObject();
                     }
@@ -308,7 +311,9 @@ public class ModuleImpl implements Modul
                     // TapestryIOCModule prevents decoration of its services. Note that all decorators will decorate
                     // around the aspect interceptor, which wraps around the core service implementation.
 
-                    if (!def.isPreventDecoration())
+                    boolean allowDecoration = !def.isPreventDecoration();
+
+                    if (allowDecoration)
                     {
                         creator = new AdvisorStackBuilder(def, creator, getAspectDecorator(), registry);
                         creator = new InterceptorStackBuilder(def, creator, registry);
@@ -326,11 +331,9 @@ public class ModuleImpl implements Modul
 
                     registry.addRegistryShutdownListener(delegate);
 
-                    // Occasionally service A may invoke service B from its service builder method; if
-                    // service B
-                    // is eager loaded, we'll hit this method but eagerLoadProxies will be null. That's OK
-                    // ... service B
-                    // is being realized anyway.
+                    // Occasionally eager load service A may invoke service B from its service builder method; if
+                    // service B is eager loaded, we'll hit this method but eagerLoadProxies will be null. That's OK
+                    // ... service B is being realized anyway.
 
                     if (def.isEagerLoad() && eagerLoadProxies != null)
                         eagerLoadProxies.add(delegate);
@@ -351,15 +354,13 @@ public class ModuleImpl implements Modul
 
     private AspectDecorator getAspectDecorator()
     {
-        return registry.invoke(
-                "Obtaining AspectDecorator service",
-                new Invokable<AspectDecorator>()
-                {
-                    public AspectDecorator invoke()
-                    {
-                        return registry.getService(AspectDecorator.class);
-                    }
-                });
+        return registry.invoke("Obtaining AspectDecorator service", new Invokable<AspectDecorator>()
+        {
+            public AspectDecorator invoke()
+            {
+                return registry.getService(AspectDecorator.class);
+            }
+        });
     }
 
     private final Runnable instantiateModule = new Runnable()
@@ -367,13 +368,13 @@ public class ModuleImpl implements Modul
         public void run()
         {
             moduleInstance = registry.invoke("Constructing module class " + moduleDef.getBuilderClass().getName(),
-                                             new Invokable()
-                                             {
-                                                 public Object invoke()
-                                                 {
-                                                     return instantiateModuleInstance();
-                                                 }
-                                             });
+                    new Invokable()
+                    {
+                        public Object invoke()
+                        {
+                            return instantiateModuleInstance();
+                        }
+                    });
         }
     };
 
@@ -381,7 +382,8 @@ public class ModuleImpl implements Modul
     {
         public Object invoke()
         {
-            if (moduleInstance == null) BARRIER.withWrite(instantiateModule);
+            if (moduleInstance == null)
+                BARRIER.withWrite(instantiateModule);
 
             return moduleInstance;
         }
@@ -398,7 +400,8 @@ public class ModuleImpl implements Modul
 
         Constructor[] constructors = moduleClass.getConstructors();
 
-        if (constructors.length == 0) throw new RuntimeException(IOCMessages.noPublicConstructors(moduleClass));
+        if (constructors.length == 0)
+            throw new RuntimeException(IOCMessages.noPublicConstructors(moduleClass));
 
         if (constructors.length > 1)
         {
@@ -439,11 +442,9 @@ public class ModuleImpl implements Modul
         {
             insideConstructor = true;
 
-            Object[] parameterValues = InternalUtils.calculateParameters(locator, resources,
-                                                                         constructor.getParameterTypes(),
-                                                                         constructor.getGenericParameterTypes(),
-                                                                         constructor.getParameterAnnotations(),
-                                                                         registry);
+            Object[] parameterValues = InternalUtils.calculateParameters(locator, resources, constructor
+                    .getParameterTypes(), constructor.getGenericParameterTypes(),
+                    constructor.getParameterAnnotations(), registry);
 
             Object result = constructor.newInstance(parameterValues);
 
@@ -478,7 +479,7 @@ public class ModuleImpl implements Modul
     }
 
     private Object createProxyInstance(ObjectCreator creator, String serviceId, Class serviceInterface,
-                                       String description)
+            String description)
     {
         ServiceProxyToken token = SerializationSupport.createToken(serviceId);
 
@@ -487,8 +488,8 @@ public class ModuleImpl implements Modul
         classFab.addField("creator", Modifier.PRIVATE | Modifier.FINAL, ObjectCreator.class);
         classFab.addField("token", Modifier.PRIVATE | Modifier.FINAL, ServiceProxyToken.class);
 
-        classFab.addConstructor(new Class[] { ObjectCreator.class, ServiceProxyToken.class }, null,
-                                "{ creator = $1; token = $2; }");
+        classFab.addConstructor(new Class[]
+        { ObjectCreator.class, ServiceProxyToken.class }, null, "{ creator = $1; token = $2; }");
 
         // Make proxies serializable by writing the token to the stream.
 
@@ -496,8 +497,8 @@ public class ModuleImpl implements Modul
 
         // This is the "magic" signature that allows an object to substitute some other
         // object for itself.
-        MethodSignature writeReplaceSig = new MethodSignature(Object.class, "writeReplace", null,
-                                                              new Class[] { ObjectStreamException.class });
+        MethodSignature writeReplaceSig = new MethodSignature(Object.class, "writeReplace", null, new Class[]
+        { ObjectStreamException.class });
 
         classFab.addMethod(Modifier.PRIVATE, writeReplaceSig, "return token;");
 
@@ -532,24 +533,25 @@ public class ModuleImpl implements Modul
         for (ContributionDef next : moduleDef.getContributionDefs())
         {
             ContributionDef2 def = InternalUtils.toContributionDef2(next);
-            
-            if (serviceDef.getServiceId().equals(def.getServiceId())) 
+
+            if (serviceDef.getServiceId().equals(def.getServiceId()))
             {
                 result.add(def);
             }
             else
             {
                 Set<Class> markers = CollectionFactory.newSet(def.getMarkers());
-                
-                if(markers.contains(Local.class))
+
+                if (markers.contains(Local.class))
                 {
-                    if(moduleDef.getServiceDef(serviceDef.getServiceId()) == null)
+                    if (moduleDef.getServiceDef(serviceDef.getServiceId()) == null)
                         continue;
-                    
+
                     markers.remove(Local.class);
                 }
-                
-                if(serviceDef.getMarkers().equals(markers) && serviceDef.getServiceInterface() == def.getServiceInterface())
+
+                if (serviceDef.getMarkers().equals(markers)
+                        && serviceDef.getServiceInterface() == def.getServiceInterface())
                 {
                     result.add(def);
                 }
@@ -558,7 +560,6 @@ public class ModuleImpl implements Modul
 
         return result;
     }
-    
 
     public ServiceDef2 getServiceDef(String serviceId)
     {
@@ -575,4 +576,4 @@ public class ModuleImpl implements Modul
     {
         return String.format("ModuleImpl[%s]", moduleDef.getLoggerName());
     }
-}
\ No newline at end of file
+}

Added: tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/ReloadableObjectCreator.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/ReloadableObjectCreator.java?rev=922145&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/ReloadableObjectCreator.java (added)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/ReloadableObjectCreator.java Fri Mar 12 05:21:12 2010
@@ -0,0 +1,225 @@
+// Copyright 2010 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.tapestry5.ioc.internal;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Constructor;
+import java.net.URL;
+import java.security.ProtectionDomain;
+
+import org.apache.tapestry5.ioc.ObjectCreator;
+import org.apache.tapestry5.ioc.ServiceBuilderResources;
+import org.apache.tapestry5.ioc.internal.util.InternalUtils;
+import org.apache.tapestry5.ioc.services.ClassFabUtils;
+import org.apache.tapestry5.services.UpdateListener;
+import org.slf4j.Logger;
+
+/**
+ * Returns an {@link ObjectCreator} for lazily instantiation a given implementation class (with dependencies).
+ * Once an instance is instantiated, it is cached ... until the underlying .class file changes, at which point
+ * the class is reloaded and a new instance instantiated.
+ */
+public class ReloadableObjectCreator implements ObjectCreator, UpdateListener
+{
+    private class ReloadingClassLoader extends ClassLoader
+    {
+        private ReloadingClassLoader(ClassLoader parent)
+        {
+            super(parent);
+        }
+
+        @Override
+        public Class<?> loadClass(String name) throws ClassNotFoundException
+        {
+            if (isReloadingClass(name))
+            {
+                byte[] classData = readClassData(name);
+
+                return defineClass(name, classData, 0, classData.length);
+            }
+
+            return super.loadClass(name);
+        }
+
+        private boolean isReloadingClass(String name)
+        {
+            // This class loader exists to reload the implementation class and any inner classes of the
+            // implementation class.
+            return name.equals(implementationClassName) || name.startsWith(implementationClassName + "$");
+        }
+    }
+
+    private final ServiceBuilderResources resources;
+
+    private final ClassLoader baseClassLoader;
+
+    private final String implementationClassName;
+
+    private final ProtectionDomain protectionDomain;
+
+    private final String classFilePath;
+
+    private final Logger logger;
+
+    private Object instance;
+
+    private File classFile;
+
+    private long lastModifiedTimestamp = 0;
+
+    private boolean firstTime = true;
+
+    public ReloadableObjectCreator(ServiceBuilderResources resources, ClassLoader baseClassLoader,
+            String implementationClassName, ProtectionDomain protectionDomain)
+    {
+        this.resources = resources;
+        this.baseClassLoader = baseClassLoader;
+        this.implementationClassName = implementationClassName;
+        this.protectionDomain = protectionDomain;
+
+        this.classFilePath = ClassFabUtils.getPathForClassNamed(implementationClassName);
+
+        logger = resources.getLogger();
+    }
+
+    public synchronized void checkForUpdates()
+    {
+        if (instance == null)
+            return;
+
+        if (classFile.lastModified() == lastModifiedTimestamp)
+            return;
+
+        if (logger.isDebugEnabled())
+            logger.debug(String.format("Implementation class %s has changed and will be reloaded on next use.",
+                    implementationClassName));
+
+        instance = null;
+        classFile = null;
+        lastModifiedTimestamp = 0;
+    }
+
+    public synchronized Object createObject()
+    {
+        if (instance == null)
+            instance = createInstance();
+
+        return instance;
+    }
+
+    private Object createInstance()
+    {
+        updateTrackingInfo();
+
+        Class reloadedClass = reloadImplementationClass();
+
+        final Constructor constructor = InternalUtils.findAutobuildConstructor(reloadedClass);
+
+        if (constructor == null)
+            throw new RuntimeException(String.format(
+                    "Service implementation class %s does not have a suitable public constructor.",
+                    implementationClassName));
+
+        ObjectCreator constructorServiceCreator = new ConstructorServiceCreator(resources, String.format(
+                "%s (last modified %tc)", constructor, lastModifiedTimestamp), constructor);
+
+        return constructorServiceCreator.createObject();
+    }
+
+    private Class reloadImplementationClass()
+    {
+        if (logger.isDebugEnabled())
+            logger.debug("%s class %s.", firstTime ? "Loading" : "Reloading", implementationClassName);
+
+        ClassLoader reloadingClassLoader = new ReloadingClassLoader(baseClassLoader);
+
+        try
+        {
+            Class result = reloadingClassLoader.loadClass(implementationClassName);
+
+            firstTime = false;
+
+            return result;
+        }
+        catch (ClassNotFoundException ex)
+        {
+            throw new RuntimeException(String.format("Unable to %s class %s: %s", firstTime ? "load" : "reload",
+                    implementationClassName, InternalUtils.toMessage(ex)), ex);
+        }
+    }
+
+    private byte[] readClassData(String name) throws ClassNotFoundException
+    {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+
+        byte[] buffer = new byte[10000];
+
+        URL url = getURLForClass(name);
+
+        InputStream in = null;
+
+        try
+        {
+            in = url.openStream();
+
+            while (true)
+            {
+                int length = in.read(buffer);
+
+                if (length < 0)
+                    break;
+
+                baos.write(buffer, 0, length);
+            }
+
+            in.close();
+
+            in = null;
+        }
+        catch (IOException ex)
+        {
+            InternalUtils.close(in);
+
+            throw new ClassNotFoundException(InternalUtils.toMessage(ex), ex);
+        }
+
+        return baos.toByteArray();
+    }
+
+    private URL getURLForClass(String className) throws ClassNotFoundException
+    {
+        String path = ClassFabUtils.getPathForClassNamed(className);
+
+        URL result = baseClassLoader.getResource(path);
+
+        if (result == null)
+            throw new ClassNotFoundException(String.format("Unable to locate URL for class %s.", className));
+
+        return result;
+    }
+
+    private void updateTrackingInfo()
+    {
+        URL url = baseClassLoader.getResource(classFilePath);
+
+        classFile = ClassFabUtils.toFileFromFileProtocolURL(url);
+
+        lastModifiedTimestamp = classFile.lastModified();
+    }
+
+}

Propchange: tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/ReloadableObjectCreator.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/ReloadableObjectCreatorSource.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/ReloadableObjectCreatorSource.java?rev=922145&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/ReloadableObjectCreatorSource.java (added)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/ReloadableObjectCreatorSource.java Fri Mar 12 05:21:12 2010
@@ -0,0 +1,73 @@
+// Copyright 2010 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.tapestry5.ioc.internal;
+
+import java.lang.reflect.Method;
+
+import org.apache.tapestry5.ioc.ObjectCreator;
+import org.apache.tapestry5.ioc.ServiceBuilderResources;
+import org.apache.tapestry5.ioc.services.ClassFactory;
+import org.apache.tapestry5.services.UpdateListenerHub;
+
+/**
+ * Responsible for creating a {@link ReloadableObjectCreator} for a service implementation.
+ */
+public class ReloadableObjectCreatorSource implements ObjectCreatorSource
+{
+    private final ClassFactory classFactory;
+
+    private final Method bindMethod;
+
+    private final Class serviceInterfaceClass;
+
+    private final Class serviceImplementationClass;
+
+    public ReloadableObjectCreatorSource(ClassFactory classFactory, Method bindMethod, Class serviceInterfaceClass,
+            Class serviceImplementationClass)
+    {
+        this.classFactory = classFactory;
+        this.bindMethod = bindMethod;
+        this.serviceInterfaceClass = serviceInterfaceClass;
+        this.serviceImplementationClass = serviceImplementationClass;
+    }
+
+    public ObjectCreator constructCreator(final ServiceBuilderResources resources)
+    {
+        return new ObjectCreator()
+        {
+            public Object createObject()
+            {
+                return createReloadableProxy(resources);
+            }
+        };
+    }
+
+    public String getDescription()
+    {
+        return String.format("Reloadable %s via %s", serviceImplementationClass.getName(), classFactory
+                .getMethodLocation(bindMethod));
+    }
+
+    private Object createReloadableProxy(ServiceBuilderResources resources)
+    {
+        ReloadableObjectCreator reloadableCreator = new ReloadableObjectCreator(resources, classFactory
+                .getClassLoader(), serviceImplementationClass.getName(), serviceImplementationClass
+                .getProtectionDomain());
+
+        resources.getService(UpdateListenerHub.class).addUpdateListener(reloadableCreator);
+
+        return classFactory.createProxy(serviceInterfaceClass, reloadableCreator, getDescription());
+    }
+}

Propchange: tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/ReloadableObjectCreatorSource.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/ServiceBinderImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/ServiceBinderImpl.java?rev=922145&r1=922144&r2=922145&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/ServiceBinderImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/ServiceBinderImpl.java Fri Mar 12 05:21:12 2010
@@ -4,7 +4,7 @@
 // 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
+// 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,
@@ -25,11 +25,13 @@ import org.apache.tapestry5.ioc.internal
 import org.apache.tapestry5.ioc.internal.util.Defense;
 import org.apache.tapestry5.ioc.internal.util.InternalUtils;
 import org.apache.tapestry5.ioc.internal.util.OneShotLock;
+import org.apache.tapestry5.ioc.services.ClassFabUtils;
 import org.apache.tapestry5.ioc.services.ClassFactory;
 
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Method;
+import java.net.URL;
 import java.util.Arrays;
 import java.util.Set;
 
@@ -47,9 +49,8 @@ public class ServiceBinderImpl implement
 
     private final boolean moduleDefaultPreventDecoration;
 
-    public ServiceBinderImpl(ServiceDefAccumulator accumulator, Method bindMethod,
-                             ClassFactory classFactory,
-                             Set<Class> defaultMarkers, boolean moduleDefaultPreventDecoration)
+    public ServiceBinderImpl(ServiceDefAccumulator accumulator, Method bindMethod, ClassFactory classFactory,
+            Set<Class> defaultMarkers, boolean moduleDefaultPreventDecoration)
     {
         this.accumulator = accumulator;
         this.bindMethod = bindMethod;
@@ -85,7 +86,8 @@ public class ServiceBinderImpl implement
 
     protected void flush()
     {
-        if (serviceInterface == null) return;
+        if (serviceInterface == null)
+            return;
 
         // source will be null when the implementation class is provided; non-null when using
         // a ServiceBuilder callback
@@ -98,7 +100,7 @@ public class ServiceBinderImpl implement
         markers.addAll(this.markers);
 
         ServiceDef serviceDef = new ServiceDefImpl(serviceInterface, serviceId, markers, scope, eagerLoad,
-                                                   preventDecoration, source);
+                preventDecoration, source);
 
         accumulator.addServiceDef(serviceDef);
 
@@ -119,6 +121,38 @@ public class ServiceBinderImpl implement
 
     private ObjectCreatorSource createObjectCreatorSourceFromImplementationClass()
     {
+        if (preventDecoration || !isProxiable() || !reloadableScope() || !isLocalFile(serviceImplementation))
+            return createStandardConstructorBasedObjectCreatorSource();
+
+        return createReloadableConstructorBasedObjectCreatorSource();
+    }
+
+    private boolean isProxiable()
+    {
+        return serviceInterface.isInterface();
+    }
+
+    private boolean reloadableScope()
+    {
+        return scope.equalsIgnoreCase(ScopeConstants.DEFAULT);
+    }
+
+    /**
+     * Determines if the indicated class is stored as a locally accessible file
+     * (and not, typically, as a file inside a JAR). This is related to automatic
+     * reloading of services.
+     */
+    private boolean isLocalFile(Class clazz)
+    {
+        String path = ClassFabUtils.getPathForClass(clazz);
+
+        URL classFileURL = clazz.getClassLoader().getResource(path);
+
+        return classFileURL != null && classFileURL.getProtocol().equals("file");
+    }
+
+    private ObjectCreatorSource createStandardConstructorBasedObjectCreatorSource()
+    {
         final Constructor constructor = InternalUtils.findAutobuildConstructor(serviceImplementation);
 
         if (constructor == null)
@@ -133,13 +167,17 @@ public class ServiceBinderImpl implement
 
             public String getDescription()
             {
-                return String.format("%s via %s",
-                                     classFactory.getConstructorLocation(constructor),
-                                     classFactory.getMethodLocation(bindMethod));
+                return String.format("%s via %s", classFactory.getConstructorLocation(constructor), classFactory
+                        .getMethodLocation(bindMethod));
             }
         };
     }
 
+    private ObjectCreatorSource createReloadableConstructorBasedObjectCreatorSource()
+    {
+        return new ReloadableObjectCreatorSource(classFactory, bindMethod, serviceInterface, serviceImplementation);
+    }
+
     public <T> ServiceBindingOptions bind(Class<T> serviceClass)
     {
         if (serviceClass.isInterface())
@@ -148,10 +186,8 @@ public class ServiceBinderImpl implement
             {
                 Class<T> implementationClass = (Class<T>) Class.forName(serviceClass.getName() + "Impl");
 
-                if (!implementationClass.isInterface() && serviceClass.isAssignableFrom(implementationClass))
-                {
-                    return bind(serviceClass, implementationClass);
-                }
+                if (!implementationClass.isInterface() && serviceClass.isAssignableFrom(implementationClass)) { return bind(
+                        serviceClass, implementationClass); }
                 throw new RuntimeException(IOCMessages.noServiceMatchesType(serviceClass));
             }
             catch (ClassNotFoundException ex)
@@ -213,13 +249,12 @@ public class ServiceBinderImpl implement
         this.serviceImplementation = serviceImplementation;
 
         // Set defaults for the other properties.
-        
-        
+
         eagerLoad = serviceImplementation.getAnnotation(EagerLoad.class) != null;
-        
+
         ServiceId serviceIdAnnotation = serviceImplementation.getAnnotation(ServiceId.class);
-        
-        if(serviceIdAnnotation != null)
+
+        if (serviceIdAnnotation != null)
         {
             serviceId = serviceIdAnnotation.value();
         }

Modified: tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/ClassFabUtils.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/ClassFabUtils.java?rev=922145&r1=922144&r2=922145&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/ClassFabUtils.java (original)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/ClassFabUtils.java Fri Mar 12 05:21:12 2010
@@ -15,11 +15,17 @@
 package org.apache.tapestry5.ioc.services;
 
 import org.apache.tapestry5.ioc.ObjectCreator;
+import org.apache.tapestry5.ioc.internal.util.Defense;
+
 import static org.apache.tapestry5.ioc.internal.util.CollectionFactory.newMap;
 
 import static java.lang.String.format;
+
+import java.io.File;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
+import java.net.URISyntaxException;
+import java.net.URL;
 import java.util.Map;
 import java.util.concurrent.atomic.AtomicLong;
 
@@ -292,4 +298,51 @@ public final class ClassFabUtils
             throw new RuntimeException(ex.getMessage(), ex);
         }
     }
+
+    /**
+     * Given a Class instance, convert the name into a path that can be used to locate
+     * the underlying class file on the classpath.
+     * 
+     * @since 5.2.0
+     */
+    public static String getPathForClass(Class clazz)
+    {
+        Defense.notNull(clazz, "clazz");
+
+        return getPathForClassNamed(clazz.getName());
+    }
+
+    /**
+     * Given a fully qualified class name, converts to a path on the classpath.
+     * 
+     * @since 5.2.0
+     */
+    public static String getPathForClassNamed(String className)
+    {
+        return className.replace('.', '/') + ".class";
+    }
+
+    /**
+     * Converts a URL with protocol "file" to a File instance.
+     * 
+     * @since 5.2.0
+     */
+    public static File toFileFromFileProtocolURL(URL url)
+    {
+        Defense.notNull(url, "url");
+
+        if (!url.getProtocol().equals("file"))
+            throw new IllegalArgumentException(String.format("URL %s does not use the 'file' protocol.", url));
+
+        // http://weblogs.java.net/blog/kohsuke/archive/2007/04/how_to_convert.html
+
+        try
+        {
+            return new File(url.toURI());
+        }
+        catch (URISyntaxException ex)
+        {
+            return new File(url.getPath());
+        }
+    }
 }

Modified: tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/IntegrationTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/IntegrationTest.java?rev=922145&r1=922144&r2=922145&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/IntegrationTest.java (original)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/IntegrationTest.java Fri Mar 12 05:21:12 2010
@@ -426,8 +426,9 @@ public class IntegrationTest extends IOC
             assertMessageContains(
                     ex,
                     "Error invoking constructor",
-                    "ExceptionInConstructorServiceImpl() (at ExceptionInConstructorServiceImpl.java",
-                    "for service 'Pingable'", "Yes, we have no tomatoes.");
+                    "ExceptionInConstructorServiceImpl()",
+                    "for service 'Pingable'", 
+                    "Yes, we have no tomatoes.");
         }
 
         r.shutdown();

Modified: tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/internal/MutlipleAutobuildServiceConstructorsModule.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/internal/MutlipleAutobuildServiceConstructorsModule.java?rev=922145&r1=922144&r2=922145&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/internal/MutlipleAutobuildServiceConstructorsModule.java (original)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/internal/MutlipleAutobuildServiceConstructorsModule.java Fri Mar 12 05:21:12 2010
@@ -1,4 +1,4 @@
-// Copyright 2007 The Apache Software Foundation
+// Copyright 2007, 2010 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.
@@ -16,7 +16,9 @@ package org.apache.tapestry5.ioc.interna
 
 import org.apache.tapestry5.ioc.ServiceBinder;
 import org.apache.tapestry5.ioc.StringHolder;
+import org.apache.tapestry5.ioc.annotations.PreventServiceDecoration;
 
+@PreventServiceDecoration
 public class MutlipleAutobuildServiceConstructorsModule
 {
     public static void bind(ServiceBinder binder)

Modified: tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/internal/UninstantiableAutobuildServiceModule.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/internal/UninstantiableAutobuildServiceModule.java?rev=922145&r1=922144&r2=922145&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/internal/UninstantiableAutobuildServiceModule.java (original)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/internal/UninstantiableAutobuildServiceModule.java Fri Mar 12 05:21:12 2010
@@ -1,4 +1,4 @@
-// Copyright 2007 The Apache Software Foundation
+// Copyright 2007, 2010 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.
@@ -15,7 +15,9 @@
 package org.apache.tapestry5.ioc.internal;
 
 import org.apache.tapestry5.ioc.ServiceBinder;
+import org.apache.tapestry5.ioc.annotations.PreventServiceDecoration;
 
+@PreventServiceDecoration
 public class UninstantiableAutobuildServiceModule
 {
     public static void bind(ServiceBinder binder)