Adding AssistedInject and ThrowingProviders as Guice extensions.

git-svn-id: https://google-guice.googlecode.com/svn/trunk@391 d779f126-a31b-0410-b53b-1d3aecad763e
diff --git a/extensions/assistedinject/assistedinject.iml b/extensions/assistedinject/assistedinject.iml
new file mode 100644
index 0000000..336fd39
--- /dev/null
+++ b/extensions/assistedinject/assistedinject.iml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module version="4" relativePaths="true" type="JAVA_MODULE">
+  <component name="ModuleRootManager" />
+  <component name="NewModuleRootManager" inherit-compiler-output="true">
+    <exclude-output />
+    <content url="file://$MODULE_DIR$">
+      <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
+    </content>
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+    <orderEntry type="module" module-name="guice" />
+    <orderEntry type="module-library">
+      <library>
+        <CLASSES>
+          <root url="jar://$MODULE_DIR$/../../lib/build/junit.jar!/" />
+        </CLASSES>
+        <JAVADOC />
+        <SOURCES />
+      </library>
+    </orderEntry>
+    <orderEntryProperties />
+  </component>
+</module>
+
diff --git a/extensions/assistedinject/build.properties b/extensions/assistedinject/build.properties
new file mode 100644
index 0000000..f7a30b2
--- /dev/null
+++ b/extensions/assistedinject/build.properties
@@ -0,0 +1,5 @@
+lib.dir=../../lib
+src.dir=src
+test.dir=test
+build.dir=build
+test.class=com.google.inject.assistedinject.FactoryProviderTest
diff --git a/extensions/assistedinject/build.xml b/extensions/assistedinject/build.xml
new file mode 100644
index 0000000..8e4b9bf
--- /dev/null
+++ b/extensions/assistedinject/build.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0"?>
+
+<project name="guice-extensions-assistedinject" basedir="." default="jar">
+
+  <import file="../../common.xml"/>
+  
+  <path id="compile.classpath">
+    <fileset dir="${lib.dir}" includes="*.jar"/>
+    <fileset dir="${lib.dir}/build" includes="*.jar"/>
+    <fileset dir="../../build/dist" includes="*.jar"/>
+  </path>
+
+  <target name="jar" depends="compile"
+       description="Build jar.">
+    <mkdir dir="${build.dir}"/>
+    <jar destfile="${build.dir}/${ant.project.name}-${version}.jar">
+      <fileset dir="${build.dir}/classes"/>
+    </jar>
+  </target>
+
+</project>
diff --git a/extensions/assistedinject/src/com/google/inject/assistedinject/Assisted.java b/extensions/assistedinject/src/com/google/inject/assistedinject/Assisted.java
new file mode 100755
index 0000000..4a24b86
--- /dev/null
+++ b/extensions/assistedinject/src/com/google/inject/assistedinject/Assisted.java
@@ -0,0 +1,35 @@
+/**
+ * Copyright (C) 2007 Google Inc.
+ *
+ * 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 com.google.inject.assistedinject;
+
+import static java.lang.annotation.ElementType.PARAMETER;
+import java.lang.annotation.Retention;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+import java.lang.annotation.Target;
+
+/**
+ * The {@code @Assisted} annotation should be used on paramters within
+ * a constructor annotated with {@code @AssistedInject}.  The annotation
+ * indicates that the parameter will be supplied through a factory 
+ * method (the parameter will not be injected by Guice).
+ * 
+ * @author jmourits@google.com (Jerome Mourits)
+ * @author jessewilson@google.com (Jesse Wilson)
+ */
+@Target({PARAMETER})
+@Retention(RUNTIME)
+public @interface Assisted {}
\ No newline at end of file
diff --git a/extensions/assistedinject/src/com/google/inject/assistedinject/AssistedConstructor.java b/extensions/assistedinject/src/com/google/inject/assistedinject/AssistedConstructor.java
new file mode 100755
index 0000000..4976bd0
--- /dev/null
+++ b/extensions/assistedinject/src/com/google/inject/assistedinject/AssistedConstructor.java
@@ -0,0 +1,102 @@
+/**
+ * Copyright (C) 2007 Google Inc.
+ *
+ * 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 com.google.inject.assistedinject;
+
+import com.google.inject.Inject;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Internal respresentation of a constructor annotated with
+ * {@link AssistedInject}
+ * 
+ * @author jmourits@google.com (Jerome Mourits)
+ * @author jessewilson@google.com (Jesse Wilson)
+ */
+class AssistedConstructor<T> {
+
+  private final Constructor<T> constructor;
+  private final ParameterListKey assistedParameters;
+  private final List<Parameter> allParameters;
+
+  @SuppressWarnings("unchecked")
+  public AssistedConstructor(Constructor<T> constructor) {
+    this.constructor = constructor;
+
+    Type[] parameterTypes = constructor.getGenericParameterTypes();
+    Annotation[][] annotations = constructor.getParameterAnnotations();
+
+    List<Type> typeList = new ArrayList<Type>();
+    allParameters = new ArrayList<Parameter>();
+
+    // categorize params as @Assisted or @Injected
+    for (int i = 0; i < parameterTypes.length; i++) {
+      Parameter parameter = new Parameter(parameterTypes[i], annotations[i]);
+      allParameters.add(parameter);
+      if (parameter.isProvidedByFactory()) {
+        typeList.add(parameter.getType());
+      }
+    }
+    this.assistedParameters = new ParameterListKey(typeList);
+  }
+  
+  /**
+   * Returns the {@link ParameterListKey} for this constructor.  The
+   * {@link ParameterListKey} is created from the ordered list of {@link Assisted}
+   * constructor parameters.
+   */
+  public ParameterListKey getAssistedParameters() {
+    return assistedParameters;
+  }
+  
+  /**
+   * Returns an ordered list of all constructor parameters (both
+   * {@link Assisted} and {@link Inject}ed).
+   */
+  public List<Parameter> getAllParameters() {
+    return allParameters;
+  }
+
+  public Set<Class<?>> getDeclaredExceptions() {
+    return new HashSet<Class<?>>(Arrays.asList(constructor.getExceptionTypes()));
+  }
+  
+  /**
+   * Returns an instance of T, constructed using this constructor, with the
+   * supplied arguments.
+   */
+  public T newInstance(Object[] args) throws Throwable {
+    constructor.setAccessible(true);
+    try {
+      return constructor.newInstance(args);
+    } catch (InvocationTargetException e) {
+      throw e.getCause();
+    }
+  }
+  
+  @Override
+  public String toString() {
+    return constructor.toString();
+  }
+}
diff --git a/extensions/assistedinject/src/com/google/inject/assistedinject/AssistedInject.java b/extensions/assistedinject/src/com/google/inject/assistedinject/AssistedInject.java
new file mode 100755
index 0000000..a936dd4
--- /dev/null
+++ b/extensions/assistedinject/src/com/google/inject/assistedinject/AssistedInject.java
@@ -0,0 +1,37 @@
+/**
+ * Copyright (C) 2007 Google Inc.
+ *
+ * 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 com.google.inject.assistedinject;
+
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
+import java.lang.annotation.Retention;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+import java.lang.annotation.Target;
+
+/**
+ * Constructors annotated with <code>@AssistedInject</code> indicate that will
+ * can be instantiated by the {@link FactoryProvider}.  Each constructor must
+ * exactly one corresponding factory method within the Factory Interface.
+ * 
+ * <p>Constructor parameters must be either supplied by the Factory Interface and
+ * marked with <code>@Assisted</code>, or they must be injectable.
+ * 
+ * @author jmourits@google.com (Jerome Mourits)
+ * @author jessewilson@google.com (Jesse Wilson)
+ */
+@Target({CONSTRUCTOR})
+@Retention(RUNTIME)
+public @interface AssistedInject {}
\ No newline at end of file
diff --git a/extensions/assistedinject/src/com/google/inject/assistedinject/FactoryProvider.java b/extensions/assistedinject/src/com/google/inject/assistedinject/FactoryProvider.java
new file mode 100755
index 0000000..2396133
--- /dev/null
+++ b/extensions/assistedinject/src/com/google/inject/assistedinject/FactoryProvider.java
@@ -0,0 +1,241 @@
+/**
+ * Copyright (C) 2007 Google Inc.
+ *
+ * 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 com.google.inject.assistedinject;
+
+import com.google.inject.Inject;
+import com.google.inject.Injector;
+import com.google.inject.Provider;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Provides a mechanism to combine user-specified paramters with
+ * {@link Injector}-specified paramters when creating new objects.
+ *
+ * <p>To use a {@link FactoryProvider}:
+ *
+ * <p>Annotate your implementation class' constructor with the
+ * {@link @AssistedInject} and the user-specified parameters with
+ * {@link @Assisted}:
+ * <pre><code>public class RealPayment implements Payment {
+ *    &#64;AssistedInject
+ *    public RealPayment(CreditService creditService, AuthService authService,
+ *      &#64;Assisted Date startDate, &#64;Assisted Money amount) {
+ *     ...
+ *  }
+ * }</code></pre>
+ *
+ * <p>Write an interface with a <i>create</i> method that accepts the user-specified
+ * parameters in the same order as they appear in the implementation class' constructor:
+ * <pre><code>public interface PaymentFactory {
+ *    Payment create(Date startDate, Money amount);
+ * }</code></pre>
+ *
+ * <p>You can name your create methods whatever you like, such as <i>create</i>,
+ * or <i>createPayment</i> or <i>newPayment</i>. The concrete class must
+ * be assignable to the return type of your create method. You can also provide
+ * multiple factory methods, but there must be exactly one {@link @AssistedInject}
+ * constructor on the implementation class for each.
+ *
+ * <p>In your Guice {@link com.google.inject.Module module}, bind your factory
+ * interface to an instance of {@link FactoryProvider} that was created with
+ * the same factory interface and implementation type:
+ * <pre><code>  bind(PaymentFactory.class).toProvider(
+ *     FactoryProvider.newFactory(PaymentFactory.class, RealPayment.class));</code></pre>
+ *
+ * <p>Now you can {@code @Inject} your factory interface into your Guice-injected
+ * classes. When you invoke the create method on that factory, the
+ * {@link FactoryProvider} will instantiate the implementation class using
+ * parameters from the injector and the factory method.
+ *
+ * <pre><code>public class PaymentAction {
+ *    @Inject private PaymentFactory paymentFactory;
+ *
+ *    public void doPayment(Money amount) {
+ *       Payment payment = paymentFactory.create(new Date(), amount);
+ *       payment.apply();
+ *    }
+ * }
+ * 
+ * @typeparam F The factory interface 
+ * @typeparam R The concrete class to be created.
+ * 
+ * @author jmourits@google.com (Jerome Mourits)
+ * @author jessewilson@google.com (Jesse Wilson)
+ */
+public class FactoryProvider<F, R> implements Provider<F> {
+
+  private Injector injector;
+
+  private final Class<F> factoryType;
+  private final Class<R> implementationType;
+  private final Map<Method, AssistedConstructor<?>> factoryMethodToConstructor;
+
+  public static <X,Y> FactoryProvider<X,Y> newFactory(
+      Class<X> factoryType, Class<Y> implementationType){
+    return new FactoryProvider<X, Y>(factoryType,implementationType);
+  }
+  
+  private FactoryProvider(Class<F> factoryType, Class<R> implementationType) {
+    this.factoryType = factoryType;
+    this.implementationType = implementationType;
+    this.factoryMethodToConstructor = createMethodMapping();
+    checkDeclaredExceptionsMatch();
+  }
+  
+  @Inject
+  @SuppressWarnings({"unchecked", "unused"})
+  private void setInjectorAndCheckUnboundParametersAreInjectable(
+      Injector injector) {
+    this.injector = injector;
+    for (AssistedConstructor<?> c : factoryMethodToConstructor.values()) {
+      for (Parameter p : c.getAllParameters()) {
+        if(!p.isProvidedByFactory() && !paramCanBeInjected(p, injector)) {
+          throw new IllegalStateException(String.format(
+              "Parameter of type '%s' is not injectable or annotated "
+                + "with @Assisted for Constructor '%s'", p, c));
+        }
+      }
+    }
+  }
+  
+  private void checkDeclaredExceptionsMatch() {
+    for (Map.Entry<Method, AssistedConstructor<?>> entry : factoryMethodToConstructor.entrySet()) {
+      for (Class<?> constructorException : entry.getValue().getDeclaredExceptions()) {
+        if (!isConstructorExceptionCompatibleWithFactoryExeception(
+            constructorException, entry.getKey().getExceptionTypes())) {
+          throw new IllegalStateException(String.format(
+              "Constructor %s declares an exception, but no compatible exception is thrown " 
+                  + "by the factory method %s", entry.getValue(), entry.getKey()));
+        }
+      }
+    }
+  }
+  
+  private boolean isConstructorExceptionCompatibleWithFactoryExeception(
+      Class<?> constructorException, Class<?>[] factoryExceptions) {
+    for (Class<?> factoryException : factoryExceptions) {
+      if (factoryException.isAssignableFrom(constructorException)) {
+        return true;
+      }
+    }
+    return false;
+  }
+  
+  @SuppressWarnings("unchecked")
+  private boolean paramCanBeInjected(Parameter parameter, Injector injector) {
+    return parameter.isBound(injector);
+  }
+
+  @SuppressWarnings({"unchecked"})
+  private Map<Method, AssistedConstructor<?>> createMethodMapping() {
+    
+    List<AssistedConstructor<?>> constructors = new ArrayList<AssistedConstructor<?>>();
+    
+    for (Constructor c : implementationType.getDeclaredConstructors()) {
+      if (c.getAnnotation(AssistedInject.class) != null) {
+        constructors.add(new AssistedConstructor(c));
+      }
+    }
+    
+    if (constructors.size() != factoryType.getMethods().length) {
+      throw new IllegalArgumentException(
+          String.format(
+              "Constructor mismatch: %s has %s @AssistedInject " +
+              "constructors, factory %s has %s creation methods",
+              implementationType.getSimpleName(),
+              constructors.size(),
+              factoryType.getSimpleName(),
+              factoryType.getMethods().length));
+    }
+    
+    Map<ParameterListKey, AssistedConstructor> paramsToConstructor
+        = new HashMap<ParameterListKey, AssistedConstructor>();
+    
+    for (AssistedConstructor c : constructors) {
+      if (paramsToConstructor.containsKey(c.getAssistedParameters())) {
+        throw new RuntimeException("Duplicate constructor, " + c);
+      }
+      paramsToConstructor.put(c.getAssistedParameters(), c);
+    }
+    
+    Map<Method, AssistedConstructor<?>> result = new HashMap<Method, AssistedConstructor<?>>();
+    for (Method method : factoryType.getMethods()) {
+      if (!method.getReturnType().isAssignableFrom(implementationType)) {
+        throw new RuntimeException(String.format("Return type of method \"%s\""
+            + " is not assignable from class \"%s\"", method,
+            implementationType.getName()));
+      }
+      ParameterListKey methodParams = new ParameterListKey(method.getGenericParameterTypes());
+      
+      if (!paramsToConstructor.containsKey(methodParams)) {
+        throw new IllegalArgumentException(String.format("%s has no " +
+            "@AssistInject constructor that takes the @Assisted parameters %s " +
+            "in that order. @AssistInject constructors are %s",
+            implementationType, methodParams, paramsToConstructor.values()));
+      }
+      AssistedConstructor matchingConstructor = paramsToConstructor.remove(methodParams);
+      
+      result.put(method, matchingConstructor);
+    }
+    return result;
+  }
+
+  @SuppressWarnings({"unchecked"})
+  public F get() {
+    InvocationHandler invocationHandler = new InvocationHandler() {
+
+      public Object invoke(Object proxy, Method method, Object[] creationArgs) throws Throwable {
+        AssistedConstructor<?> constructor = factoryMethodToConstructor.get(method);
+
+        Object[] constructorArgs = gatherArgsForConstructor(
+            constructor, creationArgs);
+        Object objectToReturn = constructor.newInstance(constructorArgs);
+        injector.injectMembers(objectToReturn);
+        return objectToReturn;
+      }
+
+      public Object[] gatherArgsForConstructor(
+          AssistedConstructor<?> constructor,
+          Object[] factoryArgs) {
+        int numParams = constructor.getAllParameters().size();
+        int argPosition = 0;
+        Object[] result = new Object[numParams];
+        
+        for (int i = 0; i < numParams; i++) {
+          Parameter parameter = constructor.getAllParameters().get(i);
+          if (parameter.isProvidedByFactory()) {
+            result[i] = factoryArgs[argPosition];
+            argPosition++;
+          } else {
+            result[i] = parameter.getValue(injector);
+          }
+        }
+        return result;
+      }
+    };
+
+    return (F) Proxy.newProxyInstance(factoryType.getClassLoader(),
+        new Class[] {factoryType}, invocationHandler);
+  }
+}
diff --git a/extensions/assistedinject/src/com/google/inject/assistedinject/Parameter.java b/extensions/assistedinject/src/com/google/inject/assistedinject/Parameter.java
new file mode 100755
index 0000000..a4934c8
--- /dev/null
+++ b/extensions/assistedinject/src/com/google/inject/assistedinject/Parameter.java
@@ -0,0 +1,185 @@
+/**
+ * Copyright (C) 2007 Google Inc.
+ *
+ * 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 com.google.inject.assistedinject;
+
+import com.google.inject.BindingAnnotation;
+import com.google.inject.Injector;
+import com.google.inject.Key;
+import com.google.inject.Provider;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Models a method or constructor parameter.
+ *
+ * @author jmourits@google.com (Jerome Mourits)
+ * @author jessewilson@google.com (Jesse Wilson)
+ */
+class Parameter {
+  
+  private static final Map<Type, Type> PRIMITIVE_COUNTERPARTS;
+  static {
+    Map<Type, Type> primitiveToWrapper = new HashMap<Type, Type>() {{
+        put(int.class, Integer.class);
+        put(long.class, Long.class);
+        put(boolean.class, Boolean.class);
+        put(byte.class, Byte.class);
+        put(short.class, Short.class);
+        put(float.class, Float.class);
+        put(double.class, Double.class);
+        put(char.class, Character.class);
+    }};
+
+    Map<Type, Type> counterparts = new HashMap<Type, Type>();
+    for (Map.Entry<Type, Type> entry : primitiveToWrapper.entrySet()) {
+      Type key = entry.getKey();
+      Type value = entry.getValue();
+      counterparts.put(key, value);
+      counterparts.put(value, key);
+    }
+
+    PRIMITIVE_COUNTERPARTS = Collections.unmodifiableMap(counterparts);
+  }
+
+  private final Type type;
+  private final boolean isAssisted;
+  private final Annotation bindingAnnotation;
+  private final boolean isProvider;
+
+  public Parameter(Type type, Annotation[] annotations) {
+    this.type = type;
+    this.bindingAnnotation = getBindingAnnotation(annotations);
+    this.isAssisted = hasAssistedAnnotation(annotations);
+    this.isProvider = isProvider(type);
+  }
+
+  public boolean isProvidedByFactory() {
+    return isAssisted;
+  }
+  
+  public Type getType() {
+    return type;
+  }
+
+  @Override
+  public String toString() {
+    StringBuilder result = new StringBuilder();
+    if (isAssisted) {
+      result.append("@Assisted");
+      result.append(" ");
+    }
+    if (bindingAnnotation != null) {
+      result.append(bindingAnnotation.toString());
+      result.append(" ");
+    }
+    result.append(type.toString());
+    return result.toString();
+  }
+
+  private boolean hasAssistedAnnotation(Annotation[] annotations) {
+    for (Annotation annotation : annotations) {
+      if (annotation.annotationType().equals(Assisted.class)) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  /**
+   * Returns the Guice {@link Key} for this parameter.
+   */
+  public Object getValue(Injector injector) {
+    return isProvider
+        ? injector.getProvider(getBindingForType(getProvidedType(type)))
+        : injector.getInstance(getPrimaryBindingKey());
+  }
+
+  public boolean isBound(Injector injector) {
+    return injector.getBinding(getPrimaryBindingKey()) != null
+        || injector.getBinding(getAlternateGuiceBindingKey()) != null
+        || injector.getBinding(fixAnnotations(getPrimaryBindingKey())) != null
+        || injector.getBinding(fixAnnotations(getAlternateGuiceBindingKey())) != null;
+  }
+
+  /**
+   * Replace annotation instances with annotation types, this is only
+   * appropriate for testing if a key is bound and not for injecting.
+   *
+   * See Guice bug 125,
+   * http://code.google.com/p/google-guice/issues/detail?id=125
+   */
+  public Key<?> fixAnnotations(Key<?> key) {
+    return key.getAnnotation() == null
+        ? key
+        : Key.get(key.getTypeLiteral(), key.getAnnotation().annotationType());
+  }
+
+  private Key<?> getPrimaryBindingKey() {
+    return isProvider
+        ? getBindingForType(getProvidedType(type))
+        : getBindingForType(type);
+  }
+
+  private Key<?> getAlternateGuiceBindingKey() {
+    Type counterpart = (PRIMITIVE_COUNTERPARTS.containsKey(type))
+      ? PRIMITIVE_COUNTERPARTS.get(type)
+      : type;
+    return isProvider
+        ? getBindingForType(getProvidedType(counterpart))
+        : getBindingForType(counterpart);
+  }
+
+
+  private Type getProvidedType(Type type) {
+    return ((ParameterizedType)type).getActualTypeArguments()[0];
+  }
+
+  private boolean isProvider(Type type) {
+    return type instanceof ParameterizedType
+        && ((ParameterizedType)type).getRawType() == Provider.class;
+  }
+
+  private Key<?> getBindingForType(Type type) {
+    return bindingAnnotation != null
+        ? Key.get(type, bindingAnnotation)
+        : Key.get(type);
+  }
+
+  /**
+   * Returns the unique binding annotation from the specified list, or
+   * {@code null} if there are none.
+   *
+   * @throws IllegalStateException if multiple binding annotations exist.
+   */
+  private Annotation getBindingAnnotation(Annotation[] annotations) {
+    Annotation bindingAnnotation = null;
+    for (Annotation a : annotations) {
+      if (a.annotationType().getAnnotation(BindingAnnotation.class) != null) {
+        if (bindingAnnotation != null) {
+          throw new IllegalArgumentException(String.format("Parameter has " +
+              "multiple binding annotations: %s and %s", bindingAnnotation, a));
+        }
+        bindingAnnotation = a;
+      }
+    }
+    return bindingAnnotation;
+  }
+}
\ No newline at end of file
diff --git a/extensions/assistedinject/src/com/google/inject/assistedinject/ParameterListKey.java b/extensions/assistedinject/src/com/google/inject/assistedinject/ParameterListKey.java
new file mode 100755
index 0000000..c0e3274
--- /dev/null
+++ b/extensions/assistedinject/src/com/google/inject/assistedinject/ParameterListKey.java
@@ -0,0 +1,65 @@
+/**
+ * Copyright (C) 2007 Google Inc.
+ *
+ * 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 com.google.inject.assistedinject;
+
+import com.google.inject.TypeLiteral;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * A list of {@link TypeLiteral}s to match an injectable Constructor's assited
+ * parameter types to the corresponding factory method.
+ *
+ * @author jmourits@google.com (Jerome Mourits)
+ * @author jessewilson@google.com (Jesse Wilson)
+ */
+class ParameterListKey {
+  
+  private final List<Type> paramList;
+  
+  public ParameterListKey(List<Type> paramList) {
+    this.paramList = new ArrayList<Type>(paramList);
+  }
+  
+  public ParameterListKey(Type[] types) {
+    this(Arrays.asList(types));
+  }
+  
+  @Override
+  public boolean equals(Object o) {
+    if (o == this) {
+      return true;
+    }
+    if (!(o instanceof ParameterListKey)) {
+      return false;
+    }
+    ParameterListKey other = (ParameterListKey) o;
+    return paramList.equals(other.paramList);
+  }
+  
+  @Override
+  public int hashCode() {
+    return paramList.hashCode();
+  }
+
+  @Override
+  public String toString() {
+    return paramList.toString();
+  }
+}
\ No newline at end of file
diff --git a/extensions/assistedinject/test/com/google/inject/assistedinject/FactoryProviderTest.java b/extensions/assistedinject/test/com/google/inject/assistedinject/FactoryProviderTest.java
new file mode 100755
index 0000000..6f547f3
--- /dev/null
+++ b/extensions/assistedinject/test/com/google/inject/assistedinject/FactoryProviderTest.java
@@ -0,0 +1,475 @@
+/**
+ * Copyright (C) 2007 Google Inc.
+ *
+ * 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 com.google.inject.assistedinject;
+
+import com.google.inject.AbstractModule;
+import com.google.inject.Guice;
+import com.google.inject.Inject;
+import com.google.inject.Injector;
+import com.google.inject.Provider;
+import com.google.inject.TypeLiteral;
+import com.google.inject.name.Named;
+import com.google.inject.name.Names;
+import java.awt.Color;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Set;
+import junit.framework.TestCase;
+
+/**
+ * @author jmourits@google.com (Jerome Mourits)
+ * @author jessewilson@google.com (Jesse Wilson)
+ */
+public class FactoryProviderTest extends TestCase {
+
+  public void testAssistedFactory() {
+    Injector injector = Guice.createInjector(new AbstractModule() {
+      @Override
+      protected void configure() {
+        bind(Double.class).toInstance(5.0d);
+        bind(ColoredCarFactory.class)
+            .toProvider(FactoryProvider.newFactory(ColoredCarFactory.class, Mustang.class));
+      }
+    });
+    ColoredCarFactory carFactory = injector.getInstance(ColoredCarFactory.class);
+
+    Mustang blueMustang = (Mustang) carFactory.create(Color.BLUE);
+    assertEquals(Color.BLUE, blueMustang.color);
+    assertEquals(5.0d, blueMustang.engineSize);
+
+    Mustang redMustang = (Mustang) carFactory.create(Color.RED);
+    assertEquals(Color.RED, redMustang.color);
+    assertEquals(5.0d, redMustang.engineSize);
+  }
+
+  public void testAssistedFactoryWithAnnotations() {
+    Injector injector = Guice.createInjector(new AbstractModule() {
+      @Override
+      protected void configure() {
+        bind(int.class).annotatedWith(Names.named("horsePower")).toInstance(250);
+        bind(int.class).annotatedWith(Names.named("modelYear")).toInstance(1984);
+        bind(ColoredCarFactory.class)
+            .toProvider(FactoryProvider.newFactory(ColoredCarFactory.class, Camaro.class));
+      }
+    });
+
+    ColoredCarFactory carFactory = injector.getInstance(ColoredCarFactory.class);
+
+    Camaro blueCamaro = (Camaro) carFactory.create(Color.BLUE);
+    assertEquals(Color.BLUE, blueCamaro.color);
+    assertEquals(1984, blueCamaro.modelYear);
+    assertEquals(250, blueCamaro.horsePower);
+
+    Camaro redCamaro = (Camaro) carFactory.create(Color.RED);
+    assertEquals(Color.RED, redCamaro.color);
+    assertEquals(1984, redCamaro.modelYear);
+    assertEquals(250, redCamaro.horsePower);
+  }
+
+  interface Car {
+  }
+
+  interface ColoredCarFactory {
+    Car create(Color color);
+  }
+
+  public static class Mustang implements Car {
+    private final double engineSize;
+    private final Color color;
+
+    @AssistedInject
+    public Mustang(double engineSize, @Assisted Color color) {
+      this.engineSize = engineSize;
+      this.color = color;
+    }
+  }
+
+  public static class Camaro implements Car {
+    private final int horsePower;
+    private final int modelYear;
+    private final Color color;
+
+    @AssistedInject
+    public Camaro(
+        @Named("horsePower")int horsePower,
+        @Named("modelYear")int modelYear,
+        @Assisted Color color) {
+      this.horsePower = horsePower;
+      this.modelYear = modelYear;
+      this.color = color;
+    }
+  }
+
+  interface SummerCarFactory {
+    Car create(Color color, boolean convertable);
+    Car createConvertible(Color color);
+  }
+
+  public void testFactoryWithMultipleMethods() {
+    Injector injector = Guice.createInjector(new AbstractModule() {
+      @Override
+      protected void configure() {
+        bind(float.class).toInstance(140f);
+        bind(SummerCarFactory.class).toProvider(
+            FactoryProvider.newFactory(SummerCarFactory.class, Corvette.class));
+      }
+    });
+
+    SummerCarFactory carFactory = injector.getInstance(SummerCarFactory.class);
+
+    Corvette blueCorvette = (Corvette) carFactory.createConvertible(Color.BLUE);
+    assertEquals(Color.BLUE, blueCorvette.color);
+    assertEquals(100f, blueCorvette.maxMph);
+    assertTrue(blueCorvette.isConvertable);
+
+    Corvette redCorvette = (Corvette) carFactory.create(Color.RED, false);
+    assertEquals(Color.RED, redCorvette.color);
+    assertEquals(140f, redCorvette.maxMph);
+    assertFalse(redCorvette.isConvertable);
+  }
+
+  public static class Corvette implements Car {
+    private boolean isConvertable;
+    private Color color;
+    private float maxMph;
+
+    @AssistedInject
+    public Corvette(@Assisted Color color) {
+      this(color, 100f, true);
+    }
+
+    public Corvette(@Assisted Color color, @Assisted boolean isConvertable) {
+      throw new IllegalStateException("Not an @AssistedInject constructor");
+    }
+
+    @AssistedInject
+    public Corvette(@Assisted Color color, Float maxMph, @Assisted boolean isConvertable) {
+      this.isConvertable = isConvertable;
+      this.color = color;
+      this.maxMph = maxMph;
+    }
+  }
+
+  public void testFactoryMethodsMismatch() {
+    try {
+      FactoryProvider.newFactory(SummerCarFactory.class, Beetle.class);
+      fail();
+    } catch(IllegalArgumentException e) {
+      assertTrue(e.getMessage().startsWith("Constructor mismatch"));
+    }
+  }
+
+  public static class Beetle implements Car {
+    @AssistedInject
+    public Beetle(@Assisted Color color) {
+      throw new IllegalStateException("Conflicting constructors");
+    }
+    @AssistedInject
+    public Beetle(@Assisted Color color, @Assisted boolean isConvertable) {
+      throw new IllegalStateException("Conflicting constructors");
+    }
+    @AssistedInject
+    public Beetle(@Assisted Color color, @Assisted boolean isConvertable, float maxMph) {
+      throw new IllegalStateException("Conflicting constructors");
+    }
+  }
+
+  public void testMethodsAndFieldsGetInjected() {
+    Injector injector = Guice.createInjector(new AbstractModule() {
+      @Override
+      protected void configure() {
+        bind(String.class).toInstance("turbo");
+        bind(int.class).toInstance(911);
+        bind(double.class).toInstance(50000d);
+        bind(ColoredCarFactory.class)
+            .toProvider(FactoryProvider.newFactory(ColoredCarFactory.class, Porshe.class));
+      }
+    });
+    ColoredCarFactory carFactory = injector.getInstance(ColoredCarFactory.class);
+
+    Porshe grayPorshe = (Porshe) carFactory.create(Color.GRAY);
+    assertEquals(Color.GRAY, grayPorshe.color);
+    assertEquals(50000d, grayPorshe.price);
+    assertEquals(911, grayPorshe.model);
+    assertEquals("turbo", grayPorshe.name);
+  }
+
+  public static class Porshe implements Car {
+    private final Color color;
+    private final double price;
+    private @Inject String name;
+    private int model;
+
+    @AssistedInject
+    public Porshe(@Assisted Color color, double price) {
+      this.color = color;
+      this.price = price;
+    }
+
+    @Inject void setModel(int model) {
+      this.model = model;
+    }
+  }
+
+  public void testProviderInjection() {
+    Injector injector = Guice.createInjector(new AbstractModule() {
+      @Override
+      protected void configure() {
+        bind(String.class).toInstance("trans am");
+        bind(ColoredCarFactory.class)
+            .toProvider(FactoryProvider.newFactory(ColoredCarFactory.class, Firebird.class));
+      }
+    });
+    ColoredCarFactory carFactory = injector.getInstance(ColoredCarFactory.class);
+
+    Firebird blackFirebird = (Firebird) carFactory.create(Color.BLACK);
+    assertEquals(Color.BLACK, blackFirebird.color);
+    assertEquals("trans am", blackFirebird.modifiersProvider.get());
+  }
+
+  public static class Firebird implements Car {
+    private final Provider<String> modifiersProvider;
+    private final Color color;
+
+    @AssistedInject
+    public Firebird(Provider<String> modifiersProvider, @Assisted Color color) {
+      this.modifiersProvider = modifiersProvider;
+      this.color = color;
+    }
+  }
+
+  public void testTypeTokenInjection() {
+    Injector injector = Guice.createInjector(new AbstractModule() {
+      @Override
+      protected void configure() {
+        bind(new TypeLiteral<Set<String>>() {}).toInstance(Collections.singleton("Flux Capacitor"));
+        bind(new TypeLiteral<Set<Integer>>() {}).toInstance(Collections.singleton(88));
+        bind(ColoredCarFactory.class)
+            .toProvider(FactoryProvider.newFactory(ColoredCarFactory.class, DeLorean.class));
+      }
+    });
+    ColoredCarFactory carFactory = injector.getInstance(ColoredCarFactory.class);
+
+    DeLorean deLorean = (DeLorean) carFactory.create(Color.GRAY);
+    assertEquals(Color.GRAY, deLorean.color);
+    assertEquals("Flux Capacitor", deLorean.features.iterator().next());
+    assertEquals(new Integer(88), deLorean.featureActivationSpeeds.iterator().next());
+  }
+
+  public static class DeLorean implements Car {
+    private final Set<String> features;
+    private final Set<Integer> featureActivationSpeeds;
+    private final Color color;
+
+    @AssistedInject
+    public DeLorean(
+        Set<String> extraFeatures, Set<Integer> featureActivationSpeeds, @Assisted Color color) {
+      this.features = extraFeatures;
+      this.featureActivationSpeeds = featureActivationSpeeds;
+      this.color = color;
+    }
+  }
+
+  public void testTypeTokenProviderInjection() {
+    Injector injector = Guice.createInjector(new AbstractModule() {
+      @Override
+      protected void configure() {
+        bind(new TypeLiteral<Set<String>>() { }).toInstance(Collections.singleton("Datsun"));
+        bind(ColoredCarFactory.class)
+            .toProvider(FactoryProvider.newFactory(ColoredCarFactory.class, Z.class));
+      }
+    });
+    ColoredCarFactory carFactory = injector.getInstance(ColoredCarFactory.class);
+
+    Z orangeZ = (Z) carFactory.create(Color.ORANGE);
+    assertEquals(Color.ORANGE, orangeZ.color);
+    assertEquals("Datsun", orangeZ.manufacturersProvider.get().iterator().next());
+  }
+
+  public static class Z implements Car {
+    private final Provider<Set<String>> manufacturersProvider;
+    private final Color color;
+
+    @AssistedInject
+    public Z(Provider<Set<String>> manufacturersProvider, @Assisted Color color) {
+      this.manufacturersProvider = manufacturersProvider;
+      this.color = color;
+    }
+  }
+  
+  public static class Prius implements Car {
+    @SuppressWarnings("unused")
+    private final Color color;
+    
+    @AssistedInject
+    private Prius(@Assisted Color color) {
+      this.color = color;
+    }
+  }
+  
+  public void testAssistInjectionInNonPublicConstructor() {
+    Injector injector = Guice.createInjector(new AbstractModule() {
+      @Override
+      protected void configure() {
+        bind(ColoredCarFactory.class)
+            .toProvider(FactoryProvider.newFactory(ColoredCarFactory.class, Prius.class));
+      }
+    });
+    Car car = injector.getInstance(ColoredCarFactory.class).create(Color.ORANGE);
+  }
+
+  public static class ExplodingCar implements Car {
+    @AssistedInject
+    public ExplodingCar(@Assisted Color color) {
+      throw new IllegalStateException("kaboom!");
+    }
+  }
+
+  public void testExceptionDuringConstruction() {
+    Injector injector = Guice.createInjector(new AbstractModule() {
+      @Override
+      protected void configure() {
+        bind(ColoredCarFactory.class).toProvider(
+            FactoryProvider.newFactory(ColoredCarFactory.class, ExplodingCar.class));
+      }
+    });
+    try {
+      injector.getInstance(ColoredCarFactory.class).create(Color.ORANGE);
+      fail();
+    } catch (IllegalStateException e) {
+      assertEquals("kaboom!", e.getMessage());
+    }
+  }
+  
+  public static class DefectiveCar implements Car {
+    @AssistedInject
+    public DefectiveCar() throws ExplosionException, FireException {
+      throw new ExplosionException();
+    }
+  }
+  
+  public static class ExplosionException extends Exception { }
+  public static class FireException extends Exception { }
+  
+  public interface DefectiveCarFactoryWithNoExceptions {
+    Car createCar();
+  }
+
+  public interface DefectiveCarFactory {
+    Car createCar() throws FireException;
+  }
+
+  public void testFactoryMethodMustDeclareAllConstructorExceptions() {
+    try {
+      FactoryProvider.newFactory(DefectiveCarFactoryWithNoExceptions.class, DefectiveCar.class);
+      fail();
+    } catch (IllegalStateException e) {
+      assertTrue(e.getMessage().contains("no compatible exception is thrown"));
+    }
+  }
+
+  public interface CorrectDefectiveCarFactory {
+    Car createCar() throws FireException, ExplosionException;
+  }
+  
+  public void testConstructorExceptionsAreThrownByFactory() {
+    Injector injector = Guice.createInjector(new AbstractModule() {
+      @Override
+      protected void configure() {
+        bind(CorrectDefectiveCarFactory.class).toProvider(
+            FactoryProvider.newFactory(
+                CorrectDefectiveCarFactory.class, DefectiveCar.class));
+      }
+    });
+    try {
+      injector.getInstance(CorrectDefectiveCarFactory.class).createCar();
+      fail();
+    } catch (FireException e) {
+      fail();
+    } catch (ExplosionException expected) {
+    }
+  }
+
+  public static class MultipleConstructorDefectiveCar implements Car {
+    @AssistedInject
+    public MultipleConstructorDefectiveCar() throws ExplosionException {
+      throw new ExplosionException();
+    }
+
+    @AssistedInject
+    public MultipleConstructorDefectiveCar(@Assisted Color c) throws FireException {
+      throw new FireException();
+    }
+  }
+
+  public interface MultipleConstructorDefectiveCarFactory {
+    Car createCar() throws ExplosionException;
+    Car createCar(Color r) throws FireException;
+  }
+
+  public void testMultipleConstructorExceptionMatching() {
+    Injector injector = Guice.createInjector(new AbstractModule() {
+      @Override
+      protected void configure() {
+        bind(MultipleConstructorDefectiveCarFactory.class).toProvider(
+            FactoryProvider.newFactory(
+                MultipleConstructorDefectiveCarFactory.class,
+                MultipleConstructorDefectiveCar.class));
+      }
+    });
+    MultipleConstructorDefectiveCarFactory factory
+        = injector.getInstance(MultipleConstructorDefectiveCarFactory.class);
+    try {
+      factory.createCar();
+      fail();
+    } catch (ExplosionException expected) {
+    }
+
+    try {
+      factory.createCar(Color.RED);
+      fail();
+    } catch (FireException expected) {
+    }
+  }
+  
+  public static class WildcardCollection {
+    
+    public interface Factory {
+      WildcardCollection create(Collection<?> items);
+    }
+
+    @AssistedInject
+    public WildcardCollection(@Assisted Collection<?> items) { }
+  }
+  
+  public void testWildcardGenerics() {
+    Injector injector = Guice.createInjector(new AbstractModule() {
+      @Override
+      protected void configure() {
+        bind(WildcardCollection.Factory.class).toProvider(
+            FactoryProvider.newFactory(
+                WildcardCollection.Factory.class,
+                WildcardCollection.class));
+      }
+    });
+    WildcardCollection.Factory factory = injector.getInstance(WildcardCollection.Factory.class);
+    factory.create(Collections.emptyList());
+  }
+  
+
+  // TODO(jessewilson): test for duplicate constructors
+}
\ No newline at end of file
diff --git a/extensions/throwingproviders/build.properties b/extensions/throwingproviders/build.properties
new file mode 100644
index 0000000..0222b47
--- /dev/null
+++ b/extensions/throwingproviders/build.properties
@@ -0,0 +1,5 @@
+lib.dir=../../lib
+src.dir=src
+test.dir=test
+build.dir=build
+test.class=com.google.inject.throwingproviders.ThrowingProviderBinderTest
diff --git a/extensions/throwingproviders/build.xml b/extensions/throwingproviders/build.xml
new file mode 100644
index 0000000..b324cfb
--- /dev/null
+++ b/extensions/throwingproviders/build.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0"?>
+
+<project name="guice-extensions-throwingproviders" basedir="." default="jar">
+
+  <import file="../../common.xml"/>
+  
+  <path id="compile.classpath">
+    <fileset dir="${lib.dir}" includes="*.jar"/>
+    <fileset dir="${lib.dir}/build" includes="*.jar"/>
+    <fileset dir="../../build/dist" includes="*.jar"/>
+  </path>
+
+  <target name="jar" depends="compile"
+       description="Build jar.">
+    <mkdir dir="${build.dir}"/>
+    <jar destfile="${build.dir}/${ant.project.name}-${version}.jar">
+      <fileset dir="${build.dir}/classes"/>
+    </jar>
+  </target>
+
+</project>
diff --git a/extensions/throwingproviders/src/com/google/inject/throwingproviders/ThrowingProvider.java b/extensions/throwingproviders/src/com/google/inject/throwingproviders/ThrowingProvider.java
new file mode 100644
index 0000000..a4189cf
--- /dev/null
+++ b/extensions/throwingproviders/src/com/google/inject/throwingproviders/ThrowingProvider.java
@@ -0,0 +1,38 @@
+/**
+ * Copyright (C) 2007 Google Inc.
+ *
+ * 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 com.google.inject.throwingproviders;
+
+/**
+ * Alternative to the Guice {@link com.google.inject.Provider} that throws
+ * a checked Exception. Users may not inject {@code T} directly.
+ *
+ * <p>This interface must be extended to use application-specific exception types.
+ * Such subinterfaces may not define new methods:
+ * <pre>
+ * public interface RemoteProvider&lt;T&gt; extends ThrowingProvider&lt;T, RemoteException&gt; { }
+ * </pre>
+ *
+ * <p>When this type is bound using {@link ThrowingProviderBinder}, the value returned
+ * or exception thrown by {@link #get} will be scoped. As a consequence, {@link #get}
+ * will invoked at most once within each scope.
+ *
+ * @author jmourits@google.com (Jerome Mourits)
+ * @author jessewilson@google.com (Jesse Wilson)
+ */
+public interface ThrowingProvider<T,E extends Exception> {
+  T get() throws E;
+}
diff --git a/extensions/throwingproviders/src/com/google/inject/throwingproviders/ThrowingProviderBinder.java b/extensions/throwingproviders/src/com/google/inject/throwingproviders/ThrowingProviderBinder.java
new file mode 100644
index 0000000..0dc7cae
--- /dev/null
+++ b/extensions/throwingproviders/src/com/google/inject/throwingproviders/ThrowingProviderBinder.java
@@ -0,0 +1,293 @@
+/**
+ * Copyright (C) 2007 Google Inc.
+ *
+ * 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 com.google.inject.throwingproviders;
+
+import com.google.inject.Binder;
+import com.google.inject.BindingAnnotation;
+import com.google.inject.Inject;
+import com.google.inject.Injector;
+import com.google.inject.Key;
+import com.google.inject.Provider;
+import com.google.inject.TypeLiteral;
+import com.google.inject.binder.ScopedBindingBuilder;
+import com.google.inject.internal.Objects;
+import java.lang.annotation.Annotation;
+import java.lang.annotation.Retention;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Proxy;
+import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
+
+/**
+ * <p>Builds a binding for an {@link ThrowingProvider} using a fluent API.
+ * For example:
+ * <pre>
+ *  ThrowingProviderBinder.create(binder())
+ *    .bind(RemoteProvider.class, Customer.class)
+ *    .to(RemoteCustomerProvider.class)
+ *    .in(RequestScope.class);
+ * </pre>
+ * 
+ * @author jmourits@google.com (Jerome Mourits)
+ * @author jessewilson@google.com (Jesse Wilson)
+ */
+public class ThrowingProviderBinder {
+
+  private final Binder binder;
+
+  private ThrowingProviderBinder(Binder binder) {
+    this.binder = binder;
+  }
+
+  public static ThrowingProviderBinder create(Binder binder) { 
+    return new ThrowingProviderBinder(binder);
+  }
+
+  public <P extends ThrowingProvider> SecondaryBinder<P> 
+      bind(final Class<P> interfaceType, final Type valueType) {
+    return new SecondaryBinder<P>(interfaceType, valueType);
+  }
+
+  public class SecondaryBinder<P extends ThrowingProvider> {
+    private final Class<P> interfaceType;
+    private final Type valueType;
+    private Class<? extends Annotation> annotationType;
+    private Annotation annotation;
+    private final Class<?> exceptionType;
+
+    public SecondaryBinder(Class<P> interfaceType, Type valueType) {
+      this.interfaceType = Objects.nonNull(interfaceType, "interfaceType");
+      this.valueType = Objects.nonNull(valueType, "valueType");
+      checkInterface();
+      this.exceptionType = getExceptionType(interfaceType);
+    }
+
+    public SecondaryBinder<P> annotatedWith(Class<? extends Annotation> annotationType) {
+      if (!(this.annotationType == null && this.annotation == null)) {
+        throw new IllegalStateException();
+      }
+      this.annotationType = annotationType;
+      return this;
+    }
+
+    public SecondaryBinder<P> annotatedWith(Annotation annotation) {
+      if (!(this.annotationType == null && this.annotation == null)) {
+        throw new IllegalStateException();
+      }
+      this.annotation = annotation;
+      return this;
+    }
+
+    public ScopedBindingBuilder to(P target) {
+      Key<P> targetKey = Key.get(interfaceType, uniqueAnnotation());
+      binder.bind(targetKey).toInstance(target);
+      return to(targetKey);
+    }
+
+    public ScopedBindingBuilder to(Class<? extends P> targetType) {
+      return to(Key.get(targetType));
+    }
+
+    public ScopedBindingBuilder to(final Key<? extends P> targetKey) {
+      Objects.nonNull(targetKey, "targetKey");
+      final Key<Result> resultKey = Key.get(Result.class, uniqueAnnotation());
+      final Key<P> key = createKey();
+
+      binder.bind(key).toProvider(new Provider<P>() {
+        private P instance;
+
+        @Inject void initialize(final Injector injector) {
+          instance = interfaceType.cast(Proxy.newProxyInstance(
+              interfaceType.getClassLoader(), new Class<?>[] { interfaceType },
+              new InvocationHandler() {
+                public Object invoke(Object proxy, Method method, Object[] args)
+                    throws Throwable {
+                  return injector.getInstance(resultKey).getOrThrow();
+                }
+              }));
+          }
+
+          public P get() {
+            return instance;
+          }
+        });
+
+      return binder.bind(resultKey).toProvider(new Provider<Result>() {
+        private Injector injector;
+
+        @Inject void initialize(Injector injector) {
+          this.injector = injector;
+        }
+
+        public Result get() {
+          try {
+            return Result.forValue(injector.getInstance(targetKey).get());
+          } catch (Exception e) {
+            if (exceptionType.isInstance(e)) {
+              return Result.forException(e);
+            } else if (e instanceof RuntimeException) {
+              throw (RuntimeException) e;
+            } else {
+              // this should never happen
+              throw new RuntimeException(e);
+            }
+          }
+        }
+      });
+    }
+
+    /**
+     * Returns the exception type declared to be thrown by the get method of
+     * {@code interfaceType}.
+     */
+    @SuppressWarnings({"unchecked"})
+    private <P extends ThrowingProvider> Class<?> getExceptionType(Class<P> interfaceType) {
+      ParameterizedType genericUnreliableProvider
+          = (ParameterizedType) interfaceType.getGenericInterfaces()[0];
+      return (Class<? extends Exception>) genericUnreliableProvider.getActualTypeArguments()[1];
+    }
+
+    private void checkInterface() {
+      String errorMessage = "%s is not a compliant interface "
+          + "- see the Javadoc for ThrowingProvider";
+
+      checkArgument(interfaceType.isInterface(), errorMessage, interfaceType.getName());
+      checkArgument(interfaceType.getGenericInterfaces().length == 1, errorMessage,
+          interfaceType.getName());
+      checkArgument(interfaceType.getInterfaces()[0] == ThrowingProvider.class,
+          errorMessage, interfaceType.getName());
+
+      // Ensure that T is parameterized and unconstrained.
+      ParameterizedType genericThrowingProvider
+          = (ParameterizedType) interfaceType.getGenericInterfaces()[0];
+      if (interfaceType.getTypeParameters().length == 1) {
+        checkArgument(interfaceType.getTypeParameters().length == 1, errorMessage,
+            interfaceType.getName());
+        String returnTypeName = interfaceType.getTypeParameters()[0].getName();
+        Type returnType = genericThrowingProvider.getActualTypeArguments()[0];
+        checkArgument(returnType instanceof TypeVariable, errorMessage, interfaceType.getName());
+        checkArgument(returnTypeName.equals(((TypeVariable) returnType).getName()),
+            errorMessage, interfaceType.getName());
+      } else {
+        checkArgument(interfaceType.getTypeParameters().length == 0,
+            errorMessage, interfaceType.getName());
+        checkArgument(genericThrowingProvider.getActualTypeArguments()[0].equals(valueType),
+            errorMessage, interfaceType.getName());
+      }
+
+      Type exceptionType = genericThrowingProvider.getActualTypeArguments()[1];
+      checkArgument(exceptionType instanceof Class, errorMessage, interfaceType.getName());
+      
+      if (interfaceType.getDeclaredMethods().length == 1) {
+        Method method = interfaceType.getDeclaredMethods()[0];
+        checkArgument(method.getName().equals("get"), errorMessage, interfaceType.getName());
+        checkArgument(method.getParameterTypes().length == 0,
+            errorMessage, interfaceType.getName());
+      } else {
+        checkArgument(interfaceType.getDeclaredMethods().length == 0,
+            errorMessage, interfaceType.getName());
+      }
+    }
+
+    private void checkArgument(boolean condition,
+        String messageFormat, Object... args) {
+      if (!condition) {
+        throw new IllegalArgumentException(String.format(messageFormat, args));
+      }
+    }
+
+    @SuppressWarnings({"unchecked"})
+    private Key<P> createKey() {
+      TypeLiteral<P> typeLiteral;
+      if (interfaceType.getTypeParameters().length == 1) {
+        typeLiteral = (TypeLiteral<P>) TypeLiteral.get(new ParameterizedType() {
+
+          public Type[] getActualTypeArguments() {
+            return new Type[]{valueType};
+          }
+
+          public Type getRawType() {
+            return interfaceType;
+          }
+
+          public Type getOwnerType() {
+            throw new UnsupportedOperationException();
+          }
+        });
+      } else {
+        typeLiteral = TypeLiteral.get(interfaceType);
+      }
+
+      if (annotation != null) {
+        return Key.get(typeLiteral, annotation);
+        
+      } else if (annotationType != null) {
+        return Key.get(typeLiteral, annotationType);
+        
+      } else {
+        return Key.get(typeLiteral);
+      }
+    }
+  }
+
+  /**
+   * Returns an annotation instance that is not equal to any other annotation
+   * instances, for use in creating distinct {@link Key}s.
+   */
+  private static Annotation uniqueAnnotation() {
+    return new Annotation() {
+      public Class<? extends Annotation> annotationType() {
+        return Internal.class;
+      }
+    };
+  }
+  @Retention(RUNTIME) @BindingAnnotation
+  private @interface Internal { }
+
+  /**
+   * Represents the returned value from a call to {@link
+   * ThrowingProvider#get()}. This is the value that will be scoped by Guice.
+   */
+  private static class Result {
+    private final Object value;
+    private final Exception exception;
+
+    private Result(Object value, Exception exception) {
+      this.value = value;
+      this.exception = exception;
+    }
+
+    public static Result forValue(Object value) {
+      return new Result(value, null);
+    }
+
+    public static Result forException(Exception e) {
+      return new Result(null, e);
+    }
+
+    public Object getOrThrow() throws Exception {
+      if (exception != null) {
+        throw exception;
+      } else {
+        return value;
+      }
+    }
+  }
+}
diff --git a/extensions/throwingproviders/test/com/google/inject/throwingproviders/TestScope.java b/extensions/throwingproviders/test/com/google/inject/throwingproviders/TestScope.java
new file mode 100644
index 0000000..1e435c3
--- /dev/null
+++ b/extensions/throwingproviders/test/com/google/inject/throwingproviders/TestScope.java
@@ -0,0 +1,58 @@
+/**
+ * Copyright (C) 2007 Google Inc.
+ *
+ * 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 com.google.inject.throwingproviders;
+
+import com.google.inject.Key;
+import com.google.inject.Provider;
+import com.google.inject.Scope;
+import com.google.inject.ScopeAnnotation;
+import java.lang.annotation.Retention;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * A simple scope that can be explicitly reset.
+ *
+ * @author jmourits@google.com (Jerome Mourits)
+ */
+class TestScope implements Scope {
+
+  @Retention(RUNTIME) @ScopeAnnotation
+  public @interface Scoped { }
+
+  private Map<Key, Object> inScopeObjectsMap = new HashMap<Key, Object>();
+
+  public <T> Provider<T> scope(
+      final Key<T> key, final Provider<T> provider) {
+    return new Provider<T>() {
+      @SuppressWarnings({"unchecked"})
+      public T get() {
+        T t = (T) inScopeObjectsMap.get(key);
+        if (t == null) {
+          t = provider.get();
+          inScopeObjectsMap.put(key, t);
+        }
+        return t;
+      }
+    };
+  }
+
+  public void beginNewScope() {
+    inScopeObjectsMap = new HashMap<Key, Object>();
+  }
+}
diff --git a/extensions/throwingproviders/test/com/google/inject/throwingproviders/ThrowingProviderBinderTest.java b/extensions/throwingproviders/test/com/google/inject/throwingproviders/ThrowingProviderBinderTest.java
new file mode 100644
index 0000000..f70bcc8
--- /dev/null
+++ b/extensions/throwingproviders/test/com/google/inject/throwingproviders/ThrowingProviderBinderTest.java
@@ -0,0 +1,276 @@
+/**
+ * Copyright (C) 2007 Google Inc.
+ *
+ * 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 com.google.inject.throwingproviders;
+
+import com.google.inject.AbstractModule;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.google.inject.Key;
+import com.google.inject.TypeLiteral;
+import com.google.inject.name.Names;
+import java.rmi.RemoteException;
+import java.util.Arrays;
+import java.util.List;
+import junit.framework.TestCase;
+
+/**
+ * @author jmourits@google.com (Jerome Mourits)
+ * @author jessewilson@google.com (Jesse Wilson)
+ */
+public class ThrowingProviderBinderTest extends TestCase {
+
+  private final TypeLiteral<RemoteProvider<String>> remoteProviderOfString
+      = new TypeLiteral<RemoteProvider<String>>() { };
+  private final MockRemoteProvider<String> mockRemoteProvider = new MockRemoteProvider<String>();
+  private final TestScope testScope = new TestScope();
+  private Injector injector = Guice.createInjector(new AbstractModule() {
+    protected void configure() {
+      ThrowingProviderBinder.create(binder())
+          .bind(RemoteProvider.class, String.class)
+          .to(mockRemoteProvider)
+          .in(testScope);
+    }
+  });
+
+  public void testExceptionsThrown() {
+    RemoteProvider<String> remoteProvider = 
+        injector.getInstance(Key.get(remoteProviderOfString));
+
+    mockRemoteProvider.throwOnNextGet("kaboom!");
+    try {
+      remoteProvider.get();
+      fail();
+    } catch (RemoteException expected) {
+      assertEquals("kaboom!", expected.getMessage());
+    }
+  }
+
+  public void testValuesScoped() throws RemoteException {
+    RemoteProvider<String> remoteProvider = 
+        injector.getInstance(Key.get(remoteProviderOfString));
+
+    mockRemoteProvider.setNextToReturn("A");
+    assertEquals("A", remoteProvider.get());
+
+    mockRemoteProvider.setNextToReturn("B");
+    assertEquals("A", remoteProvider.get());
+
+    testScope.beginNewScope();
+    assertEquals("B", remoteProvider.get());
+  }
+
+  public void testExceptionsScoped() {
+    RemoteProvider<String> remoteProvider = 
+        injector.getInstance(Key.get(remoteProviderOfString));
+
+    mockRemoteProvider.throwOnNextGet("A");
+    try {
+      remoteProvider.get();
+      fail();
+    } catch (RemoteException expected) {
+      assertEquals("A", expected.getMessage());
+    }
+    
+    mockRemoteProvider.throwOnNextGet("B");
+    try {
+      remoteProvider.get();
+      fail();
+    } catch (RemoteException expected) {
+      assertEquals("A", expected.getMessage());
+    }
+  }
+  
+  public void testAnnotations() throws RemoteException {
+    final MockRemoteProvider<String> mockRemoteProviderA = new MockRemoteProvider<String>();
+    mockRemoteProviderA.setNextToReturn("A");
+    final MockRemoteProvider<String> mockRemoteProviderB = new MockRemoteProvider<String>();
+    mockRemoteProviderB.setNextToReturn("B");
+
+    injector = Guice.createInjector(new AbstractModule() {
+      protected void configure() {
+        ThrowingProviderBinder.create(binder())
+            .bind(RemoteProvider.class, String.class)
+            .annotatedWith(Names.named("a"))
+            .to(mockRemoteProviderA);
+
+        ThrowingProviderBinder.create(binder())
+            .bind(RemoteProvider.class, String.class)
+            .to(mockRemoteProviderB);
+      }
+    });
+
+    assertEquals("A", 
+        injector.getInstance(Key.get(remoteProviderOfString, Names.named("a"))).get());
+
+    assertEquals("B", 
+        injector.getInstance(Key.get(remoteProviderOfString)).get());
+
+  }
+  
+  public void testUndeclaredExceptions() throws RemoteException {
+    RemoteProvider<String> remoteProvider = 
+        injector.getInstance(Key.get(remoteProviderOfString));
+
+    mockRemoteProvider.throwOnNextGet(new IndexOutOfBoundsException("A"));
+    try {
+      remoteProvider.get();
+      fail();
+    } catch (RuntimeException e) {
+      assertEquals("A", e.getMessage());
+    }
+
+    // undeclared exceptions shouldn't be scoped
+    mockRemoteProvider.throwOnNextGet(new IndexOutOfBoundsException("B"));
+    try {
+      remoteProvider.get();
+      fail();
+    } catch (RuntimeException e) {
+      assertEquals("B", e.getMessage());
+    }
+  }
+
+  public void testThrowingProviderSubclassing() throws RemoteException {
+    final SubMockRemoteProvider aProvider = new SubMockRemoteProvider();
+    aProvider.setNextToReturn("A");
+
+    injector = Guice.createInjector(new AbstractModule() {
+      protected void configure() {
+        ThrowingProviderBinder.create(binder())
+            .bind(RemoteProvider.class, String.class)
+            .to(aProvider);
+      }
+    });
+
+    assertEquals("A",
+        injector.getInstance(Key.get(remoteProviderOfString)).get());
+  }
+
+  static class SubMockRemoteProvider extends MockRemoteProvider<String> { }
+
+  public void testBindingToNonInterfaceType() throws RemoteException {
+    try {
+      injector = Guice.createInjector(new AbstractModule() {
+        protected void configure() {
+          ThrowingProviderBinder.create(binder())
+              .bind(MockRemoteProvider.class, String.class)
+              .to(mockRemoteProvider);
+        }
+      });
+    } catch (IllegalArgumentException e) {
+      assertTrue(e.getMessage().contains("is not a compliant interface"));
+    }
+  }
+  
+  public void testBindingToSubSubInterface() throws RemoteException {
+    try {
+      injector = Guice.createInjector(new AbstractModule() {
+        protected void configure() {
+          ThrowingProviderBinder.create(binder())
+              .bind(SubRemoteProvider.class, String.class);
+        }
+      });
+    } catch (IllegalArgumentException e) {
+      assertTrue(e.getMessage().contains("is not a compliant interface"));
+    }
+  }
+
+  interface SubRemoteProvider extends RemoteProvider<String> { }
+
+  public void testBindingToInterfaceWithExtraMethod() throws RemoteException {
+    try {
+      injector = Guice.createInjector(new AbstractModule() {
+        protected void configure() {
+          ThrowingProviderBinder.create(binder())
+              .bind(RemoteProviderWithExtraMethod.class, String.class);
+        }
+      });
+    } catch (IllegalArgumentException e) {
+      assertTrue(e.getMessage().contains("is not a compliant interface"));
+    }
+  }
+
+  interface RemoteProviderWithExtraMethod<T> extends ThrowingProvider<T, RemoteException> {
+    T get(T defaultValue) throws RemoteException;
+  }
+
+  interface RemoteProvider<T> extends ThrowingProvider<T, RemoteException> { }
+  
+  static class MockRemoteProvider<T> implements RemoteProvider<T> {
+    Exception nextToThrow;
+    T nextToReturn;
+    
+    public void throwOnNextGet(String message) {
+      throwOnNextGet(new RemoteException(message));
+    }
+
+    public void throwOnNextGet(Exception nextToThrow) {
+      this.nextToThrow = nextToThrow;
+    }
+
+    public void setNextToReturn(T nextToReturn) {
+      this.nextToReturn = nextToReturn;
+    }
+    
+    public T get() throws RemoteException {
+      if (nextToThrow instanceof RemoteException) {
+        throw (RemoteException) nextToThrow;
+      } else if (nextToThrow instanceof RuntimeException) {
+        throw (RuntimeException) nextToThrow;
+      } else if (nextToThrow == null) {
+        return nextToReturn;
+      } else {
+        throw new AssertionError("nextToThrow must be a runtime or remote exception");
+      }
+    }
+  }
+
+  public void testBindingToInterfaceWithBoundValueType() throws RemoteException {
+    injector = Guice.createInjector(new AbstractModule() {
+      protected void configure() {
+        ThrowingProviderBinder.create(binder())
+            .bind(StringRemoteProvider.class, String.class)
+            .to(new StringRemoteProvider() {
+              public String get() throws RemoteException {
+                return "A";
+              }
+            });
+      }
+    });
+    
+    assertEquals("A", injector.getInstance(StringRemoteProvider.class).get());
+  }
+
+  interface StringRemoteProvider extends ThrowingProvider<String, RemoteException> { }
+
+  public void testBindingToInterfaceWithGeneric() throws RemoteException {
+    injector = Guice.createInjector(new AbstractModule() {
+      protected void configure() {
+        ThrowingProviderBinder.create(binder())
+            .bind(RemoteProvider.class, new TypeLiteral<List<String>>() { }.getType())
+            .to(new RemoteProvider<List<String>>() {
+              public List<String> get() throws RemoteException {
+                return Arrays.asList("A", "B");
+              }
+            });
+      }
+    });
+
+    Key<RemoteProvider<List<String>>> key
+        = Key.get(new TypeLiteral<RemoteProvider<List<String>>>() { });
+    assertEquals(Arrays.asList("A", "B"), injector.getInstance(key).get());
+  }
+}
diff --git a/extensions/throwingproviders/throwingproviders.iml b/extensions/throwingproviders/throwingproviders.iml
new file mode 100644
index 0000000..336fd39
--- /dev/null
+++ b/extensions/throwingproviders/throwingproviders.iml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module version="4" relativePaths="true" type="JAVA_MODULE">
+  <component name="ModuleRootManager" />
+  <component name="NewModuleRootManager" inherit-compiler-output="true">
+    <exclude-output />
+    <content url="file://$MODULE_DIR$">
+      <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
+    </content>
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+    <orderEntry type="module" module-name="guice" />
+    <orderEntry type="module-library">
+      <library>
+        <CLASSES>
+          <root url="jar://$MODULE_DIR$/../../lib/build/junit.jar!/" />
+        </CLASSES>
+        <JAVADOC />
+        <SOURCES />
+      </library>
+    </orderEntry>
+    <orderEntryProperties />
+  </component>
+</module>
+
diff --git a/guice.iml b/guice.iml
index f5b8cbc..44ee798 100644
--- a/guice.iml
+++ b/guice.iml
@@ -9,7 +9,6 @@
       <excludeFolder url="file://$MODULE_DIR$/build" />
       <excludeFolder url="file://$MODULE_DIR$/classes" />
       <excludeFolder url="file://$MODULE_DIR$/javadoc" />
-      <excludeFolder url="file://$MODULE_DIR$/lib" />
       <excludeFolder url="file://$MODULE_DIR$/struts2" />
     </content>
     <orderEntry type="inheritedJdk" />
@@ -86,6 +85,33 @@
         <SOURCES />
       </library>
     </orderEntry>
+    <orderEntry type="module-library">
+      <library>
+        <CLASSES>
+          <root url="jar://$MODULE_DIR$/lib/build/asm-3.0.jar!/" />
+        </CLASSES>
+        <JAVADOC />
+        <SOURCES />
+      </library>
+    </orderEntry>
+    <orderEntry type="module-library">
+      <library>
+        <CLASSES>
+          <root url="jar://$MODULE_DIR$/lib/build/cglib-nodep-2.2_beta1.jar!/" />
+        </CLASSES>
+        <JAVADOC />
+        <SOURCES />
+      </library>
+    </orderEntry>
+    <orderEntry type="module-library">
+      <library>
+        <CLASSES>
+          <root url="jar://$MODULE_DIR$/lib/build/cglib-src-2.2_beta1.jar!/" />
+        </CLASSES>
+        <JAVADOC />
+        <SOURCES />
+      </library>
+    </orderEntry>
     <orderEntryProperties />
   </component>
 </module>