Okay, I got to work a bit and here's a patch that does what I described.
Uhm, I'm not sure if I'm supposed to submit patches this way.
Anyway, it allows extra ModuleDef objects to be added.

The patch shows the general idea, I'm pretty sure I'm missing something
that breaks everything (although it builds fine) and I suppose I'm not
really following coding/commenting conventions.

Unfortunately, tapestry-spring can't be modified to use this technique,
since it relies on a ServerContext object which is unreachable because
ApplicationGlobals doesn't have it before performRegistryStartup.

Tom.


On Sat, 16 Oct 2010 23:51:04 +0200, Tom van Dijk <[email protected]> wrote:
> I'm sorry, but I don't "get" it.
> 
> Where can I find this service builder factory service? I use
> 5.2.2-SNAPSHOT from your git.
> 
> The basic idea I'm now trying to work upon is that through some kind of
> configuration, the MultiHibernateModule class can figure out which
> databases are used. Ideally I want it to be possible to access two
> databases that have the same model (e.g. two book libraries). I don't
want
> to have to specify services in the web application or in model modules,
but
> I would like the MultiHibernateModule to take care of creating the
> appropriate Session«name», HibernateSessionManager«name» and similar
> services.
> 
> What tapestry-spring does is override TapestryFilter, requiring
developers
> to modify web.xml; besides, it doesn't scale as I don't see how multiple
> tapestry modules can override TapestryFilter to add their "dynamic"
> services.
> 
> As said, I can't find a service builder factory service. Where can I
find
> it? I can imagine contributing a service or configuration to a
tapestry-ioc
> service that adds 'dynamic' services [*] without "hacks" like a custom
> TapestryFilter. [*] 'dynamic' defined as "determined when starting the
> registry". Non-dynamic (call it static, whatever) could be defined as
> compile-time / binary defined. There is no way for the
MultiHibernateModule
> to know which services it should define before the application is being
> initialized.
> 
> I can imagine such a dynamic service builder work as follows. After the
> "normal" service definitions have been collected, all contributions
> (OrderedConfiguration) to the dynamic service builder are collected as
well
> and processed, resulting in possible extra services. I think this
concept
> would make tapestry-spring cleaner as well.
> 
> So, I'm again at your mercy, sir! Unless I'm overlooking something, my
> "humble request" would need something new in tapestry-ioc.
> 
> 
> 
> On Sat, 16 Oct 2010 10:52:15 -0700, Howard Lewis Ship <[email protected]>
> wrote:
>> I think you are on the right track, but I think you'll find it easier
>> to go with the flow of Tapestry using a marker annotation for each
>> database, rather than a single annotation that names the database.
>> 
>> Either way, the idea that you inject the Session you need is at the
> core.
>> 
>> You'll also want to tweak @CommitAfter to search for an annotation to
>> determine *which* Session's transaction to commit.
>> 
>> You may also want to consider a service builder factory service to
>> make it easy to define new Hibernate services, since there's a lot in
>> parallel.
>> 
>> The new @Contribute annotation will make it easier to contribute
>> configuration to a specific Hibernate connection factory, or to any of
>> them (via marker annotations).
>> 
>> On Sat, Oct 16, 2010 at 7:56 AM, Tom van Dijk <[email protected]> wrote:
>>>  After a "slight" delay, I've been working on a solution. (I'm a
>>> Master's
>>> student and it appears I have little spare time for private projects
>>> right
>>> now, hence the delay)
>>> I didn't want to use Spring if it's not needed; in my opinion just
> using
>>> Tapestry should be enough.
>>>
>>>
>>> One line of thought that I've been toying with the last couple of days
>>> is to
>>> have a @UseDatabase annotation, e.g.
>>> @Inject @UseDatabase("one") Session sessionOne,
>>> @Inject @UseDatabase("two") Session sessionTwo
>>>
>>> The implementation is a custom object provider, which provides objects
>>> for
>>> the interfaces Session, HibernateSessionSource,
> HibernateSessionManager,
>>> HibernateTransactionDecorator and HibernateTransactionAdvisor.
>>> There is a configurator object (MultiHibernateEntityPackageManager)
> that
>>> is
>>> configured by Map<String, Collection<String>>, with the semantics
>>> (database-id) -> {packageName}
>>> By default is uses "/hibernate-«name».cfg.xml" for the configuration.
>>>
>>> Obviously, because it's a custom object provider, the whole lifecycle
>>> stuff
>>> doesn't apply, which is a terribly ugly hack really. Besides, it's
>>> absolutely not compatible with the original tapestry-hibernate-core
>>> module.
>>>
>>> A different issue is that the Tapestry/Hibernate (web) integration
> module
>>> also depends on Session objects, which needs to be solved at some
point
>>> (right now I just deleted them, keeping only a modified
>>> CommitAfterWorker,
>>> which works btw).
>>>
>>> All in all, this seems to work, but it's ugly and I have some feeling
> I'm
>>> also violating Tapestry principles somewhere.
>>>
>>>
>>>
>>> A different line of thought that I am considering is using a construct
>>> like
>>> in the Tapestry / Swing integration package, to create all services.
>>> There
>>> is still the issue of the Tapestry/Hibernate (web) integration module
>>> assuming that there is only one database.
>>> It would have
>>> @InjectService("SessionOne") Session sessionOne.
>>>
>>> I don't really see how the Marker option would be implemented, but
this
>>> may
>>> be my lack of experience with Tapestry. You say it can be done without
>>> changing the framework? At the very least, it seems Persisting /
>>> ValueEncoder / all that stuff would be broken.
>>>
>>> Any thoughts?
>>>
>>>
>>>
>>> Op 2-6-2010 17:23, Howard Lewis Ship schreef:
>>>>
>>>> there's been some talk about allowing for seperate Sessions (and
>>>> related), using marker annotation to identify which one you need.
>>>> I.e., you might have
>>>>
>>>> @Inject @Product
>>>> private Session productSession;
>>>>
>>>> @Inject @Content
>>>> private Session contentSession;
>>>>
>>>> That's not too hard; the tricky part is the replication of several
>>>> services with the same interface; i.e., you will need an @Product
>>>> HibernateConfigurer and a @Content HibernateConfigurer, etc.
>>>>
>>>> This could all be done, and done on top of tapestry-hibernate without
>>>> changing the framework, I think.  The only tricky part is that the
>>>> default HibernateConfigurer assumes that you start by reading
>>>> hibernate.cfg.xml and that would need to be more flexible
>>>> (hibernate-products.cfg.xml, hibernate-session.cfg.xml).
>>>>
>>>> On Tue, Jun 1, 2010 at 3:25 PM, Tom van Dijk<[email protected]>  wrote:
>>>>>
>>>>> Hi,
>>>>>
>>>>> I'll be brief to save you time.
>>>>>
>>>>> I want to turn my webapplication (for a company I work for, I'm
doing
> a
>>>>> proof of concept to see if what I want is possible) into a Tapestry5
>>>>> based
>>>>> system.
>>>>>
>>>>> Our web applications use multiple databases (to enforce seperation
of
>>>>> concerns and for possible future scaling)
>>>>>
>>>>> We currently use two filters that provide two EntityManagerFactory
>>>>> objects
>>>>> (this is just for a small proof of concept webapp) and what I would
>>>>> like
>>>>> is
>>>>> to have two differently configured Session services. Obviously, I
> want
>>>>> to
>>>>> move on, embrace the future and start using a proper IoC framework.
>>>>>
>>>>> Now I see in the issue system that this (using multiple databases)
is
>>>>> unsupported. My question concerns a generalized approach. My first
>>>>> thoughts
>>>>> were on creating some kind of a general Factory service that would
>>>>> spawn
>>>>> the
>>>>> necessary custom services, but at second thought I found something
> that
>>>>> might be less complex.
>>>>>
>>>>> What if there were a way to copy existing services and override
their
>>>>> configurations? Unless I'm really stupid, this is not yet possible.
>>>>>
>>>>> One rather obvious issue is that the services I'd like to copy
depend
>>>>> on
>>>>> eachother, so there would have to be a method to map them all to a
>>>>> copy.
>>>>> Else I would copy a service only to find that it's still depending
on
>>>>> the
>>>>> original's services.
>>>>>
>>>>> Constraints:
>>>>> The software is supposed to be unaware of the implementation classes
>>>>> but
>>>>> it
>>>>> may of course be aware of the externally provided services.
>>>>> I don't want to copy existing code. Obviously. It would hurt
>>>>> reusability.
>>>>> Basically, what we're dealing with is a matter of creating different
>>>>> services (or groups of services) with different configurations.
>>>>>
>>>>> So, we have 3 services
>>>>> HibernateSessionManager
>>>>> HibernateSessionSource
>>>>> HibernateEntityPackageManager
>>>>> Session
>>>>>
>>>>> Now what I would like is to create 3 new services:
>>>>> ContentSessionManager
>>>>> ContentSessionSource
>>>>> ContentEntityPackageManager
>>>>> ContentSession
>>>>> ProductsSessionManager
>>>>> ProductsSessionSource
>>>>> ProductsEntityPackageManager
>>>>> ProductsSession
>>>>>
>>>>> Now I would like a service "ServiceCopier" or something like that to
>>>>> implement: [pseudocode]
>>>>> Map<String, String>  content = { "Session" =>  "ContentSession",
>>>>> "HibernateSessionManager" =>  "ContentSessionManager",
>>>>>  "HibernateSessionSource"=>"ContentSessionSource",
>>>>> "HibernateEntityPackageManager"=>"ContentEntityPackageManager" }
>>>>> Map<String, String>  products = {"Session" =>  "ProductsSession",
>>>>> "HibernateSessionManager" =>  "ProductsSessionManager",
>>>>>  "HibernateSessionSource"=>"ProductsSessionSource",
>>>>> "HibernateEntityPackageManager"=>"PrudctsEntityPackageManager" }
>>>>> copier.add(first);
>>>>> copier.add(second);
>>>>>
>>>>> This would happen in the Module, of course. I would also set
>>>>> DefaultConfiguration to false and provide my own thingie for that,
> but
>>>>> that's simple once the new services are coaxed to use the
> contributions
>>>>> using their new service id.
>>>>>
>>>>> The service builder would construct the three services as normal,
>>>>> except
>>>>> that whenever ContentSessionManager depends on
> HibernateSessionSource,
>>>>> it
>>>>> will depend on ContentSessionSource instead, et cetera. I can then
> use
>>>>> @InjectService("ContentSession")
>>>>>
>>>>> There is one important thing: the contributions from the original
>>>>> service
>>>>> will have to be included, as well as contributions for the new
> service
>>>>> id.
>>>>> If this would not happen, configurers such as
>>>>> PackageNameHibernateConfigurer.java would not be included. The
proper
>>>>> usage
>>>>> of the concept explained here is that a user would not provide extra
>>>>> configuration contributions for the base case, but only for the
> derived
>>>>> services.
>>>>>
>>>>> Hmmm this sounds conceptually a bit like namespaces, but you're way
>>>>> ahead
>>>>> of
>>>>> me in experience so I can't really comment on the similarity.
>>>>>
>>>>> Anyway, I promised to keep it brief and I doubt I could describe it
> in
>>>>> less
>>>>> words. I've described my problem and leave it to you to reply.
>>>>>
>>>>> Hoping for an answer,
>>>>>
>>>>> Tom van Dijk.
>>>>>
>>>>>
---------------------------------------------------------------------
>>>>> To unsubscribe, e-mail: [email protected]
>>>>> For additional commands, e-mail: [email protected]
>>>>>
>>>>>
>>>>
>>>>
>>>
>>>
>>> ---------------------------------------------------------------------
>>> To unsubscribe, e-mail: [email protected]
>>> For additional commands, e-mail: [email protected]
>>>
>>>
> 
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: [email protected]
> For additional commands, e-mail: [email protected]
From b51ccb11f73f204e97611537aaf8c58b229234d6 Mon Sep 17 00:00:00 2001
From: Tom van Dijk <[email protected]>
Date: Sun, 17 Oct 2010 15:57:01 +0200
Subject: [PATCH] Add new ModuleDef by contribution

---
 .../tapestry5/ioc/internal/RegistryImpl.java       |   74 ++++++++------
 .../ioc/services/ExtraModulesCollector.java        |   30 ++++++
 .../ioc/services/ExtraModulesCollectorFilter.java  |   26 +++++
 .../tapestry5/ioc/services/TapestryIOCModule.java  |   25 +++++
 .../apache/tapestry5/ioc/ExtraModulesModule.java   |  107 ++++++++++++++++++++
 .../org/apache/tapestry5/ioc/IntegrationTest.java  |   12 ++
 .../tapestry5/ioc/PreventDecorationModule.java     |    6 +-
 7 files changed, 249 insertions(+), 31 deletions(-)
 create mode 100644 tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/ExtraModulesCollector.java
 create mode 100644 tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/ExtraModulesCollectorFilter.java
 create mode 100644 tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/ExtraModulesModule.java

diff --git a/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/RegistryImpl.java b/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/RegistryImpl.java
index 6cf54b6..cf3c0b6 100644
--- a/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/RegistryImpl.java
+++ b/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/RegistryImpl.java
@@ -16,6 +16,7 @@ package org.apache.tapestry5.ioc.internal;
 
 import java.lang.reflect.Constructor;
 import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashSet;
@@ -33,6 +34,7 @@ import org.apache.tapestry5.ioc.def.ContributionDef;
 import org.apache.tapestry5.ioc.def.ContributionDef2;
 import org.apache.tapestry5.ioc.def.DecoratorDef;
 import org.apache.tapestry5.ioc.def.ModuleDef;
+import org.apache.tapestry5.ioc.def.ModuleDef3;
 import org.apache.tapestry5.ioc.def.ServiceDef;
 import org.apache.tapestry5.ioc.def.ServiceDef2;
 import org.apache.tapestry5.ioc.def.StartupDef;
@@ -159,50 +161,62 @@ public class RegistryImpl implements Registry, InternalRegistry, ServiceProxyPro
             }
         });
 
-        for (ModuleDef def : moduleDefs)
-        {
-            logger = this.loggerSource.getLogger(def.getLoggerName());
+        addMethodDefs(moduleDefs, classFactory);
 
-            Module module = new ModuleImpl(this, tracker, def, classFactory, logger);
+        addBuiltin(SERVICE_ACTIVITY_SCOREBOARD_SERVICE_ID, ServiceActivityScoreboard.class, scoreboardAndTracker);
+        addBuiltin(LOGGER_SOURCE_SERVICE_ID, LoggerSource.class, this.loggerSource);
+        addBuiltin(CLASS_FACTORY_SERVICE_ID, ClassFactory.class, this.classFactory);
+        addBuiltin(PERTHREAD_MANAGER_SERVICE_ID, PerthreadManager.class, perthreadManager);
+        addBuiltin(REGISTRY_SHUTDOWN_HUB_SERVICE_ID, RegistryShutdownHub.class, registryShutdownHub);
 
-            Set<ServiceDef2> moduleServiceDefs = CollectionFactory.newSet();
+        /*
+         * Implementation to add additional services.
+         * What needs to be done to make this implementation better
+         * - possibly split validateContributeDefs into two parts
+         */
 
-            for (String serviceId : def.getServiceIds())
-            {
-                ServiceDef2 serviceDef = module.getServiceDef(serviceId);
+        // Note that contribute defs have not been validated yet...
+        // Containing module is TapestryIOCModule
+        Module containingModule = locateModuleForService("ExtraModulesCollector");
+        // This will also instantiate PipelineBuilder and perhaps other services
+        ExtraModulesCollector collector =
+                containingModule.getService("ExtraModulesCollector",
+                ExtraModulesCollector.class);
+        // Collect modules and add them
+        Collection<ModuleDef> extraModules = new ArrayList<ModuleDef>();
+        collector.collectModules(extraModules);
+        addMethodDefs(extraModules, classFactory);
 
-                moduleServiceDefs.add(serviceDef);
-                allServiceDefs.add(serviceDef);
+        validateContributeDefs(moduleDefs); // This method doesn't change state
 
-                Module existing = serviceIdToModule.get(serviceId);
+        scoreboardAndTracker.startup(); // This actually
 
-                if (existing != null)
-                    throw new RuntimeException(IOCMessages.serviceIdConflict(serviceId,
-                            existing.getServiceDef(serviceId), serviceDef));
+        SerializationSupport.setProvider(this);
+    }
 
+    private void addMethodDefs(Collection<ModuleDef> moduleDefs, ClassFactory classFactory) {
+        Logger logger;
+        for (ModuleDef def : moduleDefs) {
+            logger = this.loggerSource.getLogger(def.getLoggerName());
+            Module module = new ModuleImpl(this, tracker, def, classFactory, logger);
+            Set<ServiceDef2> moduleServiceDefs = CollectionFactory.newSet();
+            for (String serviceId : def.getServiceIds()) {
+                ServiceDef2 serviceDef = module.getServiceDef(serviceId);
+                moduleServiceDefs.add(serviceDef);
+                allServiceDefs.add(serviceDef);
+                Module existing = serviceIdToModule.get(serviceId);
+                if (existing != null) {
+                    throw new RuntimeException(IOCMessages.serviceIdConflict(serviceId, existing.getServiceDef(serviceId), serviceDef));
+                }
                 serviceIdToModule.put(serviceId, module);
-
                 // The service is defined but will not have gone further than that.
                 tracker.define(serviceDef, Status.DEFINED);
-
-                for (Class marker : serviceDef.getMarkers())
+                for (Class marker : serviceDef.getMarkers()) {
                     InternalUtils.addToMapList(markerToServiceDef, marker, serviceDef);
+                }
             }
-
             moduleToServiceDefs.put(module, moduleServiceDefs);
         }
-
-        addBuiltin(SERVICE_ACTIVITY_SCOREBOARD_SERVICE_ID, ServiceActivityScoreboard.class, scoreboardAndTracker);
-        addBuiltin(LOGGER_SOURCE_SERVICE_ID, LoggerSource.class, this.loggerSource);
-        addBuiltin(CLASS_FACTORY_SERVICE_ID, ClassFactory.class, this.classFactory);
-        addBuiltin(PERTHREAD_MANAGER_SERVICE_ID, PerthreadManager.class, perthreadManager);
-        addBuiltin(REGISTRY_SHUTDOWN_HUB_SERVICE_ID, RegistryShutdownHub.class, registryShutdownHub);
-
-        validateContributeDefs(moduleDefs);
-
-        scoreboardAndTracker.startup();
-
-        SerializationSupport.setProvider(this);
     }
 
     /**
diff --git a/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/ExtraModulesCollector.java b/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/ExtraModulesCollector.java
new file mode 100644
index 0000000..5dfb60d
--- /dev/null
+++ b/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/ExtraModulesCollector.java
@@ -0,0 +1,30 @@
+// Copyright 2006, 2008 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.services;
+
+import java.util.Collection;
+import org.apache.tapestry5.ioc.annotations.UsesOrderedConfiguration;
+import org.apache.tapestry5.ioc.def.ModuleDef;
+
+/**
+ * Service interface for initializing Tapestry for the application.  The service is a {...@linkplain
+ * org.apache.tapestry5.ioc.services.PipelineBuilder pipeline}, into which {...@linkplain
+ * org.apache.tapestry5.services.ApplicationInitializerFilter filters} may be contributed.
+ */
+...@usesorderedconfiguration(ExtraModulesCollectorFilter.class)
+public interface ExtraModulesCollector
+{
+    void collectModules(Collection<ModuleDef> collection);
+}
diff --git a/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/ExtraModulesCollectorFilter.java b/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/ExtraModulesCollectorFilter.java
new file mode 100644
index 0000000..f0b6f33
--- /dev/null
+++ b/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/ExtraModulesCollectorFilter.java
@@ -0,0 +1,26 @@
+// 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.tapestry5.ioc.services;
+
+import java.util.Collection;
+import org.apache.tapestry5.ioc.def.ModuleDef;
+
+/**
+ * Filter interface for {...@link ApplicationInitializer}.
+ */
+public interface ExtraModulesCollectorFilter
+{
+    void collectModules(Collection<ModuleDef> collection, ExtraModulesCollector initializer);
+}
diff --git a/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/TapestryIOCModule.java b/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/TapestryIOCModule.java
index 8f786cc..a2bf4ec 100644
--- a/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/TapestryIOCModule.java
+++ b/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/TapestryIOCModule.java
@@ -37,11 +37,13 @@ import org.apache.tapestry5.ioc.annotations.Local;
 import org.apache.tapestry5.ioc.annotations.Marker;
 import org.apache.tapestry5.ioc.annotations.PreventServiceDecoration;
 import org.apache.tapestry5.ioc.annotations.Symbol;
+import org.apache.tapestry5.ioc.def.ModuleDef;
 import org.apache.tapestry5.ioc.internal.services.*;
 import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
 import org.apache.tapestry5.ioc.internal.util.InternalUtils;
 import org.apache.tapestry5.ioc.util.TimeInterval;
 import org.apache.tapestry5.services.UpdateListenerHub;
+import org.slf4j.Logger;
 
 /**
  * Defines the base set of services for the Tapestry IOC container.
@@ -150,6 +152,29 @@ public final class TapestryIOCModule
         configuration.add("ServiceOverride", wrapper, after("AnnotationBasedContributions").build());
     }
 
+    private class ExtraModulesCollectorTerminator implements ExtraModulesCollector
+    {
+        public void collectModules(Collection<ModuleDef> collection) {
+        }
+    }
+
+    /**
+     * Initializes the application, using a pipeline of {...@link org.apache.tapestry5.services.ApplicationInitializer}s.
+     */
+    public ExtraModulesCollector buildExtraModulesCollector(
+            PipelineBuilder pipelineBuilder,
+            Logger logger,
+            List<ExtraModulesCollectorFilter> configuration)
+    {
+        ExtraModulesCollector terminator = new ExtraModulesCollectorTerminator();
+
+        return pipelineBuilder.build(logger,
+                ExtraModulesCollector.class,
+                ExtraModulesCollectorFilter.class,
+                configuration, terminator);
+    }
+
+
     /**
      * Contributes a set of standard type coercions to the {...@link TypeCoercer} service:
      * <ul>
diff --git a/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/ExtraModulesModule.java b/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/ExtraModulesModule.java
new file mode 100644
index 0000000..e2a0bcd
--- /dev/null
+++ b/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/ExtraModulesModule.java
@@ -0,0 +1,107 @@
+// Copyright 2009 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;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import org.apache.tapestry5.ioc.def.ContributionDef;
+import org.apache.tapestry5.ioc.def.DecoratorDef;
+import org.apache.tapestry5.ioc.def.ModuleDef;
+import org.apache.tapestry5.ioc.def.ServiceDef;
+import org.apache.tapestry5.ioc.services.ExtraModulesCollector;
+import org.apache.tapestry5.ioc.services.ExtraModulesCollectorFilter;
+
+public class ExtraModulesModule {
+
+    public static void bind(ServiceBinder binder) {
+    }
+
+    public static void contributeExtraModulesCollector(
+            OrderedConfiguration<ExtraModulesCollectorFilter> configuration) {
+        configuration.add("Test", new ExtraModulesCollectorFilter() {
+
+            public void collectModules(Collection<ModuleDef> collection, ExtraModulesCollector initializer) {
+                collection.add(new ModuleDefImpl());
+                initializer.collectModules(collection);
+            }
+        });
+    }
+
+    private static class ModuleDefImpl implements ModuleDef {
+
+        private final Map<String, ServiceDef> services = new HashMap<String, ServiceDef>();
+
+        public ModuleDefImpl() {
+            services.put("Rocket", new ServiceDef() {
+
+                public ObjectCreator createServiceCreator(ServiceBuilderResources resources) {
+                    return new ObjectCreator() {
+
+                        public Object createObject() {
+                            return new RocketImpl();
+                        }
+                    };
+                }
+
+                public String getServiceId() {
+                    return "Rocket";
+                }
+
+                public Set<Class> getMarkers() {
+                    return Collections.EMPTY_SET;
+                }
+
+                public Class getServiceInterface() {
+                    return Rocket.class;
+                }
+
+                public String getServiceScope() {
+                    return ScopeConstants.DEFAULT;
+                }
+
+                public boolean isEagerLoad() {
+                    return false;
+                }
+            });
+        }
+
+        public Set<String> getServiceIds() {
+            return services.keySet();
+        }
+
+        public ServiceDef getServiceDef(String serviceId) {
+            return services.get(serviceId);
+        }
+
+        public Set<DecoratorDef> getDecoratorDefs() {
+            return Collections.EMPTY_SET;
+        }
+
+        public Set<ContributionDef> getContributionDefs() {
+            return Collections.EMPTY_SET;
+        }
+
+        public Class getBuilderClass() {
+            return null;
+        }
+
+        public String getLoggerName() {
+            return ModuleDefImpl.class.getName();
+        }
+    }
+}
diff --git a/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/IntegrationTest.java b/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/IntegrationTest.java
index 2e55991..0c154cd 100644
--- a/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/IntegrationTest.java
+++ b/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/IntegrationTest.java
@@ -1349,6 +1349,18 @@ public class IntegrationTest extends IOCInternalTestCase
         }
     }
 
+    @Test
+    public void bind_service_from_extra_module()
+    {
+        Registry r = buildRegistry(ExtraModulesModule.class);
+
+        Rocket rocket = r.getService(Rocket.class);
+
+        assertEquals(rocket.getCountdown(), "3, 2, 1, Launch!");
+
+        r.shutdown();
+    }
+
     /**
      * TAP-437
      */
diff --git a/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/PreventDecorationModule.java b/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/PreventDecorationModule.java
index 22601f1..f6cbc7e 100644
--- a/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/PreventDecorationModule.java
+++ b/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/PreventDecorationModule.java
@@ -34,6 +34,10 @@ public class PreventDecorationModule
     @Match("*")
     public <T> T decorateEverything(Class<T> type, T instance)
     {
-        throw new RuntimeException(String.format("Failure to decorate type %s.", type.getName()));
+        // TvD, new ExtraServicesContributor also requires PipelineBuilder, Logger...
+        // So I inserted a check...
+        if (type==StringTransformer.class || type == ServiceIdGreeter.class)
+            throw new RuntimeException(String.format("Failure to decorate type %s.", type.getName()));
+        return instance;
     }
 }
-- 
1.7.0.4

---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to