You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tapestry.apache.org by hl...@apache.org on 2006/11/21 01:58:30 UTC
svn commit: r477448 [2/3] - in /tapestry/tapestry5:
tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/
tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/
tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/s...
Added: tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/internal/ioc/DefaultModuleDefImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/internal/ioc/DefaultModuleDefImpl.java?view=auto&rev=477448
==============================================================================
--- tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/internal/ioc/DefaultModuleDefImpl.java (added)
+++ tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/internal/ioc/DefaultModuleDefImpl.java Mon Nov 20 16:58:25 2006
@@ -0,0 +1,364 @@
+// Copyright 2006 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry.internal.ioc;
+
+import static org.apache.tapestry.internal.ioc.ConfigurationType.MAPPED;
+import static org.apache.tapestry.internal.ioc.ConfigurationType.ORDERED;
+import static org.apache.tapestry.internal.ioc.ConfigurationType.UNORDERED;
+import static org.apache.tapestry.internal.ioc.IOCMessages.buildMethodConflict;
+import static org.apache.tapestry.internal.ioc.IOCMessages.buildMethodWrongReturnType;
+import static org.apache.tapestry.internal.ioc.IOCMessages.decoratorMethodWrongReturnType;
+import static org.apache.tapestry.ioc.IOCUtilities.toQualifiedId;
+import static org.apache.tapestry.util.CollectionFactory.newMap;
+import static org.apache.tapestry.util.CollectionFactory.newSet;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.commons.logging.Log;
+import org.apache.tapestry.ioc.Configuration;
+import org.apache.tapestry.ioc.IOCConstants;
+import org.apache.tapestry.ioc.IOCUtilities;
+import org.apache.tapestry.ioc.MappedConfiguration;
+import org.apache.tapestry.ioc.OrderedConfiguration;
+import org.apache.tapestry.ioc.annotations.Contribute;
+import org.apache.tapestry.ioc.annotations.EagerLoad;
+import org.apache.tapestry.ioc.annotations.Id;
+import org.apache.tapestry.ioc.annotations.Lifecycle;
+import org.apache.tapestry.ioc.annotations.Match;
+import org.apache.tapestry.ioc.annotations.Order;
+import org.apache.tapestry.ioc.annotations.Private;
+import org.apache.tapestry.ioc.def.ContributionDef;
+import org.apache.tapestry.ioc.def.DecoratorDef;
+import org.apache.tapestry.ioc.def.ModuleDef;
+import org.apache.tapestry.ioc.def.ServiceDef;
+
+/**
+ * Starting from the Class for a module builder, identifies all the services (service builder
+ * methods), decorators (service decorator methods) and (not yet implemented) contributions (service
+ * contributor methods).
+ *
+ *
+ */
+public class DefaultModuleDefImpl implements ModuleDef
+{
+ /** The prefix used to identify service builder methods. */
+ private static final String BUILD_METHOD_NAME_PREFIX = "build";
+
+ /** The prefix used to identify service decorator methods. */
+ private static final String DECORATE_METHOD_NAME_PREFIX = "decorate";
+
+ /** The prefix used to identify service contribution methods. */
+ private static final String CONTRIBUTE_METHOD_NAME_PREFIX = "contribute";
+
+ private final Class _builderClass;
+
+ private final Log _log;
+
+ /** Keyed on fully qualified service id. */
+ private final Map<String, ServiceDef> _serviceDefs = newMap();
+
+ /** Keyed on fully qualified decorator id. */
+ private final Map<String, DecoratorDef> _decoratorDefs = newMap();
+
+ private final Set<ContributionDef> _contributionDefs = newSet();
+
+ private final String _moduleId;
+
+ private final static Map<Class, ConfigurationType> PARAMETER_TYPE_TO_CONFIGURATION_TYPE = newMap();
+
+ static
+ {
+ PARAMETER_TYPE_TO_CONFIGURATION_TYPE.put(Configuration.class, UNORDERED);
+ PARAMETER_TYPE_TO_CONFIGURATION_TYPE.put(OrderedConfiguration.class, ORDERED);
+ PARAMETER_TYPE_TO_CONFIGURATION_TYPE.put(MappedConfiguration.class, MAPPED);
+ }
+
+ /**
+ * @param builderClass
+ * the class that is responsible for building services, etc.
+ * @param log
+ */
+ public DefaultModuleDefImpl(Class builderClass, Log log)
+ {
+ _builderClass = builderClass;
+ _log = log;
+
+ _moduleId = extractModuleId();
+
+ grind();
+ }
+
+ public Class getBuilderClass()
+ {
+ return _builderClass;
+ }
+
+ public Set<String> getServiceIds()
+ {
+ return _serviceDefs.keySet();
+ }
+
+ public ServiceDef getServiceDef(String serviceId)
+ {
+ return _serviceDefs.get(serviceId);
+ }
+
+ public String getModuleId()
+ {
+ return _moduleId;
+ }
+
+ private String extractModuleId()
+ {
+ Id id = getAnnotation(_builderClass, Id.class);
+
+ if (id != null)
+ return id.value();
+
+ String className = _builderClass.getName();
+
+ // Don't try to do this with classes in the default package. Then again, you should
+ // never put classes in the default package!
+
+ int lastdot = className.lastIndexOf('.');
+
+ return className.substring(0, lastdot);
+ }
+
+ // This appears useless, but it's really about some kind of ambiguity in Class, which seems
+ // to think getAnnotation() returns Object, not <? extends Annotation>. This may be a bug
+ // in Eclipse's Java compiler.
+
+ private <T extends Annotation> T getAnnotation(AnnotatedElement element, Class<T> annotationType)
+ {
+ return element.getAnnotation(annotationType);
+ }
+
+ private void grind()
+ {
+ Method[] methods = _builderClass.getMethods();
+
+ Comparator<Method> c = new Comparator<Method>()
+ {
+ // By name, ascending, then by parameter count, descending.
+
+ public int compare(Method o1, Method o2)
+ {
+ int result = o1.getName().compareTo(o2.getName());
+
+ if (result == 0)
+ result = o2.getParameterTypes().length - o1.getParameterTypes().length;
+
+ return result;
+ }
+
+ };
+
+ Arrays.sort(methods, c);
+
+ for (Method m : methods)
+ {
+ String name = m.getName();
+
+ if (name.startsWith(BUILD_METHOD_NAME_PREFIX))
+ {
+ addServiceDef(m);
+ continue;
+ }
+
+ if (name.startsWith(DECORATE_METHOD_NAME_PREFIX))
+ {
+ addDecoratorDef(m);
+ continue;
+ }
+
+ if (name.startsWith(CONTRIBUTE_METHOD_NAME_PREFIX))
+ {
+ addContributionDef(m);
+ continue;
+ }
+
+ }
+ }
+
+ private void addContributionDef(Method method)
+ {
+ String serviceId = stripMethodPrefix(method, CONTRIBUTE_METHOD_NAME_PREFIX);
+
+ Class returnType = method.getReturnType();
+ if (!returnType.equals(void.class))
+ _log.warn(IOCMessages.contributionWrongReturnType(method));
+
+ Contribute contribute = method.getAnnotation(Contribute.class);
+ if (contribute != null)
+ serviceId = contribute.value();
+
+ ConfigurationType type = null;
+
+ for (Class parameterType : method.getParameterTypes())
+ {
+ ConfigurationType thisParameter = PARAMETER_TYPE_TO_CONFIGURATION_TYPE
+ .get(parameterType);
+
+ if (thisParameter != null)
+ {
+ if (type != null)
+ {
+ _log.warn(IOCMessages.tooManyContributionParameters(method));
+ return;
+ }
+
+ type = thisParameter;
+ }
+ }
+
+ if (type == null)
+ {
+ _log.warn(IOCMessages.noContributionParameter(method));
+ return;
+ }
+
+ // Any other parameters will be validated and worked out at runtime, when we invoke the
+ // service contribution method.
+
+ String qualifiedId = IOCUtilities.toQualifiedId(_moduleId, serviceId);
+
+ ContributionDef def = new ContributionDefImpl(qualifiedId, method);
+
+ _contributionDefs.add(def);
+ }
+
+ private void addDecoratorDef(Method method)
+ {
+ // TODO: methods just named "decorate"
+
+ String simpleDecoratorId = stripMethodPrefix(method, DECORATE_METHOD_NAME_PREFIX);
+ String id = _moduleId + "." + simpleDecoratorId;
+
+ // TODO: Check for duplicates
+
+ Class returnType = method.getReturnType();
+
+ if (returnType.isPrimitive() || returnType.isArray())
+ {
+ _log.warn(decoratorMethodWrongReturnType(method), null);
+ return;
+ }
+
+ if (!methodContainsObjectParameter(method))
+ {
+ _log.warn(IOCMessages.decoratorMethodNeedsDelegateParameter(method), null);
+ return;
+ }
+
+ // TODO: Check that at least one parameter is type java.lang.Object,
+ // since that's how the delegate is passed in.
+
+ Order orderAnnotation = method.getAnnotation(Order.class);
+ Match match = method.getAnnotation(Match.class);
+
+ String[] constraints = orderAnnotation != null ? orderAnnotation.value() : null;
+
+ // TODO: Validate constraints here?
+
+ String[] patterns = match == null ? new String[]
+ { simpleDecoratorId } : match.value();
+
+ // Qualify any unqualified match patterns with the decorator's module id.
+
+ for (int i = 0; i < patterns.length; i++)
+ patterns[i] = toQualifiedId(_moduleId, patterns[i]);
+
+ DecoratorDef def = new DecoratorDefImpl(id, method, patterns, constraints);
+
+ _decoratorDefs.put(id, def);
+ }
+
+ private boolean methodContainsObjectParameter(Method method)
+ {
+ for (Class parameterType : method.getParameterTypes())
+ {
+ // TODO: But what if the type Object parameter has an injection?
+ // We should skip it and look for a different parameter.
+
+ if (parameterType.equals(Object.class))
+ return true;
+ }
+
+ return false;
+ }
+
+ private String stripMethodPrefix(Method method, String prefix)
+ {
+ return method.getName().substring(prefix.length());
+ }
+
+ /** Invoked for public methods that have the proper prefix. */
+ private void addServiceDef(Method method)
+ {
+ // TODO: Methods named just "build"
+ String serviceId = _moduleId + "." + stripMethodPrefix(method, BUILD_METHOD_NAME_PREFIX);
+
+ ServiceDef existing = _serviceDefs.get(serviceId);
+ if (existing != null)
+ {
+ _log.warn(buildMethodConflict(method, existing.toString()), null);
+ return;
+ }
+
+ // Any number of parameters is fine, we'll adapt. Eventually we have to check
+ // that we can satisfy the parameters requested. Thrown exceptions of the method
+ // will be caught and wrapped, so we don't need to check those. But we do need a proper
+ // return type.
+
+ Class returnType = method.getReturnType();
+
+ if (!returnType.isInterface())
+ {
+ _log.warn(buildMethodWrongReturnType(method), null);
+ return;
+ }
+
+ String lifecycle = extractLifecycle(method);
+ boolean isPrivate = method.isAnnotationPresent(Private.class);
+ boolean eagerLoad = method.isAnnotationPresent(EagerLoad.class);
+
+ _serviceDefs.put(serviceId, new ServiceDefImpl(serviceId, lifecycle, method, isPrivate,
+ eagerLoad));
+ }
+
+ private String extractLifecycle(Method method)
+ {
+ Lifecycle lifecycle = method.getAnnotation(Lifecycle.class);
+
+ return lifecycle != null ? lifecycle.value() : IOCConstants.DEFAULT_LIFECYCLE;
+ }
+
+ public Set<DecoratorDef> getDecoratorDefs()
+ {
+ return newSet(_decoratorDefs.values());
+ }
+
+ public Set<ContributionDef> getContributionDefs()
+ {
+ return _contributionDefs;
+ }
+}
Added: tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/internal/ioc/EagerLoadServiceProxy.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/internal/ioc/EagerLoadServiceProxy.java?view=auto&rev=477448
==============================================================================
--- tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/internal/ioc/EagerLoadServiceProxy.java (added)
+++ tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/internal/ioc/EagerLoadServiceProxy.java Mon Nov 20 16:58:25 2006
@@ -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.tapestry.internal.ioc;
+
+/**
+ * Interface implemented by all service proxies. Service proxies are always
+ * {@link org.apache.tapestry.ioc.services.RegistryShutdownListener}s, they also can be eager-load
+ *
+ *
+ */
+public interface EagerLoadServiceProxy
+{
+ void eagerLoadService();
+}
Added: tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/internal/ioc/IOCMessages.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/internal/ioc/IOCMessages.java?view=auto&rev=477448
==============================================================================
--- tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/internal/ioc/IOCMessages.java (added)
+++ tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/internal/ioc/IOCMessages.java Mon Nov 20 16:58:25 2006
@@ -0,0 +1,291 @@
+// Copyright 2006 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry.internal.ioc;
+
+import static org.apache.tapestry.internal.util.InternalUtils.asString;
+import static org.apache.tapestry.ioc.services.ClassFabUtils.getJavaClassName;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.lang.reflect.Type;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.tapestry.internal.MessagesImpl;
+import org.apache.tapestry.ioc.Messages;
+import org.apache.tapestry.ioc.def.ContributionDef;
+import org.apache.tapestry.ioc.def.ServiceDef;
+
+public final class IOCMessages
+{
+ private static final Messages MESSAGES = MessagesImpl.forClass(IOCMessages.class);
+
+ private IOCMessages()
+ {
+ }
+
+ static String buildMethodConflict(Method conflict, String existing)
+ {
+ return MESSAGES.format("build-method-conflict", asString(conflict), existing);
+ }
+
+ static String buildMethodWrongReturnType(Method method)
+ {
+ return MESSAGES.format("build-method-wrong-return-type", asString(method), method
+ .getReturnType().getCanonicalName());
+ }
+
+ static String decoratorMethodWrongReturnType(Method method)
+ {
+ return MESSAGES.format("decorator-method-wrong-return-type", asString(method), method
+ .getReturnType().getCanonicalName());
+ }
+
+ static String noSuchModule(String moduleId)
+ {
+ return MESSAGES.format("no-such-module", moduleId);
+ }
+
+ static String missingService(String serviceId)
+ {
+ return MESSAGES.format("missing-service", serviceId);
+ }
+
+ public static String builderLocked()
+ {
+ return MESSAGES.get("builder-locked");
+ }
+
+ public static String moduleIdConflict(String id)
+ {
+ return MESSAGES.format("module-id-conflict", id);
+ }
+
+ static String serviceWrongInterface(String serviceId, Class actualInterface,
+ Class requestedInterface)
+ {
+ return MESSAGES.format(
+ "service-wrong-interface",
+ serviceId,
+ actualInterface.getName(),
+ requestedInterface.getName());
+ }
+
+ static String instantiateBuilderError(Class builderClass, String moduleId, Throwable cause)
+ {
+ return MESSAGES
+ .format("instantiate-builder-error", builderClass.getName(), moduleId, cause);
+ }
+
+ static String builderMethodError(Method method, String serviceId, Throwable cause)
+ {
+ return MESSAGES.format("builder-method-error", asString(method), serviceId, cause);
+ }
+
+ static String decoratorMethodError(Method method, String serviceId, Throwable cause)
+ {
+ return MESSAGES.format("decorator-method-error", asString(method), serviceId, cause);
+ }
+
+ static String builderMethodReturnedNull(Method method, String serviceId)
+ {
+ return MESSAGES.format("builder-method-returned-null", asString(method), serviceId);
+ }
+
+ static String serviceIsPrivate(String serviceId)
+ {
+ return MESSAGES.format("service-is-private", serviceId);
+ }
+
+ static String noServiceMatchesType(Class serviceInterface)
+ {
+ return MESSAGES.format("no-service-matches-type", serviceInterface.getName());
+ }
+
+ static String manyServiceMatches(Class serviceInterface, List<String> ids)
+ {
+ StringBuilder buffer = new StringBuilder();
+
+ for (int i = 0; i < ids.size(); i++)
+ {
+ if (i > 0)
+ buffer.append(", ");
+
+ buffer.append(ids.get(i));
+ }
+
+ return MESSAGES.format(
+ "many-service-matches",
+ serviceInterface.getName(),
+ ids.size(),
+ buffer.toString());
+ }
+
+ static String unknownLifecycle(String name)
+ {
+ return MESSAGES.format("unknown-lifecycle", name);
+ }
+
+ static String decoratorMethodNeedsDelegateParameter(Method method)
+ {
+ return MESSAGES.format("decorator-method-needs-delegate-parameter", asString(method));
+ }
+
+ static String decoratorReturnedWrongType(Method method, String serviceId, Object returned,
+ Class serviceInterface)
+ {
+ return MESSAGES.format(
+ "decorator-returned-wrong-type",
+ asString(method),
+ serviceId,
+ returned,
+ serviceInterface.getName());
+ }
+
+ static String creatingService(String serviceId)
+ {
+ return MESSAGES.format("creating-service", serviceId);
+ }
+
+ static String invokingMethod(Method method)
+ {
+ return MESSAGES.format("invoking-method", asString(method));
+ }
+
+ static String invokingMethod(ContributionDef def)
+ {
+ // The toString() of a contribution def is the name of the method.
+ return MESSAGES.format("invoking-method", def);
+ }
+
+ static String recursiveServiceBuild(ServiceDef def)
+ {
+ return MESSAGES.format("recursive-service-build", def.getServiceId(), def.toString());
+ }
+
+ static String contributionWrongReturnType(Method method)
+ {
+ return MESSAGES.format(
+ "contribution-wrong-return-type",
+ asString(method),
+ getJavaClassName(method.getReturnType()));
+ }
+
+ static String tooManyContributionParameters(Method method)
+ {
+ return MESSAGES.format("too-many-contribution-parameters", asString(method));
+ }
+
+ static String noContributionParameter(Method method)
+ {
+ return MESSAGES.format("no-contribution-parameter", asString(method));
+ }
+
+ static String contributionMethodError(Method method, Throwable cause)
+ {
+ return MESSAGES.format("contribution-method-error", asString(method), cause);
+ }
+
+ static String contributionWasNull(String serviceId, ContributionDef def)
+ {
+ return MESSAGES.format("contribution-was-null", serviceId, def);
+ }
+
+ static String contributionKeyWasNull(String serviceId, ContributionDef def)
+ {
+ return MESSAGES.format("contribution-key-was-null", serviceId, def);
+ }
+
+ static String contributionWrongValueType(String serviceId, ContributionDef def,
+ Class actualClass, Class expectedClass)
+ {
+ return MESSAGES.format("contribution-wrong-value-type", serviceId, def, actualClass
+ .getName(), expectedClass.getName());
+ }
+
+ static String contributionWrongKeyType(String serviceId, ContributionDef def,
+ Class actualClass, Class expectedClass)
+ {
+ return MESSAGES.format(
+ "contribution-wrong-key-type",
+ serviceId,
+ def,
+ actualClass.getName(),
+ expectedClass.getName());
+ }
+
+ static String tooManyConfigurationParameters(Method method)
+ {
+ return MESSAGES.format("too-many-configuration-parameters", asString(method));
+ }
+
+ static String genericTypeNotSupported(Type type)
+ {
+ return MESSAGES.format("generic-type-not-supported", type);
+ }
+
+ static String contributionDuplicateKey(String serviceId, ContributionDef contributionDef,
+ ContributionDef existingDef)
+ {
+ return MESSAGES.format(
+ "contribution-duplicate-key",
+ serviceId,
+ contributionDef,
+ existingDef);
+ }
+
+ static String errorBuildingService(String serviceId, ServiceDef serviceDef, Throwable cause)
+ {
+ return MESSAGES.format("error-building-service", serviceId, serviceDef, cause);
+ }
+
+ static String noPublicConstructors(String moduleId, Class moduleBuilderClass)
+ {
+ return MESSAGES.format("no-public-constructors", moduleId, moduleBuilderClass.getName());
+ }
+
+ static String tooManyPublicConstructors(String moduleId, Class moduleBuilderClass,
+ Constructor constructor)
+ {
+ return MESSAGES.format("too-many-public-constructors", moduleId, moduleBuilderClass
+ .getName(), constructor);
+ }
+
+ static String recursiveModuleConstructor(String moduleId, Class builderClass,
+ Constructor constructor)
+ {
+ return MESSAGES.format(
+ "recursive-module-constructor",
+ moduleId,
+ builderClass.getName(),
+ constructor);
+ }
+
+ static String registryShutdown(String serviceId)
+ {
+ return MESSAGES.format("registry-shutdown", serviceId);
+ }
+
+ static String constructedConfiguration(Collection result)
+ {
+ return MESSAGES.format("constructed-configuration", result);
+ }
+
+ static String constructedConfiguration(Map result)
+ {
+ return MESSAGES.format("constructed-configuration", result);
+ }
+}
Added: tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/internal/ioc/IOCProxyUtilities.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/internal/ioc/IOCProxyUtilities.java?view=auto&rev=477448
==============================================================================
--- tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/internal/ioc/IOCProxyUtilities.java (added)
+++ tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/internal/ioc/IOCProxyUtilities.java Mon Nov 20 16:58:25 2006
@@ -0,0 +1,36 @@
+// Copyright 2006 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry.internal.ioc;
+
+/**
+ * Contains static methods used by proxies generated at runtime.
+ */
+public final class IOCProxyUtilities
+{
+ private IOCProxyUtilities()
+ {
+ }
+
+ /**
+ * Invoked by a proxy when the registry has shutdown.
+ *
+ * @throws IllegalStateException
+ * always
+ */
+ public static void registryShutdown(String serviceId)
+ {
+ throw new IllegalStateException(IOCMessages.registryShutdown(serviceId));
+ }
+}
Added: tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/internal/ioc/IdMatcherImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/internal/ioc/IdMatcherImpl.java?view=auto&rev=477448
==============================================================================
--- tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/internal/ioc/IdMatcherImpl.java (added)
+++ tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/internal/ioc/IdMatcherImpl.java Mon Nov 20 16:58:25 2006
@@ -0,0 +1,59 @@
+// Copyright 2006 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry.internal.ioc;
+
+import org.apache.tapestry.ioc.GlobPatternMatcher;
+import org.apache.tapestry.ioc.IdMatcher;
+
+/**
+ * Used to match input values (as fully qualified ids) to a pattern. The pattern is split into two
+ * glob match patterns at the last dot.
+ *
+ *
+ */
+public class IdMatcherImpl implements IdMatcher
+{
+ private final GlobPatternMatcher _moduleMatcher;
+
+ private final GlobPatternMatcher _serviceMatcher;
+
+ public IdMatcherImpl(String pattern)
+ {
+ int dotx = pattern.lastIndexOf('.');
+
+ if (dotx < 0)
+ throw new IllegalArgumentException(String.format(
+ "Pattern '%s' does not contain a '.' seperator character.",
+ pattern));
+
+ _moduleMatcher = new GlobPatternMatcher(pattern.substring(0, dotx));
+ _serviceMatcher = new GlobPatternMatcher(pattern.substring(dotx + 1));
+ }
+
+ public boolean matches(String id)
+ {
+ int dotx = id.lastIndexOf('.');
+
+ if (dotx < 0)
+ throw new IllegalArgumentException(String.format(
+ "Input id '%s' does not contain a '.' seperator character.",
+ id));
+
+ String moduleId = id.substring(0, dotx);
+ String serviceId = id.substring(dotx + 1);
+
+ return _moduleMatcher.matches(moduleId) && _serviceMatcher.matches(serviceId);
+ }
+}
Added: tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/internal/ioc/InterceptorStackBuilder.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/internal/ioc/InterceptorStackBuilder.java?view=auto&rev=477448
==============================================================================
--- tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/internal/ioc/InterceptorStackBuilder.java (added)
+++ tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/internal/ioc/InterceptorStackBuilder.java Mon Nov 20 16:58:25 2006
@@ -0,0 +1,82 @@
+// Copyright 2006 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry.internal.ioc;
+
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.tapestry.ioc.ObjectCreator;
+import org.apache.tapestry.ioc.ServiceDecorator;
+
+/**
+ * Responsible for constructing the interceptor stack, on demand, by invoking an ordered series of
+ * decorators ({@link org.apache.tapestry.ioc.def.DecoratorDef}.
+ *
+ *
+ */
+public class InterceptorStackBuilder implements ObjectCreator
+{
+ private final String _serviceId;
+
+ private final ObjectCreator _coreServiceCreator;
+
+ private final Module _module;
+
+ /**
+ * @param module
+ * the module containing the decorator method
+ * @param serviceId
+ * identifies the service to be decorated
+ * @param coreServiceCreator
+ * responsible for creating the core service which is then decorated with a stack of
+ * interceptors
+ */
+ public InterceptorStackBuilder(Module module, String serviceId, ObjectCreator coreServiceCreator)
+ {
+ _module = module;
+ _serviceId = serviceId;
+ _coreServiceCreator = coreServiceCreator;
+ }
+
+ public Object createObject()
+ {
+ Object current = _coreServiceCreator.createObject();
+
+ List<ServiceDecorator> decorators = _module.findDecoratorsForService(_serviceId);
+
+ // We get the decorators ordered according to their dependencies. However, we want to
+ // process from the last interceptor to the first, so we reverse the list.
+
+ Collections.reverse(decorators);
+
+ for (ServiceDecorator decorator : decorators)
+ {
+ Object interceptor = decorator.createInterceptor(current);
+
+ // Decorator methods may return null; this indicates that the decorator chose not to
+ // decorate.
+
+ if (interceptor != null)
+ current = interceptor;
+ }
+
+ // The stack of interceptors (plus the core service implementation) are "represented" to the
+ // outside world
+ // as the outermost interceptor. That will still be buried inside the service proxy.
+
+ return current;
+ }
+
+}
Added: tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/internal/ioc/InternalRegistry.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/internal/ioc/InternalRegistry.java?view=auto&rev=477448
==============================================================================
--- tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/internal/ioc/InternalRegistry.java (added)
+++ tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/internal/ioc/InternalRegistry.java Mon Nov 20 16:58:25 2006
@@ -0,0 +1,149 @@
+// Copyright 2006 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry.internal.ioc;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.tapestry.ioc.LogSource;
+import org.apache.tapestry.ioc.Registry;
+import org.apache.tapestry.ioc.ServiceDecorator;
+import org.apache.tapestry.ioc.ServiceLifecycle;
+import org.apache.tapestry.ioc.ServiceLocator;
+import org.apache.tapestry.ioc.def.ServiceDef;
+import org.apache.tapestry.ioc.services.ClassFab;
+import org.apache.tapestry.ioc.services.RegistryShutdownHub;
+
+/**
+ * Internal view of the module registry, adding additional methods needed by modules.
+ *
+ *
+ */
+public interface InternalRegistry extends Registry, LogSource, RegistryShutdownHub
+{
+ /**
+ * Locates a service given a fully qualified service id and the corresponding service interface
+ * type. This is typically invoked by a module (passing itself as the third parameter), but may
+ * also be invoked by the registry itself, passing null for module.
+ *
+ * @param <T>
+ * @param serviceId
+ * the simple or fully qualified service id
+ * @param serviceInterface
+ * the interface the service implements
+ * @param module
+ * the module requesting the service (used for visibility checks)
+ * @return the service's proxy
+ * @throws RuntimeException
+ * if the service does not exist (this is considered programmer error)
+ */
+ <T> T getService(String serviceId, Class<T> serviceInterface, Module module);
+
+ /**
+ * Locates a service given just a service interface. A single service must implement the service
+ * interface (which can be hard to guarantee). This is typically invoked by a module (passing
+ * itself as the third parameter), but may also be invoked by the registry itself, passing null
+ * for module.
+ *
+ * @param <T>
+ * @param serviceInterface
+ * the interface the service implements
+ * @param module
+ * the module requesting the service (used for visibility checks)
+ * @return the service's proxy
+ * @throws RuntimeException
+ * if the service does not exist (this is considered programmer error), or multiple
+ * services implement the service interface
+ */
+ <T> T getService(Class<T> serviceInterface, Module module);
+
+ /**
+ * Returns a service lifecycle by name.
+ *
+ * @param lifecycle
+ * the name of the lifecycle
+ * @return the lifecycle instance
+ * @throws RuntimeException
+ * if the lifecycle name does not match a known lifecycle
+ */
+ ServiceLifecycle getServiceLifecycle(String lifecycle);
+
+ /**
+ * Searches for decorators for a particular service. The resulting
+ * {@link org.apache.tapestry.ioc.def.DecoratorDef}s are ordered, then converted into
+ * {@link ServiceDecorator}s.
+ */
+ List<ServiceDecorator> findDecoratorsForService(ServiceDef serviceDef);
+
+ /**
+ * Builds up an unordered collection by invoking service contributor methods that target the
+ * service (from any module, unless the service is private).
+ *
+ * @param <T>
+ * @param serviceDef
+ * defines the service for which configuration data is being assembled
+ * @param valueType
+ * identifies the type of object allowed into the collection
+ * @return the final collection
+ */
+ <T> Collection<T> getUnorderedConfiguration(ServiceDef serviceDef, Class<T> valueType);
+
+ /**
+ * Builds up an ordered collection by invoking service contributor methods that target the
+ * service (from any module, unless the service is private). Once all values have been added
+ * (each with an id, and pre/post constraints), the values are ordered, null values dropped, and
+ * the final sorted list is returned.
+ *
+ * @param <T>
+ * @param serviceDef
+ * defines the service for which configuration data is being assembled
+ * @param valueType
+ * identifies the type of object allowed into the collection
+ * @return the final ordered list
+ */
+ <T> List<T> getOrderedConfiguration(ServiceDef serviceDef, Class<T> valueType);
+
+ /**
+ * Builds up a map of key/value pairs by invoking service contribution methods that tharget the
+ * service (from any module, unless the service is private). Values and keys may not be null.
+ * Invalid values (keys or values that are the wrong type, or duplicate keys) result in warnings
+ * and are ignored.
+ *
+ * @param <K,
+ * V>
+ * @param serviceDef
+ * defines the service for which configuration data is being assembled
+ * @param keyType
+ * identifies the type of key object allowed into the map
+ * @param valueType
+ * identifies the type of value object allowed into the map
+ * @return the final ordered list
+ */
+ <K, V> Map<K, V> getMappedConfiguration(ServiceDef serviceDef, Class<K> keyType,
+ Class<V> valueType);
+
+ /**
+ * Convieience for creating a new {@link ClassFab} instance using a
+ * {@link org.apache.tapestry.ioc.services.ClassFactory}.
+ *
+ * @param serviceInterface
+ * the interface to be implemented by the provided class
+ */
+ ClassFab newClass(Class serviceInterface);
+
+ /** Provides an object by delegating to the tapestry.ioc.MasterObjectProvider service. */
+ <T> T getObject(String reference, Class<T> objectType, ServiceLocator locator);
+}
Added: tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/internal/ioc/LifecycleWrappedServiceCreator.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/internal/ioc/LifecycleWrappedServiceCreator.java?view=auto&rev=477448
==============================================================================
--- tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/internal/ioc/LifecycleWrappedServiceCreator.java (added)
+++ tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/internal/ioc/LifecycleWrappedServiceCreator.java Mon Nov 20 16:58:25 2006
@@ -0,0 +1,53 @@
+// Copyright 2006 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry.internal.ioc;
+
+import org.apache.tapestry.ioc.ObjectCreator;
+import org.apache.tapestry.ioc.ServiceLifecycle;
+import org.apache.tapestry.ioc.ServiceResources;
+
+/**
+ * Wrapper around a lifecycle, a set of resources for a service, and an underlying service creator
+ * for a service that allows the service lifecycle to alter the way that the service is created
+ * (this is needed for the more advanced, non-singleton types of service lifecycles).
+ *
+ *
+ */
+public class LifecycleWrappedServiceCreator implements ObjectCreator
+{
+ private final ServiceLifecycle _lifecycle;
+
+ private final ServiceResources _resources;
+
+ private final ObjectCreator _creator;
+
+ public LifecycleWrappedServiceCreator(ServiceLifecycle lifecycle, ServiceResources resources,
+ ObjectCreator creator)
+ {
+ _lifecycle = lifecycle;
+ _resources = resources;
+ _creator = creator;
+ }
+
+ /**
+ * Passes the resources and the service creator through the
+ * {@link org.apache.tapestry.ioc.ServiceLifecycle}.
+ */
+ public Object createObject()
+ {
+ return _lifecycle.createService(_resources, _creator);
+ }
+
+}
Added: tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/internal/ioc/LogSourceImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/internal/ioc/LogSourceImpl.java?view=auto&rev=477448
==============================================================================
--- tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/internal/ioc/LogSourceImpl.java (added)
+++ tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/internal/ioc/LogSourceImpl.java Mon Nov 20 16:58:25 2006
@@ -0,0 +1,40 @@
+// Copyright 2006 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry.internal.ioc;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.tapestry.ioc.LogSource;
+
+/**
+ * Simple wrapper around {@link org.apache.commons.logging.LogFactory}. The concept here is that
+ * Log implementations could be provided that promote warnings or errors upto thrown exceptions, for
+ * people who like their IOC container extra finicky.
+ *
+ *
+ */
+public class LogSourceImpl implements LogSource
+{
+ public Log getLog(Class clazz)
+ {
+ return LogFactory.getLog(clazz);
+ }
+
+ public Log getLog(String name)
+ {
+ return LogFactory.getLog(name);
+ }
+
+}
Added: tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/internal/ioc/Module.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/internal/ioc/Module.java?view=auto&rev=477448
==============================================================================
--- tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/internal/ioc/Module.java (added)
+++ tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/internal/ioc/Module.java Mon Nov 20 16:58:25 2006
@@ -0,0 +1,99 @@
+// Copyright 2006 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry.internal.ioc;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.tapestry.ioc.ModuleBuilderSource;
+import org.apache.tapestry.ioc.ServiceDecorator;
+import org.apache.tapestry.ioc.def.ContributionDef;
+import org.apache.tapestry.ioc.def.DecoratorDef;
+import org.apache.tapestry.ioc.def.ServiceDef;
+
+/**
+ * A module within the Tapestry IoC registry. Each Module is constructed around a corresponding
+ * module builder instance; the methods and annotations of that instance define the services
+ * provided by the module.
+ *
+ *
+ */
+public interface Module extends ModuleBuilderSource
+{
+ /**
+ * Locates a service given a service id and the corresponding service interface type.
+ *
+ * @param <T>
+ * @param serviceId
+ * the fully qualified service id
+ * @param serviceInterface
+ * the interface the service implements
+ * @param module
+ * the module requesting the service, or null for no module; this is used for
+ * performing visibility checks
+ * @return the service's proxy
+ * @throws RuntimeException
+ * if the service does not exist (this is considered programmer error)
+ */
+ <T> T getService(String serviceId, Class<T> serviceInterface, Module module);
+
+ /**
+ * Locates the ids of all services that implement the provided service interface. The requesting
+ * module is passed, so that visibility rules may be enforced.
+ *
+ * @param serviceInterface
+ * the interface to search for
+ * @param module
+ * the module requesting the search, or null if no module is requesting the search
+ * @return a collection of strings, each a fully qualified id of a service
+ */
+ Collection<String> findServiceIdsForInterface(Class serviceInterface, Module module);
+
+ /**
+ * Locates all the decorators that should apply the identified service. This includes visibility
+ * rules (private services may only be decorated by decorators in the same module) and other
+ * filtering rules. The resulting list is ordered and from the list of
+ * {@link org.apache.tapestry.ioc.def.DecoratorDef}s, a list of {@link ServiceDecorator}s is
+ * returned.
+ *
+ * @param serviceId
+ * identifies the service to be decorated
+ * @return the ordered list of service decorators
+ */
+ List<ServiceDecorator> findDecoratorsForService(String serviceId);
+
+ /**
+ * Iterates over any decorator definitions defined by the module and returns those that apply to
+ * the provided service definition.
+ *
+ * @param serviceDef
+ * for which decorators are being assembled
+ * @return set of decorators, possibly empty (but not null)
+ */
+ Set<DecoratorDef> findMatchingDecoratorDefs(ServiceDef serviceDef);
+
+ /** Returns the id that uniquely identifies this module within the registry. */
+ String getModuleId();
+
+ /** Finds any contributions that are targetted at the indicated service. */
+ Set<ContributionDef> getContributorDefsForService(String serviceId);
+
+ /**
+ * Locates services with the {@link org.apache.tapestry.ioc.annotations.EagerLoad} annotation
+ * and forces them to instantiate fully. This is part of the Registry startup.
+ */
+ void eagerLoadServices();
+}
Added: tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/internal/ioc/ModuleImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/internal/ioc/ModuleImpl.java?view=auto&rev=477448
==============================================================================
--- tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/internal/ioc/ModuleImpl.java (added)
+++ tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/internal/ioc/ModuleImpl.java Mon Nov 20 16:58:25 2006
@@ -0,0 +1,467 @@
+// Copyright 2006 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry.internal.ioc;
+
+import static java.lang.String.format;
+import static org.apache.tapestry.util.CollectionFactory.newList;
+import static org.apache.tapestry.util.CollectionFactory.newMap;
+import static org.apache.tapestry.util.CollectionFactory.newSet;
+import static org.apache.tapestry.util.Defense.notBlank;
+import static org.apache.tapestry.util.Defense.notNull;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Modifier;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.commons.logging.Log;
+import org.apache.tapestry.internal.util.InternalUtils;
+import org.apache.tapestry.ioc.ObjectCreator;
+import org.apache.tapestry.ioc.ServiceBuilderResources;
+import org.apache.tapestry.ioc.ServiceDecorator;
+import org.apache.tapestry.ioc.ServiceLifecycle;
+import org.apache.tapestry.ioc.ServiceLocator;
+import org.apache.tapestry.ioc.ServiceResources;
+import org.apache.tapestry.ioc.def.ContributionDef;
+import org.apache.tapestry.ioc.def.DecoratorDef;
+import org.apache.tapestry.ioc.def.ModuleDef;
+import org.apache.tapestry.ioc.def.ServiceDef;
+import org.apache.tapestry.ioc.services.ClassFab;
+import org.apache.tapestry.ioc.services.MethodSignature;
+import org.apache.tapestry.ioc.services.RegistryShutdownListener;
+import org.apache.tapestry.util.BodyBuilder;
+
+/**
+ *
+ */
+public class ModuleImpl implements Module
+{
+ private final InternalRegistry _registry;
+
+ private final ModuleDef _moduleDef;
+
+ private final Log _log;
+
+ private Object _moduleBuilder;
+
+ private final static String INTERNAL_MODULE_ID = "tapestry.ioc";
+
+ // Set to true when invoking the module constructor. Used to
+ // detect endless loops caused by irresponsible dependencies into
+ // the constructor.
+
+ private boolean _insideConstructor;
+
+ public ModuleImpl(InternalRegistry registry, ModuleDef moduleDef, Log log)
+ {
+ _registry = registry;
+ _moduleDef = moduleDef;
+ _log = log;
+ }
+
+ /** Keyed on fully qualified service id; values are instantiated services (proxies). */
+ private final Map<String, Object> _services = newMap();
+
+ public <T> T getService(String serviceId, Class<T> serviceInterface, Module module)
+ {
+ notBlank(serviceId, "serviceId");
+ notNull(serviceInterface, "serviceInterface");
+ // module may be null.
+
+ ServiceDef def = _moduleDef.getServiceDef(serviceId);
+
+ if (def == null)
+ throw new IllegalArgumentException(IOCMessages.missingService(serviceId));
+
+ if (notVisible(def, module))
+ throw new RuntimeException(IOCMessages.serviceIsPrivate(serviceId));
+
+ Object service = findOrCreate(def);
+
+ try
+ {
+ return serviceInterface.cast(service);
+ }
+ catch (ClassCastException ex)
+ {
+ // This may be overkill: I don't know how this could happen
+ // given that the return type of the method determines
+ // the service interface.
+
+ throw new RuntimeException(IOCMessages.serviceWrongInterface(serviceId, def
+ .getServiceInterface(), serviceInterface));
+ }
+ }
+
+ public Set<DecoratorDef> findMatchingDecoratorDefs(ServiceDef serviceDef)
+ {
+ Set<DecoratorDef> result = newSet();
+
+ for (DecoratorDef def : _moduleDef.getDecoratorDefs())
+ {
+ if (def.matches(serviceDef))
+ result.add(def);
+ }
+
+ return result;
+ }
+
+ private boolean notVisible(ServiceDef def, Module module)
+ {
+ return def.isPrivate() && this != module;
+ }
+
+ public List<ServiceDecorator> findDecoratorsForService(String serviceId)
+ {
+ ServiceDef sd = _moduleDef.getServiceDef(serviceId);
+
+ return _registry.findDecoratorsForService(sd);
+ }
+
+ public Collection<String> findServiceIdsForInterface(Class serviceInterface, Module module)
+ {
+ notNull(serviceInterface, "serviceInterface");
+
+ Collection<String> result = newList();
+
+ for (String id : _moduleDef.getServiceIds())
+ {
+ ServiceDef def = _moduleDef.getServiceDef(id);
+
+ if (def.getServiceInterface() != serviceInterface)
+ continue;
+
+ if (notVisible(def, module))
+ continue;
+
+ result.add(id);
+ }
+
+ return result;
+ }
+
+ // Why synchronized here? Two reasons. First, with some lifecycle models (or perhaps in some
+ // scenarios using interceptors), we may try to acquire the write lock a second time and the
+ // @Concurrent.Write annotation doesn't currently support that. Second, I'm concerned about
+ // multiple threads building services simultaneously, and getting into a thread deadlock. Of
+ // course, this isn't a solution for that ... we may need a global mutex to handle that specific
+ // case! Alternately, I've thought about creating a "service creation" thread at startup and
+ // queuing service creation requests to that thread, and blocking the local thread.
+
+ private synchronized Object findOrCreate(ServiceDef def)
+ {
+ String key = def.getServiceId();
+
+ Object result = _services.get(key);
+
+ if (result == null)
+ {
+ result = create(def);
+ _services.put(key, result);
+ }
+
+ return result;
+ }
+
+ public void eagerLoadServices()
+ {
+ for (String id : _moduleDef.getServiceIds())
+ {
+ ServiceDef def = _moduleDef.getServiceDef(id);
+
+ if (!def.isEagerLoad())
+ continue;
+
+ // The proxy implements the service interface, and RegistryShutdownListener, and (for
+ // eager load services), EagerLoadServiceProxy
+
+ EagerLoadServiceProxy proxy = (EagerLoadServiceProxy) findOrCreate(def);
+
+ proxy.eagerLoadService();
+ }
+ }
+
+ /**
+ * Creates the service and updates the cache of created services. Method is called from
+ * synchronized block.
+ */
+ private Object create(ServiceDef def)
+ {
+ String serviceId = def.getServiceId();
+
+ Log log = _registry.getLog(serviceId);
+
+ if (log.isDebugEnabled())
+ log.debug(IOCMessages.creatingService(serviceId));
+
+ try
+ {
+ ServiceLifecycle lifecycle = _registry.getServiceLifecycle(def.getServiceLifeycle());
+
+ ServiceBuilderResources resources = new ServiceResourcesImpl(_registry, this, def, log);
+
+ // Build up a stack of operations that will be needed to instantiate the service
+ // (by the proxy, at a later date).
+
+ ObjectCreator creator = def.createServiceCreator(resources);
+
+ creator = new LifecycleWrappedServiceCreator(lifecycle, resources, creator);
+
+ // Don't allow the tapestry.ioc services to be decorated.
+
+ if (!getModuleId().equals(INTERNAL_MODULE_ID))
+ creator = new InterceptorStackBuilder(this, serviceId, creator);
+
+ // Add a wrapper that makes sure that it only gets created once.
+
+ creator = new OneShotServiceCreator(def, creator);
+
+ Object service = createProxy(resources, creator, def.isEagerLoad());
+
+ _services.put(serviceId, service);
+
+ return service;
+ }
+ catch (Exception ex)
+ {
+ throw new RuntimeException(IOCMessages.errorBuildingService(serviceId, def, ex), ex);
+ }
+ }
+
+ public synchronized Object getModuleBuilder()
+ {
+ if (_moduleBuilder == null)
+ _moduleBuilder = instantiateModuleBuilder();
+
+ return _moduleBuilder;
+ }
+
+ private Object instantiateModuleBuilder()
+ {
+ Class builderClass = _moduleDef.getBuilderClass();
+
+ Constructor[] constructors = builderClass.getConstructors();
+
+ if (constructors.length == 0)
+ throw new RuntimeException(IOCMessages.noPublicConstructors(
+ _moduleDef.getModuleId(),
+ builderClass));
+
+ if (constructors.length > 1)
+ {
+ // Sort the constructors ascending by number of parameters (descending); this is really
+ // just to allow the test suite to work properly across different JVMs (which will
+ // often order the constructors differently).
+
+ Comparator<Constructor> comparator = new Comparator<Constructor>()
+ {
+ public int compare(Constructor c1, Constructor c2)
+ {
+ return c2.getParameterTypes().length - c1.getParameterTypes().length;
+ }
+ };
+
+ Arrays.sort(constructors, comparator);
+
+ _log.warn(IOCMessages.tooManyPublicConstructors(
+ _moduleDef.getModuleId(),
+ builderClass,
+ constructors[0]));
+ }
+
+ Constructor constructor = constructors[0];
+
+ if (_insideConstructor)
+ throw new RuntimeException(IOCMessages.recursiveModuleConstructor(_moduleDef
+ .getModuleId(), builderClass, constructor));
+
+ ServiceLocator locator = new ServiceLocatorImpl(_registry, this);
+ Map<Class, Object> parameterDefaults = newMap();
+
+ parameterDefaults.put(Log.class, _log);
+ parameterDefaults.put(String.class, _moduleDef.getModuleId());
+ parameterDefaults.put(ServiceLocator.class, locator);
+
+ Throwable fail = null;
+
+ try
+ {
+ _insideConstructor = true;
+
+ Object[] parameterValues = InternalUtils.calculateParameters(
+ locator,
+ parameterDefaults,
+ constructor.getParameterTypes(),
+ constructor.getParameterAnnotations());
+
+ return constructor.newInstance(parameterValues);
+ }
+ catch (InvocationTargetException ex)
+ {
+ fail = ex.getTargetException();
+ }
+ catch (Exception ex)
+ {
+ fail = ex;
+ }
+ finally
+ {
+ _insideConstructor = false;
+ }
+
+ throw new RuntimeException(IOCMessages.instantiateBuilderError(builderClass, _moduleDef
+ .getModuleId(), fail), fail);
+ }
+
+ private Object createProxy(ServiceResources resources, ObjectCreator creator, boolean eagerLoad)
+ {
+ String serviceId = resources.getServiceId();
+ Class serviceInterface = resources.getServiceInterface();
+
+ String toString = format("<Proxy for %s(%s)>", serviceId, serviceInterface.getName());
+
+ RegistryShutdownListener proxy = createProxyInstance(
+ creator,
+ serviceId,
+ serviceInterface,
+ eagerLoad,
+ toString);
+
+ _registry.addRegistryShutdownListener(proxy);
+
+ return proxy;
+ }
+
+ private RegistryShutdownListener createProxyInstance(ObjectCreator creator, String serviceId,
+ Class serviceInterface, boolean eagerLoad, String description)
+ {
+ Class proxyClass = createProxyClass(serviceId, serviceInterface, eagerLoad, description);
+
+ try
+ {
+ return (RegistryShutdownListener) proxyClass.getConstructors()[0].newInstance(creator);
+ }
+ catch (Exception ex)
+ {
+ // This should never happen, so we won't go to a lot of trouble
+ // reporting it.
+ throw new RuntimeException(ex.getMessage(), ex);
+ }
+ }
+
+ private Class createProxyClass(String serviceId, Class serviceInterface, boolean eagerLoad,
+ String proxyDescription)
+ {
+ ClassFab cf = _registry.newClass(serviceInterface);
+
+ cf.addField("_creator", ObjectCreator.class);
+ cf.addField("_delegate", serviceInterface);
+ cf.addField("_shutdown", boolean.class);
+
+ cf.addConstructor(new Class[]
+ { ObjectCreator.class }, null, "_creator = $1;");
+
+ addDelegateGetter(cf, serviceInterface, serviceId);
+
+ addShutdownListenerMethod(cf);
+
+ cf.proxyMethodsToDelegate(serviceInterface, "_delegate()", proxyDescription);
+
+ // For eager load services, add an eagerLoadService() method that calls _delegate(), to
+ // force the creation of the service.
+
+ if (eagerLoad)
+ {
+ cf.addInterface(EagerLoadServiceProxy.class);
+
+ cf.addMethod(Modifier.PUBLIC, new MethodSignature(void.class, "eagerLoadService", null,
+ null), "_delegate();");
+ }
+
+ return cf.createClass();
+ }
+
+ private void addDelegateGetter(ClassFab cf, Class serviceInterface, String serviceId)
+ {
+ BodyBuilder builder = new BodyBuilder();
+ builder.begin();
+
+ // Check to see if the registry has shutdown. The registryShutdown() method
+ // throws IllegalStateException.
+
+ builder.addln("if (_shutdown) %s.registryShutdown(\"%s\");", IOCProxyUtilities.class
+ .getName(), serviceId);
+
+ // We can release the creator after invoking it, we only create the service once.
+
+ builder.addln("if (_delegate == null)");
+ builder.begin();
+ builder.addln("_delegate = (%s) _creator.createObject();", serviceInterface.getName());
+ builder.addln("_creator = null;");
+ builder.end();
+
+ builder.addln("return _delegate;");
+ builder.end();
+
+ MethodSignature sig = new MethodSignature(serviceInterface, "_delegate", null, null);
+
+ // Here's the rub, this _delegate() method has to be synchronized. But after the first
+ // time through (when we create the service), the time inside the method is infintesmal.
+ // Let's hope that they aren't lying when they say that synchronized is now super cheap!
+
+ cf.addMethod(Modifier.PRIVATE | Modifier.SYNCHRONIZED, sig, builder.toString());
+ }
+
+ /**
+ * All proxies implement {@link RegistryShutdownListener}. When the registry shuts down, the
+ * proxy sets a flag that ultimately converts method invocations to
+ * {@link IllegalStateException}s, and discards its delegate and creator.
+ */
+ private void addShutdownListenerMethod(ClassFab cf)
+ {
+ cf.addInterface(RegistryShutdownListener.class);
+
+ MethodSignature sig = new MethodSignature(void.class, "registryDidShutdown", null, null);
+
+ cf.addMethod(
+ Modifier.PUBLIC | Modifier.SYNCHRONIZED,
+ sig,
+ "{ _shutdown = true; _delegate = null; _creator = null; }");
+ }
+
+ public String getModuleId()
+ {
+ return _moduleDef.getModuleId();
+ }
+
+ public Set<ContributionDef> getContributorDefsForService(String serviceId)
+ {
+ Set<ContributionDef> result = newSet();
+
+ for (ContributionDef def : _moduleDef.getContributionDefs())
+ {
+ if (def.getServiceId().equals(serviceId))
+ result.add(def);
+ }
+
+ return result;
+ }
+
+}
Added: tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/internal/ioc/OneShotServiceCreator.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/internal/ioc/OneShotServiceCreator.java?view=auto&rev=477448
==============================================================================
--- tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/internal/ioc/OneShotServiceCreator.java (added)
+++ tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/internal/ioc/OneShotServiceCreator.java Mon Nov 20 16:58:25 2006
@@ -0,0 +1,59 @@
+// Copyright 2006 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry.internal.ioc;
+
+import org.apache.tapestry.ioc.ObjectCreator;
+import org.apache.tapestry.ioc.def.ServiceDef;
+
+/**
+ * Decorator for {@link org.apache.tapestry.ioc.ObjectCreator} that ensures the service is only
+ * created once. This detects a situation where the service builder for a service directly or
+ * indirectly invokes methods on the service itself. This would show up as a second call up the
+ * ServiceCreator stack injected into the proxy.
+ * <p>
+ * We could use the {@link org.apache.tapestry.internal.annotations.OneShot} annotation, but this
+ * implementation gives us a bit more flexibility to report the error.
+ *
+ *
+ */
+public class OneShotServiceCreator implements ObjectCreator
+{
+ private final ServiceDef _serviceDef;
+
+ private final ObjectCreator _delegate;
+
+ private boolean _locked;
+
+ public OneShotServiceCreator(ServiceDef serviceDef, ObjectCreator delegate)
+ {
+ _serviceDef = serviceDef;
+ _delegate = delegate;
+ }
+
+ /**
+ * We could make this method synchronized, but in the context of creating a service for a proxy,
+ * it will already be synchronized (inside the proxy).
+ */
+ public Object createObject()
+ {
+ if (_locked)
+ throw new IllegalStateException(IOCMessages.recursiveServiceBuild(_serviceDef));
+
+ _locked = true;
+
+ return _delegate.createObject();
+ }
+
+}
Added: tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/internal/ioc/RegistryImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/internal/ioc/RegistryImpl.java?view=auto&rev=477448
==============================================================================
--- tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/internal/ioc/RegistryImpl.java (added)
+++ tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/internal/ioc/RegistryImpl.java Mon Nov 20 16:58:25 2006
@@ -0,0 +1,574 @@
+// Copyright 2006 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry.internal.ioc;
+
+import static org.apache.tapestry.util.CollectionFactory.newList;
+import static org.apache.tapestry.util.CollectionFactory.newMap;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.commons.logging.Log;
+import org.apache.tapestry.internal.ioc.services.ClassFactoryImpl;
+import org.apache.tapestry.internal.ioc.services.RegistryShutdownHubImpl;
+import org.apache.tapestry.internal.ioc.services.ThreadCleanupHubImpl;
+import org.apache.tapestry.ioc.Configuration;
+import org.apache.tapestry.ioc.IOCConstants;
+import org.apache.tapestry.ioc.IOCUtilities;
+import org.apache.tapestry.ioc.LogSource;
+import org.apache.tapestry.ioc.MappedConfiguration;
+import org.apache.tapestry.ioc.ObjectProvider;
+import org.apache.tapestry.ioc.OrderedConfiguration;
+import org.apache.tapestry.ioc.Registry;
+import org.apache.tapestry.ioc.ServiceDecorator;
+import org.apache.tapestry.ioc.ServiceLifecycle;
+import org.apache.tapestry.ioc.ServiceLocator;
+import org.apache.tapestry.ioc.ServiceResources;
+import org.apache.tapestry.ioc.def.ContributionDef;
+import org.apache.tapestry.ioc.def.DecoratorDef;
+import org.apache.tapestry.ioc.def.ModuleDef;
+import org.apache.tapestry.ioc.def.ServiceDef;
+import org.apache.tapestry.ioc.internal.util.OneShotLock;
+import org.apache.tapestry.ioc.internal.util.Orderer;
+import org.apache.tapestry.ioc.services.ClassFab;
+import org.apache.tapestry.ioc.services.ClassFactory;
+import org.apache.tapestry.ioc.services.RegistryShutdownHub;
+import org.apache.tapestry.ioc.services.RegistryShutdownListener;
+import org.apache.tapestry.ioc.services.ServiceLifecycleSource;
+import org.apache.tapestry.ioc.services.ThreadCleanupHub;
+import org.apache.tapestry.util.CollectionFactory;
+
+public class RegistryImpl implements Registry, InternalRegistry
+{
+ private static final String REGISTRY_SHUTDOWN_HUB_SERVICE_ID = "tapestry.ioc.RegistryShutdownHub";
+
+ static final String THREAD_CLEANUP_HUB_SERVICE_ID = "tapestry.ioc.ThreadCleanupHub";
+
+ /**
+ * Used to obtain the {@link org.apache.tapestry.ioc.services.ClassFactory} service, which is
+ * crucial when creating runtime classes for proxies and the like.
+ */
+ static final String CLASS_FACTORY_SERVICE_ID = "tapestry.ioc.ClassFactory";
+
+ static final String LOG_SOURCE_SERVICE_ID = "tapestry.ioc.LogSource";
+
+ private final OneShotLock _lock = new OneShotLock();
+
+ private final Map<String, Object> _builtinServices = newMap();
+
+ private final Map<String, Class> _builtinTypes = newMap();
+
+ private final RegistryShutdownHubImpl _registryShutdownHub;
+
+ private final LogSource _logSource;
+
+ /** Keyed on module id. */
+ private final Map<String, Module> _modules = newMap();
+
+ private final Map<String, ServiceLifecycle> _lifecycles = newMap();
+
+ private final ThreadCleanupHubImpl _cleanupHub;
+
+ private final ClassFactory _classFactory;
+
+ public static final class OrderedConfigurationToOrdererAdaptor<T> implements
+ OrderedConfiguration<T>
+ {
+ private final Orderer<T> _orderer;
+
+ public OrderedConfigurationToOrdererAdaptor(Orderer<T> orderer)
+ {
+ _orderer = orderer;
+ }
+
+ public void add(String id, T object, String... constraints)
+ {
+ _orderer.add(id, object, constraints);
+ }
+ }
+
+ public RegistryImpl(Collection<ModuleDef> moduleDefs, ClassLoader contextClassLoader,
+ LogSource logSource)
+ {
+ _logSource = logSource;
+
+ for (ModuleDef def : moduleDefs)
+ {
+ Log log = _logSource.getLog(def.getModuleId());
+
+ Module module = new ModuleImpl(this, def, log);
+
+ _modules.put(def.getModuleId(), module);
+ }
+
+ addBuiltin(LOG_SOURCE_SERVICE_ID, LogSource.class, _logSource);
+
+ Log log = _logSource.getLog(RegistryImpl.CLASS_FACTORY_SERVICE_ID);
+
+ _classFactory = new ClassFactoryImpl(contextClassLoader, log);
+
+ addBuiltin(RegistryImpl.CLASS_FACTORY_SERVICE_ID, ClassFactory.class, _classFactory);
+
+ log = _logSource.getLog(THREAD_CLEANUP_HUB_SERVICE_ID);
+
+ _cleanupHub = new ThreadCleanupHubImpl(log);
+
+ addBuiltin(THREAD_CLEANUP_HUB_SERVICE_ID, ThreadCleanupHub.class, _cleanupHub);
+
+ log = _logSource.getLog(REGISTRY_SHUTDOWN_HUB_SERVICE_ID);
+
+ _registryShutdownHub = new RegistryShutdownHubImpl(log);
+
+ addBuiltin(
+ REGISTRY_SHUTDOWN_HUB_SERVICE_ID,
+ RegistryShutdownHub.class,
+ _registryShutdownHub);
+
+ _lifecycles.put("singleton", new SingletonServiceLifecycle());
+
+ // Ask all modules to eager-load any services marked with @EagerLoad
+
+ for (Module m : _modules.values())
+ m.eagerLoadServices();
+ }
+
+ private <T> void addBuiltin(String serviceId, Class<T> serviceInterface, T service)
+ {
+ _builtinTypes.put(serviceId, serviceInterface);
+ _builtinServices.put(serviceId, service);
+ }
+
+ public synchronized void shutdown()
+ {
+ _lock.lock();
+
+ _registryShutdownHub.fireRegistryDidShutdown();
+ }
+
+ /** Internal access, usualy from another module. */
+ public <T> T getService(String serviceId, Class<T> serviceInterface, Module module)
+ {
+ _lock.check();
+
+ T result = checkForBuiltinService(serviceId, serviceInterface);
+ if (result != null) return result;
+
+ // Checking serviceId and serviceInterface is overkill; they have been
+ // checked and rechecked
+ // all the way to here.
+
+ Module containingModule = locateModuleForService(serviceId);
+
+ return containingModule.getService(serviceId, serviceInterface, module);
+ }
+
+ private <T> T checkForBuiltinService(String serviceId, Class<T> serviceInterface)
+ {
+ Object service = _builtinServices.get(serviceId);
+
+ if (service == null) return null;
+
+ try
+ {
+ return serviceInterface.cast(service);
+ }
+ catch (ClassCastException ex)
+ {
+ throw new RuntimeException(IOCMessages.serviceWrongInterface(serviceId, _builtinTypes
+ .get(serviceId), serviceInterface));
+ }
+ }
+
+ /**
+ * Access for a service from the outside world (limited to publically visible services).
+ */
+ public <T> T getService(String serviceId, Class<T> serviceInterface)
+ {
+ _lock.check();
+
+ return getService(serviceId, serviceInterface, null);
+ }
+
+ public void cleanupThread()
+ {
+ _lock.check();
+
+ _cleanupHub.cleanup();
+ }
+
+ private Module locateModuleForService(String serviceId)
+ {
+ String moduleId = IOCUtilities.extractModuleId(serviceId);
+
+ Module module = _modules.get(moduleId);
+
+ if (module == null) throw new RuntimeException(IOCMessages.noSuchModule(moduleId));
+
+ return module;
+ }
+
+ public <T> Collection<T> getUnorderedConfiguration(ServiceDef serviceDef, Class<T> objectType)
+ {
+ _lock.check();
+
+ final Collection<T> result = newList();
+
+ Configuration<T> configuration = new Configuration<T>()
+ {
+ public void add(T object)
+ {
+ result.add(object);
+ }
+ };
+
+ Collection<Module> modules = modulesThatContributeToService(serviceDef);
+
+ for (Module m : modules)
+ addToUnorderedConfiguration(configuration, objectType, serviceDef, m);
+
+ return result;
+ }
+
+ public <T> List<T> getOrderedConfiguration(ServiceDef serviceDef, Class<T> objectType)
+ {
+ _lock.check();
+
+ Log log = getLog(serviceDef.getServiceId());
+
+ final Orderer<T> orderer = new Orderer<T>(log);
+
+ OrderedConfiguration<T> configuration = new OrderedConfigurationToOrdererAdaptor<T>(orderer);
+
+ Collection<Module> modules = modulesThatContributeToService(serviceDef);
+
+ for (Module m : modules)
+ addToOrderedConfiguration(configuration, objectType, serviceDef, m);
+
+ return orderer.getOrdered();
+ }
+
+ public <K, V> Map<K, V> getMappedConfiguration(ServiceDef serviceDef, Class<K> keyType,
+ Class<V> objectType)
+ {
+ _lock.check();
+
+ final Map<K, V> result = newMap();
+
+ MappedConfiguration<K, V> configuration = new MappedConfiguration<K, V>()
+ {
+ public void add(K key, V value)
+ {
+ result.put(key, value);
+ }
+ };
+
+ Map<K, ContributionDef> keyToContribution = newMap();
+
+ Collection<Module> modules = modulesThatContributeToService(serviceDef);
+
+ for (Module m : modules)
+ addToMappedConfiguration(
+ configuration,
+ keyToContribution,
+ keyType,
+ objectType,
+ serviceDef,
+ m);
+
+ return result;
+ }
+
+ private Collection<Module> modulesThatContributeToService(ServiceDef serviceDef)
+ {
+ if (serviceDef.isPrivate())
+ {
+ String moduleId = IOCUtilities.extractModuleId(serviceDef.getServiceId());
+ Module module = _modules.get(moduleId);
+
+ return Arrays.asList(module);
+ }
+
+ return _modules.values();
+ }
+
+ private <K, V> void addToMappedConfiguration(MappedConfiguration<K, V> configuration,
+ Map<K, ContributionDef> keyToContribution, Class<K> keyClass, Class<V> valueType,
+ ServiceDef serviceDef, Module module)
+ {
+ String serviceId = serviceDef.getServiceId();
+ Set<ContributionDef> contributions = module.getContributorDefsForService(serviceId);
+
+ if (contributions.isEmpty()) return;
+
+ Log log = getLog(serviceId);
+ boolean debug = log.isDebugEnabled();
+
+ ServiceLocator locator = new ServiceResourcesImpl(this, module, serviceDef, log);
+
+ for (ContributionDef def : contributions)
+ {
+ MappedConfiguration<K, V> validating = new ValidatingMappedConfigurationWrapper<K, V>(
+ serviceId, def, log, keyClass, valueType, keyToContribution, configuration);
+
+ if (debug) log.debug(IOCMessages.invokingMethod(def));
+
+ def.contribute(module, locator, validating);
+ }
+
+ }
+
+ private <T> void addToUnorderedConfiguration(Configuration<T> configuration,
+ Class<T> valueType, ServiceDef serviceDef, Module module)
+ {
+ String serviceId = serviceDef.getServiceId();
+ Set<ContributionDef> contributions = module.getContributorDefsForService(serviceId);
+
+ if (contributions.isEmpty()) return;
+
+ Log log = getLog(serviceId);
+ boolean debug = log.isDebugEnabled();
+
+ ServiceLocator locator = new ServiceResourcesImpl(this, module, serviceDef, log);
+
+ for (ContributionDef def : contributions)
+ {
+ Configuration<T> validating = new ValidatingConfigurationWrapper<T>(serviceId, log,
+ valueType, def, configuration);
+
+ if (debug) log.debug(IOCMessages.invokingMethod(def));
+
+ def.contribute(module, locator, validating);
+ }
+ }
+
+ private <T> void addToOrderedConfiguration(OrderedConfiguration<T> configuration,
+ Class<T> valueType, ServiceDef serviceDef, Module module)
+ {
+ String serviceId = serviceDef.getServiceId();
+ Set<ContributionDef> contributions = module.getContributorDefsForService(serviceId);
+
+ if (contributions.isEmpty()) return;
+
+ Log log = getLog(serviceId);
+ boolean debug = log.isDebugEnabled();
+
+ ServiceLocator locator = new ServiceResourcesImpl(this, module, serviceDef, log);
+
+ for (ContributionDef def : contributions)
+ {
+ OrderedConfiguration<T> validating = new ValidatingOrderedConfigurationWrapper<T>(
+ serviceId, module.getModuleId(), def, log, valueType, configuration);
+
+ if (debug) log.debug(IOCMessages.invokingMethod(def));
+
+ def.contribute(module, locator, validating);
+ }
+ }
+
+ // Seems like something that could be cached.
+ public <T> T getService(Class<T> serviceInterface, Module module)
+ {
+ _lock.check();
+
+ List<String> ids = CollectionFactory.newList();
+
+ for (Module m : _modules.values())
+ {
+ Collection<String> matchingIds = m.findServiceIdsForInterface(serviceInterface, module);
+ ids.addAll(matchingIds);
+ }
+
+ switch (ids.size())
+ {
+ case 0:
+
+ throw new RuntimeException(IOCMessages.noServiceMatchesType(serviceInterface));
+
+ case 1:
+
+ String serviceId = ids.get(0);
+
+ return getService(serviceId, serviceInterface, module);
+
+ default:
+
+ Collections.sort(ids);
+
+ throw new RuntimeException(IOCMessages.manyServiceMatches(serviceInterface, ids));
+ }
+ }
+
+ public <T> T getService(Class<T> serviceInterface)
+ {
+ _lock.check();
+
+ return getService(serviceInterface, null);
+ }
+
+ public ServiceLifecycle getServiceLifecycle(String lifecycle)
+ {
+ _lock.check();
+
+ ServiceLifecycle result = _lifecycles.get(lifecycle);
+
+ if (result == null)
+ {
+ ServiceLifecycleSource source = getService(
+ "tapestry.ioc.ServiceLifecycleSource",
+ ServiceLifecycleSource.class);
+ result = source.get(lifecycle);
+ }
+
+ if (result == null) throw new RuntimeException(IOCMessages.unknownLifecycle(lifecycle));
+
+ return result;
+ }
+
+ public List<ServiceDecorator> findDecoratorsForService(ServiceDef serviceDef)
+ {
+ _lock.check();
+
+ Log log = getLog(serviceDef.getServiceId());
+
+ Orderer<DecoratorDef> orderer = new Orderer<DecoratorDef>(log);
+
+ addDecoratorDefsToOrderer(orderer, serviceDef);
+
+ List<DecoratorDef> ordered = orderer.getOrdered();
+
+ return convertDecoratorDefsToServiceDecorators(ordered, serviceDef, log);
+ }
+
+ private List<ServiceDecorator> convertDecoratorDefsToServiceDecorators(
+ List<DecoratorDef> ordered, ServiceDef serviceDef, Log log)
+ {
+ List<ServiceDecorator> result = newList();
+
+ ServiceResources resources = null;
+ String moduleId = null;
+ Module module = null;
+
+ for (DecoratorDef dd : ordered)
+ {
+ String decoratorModuleId = IOCUtilities.extractModuleId(dd.getDecoratorId());
+
+ // Whenever the module id containing the decorator changes,
+ // "refresh" the resources, etc., to point to the (new) module.
+ // This means that the ServiceResources will identify services
+ // within
+ // the decorators module (important in terms of service visibility
+ // and abbreviated service ids).
+
+ if (!decoratorModuleId.equals(moduleId))
+ {
+ moduleId = decoratorModuleId;
+
+ module = _modules.get(moduleId);
+
+ resources = new ServiceResourcesImpl(this, module, serviceDef, log);
+ }
+
+ ServiceDecorator decorator = dd.createDecorator(module, resources);
+
+ result.add(decorator);
+ }
+
+ return result;
+ }
+
+ private void addDecoratorDefsToOrderer(Orderer<DecoratorDef> orderer, ServiceDef serviceDef)
+ {
+ if (serviceDef.isPrivate())
+ {
+ Set<DecoratorDef> privateDecorators = findDecoratorsDefsForPrivateService(serviceDef);
+ addToOrderer(orderer, privateDecorators);
+ }
+ else
+ {
+ for (Module m : _modules.values())
+ {
+ Set<DecoratorDef> moduleDecorators = m.findMatchingDecoratorDefs(serviceDef);
+ addToOrderer(orderer, moduleDecorators);
+ }
+ }
+ }
+
+ private void addToOrderer(Orderer<DecoratorDef> orderer, Set<DecoratorDef> decorators)
+ {
+ for (DecoratorDef df : decorators)
+ {
+ orderer.add(df.getDecoratorId(), df, df.getConstraints());
+ }
+ }
+
+ private Set<DecoratorDef> findDecoratorsDefsForPrivateService(ServiceDef serviceDef)
+ {
+ String moduleId = IOCUtilities.extractModuleId(serviceDef.getServiceId());
+ Module module = _modules.get(moduleId);
+
+ return module.findMatchingDecoratorDefs(serviceDef);
+ }
+
+ public Log getLog(Class clazz)
+ {
+ _lock.check();
+
+ return _logSource.getLog(clazz);
+ }
+
+ public Log getLog(String name)
+ {
+ _lock.check();
+
+ return _logSource.getLog(name);
+ }
+
+ public ClassFab newClass(Class serviceInterface)
+ {
+ _lock.check();
+
+ return _classFactory.newClass(serviceInterface);
+ }
+
+ public <T> T getObject(String reference, Class<T> objectType, ServiceLocator locator)
+ {
+ _lock.check();
+
+ ObjectProvider masterProvider = getService(
+ IOCConstants.MASTER_OBJECT_PROVIDER_SERVICE_ID,
+ ObjectProvider.class);
+
+ return masterProvider.provide(reference, objectType, locator);
+ }
+
+ public <T> T getObject(String reference, Class<T> objectType)
+ {
+ _lock.check();
+
+ // Concerened about this causing potential endless loops.
+ return getObject(reference, objectType, this);
+ }
+
+ public void addRegistryShutdownListener(RegistryShutdownListener listener)
+ {
+ _lock.check();
+
+ _registryShutdownHub.addRegistryShutdownListener(listener);
+ }
+}