Added intercept() to ContainerBuilder. Hid ConstructionProxy, ProxyFactory, etc. Removed intercept package. Added query package. Started Struts 2 plugin and example.

git-svn-id: https://google-guice.googlecode.com/svn/trunk@111 d779f126-a31b-0410-b53b-1d3aecad763e
diff --git a/build.properties b/build.properties
index ea4aa7d..6f148d2 100644
--- a/build.properties
+++ b/build.properties
@@ -3,4 +3,4 @@
 src.dir=src
 test.dir=test
 build.dir=build
-javadoc.packagenames=com.google.inject,com.google.inject.spi,com.google.inject.intercept,com.google.inject.servlet
+javadoc.packagenames=com.google.inject,com.google.inject.spi,com.google.inject.query,com.google.inject.servlet
diff --git a/guice.ipr b/guice.ipr
index b7a3645..102ee18 100644
--- a/guice.ipr
+++ b/guice.ipr
@@ -1,16 +1,16 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<project version="4" relativePaths="false">
+<project version="4" relativePaths="true">
   <component name="AntConfiguration">
     <defaultAnt bundledAnt="true" />
   </component>
   <component name="Build editor project-level loader">
     <settings>
       <class-settings class="com.google.devtools.intellig.configcheck.ProjectPathChecker" />
-      <class-settings class="com.google.devtools.intellig.configcheck.PythonSdkChecker" />
       <class-settings class="com.google.devtools.intellig.configcheck.ProjectJdkChecker">
-        <setting name="getProjectJdk" value="/usr/local/buildtools/java/jdk1.5.0_06" />
+        <setting name="getProjectJdk" value="$PROJECT_DIR$/../../buildtools/java/jdk1.5.0_06" />
         <setting name="getModuleJdks" value="rO0ABXNyABFqYXZhLnV0aWwuSGFzaFNldLpEhZWWuLc0AwAAeHB3DAAAABA/QAAAAAAAAHg=" />
       </class-settings>
+      <class-settings class="com.google.devtools.intellig.configcheck.PythonSdkChecker" />
       <class-settings class="com.google.devtools.intellig.configcheck.ClearOutputChecker" />
     </settings>
   </component>
@@ -822,9 +822,10 @@
   <component name="ProjectModuleManager">
     <modules>
       <module fileurl="file://$PROJECT_DIR$/guice.iml" filepath="$PROJECT_DIR$/guice.iml" />
+      <module fileurl="file://$PROJECT_DIR$/struts2/struts2.iml" filepath="$PROJECT_DIR$/struts2/struts2.iml" />
     </modules>
   </component>
-  <component name="ProjectRootManager" version="2" assert-keyword="true" jdk-15="true" project-jdk-name="1.5" />
+  <component name="ProjectRootManager" version="2" assert-keyword="true" jdk-15="true" project-jdk-name="1.5" project-jdk-type="JavaSDK" />
   <component name="ProjectRunConfigurationManager" />
   <component name="RmicSettings">
     <option name="IS_EANABLED" value="false" />
diff --git a/src/com/google/inject/spi/ConstructionProxy.java b/src/com/google/inject/ConstructionProxy.java
similarity index 92%
rename from src/com/google/inject/spi/ConstructionProxy.java
rename to src/com/google/inject/ConstructionProxy.java
index e9f521d..1c2af95 100644
--- a/src/com/google/inject/spi/ConstructionProxy.java
+++ b/src/com/google/inject/ConstructionProxy.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.google.inject.spi;
+package com.google.inject;
 
 import java.lang.reflect.InvocationTargetException;
 
@@ -24,7 +24,7 @@
  *
  * @author crazybob@google.com (Bob Lee)
  */
-public interface ConstructionProxy<T> {
+interface ConstructionProxy<T> {
 
   /**
    * Constructs an instance of {@code T} for the given arguments.
diff --git a/src/com/google/inject/spi/ConstructionProxyFactory.java b/src/com/google/inject/ConstructionProxyFactory.java
similarity index 91%
rename from src/com/google/inject/spi/ConstructionProxyFactory.java
rename to src/com/google/inject/ConstructionProxyFactory.java
index a5d2649..9bdf137 100644
--- a/src/com/google/inject/spi/ConstructionProxyFactory.java
+++ b/src/com/google/inject/ConstructionProxyFactory.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.google.inject.spi;
+package com.google.inject;
 
 import java.lang.reflect.Constructor;
 
@@ -23,7 +23,7 @@
  *
  * @author crazybob@google.com (Bob Lee)
  */
-public interface ConstructionProxyFactory {
+interface ConstructionProxyFactory {
 
   /**
    * Gets a construction proxy for the given constructor.
diff --git a/src/com/google/inject/ConstructorInjector.java b/src/com/google/inject/ConstructorInjector.java
index c1f3e92..8d93cdb 100644
--- a/src/com/google/inject/ConstructorInjector.java
+++ b/src/com/google/inject/ConstructorInjector.java
@@ -16,7 +16,7 @@
 
 package com.google.inject;
 
-import com.google.inject.spi.ConstructionProxy;
+import com.google.inject.ConstructionProxy;
 
 import java.lang.reflect.Constructor;
 import java.lang.reflect.InvocationTargetException;
diff --git a/src/com/google/inject/ContainerBuilder.java b/src/com/google/inject/ContainerBuilder.java
index ac64662..af82250 100644
--- a/src/com/google/inject/ContainerBuilder.java
+++ b/src/com/google/inject/ContainerBuilder.java
@@ -16,18 +16,23 @@
 
 package com.google.inject;
 
-import com.google.inject.spi.ConstructionProxyFactory;
-import com.google.inject.spi.DefaultConstructionProxyFactory;
+import static com.google.inject.Scopes.CONTAINER;
+import static com.google.inject.Scopes.CONTAINER_NAME;
+import static com.google.inject.Scopes.DEFAULT;
+import static com.google.inject.Scopes.DEFAULT_NAME;
 import com.google.inject.spi.Message;
 import com.google.inject.spi.SourceConsumer;
 import com.google.inject.util.Objects;
 import static com.google.inject.util.Objects.nonNull;
 import com.google.inject.util.Stopwatch;
 import com.google.inject.util.ToStringBuilder;
-import static com.google.inject.Scopes.*;
+import com.google.inject.query.Query;
 
-import java.lang.reflect.Member;
+import org.aopalliance.intercept.MethodInterceptor;
+
 import java.lang.annotation.Annotation;
+import java.lang.reflect.Member;
+import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.LinkedHashSet;
@@ -98,29 +103,19 @@
 
   static final String UNKNOWN_SOURCE = "[unknown source]";
 
-  final ConstructionProxyFactory constructionProxyFactory;
+  final ProxyFactoryBuilder proxyFactoryBuilder;
 
   /**
    * Constructs a new builder.
-   *
-   * @param constructionProxyFactory to use when constructing objects
    */
-  public ContainerBuilder(ConstructionProxyFactory constructionProxyFactory) {
+  public ContainerBuilder() {
     scope(DEFAULT_NAME, DEFAULT);
     scope(CONTAINER_NAME, CONTAINER);
 
     bind(Container.class).to(CONTAINER_FACTORY);
     bind(Logger.class).to(LOGGER_FACTORY);
 
-    this.constructionProxyFactory = nonNull(constructionProxyFactory,
-        "construction proxy factory");
-  }
-
-  /**
-   * Constructs a new builder.
-   */
-  public ContainerBuilder() {
-    this(new DefaultConstructionProxyFactory());
+    this.proxyFactoryBuilder = new ProxyFactoryBuilder();
   }
 
   final List<Validation> validations = new ArrayList<Validation>();
@@ -151,6 +146,21 @@
   }
 
   /**
+   * Applies the given method interceptor to the methods matched by the class
+   * and method queries.
+   *
+   * @param classQuery matches classes the interceptor should apply to. For
+   *  example: {@code only(Runnable.class)}.
+   * @param methodQuery matches methods the interceptor should apply to. For
+   *  example: {@code annotatedWith(Transactional.class)}.
+   * @param interceptors to apply
+   */
+  public void intercept(Query<? super Class<?>> classQuery,
+      Query<? super Method> methodQuery, MethodInterceptor... interceptors) {
+    proxyFactoryBuilder.intercept(classQuery, methodQuery, interceptors);
+  }
+
+  /**
    * Adds a new scope. Maps a {@link Scope} instance to a given scope name.
    * Scopes should be mapped before used in bindings. {@link Scoped#value()}
    * references this name.
@@ -298,7 +308,8 @@
     ensureNotCreated();
     Map<Key<?>, Binding<?>> bindings =
         new HashMap<Key<?>, Binding<?>>();
-    container = new ContainerImpl(constructionProxyFactory, bindings);
+    container = new ContainerImpl(
+        proxyFactoryBuilder.create(), bindings);
 
     createConstantBindings();
 
diff --git a/src/com/google/inject/ContainerImpl.java b/src/com/google/inject/ContainerImpl.java
index 6e9eaad..564efe0 100644
--- a/src/com/google/inject/ContainerImpl.java
+++ b/src/com/google/inject/ContainerImpl.java
@@ -20,7 +20,7 @@
 import com.google.inject.util.Strings;
 import com.google.inject.util.ToStringBuilder;
 import com.google.inject.util.GuiceFastClass;
-import com.google.inject.spi.ConstructionProxyFactory;
+import com.google.inject.ConstructionProxyFactory;
 
 import net.sf.cglib.reflect.FastMethod;
 
diff --git a/src/com/google/inject/spi/DefaultConstructionProxyFactory.java b/src/com/google/inject/DefaultConstructionProxyFactory.java
similarity index 95%
rename from src/com/google/inject/spi/DefaultConstructionProxyFactory.java
rename to src/com/google/inject/DefaultConstructionProxyFactory.java
index 4192316..02c8266 100644
--- a/src/com/google/inject/spi/DefaultConstructionProxyFactory.java
+++ b/src/com/google/inject/DefaultConstructionProxyFactory.java
@@ -14,7 +14,9 @@
  * limitations under the License.
  */
 
-package com.google.inject.spi;
+package com.google.inject;
+
+import com.google.inject.util.GuiceFastClass;
 
 import net.sf.cglib.reflect.FastClass;
 import net.sf.cglib.reflect.FastConstructor;
@@ -22,8 +24,6 @@
 import java.lang.reflect.Constructor;
 import java.lang.reflect.InvocationTargetException;
 
-import com.google.inject.util.GuiceFastClass;
-
 /**
  * Default {@link ConstructionProxyFactory} implementation. Simply invokes the
  * constructor. Can be reused by other {@code ConstructionProxyFactory}
@@ -31,7 +31,7 @@
  *
  * @author crazybob@google.com (Bob Lee)
  */
-public class DefaultConstructionProxyFactory
+class DefaultConstructionProxyFactory
     implements ConstructionProxyFactory {
 
   public <T> ConstructionProxy<T> get(Constructor<T> constructor) {
diff --git a/src/com/google/inject/intercept/InterceptorStackCallback.java b/src/com/google/inject/InterceptorStackCallback.java
similarity index 98%
rename from src/com/google/inject/intercept/InterceptorStackCallback.java
rename to src/com/google/inject/InterceptorStackCallback.java
index 5c31378..015a045 100644
--- a/src/com/google/inject/intercept/InterceptorStackCallback.java
+++ b/src/com/google/inject/InterceptorStackCallback.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.google.inject.intercept;
+package com.google.inject;
 
 import net.sf.cglib.proxy.MethodProxy;
 
diff --git a/src/com/google/inject/intercept/MethodAspect.java b/src/com/google/inject/MethodAspect.java
similarity index 95%
rename from src/com/google/inject/intercept/MethodAspect.java
rename to src/com/google/inject/MethodAspect.java
index 1e5bee4..2d71add 100644
--- a/src/com/google/inject/intercept/MethodAspect.java
+++ b/src/com/google/inject/MethodAspect.java
@@ -14,9 +14,10 @@
  * limitations under the License.
  */
 
-package com.google.inject.intercept;
+package com.google.inject;
 
 import com.google.inject.util.Objects;
+import com.google.inject.query.Query;
 
 import org.aopalliance.intercept.MethodInterceptor;
 
diff --git a/src/com/google/inject/intercept/ProxyFactory.java b/src/com/google/inject/ProxyFactory.java
similarity index 95%
rename from src/com/google/inject/intercept/ProxyFactory.java
rename to src/com/google/inject/ProxyFactory.java
index 3fc784c..8ec4f9a 100644
--- a/src/com/google/inject/intercept/ProxyFactory.java
+++ b/src/com/google/inject/ProxyFactory.java
@@ -14,32 +14,28 @@
  * limitations under the License.
  */
 
-package com.google.inject.intercept;
+package com.google.inject;
 
-import com.google.inject.spi.ConstructionProxyFactory;
-import com.google.inject.spi.ConstructionProxy;
-import com.google.inject.spi.DefaultConstructionProxyFactory;
-import com.google.inject.util.ReferenceCache;
 import com.google.inject.util.GuiceFastClass;
 import com.google.inject.util.GuiceNamingPolicy;
-import com.google.inject.Factory;
+import com.google.inject.util.ReferenceCache;
 
-import net.sf.cglib.proxy.Enhancer;
 import net.sf.cglib.proxy.Callback;
-import net.sf.cglib.proxy.NoOp;
 import net.sf.cglib.proxy.CallbackFilter;
+import net.sf.cglib.proxy.Enhancer;
+import net.sf.cglib.proxy.NoOp;
 import net.sf.cglib.reflect.FastClass;
 import net.sf.cglib.reflect.FastConstructor;
 
 import org.aopalliance.intercept.MethodInterceptor;
 
-import java.util.List;
-import java.util.ArrayList;
-import java.util.Map;
-import java.util.HashMap;
 import java.lang.reflect.Constructor;
-import java.lang.reflect.Method;
 import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
 
 /**
  * Proxies classes applying interceptors to methods as specified in
@@ -47,7 +43,7 @@
  *
  * @author crazybob@google.com (Bob Lee)
  */
-public class ProxyFactory implements ConstructionProxyFactory {
+class ProxyFactory implements ConstructionProxyFactory {
 
   final List<MethodAspect> methodAspects;
   final ConstructionProxyFactory defaultFactory =
diff --git a/src/com/google/inject/intercept/ProxyFactoryBuilder.java b/src/com/google/inject/ProxyFactoryBuilder.java
similarity index 94%
rename from src/com/google/inject/intercept/ProxyFactoryBuilder.java
rename to src/com/google/inject/ProxyFactoryBuilder.java
index eb08af6..ee89f9b 100644
--- a/src/com/google/inject/intercept/ProxyFactoryBuilder.java
+++ b/src/com/google/inject/ProxyFactoryBuilder.java
@@ -14,7 +14,9 @@
  * limitations under the License.
  */
 
-package com.google.inject.intercept;
+package com.google.inject;
+
+import com.google.inject.query.Query;
 
 import org.aopalliance.intercept.MethodInterceptor;
 
@@ -27,7 +29,7 @@
  *
  * @author crazybob@google.com (Bob Lee)
  */
-public class ProxyFactoryBuilder {
+class ProxyFactoryBuilder {
 
   final List<MethodAspect> methodAspects = new ArrayList<MethodAspect>();
 
diff --git a/src/com/google/inject/intercept/AbstractQuery.java b/src/com/google/inject/query/AbstractQuery.java
similarity index 97%
rename from src/com/google/inject/intercept/AbstractQuery.java
rename to src/com/google/inject/query/AbstractQuery.java
index c6732f9..287cbdb 100644
--- a/src/com/google/inject/intercept/AbstractQuery.java
+++ b/src/com/google/inject/query/AbstractQuery.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.google.inject.intercept;
+package com.google.inject.query;
 
 /**
  * Implements {@code and()} and {@code or()}.
diff --git a/src/com/google/inject/intercept/Queries.java b/src/com/google/inject/query/Queries.java
similarity index 98%
rename from src/com/google/inject/intercept/Queries.java
rename to src/com/google/inject/query/Queries.java
index d1b8d8b..880d192 100644
--- a/src/com/google/inject/intercept/Queries.java
+++ b/src/com/google/inject/query/Queries.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.google.inject.intercept;
+package com.google.inject.query;
 
 import com.google.inject.util.Objects;
 
diff --git a/src/com/google/inject/intercept/Query.java b/src/com/google/inject/query/Query.java
similarity index 96%
rename from src/com/google/inject/intercept/Query.java
rename to src/com/google/inject/query/Query.java
index f9aded7..1b26a01 100644
--- a/src/com/google/inject/intercept/Query.java
+++ b/src/com/google/inject/query/Query.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.google.inject.intercept;
+package com.google.inject.query;
 
 /**
  * Returns {@code true} or {@code false} for a given input.
diff --git a/src/com/google/inject/intercept/package-info.java b/src/com/google/inject/query/package-info.java
similarity index 69%
rename from src/com/google/inject/intercept/package-info.java
rename to src/com/google/inject/query/package-info.java
index 663ed7e..6553436 100644
--- a/src/com/google/inject/intercept/package-info.java
+++ b/src/com/google/inject/query/package-info.java
@@ -15,10 +15,6 @@
  */
 
 /**
- * A simple and very efficient AOP Alliance-based interception framework for
- * Guice. To use with Guice, create a
- * {@link com.google.inject.intercept.ProxyFactory} and pass it to
- * {@link com.google.inject.ContainerBuilder}.
+ * A query API. Used to pick out methods to which to apply interceptors.
  */
-
-package com.google.inject.intercept;
\ No newline at end of file
+package com.google.inject.query;
\ No newline at end of file
diff --git a/src/com/google/inject/servlet/GuiceFilter.java b/src/com/google/inject/servlet/GuiceFilter.java
index 6d880b5..5a890e3 100644
--- a/src/com/google/inject/servlet/GuiceFilter.java
+++ b/src/com/google/inject/servlet/GuiceFilter.java
@@ -19,12 +19,13 @@
 import java.io.IOException;
 
 import javax.servlet.Filter;
+import javax.servlet.FilterChain;
 import javax.servlet.FilterConfig;
 import javax.servlet.ServletException;
 import javax.servlet.ServletRequest;
 import javax.servlet.ServletResponse;
-import javax.servlet.FilterChain;
 import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
 
 /**
  * Apply this filter to all requests where you plan to use servlet scopes.
@@ -33,30 +34,58 @@
  */
 public class GuiceFilter implements Filter {
 
-  static ThreadLocal<HttpServletRequest> localRequest =
-      new ThreadLocal<HttpServletRequest>();
+  static ThreadLocal<Context> localContext = new ThreadLocal<Context>();
 
   public void doFilter(ServletRequest servletRequest,
       ServletResponse servletResponse, FilterChain filterChain)
       throws IOException, ServletException {
-    HttpServletRequest previous = localRequest.get();
+    Context previous = localContext.get();
     try {
-      localRequest.set((HttpServletRequest) servletRequest);
+      localContext.set(new Context((HttpServletRequest) servletRequest,
+          (HttpServletResponse) servletResponse));
       filterChain.doFilter(servletRequest, servletResponse);
     } finally {
-      localRequest.set(previous);
+      localContext.set(previous);
     }
   }
 
   static HttpServletRequest getRequest() {
-    HttpServletRequest request = localRequest.get();
-    if (request == null) {
+    return getContext().getRequest();
+  }
+
+  static HttpServletResponse getResponse() {
+    return getContext().getResponse();
+  }
+
+  static Context getContext() {
+    Context context = localContext.get();
+    if (context == null) {
       throw new RuntimeException("Please apply " + GuiceFilter.class.getName()
-        + " to any request which uses servlet scopes.");
+          + " to any request which uses servlet scopes.");
     }
-    return request;
+    return context;
+  }
+
+  static class Context {
+
+    final HttpServletRequest request;
+    final HttpServletResponse response;
+
+    Context(HttpServletRequest request, HttpServletResponse response) {
+      this.request = request;
+      this.response = response;
+    }
+
+    HttpServletRequest getRequest() {
+      return request;
+    }
+
+    HttpServletResponse getResponse() {
+      return response;
+    }
   }
 
   public void init(FilterConfig filterConfig) throws ServletException {}
+
   public void destroy() {}
 }
diff --git a/src/com/google/inject/servlet/ServletModule.java b/src/com/google/inject/servlet/ServletModule.java
index c47bc92..fb07713 100644
--- a/src/com/google/inject/servlet/ServletModule.java
+++ b/src/com/google/inject/servlet/ServletModule.java
@@ -17,17 +17,75 @@
 package com.google.inject.servlet;
 
 import com.google.inject.AbstractModule;
-import static com.google.inject.servlet.ServletScopes.*;
+import com.google.inject.Factory;
+import com.google.inject.TypeLiteral;
+import static com.google.inject.servlet.ServletScopes.REQUEST;
+import static com.google.inject.servlet.ServletScopes.REQUEST_NAME;
+import static com.google.inject.servlet.ServletScopes.SESSION;
+import static com.google.inject.servlet.ServletScopes.SESSION_NAME;
+
+import java.util.Map;
+
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
 
 /**
- * Configures the servlet scopes.
+ * Configures the servlet scopes and creates bindings for the servlet API
+ * objects so you can inject the request, response, session, etc.
  *
  * @author crazybob@google.com (Bob Lee)
  */
 public class ServletModule extends AbstractModule {
 
+  /**
+   * Name of the request parameters binding. The type is {@code
+   * Map<String, String[]>}.
+   */
+  public static final String REQUEST_PARAMETERS = "requestParameters";
+
   protected void configure() {
+    // Scopes.
     scope(REQUEST_NAME, REQUEST);
     scope(SESSION_NAME, SESSION);
+
+    // Bind request.
+    Factory<HttpServletRequest> requestFactory =
+        new Factory<HttpServletRequest>() {
+      public HttpServletRequest get() {
+        return GuiceFilter.getRequest();
+      }
+    };
+    bind(HttpServletRequest.class).to(requestFactory);
+    bind(ServletRequest.class).to(requestFactory);
+
+    // Bind response.
+    Factory<HttpServletResponse> responseFactory =
+        new Factory<HttpServletResponse>() {
+          public HttpServletResponse get() {
+            return GuiceFilter.getResponse();
+          }
+        };
+    bind(HttpServletResponse.class).to(responseFactory);
+    bind(ServletResponse.class).to(responseFactory);
+
+    // Bind session.
+    bind(HttpSession.class).to(new Factory<HttpSession>() {
+      public HttpSession get() {
+        return GuiceFilter.getRequest().getSession();
+      }
+    });
+
+    // Bind request parameters.
+    bind(new TypeLiteral<Map<String, String[]>>() {})
+        .named(REQUEST_PARAMETERS)
+        .to(new Factory<Map<String, String[]>>() {
+          @SuppressWarnings({"unchecked"})
+          public Map<String, String[]> get() {
+            return GuiceFilter.getRequest().getParameterMap();
+          }
+        });
   }
 }
diff --git a/struts2/example/root/WEB-INF/Counter.jsp b/struts2/example/root/WEB-INF/Counter.jsp
new file mode 100644
index 0000000..0a1ead0
--- /dev/null
+++ b/struts2/example/root/WEB-INF/Counter.jsp
@@ -0,0 +1,9 @@
+<%@ taglib prefix="s" uri="/struts-tags" %>
+
+<html>
+  <body>
+    <h1>Counter Example</h1>
+    <h3><b>Hits in this session:</b>
+      <s:property value="count"/></h3>
+  </body>
+</html>
\ No newline at end of file
diff --git a/struts2/example/root/WEB-INF/classes/struts.xml b/struts2/example/root/WEB-INF/classes/struts.xml
new file mode 100644
index 0000000..a09f87a
--- /dev/null
+++ b/struts2/example/root/WEB-INF/classes/struts.xml
@@ -0,0 +1,17 @@
+<!DOCTYPE struts PUBLIC
+    "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
+    "http://struts.apache.org/dtds/struts-2.0.dtd">
+
+<struts>
+
+  <constant name="guice.module"
+            value="com.google.inject.struts2.example.ExampleModule"/>
+
+  <package name="default" extends="struts-default">
+    <action name="Counter"
+        class="com.google.inject.struts2.example.CounterAction">
+      <result>/WEB-INF/Counter.jsp</result>
+    </action>      
+  </package>
+
+</struts>
diff --git a/struts2/example/root/WEB-INF/web.xml b/struts2/example/root/WEB-INF/web.xml
new file mode 100644
index 0000000..b2d53fd
--- /dev/null
+++ b/struts2/example/root/WEB-INF/web.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0"?>
+<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
+  "http://java.sun.com/dtd/web-app_2_3.dtd">
+
+<web-app>
+  
+  <filter>
+    <filter-name>guice</filter-name>
+    <filter-class>com.google.inject.servlet.GuiceFilter</filter-class>
+  </filter>
+
+  <filter>
+    <filter-name>struts2</filter-name>
+    <filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class>
+  </filter>
+
+  <filter-mapping>
+    <filter-name>guice</filter-name>
+    <url-pattern>/*</url-pattern>
+  </filter-mapping>
+
+  <filter-mapping>
+    <filter-name>struts2</filter-name>
+    <url-pattern>/*</url-pattern>
+  </filter-mapping>
+
+</web-app>
diff --git a/src/com/google/inject/intercept/package-info.java b/struts2/example/src/com/google/inject/struts2/example/Counter.java
similarity index 67%
copy from src/com/google/inject/intercept/package-info.java
copy to struts2/example/src/com/google/inject/struts2/example/Counter.java
index 663ed7e..a3ad8a7 100644
--- a/src/com/google/inject/intercept/package-info.java
+++ b/struts2/example/src/com/google/inject/struts2/example/Counter.java
@@ -14,11 +14,20 @@
  * limitations under the License.
  */
 
-/**
- * A simple and very efficient AOP Alliance-based interception framework for
- * Guice. To use with Guice, create a
- * {@link com.google.inject.intercept.ProxyFactory} and pass it to
- * {@link com.google.inject.ContainerBuilder}.
- */
+package com.google.inject.struts2.example;
 
-package com.google.inject.intercept;
\ No newline at end of file
+import com.google.inject.servlet.SessionScoped;
+
+/**
+ * Counts requests per session.
+ */
+@SessionScoped
+public class Counter {
+
+  int count = 0;
+
+  /** Increments the count and returns the new value. */
+  public int increment() {
+    return count++;
+  }
+}
diff --git a/src/com/google/inject/spi/ConstructionProxyFactory.java b/struts2/example/src/com/google/inject/struts2/example/CounterAction.java
similarity index 60%
copy from src/com/google/inject/spi/ConstructionProxyFactory.java
copy to struts2/example/src/com/google/inject/struts2/example/CounterAction.java
index a5d2649..3df78b0 100644
--- a/src/com/google/inject/spi/ConstructionProxyFactory.java
+++ b/struts2/example/src/com/google/inject/struts2/example/CounterAction.java
@@ -14,19 +14,26 @@
  * limitations under the License.
  */
 
-package com.google.inject.spi;
+package com.google.inject.struts2.example;
 
-import java.lang.reflect.Constructor;
+import com.google.inject.Inject;
 
-/**
- * Creates {@link ConstructionProxy} instances.
- *
- * @author crazybob@google.com (Bob Lee)
- */
-public interface ConstructionProxyFactory {
+import static com.opensymphony.xwork2.Action.SUCCESS;
 
-  /**
-   * Gets a construction proxy for the given constructor.
-   */
-  <T> ConstructionProxy<T> get(Constructor<T> constructor);
+public class CounterAction {
+
+  final Counter counter;
+
+  @Inject
+  public CounterAction(Counter counter) {
+    this.counter = counter;
+  }
+
+  public String execute() {
+    return SUCCESS;
+  }
+
+  public int getCount() {
+    return counter.increment();
+  }
 }
diff --git a/src/com/google/inject/spi/ConstructionProxyFactory.java b/struts2/example/src/com/google/inject/struts2/example/ExampleModule.java
similarity index 68%
copy from src/com/google/inject/spi/ConstructionProxyFactory.java
copy to struts2/example/src/com/google/inject/struts2/example/ExampleModule.java
index a5d2649..4989efc 100644
--- a/src/com/google/inject/spi/ConstructionProxyFactory.java
+++ b/struts2/example/src/com/google/inject/struts2/example/ExampleModule.java
@@ -14,19 +14,18 @@
  * limitations under the License.
  */
 
-package com.google.inject.spi;
+package com.google.inject.struts2.example;
 
-import java.lang.reflect.Constructor;
+import com.google.inject.AbstractModule;
 
 /**
- * Creates {@link ConstructionProxy} instances.
+ * Example application module.
  *
  * @author crazybob@google.com (Bob Lee)
  */
-public interface ConstructionProxyFactory {
+public class ExampleModule extends AbstractModule {
 
-  /**
-   * Gets a construction proxy for the given constructor.
-   */
-  <T> ConstructionProxy<T> get(Constructor<T> constructor);
+  protected void configure() {
+    bind(Counter.class);
+  }
 }
diff --git a/struts2/example/src/com/google/inject/struts2/example/Main.java b/struts2/example/src/com/google/inject/struts2/example/Main.java
new file mode 100644
index 0000000..60fd0be
--- /dev/null
+++ b/struts2/example/src/com/google/inject/struts2/example/Main.java
@@ -0,0 +1,42 @@
+/**
+ * Copyright (C) 2006 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.struts2.example;
+
+import org.mortbay.jetty.Connector;
+import org.mortbay.jetty.Server;
+import org.mortbay.jetty.webapp.WebAppContext;
+import org.mortbay.jetty.nio.SelectChannelConnector;
+
+/**
+ * Starts the example web server on port 8080. Run from "./struts2/example".
+ */
+public class Main {
+
+  public static void main(String[] args) throws Exception {
+    Server server = new Server();
+
+    Connector connector = new SelectChannelConnector();
+    connector.setPort(8080);
+    server.setConnectors(new Connector[] { connector });
+
+    WebAppContext webapp = new WebAppContext("./root", "/example");
+    server.addHandler(webapp);
+
+    server.start();
+    server.join();
+  }
+}
diff --git a/struts2/lib/ant-1.6.5.jar b/struts2/lib/ant-1.6.5.jar
new file mode 100644
index 0000000..3beb3b8
--- /dev/null
+++ b/struts2/lib/ant-1.6.5.jar
Binary files differ
diff --git a/struts2/lib/commons-logging-1.0.4.jar b/struts2/lib/commons-logging-1.0.4.jar
new file mode 100644
index 0000000..b73a80f
--- /dev/null
+++ b/struts2/lib/commons-logging-1.0.4.jar
Binary files differ
diff --git a/struts2/lib/core-3.1.1.jar b/struts2/lib/core-3.1.1.jar
new file mode 100644
index 0000000..ae0b635
--- /dev/null
+++ b/struts2/lib/core-3.1.1.jar
Binary files differ
diff --git a/struts2/lib/freemarker-2.3.8.jar b/struts2/lib/freemarker-2.3.8.jar
new file mode 100644
index 0000000..737bfb5
--- /dev/null
+++ b/struts2/lib/freemarker-2.3.8.jar
Binary files differ
diff --git a/struts2/lib/jetty-6.1.0.jar b/struts2/lib/jetty-6.1.0.jar
new file mode 100644
index 0000000..6b01acd
--- /dev/null
+++ b/struts2/lib/jetty-6.1.0.jar
Binary files differ
diff --git a/struts2/lib/jetty-util-6.1.0.jar b/struts2/lib/jetty-util-6.1.0.jar
new file mode 100644
index 0000000..b2afbc0
--- /dev/null
+++ b/struts2/lib/jetty-util-6.1.0.jar
Binary files differ
diff --git a/struts2/lib/jsp-2.1.jar b/struts2/lib/jsp-2.1.jar
new file mode 100644
index 0000000..c07d0f9
--- /dev/null
+++ b/struts2/lib/jsp-2.1.jar
Binary files differ
diff --git a/struts2/lib/jsp-api-2.1.jar b/struts2/lib/jsp-api-2.1.jar
new file mode 100644
index 0000000..3ecd2f5
--- /dev/null
+++ b/struts2/lib/jsp-api-2.1.jar
Binary files differ
diff --git a/struts2/lib/ognl-2.6.9.jar b/struts2/lib/ognl-2.6.9.jar
new file mode 100644
index 0000000..0f1c0fb
--- /dev/null
+++ b/struts2/lib/ognl-2.6.9.jar
Binary files differ
diff --git a/struts2/lib/servlet-api-2.5.jar b/struts2/lib/servlet-api-2.5.jar
new file mode 100644
index 0000000..fb52493
--- /dev/null
+++ b/struts2/lib/servlet-api-2.5.jar
Binary files differ
diff --git a/struts2/lib/struts-2.0.5-lib.zip b/struts2/lib/struts-2.0.5-lib.zip
new file mode 100644
index 0000000..d5d75bf
--- /dev/null
+++ b/struts2/lib/struts-2.0.5-lib.zip
Binary files differ
diff --git a/struts2/lib/struts2-api-2.0.5.jar b/struts2/lib/struts2-api-2.0.5.jar
new file mode 100644
index 0000000..0d59962
--- /dev/null
+++ b/struts2/lib/struts2-api-2.0.5.jar
Binary files differ
diff --git a/struts2/lib/struts2-core-2.0.5.jar b/struts2/lib/struts2-core-2.0.5.jar
new file mode 100644
index 0000000..5e8702b
--- /dev/null
+++ b/struts2/lib/struts2-core-2.0.5.jar
Binary files differ
diff --git a/struts2/lib/xwork-2.0.0.jar b/struts2/lib/xwork-2.0.0.jar
new file mode 100644
index 0000000..1418069
--- /dev/null
+++ b/struts2/lib/xwork-2.0.0.jar
Binary files differ
diff --git a/struts2/plugin/src/com/google/inject/struts2/GuiceObjectFactory.java b/struts2/plugin/src/com/google/inject/struts2/GuiceObjectFactory.java
new file mode 100644
index 0000000..b8f7288
--- /dev/null
+++ b/struts2/plugin/src/com/google/inject/struts2/GuiceObjectFactory.java
@@ -0,0 +1,73 @@
+/**
+ * Copyright (C) 2006 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.struts2;
+
+import com.google.inject.Container;
+import com.google.inject.ContainerBuilder;
+import com.google.inject.ContainerCreationException;
+import com.google.inject.Module;
+import com.google.inject.servlet.ServletModule;
+
+import com.opensymphony.xwork2.ObjectFactory;
+import com.opensymphony.xwork2.inject.Inject;
+
+import java.util.Map;
+
+public class GuiceObjectFactory extends ObjectFactory {
+
+  Container container;
+
+  @Inject
+  public GuiceObjectFactory(
+      @Inject("guice.module") String moduleName,
+      @Inject("struts.devMode") String developmentMode) {
+    ContainerBuilder builder = new ContainerBuilder();
+
+    // Configure default servlet bindings.
+    builder.install(new ServletModule());
+
+    try {
+      // Instantiate user's module and install it.
+      @SuppressWarnings({"unchecked"})
+      Class<? extends Module> moduleClass =
+          (Class<? extends Module>) Class.forName(moduleName);
+
+      Module module = moduleClass.newInstance();
+
+      module.configure(builder);
+    } catch (Exception e) {
+      throw new RuntimeException(e);
+    }
+
+    System.err.println("devMode: " + developmentMode);
+
+    try {
+      container = builder.create(false);
+    } catch (ContainerCreationException e) {
+      System.err.println(e.getMessage());
+      System.exit(1);
+    }
+  }
+
+  public Object buildBean(String clazz, Map map) throws Exception {
+    return container.getCreator(Class.forName(clazz)).get();
+  }
+
+  public boolean isNoArgConstructorRequired() {
+    return false;
+  }
+}
diff --git a/struts2/plugin/src/struts-plugin.xml b/struts2/plugin/src/struts-plugin.xml
new file mode 100644
index 0000000..8fcef1c
--- /dev/null
+++ b/struts2/plugin/src/struts-plugin.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<!DOCTYPE struts PUBLIC
+  "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
+  "http://struts.apache.org/dtds/struts-2.0.dtd">
+    
+<struts>
+
+  <bean type="com.opensymphony.xwork2.ObjectFactory" 
+        name="guice"
+        class="com.google.inject.struts2.GuiceObjectFactory"/>
+
+  <!--  Make the Guice object factory the automatic default -->
+  <constant name="struts.objectFactory" value="guice" /> 
+
+</struts>
diff --git a/struts2/struts2.iml b/struts2/struts2.iml
new file mode 100644
index 0000000..bc98934
--- /dev/null
+++ b/struts2/struts2.iml
@@ -0,0 +1,143 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module version="4" relativePaths="true" type="JAVA_MODULE">
+  <component name="ModuleRootManager" />
+  <component name="NewModuleRootManager" inherit-compiler-output="false">
+    <output url="file://$MODULE_DIR$/classes" />
+    <exclude-output />
+    <content url="file://$MODULE_DIR$">
+      <sourceFolder url="file://$MODULE_DIR$/example/src" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/plugin/src" isTestSource="false" />
+    </content>
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+    <orderEntry type="module-library">
+      <library>
+        <CLASSES>
+          <root url="jar://$MODULE_DIR$/lib/commons-logging-1.0.4.jar!/" />
+        </CLASSES>
+        <JAVADOC />
+        <SOURCES />
+      </library>
+    </orderEntry>
+    <orderEntry type="module-library">
+      <library>
+        <CLASSES>
+          <root url="jar://$MODULE_DIR$/lib/xwork-2.0.0.jar!/" />
+        </CLASSES>
+        <JAVADOC />
+        <SOURCES />
+      </library>
+    </orderEntry>
+    <orderEntry type="module-library">
+      <library>
+        <CLASSES>
+          <root url="jar://$MODULE_DIR$/lib/struts-2.0.5-lib.zip!/" />
+        </CLASSES>
+        <JAVADOC />
+        <SOURCES />
+      </library>
+    </orderEntry>
+    <orderEntry type="module-library">
+      <library>
+        <CLASSES>
+          <root url="jar://$MODULE_DIR$/lib/freemarker-2.3.8.jar!/" />
+        </CLASSES>
+        <JAVADOC />
+        <SOURCES />
+      </library>
+    </orderEntry>
+    <orderEntry type="module-library">
+      <library>
+        <CLASSES>
+          <root url="jar://$MODULE_DIR$/lib/struts2-core-2.0.5.jar!/" />
+        </CLASSES>
+        <JAVADOC />
+        <SOURCES />
+      </library>
+    </orderEntry>
+    <orderEntry type="module-library">
+      <library>
+        <CLASSES>
+          <root url="jar://$MODULE_DIR$/lib/ognl-2.6.9.jar!/" />
+        </CLASSES>
+        <JAVADOC />
+        <SOURCES />
+      </library>
+    </orderEntry>
+    <orderEntry type="module" module-name="guice" />
+    <orderEntry type="module-library">
+      <library>
+        <CLASSES>
+          <root url="jar://$MODULE_DIR$/lib/jetty-util-6.1.0.jar!/" />
+        </CLASSES>
+        <JAVADOC />
+        <SOURCES />
+      </library>
+    </orderEntry>
+    <orderEntry type="module-library">
+      <library>
+        <CLASSES>
+          <root url="jar://$MODULE_DIR$/lib/jetty-6.1.0.jar!/" />
+        </CLASSES>
+        <JAVADOC />
+        <SOURCES />
+      </library>
+    </orderEntry>
+    <orderEntry type="module-library">
+      <library>
+        <CLASSES>
+          <root url="jar://$MODULE_DIR$/lib/servlet-api-2.5.jar!/" />
+        </CLASSES>
+        <JAVADOC />
+        <SOURCES />
+      </library>
+    </orderEntry>
+    <orderEntry type="module-library">
+      <library>
+        <CLASSES>
+          <root url="jar://$MODULE_DIR$/lib/core-3.1.1.jar!/" />
+        </CLASSES>
+        <JAVADOC />
+        <SOURCES />
+      </library>
+    </orderEntry>
+    <orderEntry type="module-library">
+      <library>
+        <CLASSES>
+          <root url="jar://$MODULE_DIR$/lib/jsp-api-2.1.jar!/" />
+        </CLASSES>
+        <JAVADOC />
+        <SOURCES />
+      </library>
+    </orderEntry>
+    <orderEntry type="module-library">
+      <library>
+        <CLASSES>
+          <root url="jar://$MODULE_DIR$/lib/jsp-2.1.jar!/" />
+        </CLASSES>
+        <JAVADOC />
+        <SOURCES />
+      </library>
+    </orderEntry>
+    <orderEntry type="module-library">
+      <library>
+        <CLASSES>
+          <root url="jar://$MODULE_DIR$/lib/ant-1.6.5.jar!/" />
+        </CLASSES>
+        <JAVADOC />
+        <SOURCES />
+      </library>
+    </orderEntry>
+    <orderEntry type="module-library">
+      <library>
+        <CLASSES>
+          <root url="jar://$MODULE_DIR$/lib/struts2-api-2.0.5.jar!/" />
+        </CLASSES>
+        <JAVADOC />
+        <SOURCES />
+      </library>
+    </orderEntry>
+    <orderEntryProperties />
+  </component>
+</module>
+
diff --git a/test/com/google/inject/AllTests.java b/test/com/google/inject/AllTests.java
index 1071145..4d4b4c1 100644
--- a/test/com/google/inject/AllTests.java
+++ b/test/com/google/inject/AllTests.java
@@ -16,14 +16,12 @@
 
 package com.google.inject;
 
-import com.google.inject.intercept.ProxyFactoryTest;
-import com.google.inject.intercept.QueryTest;
-import com.google.inject.intercept.IntegrationTest;
+import com.google.inject.query.QueryTest;
+import com.google.inject.servlet.ServletTest;
 import com.google.inject.util.FinalizableReferenceQueueTest;
 import com.google.inject.util.ReferenceCacheTest;
 import com.google.inject.util.ReferenceMapTest;
 import com.google.inject.util.ReferenceMapTestSuite;
-import com.google.inject.servlet.ServletTest;
 
 import junit.framework.Test;
 import junit.framework.TestSuite;
diff --git a/test/com/google/inject/intercept/IntegrationTest.java b/test/com/google/inject/IntegrationTest.java
similarity index 76%
rename from test/com/google/inject/intercept/IntegrationTest.java
rename to test/com/google/inject/IntegrationTest.java
index 4f24e87..f1aaa46 100644
--- a/test/com/google/inject/intercept/IntegrationTest.java
+++ b/test/com/google/inject/IntegrationTest.java
@@ -14,13 +14,9 @@
  * limitations under the License.
  */
 
-package com.google.inject.intercept;
+package com.google.inject;
 
-import static com.google.inject.intercept.Queries.*;
-import com.google.inject.ContainerBuilder;
-import com.google.inject.Container;
-import com.google.inject.ContainerCreationException;
-import com.google.inject.Key;
+import static com.google.inject.query.Queries.any;
 
 import junit.framework.TestCase;
 
@@ -34,12 +30,10 @@
 
   public void testIntegration() throws ContainerCreationException {
     CountingInterceptor counter = new CountingInterceptor();
-    ProxyFactoryBuilder proxyFactoryBuilder = new ProxyFactoryBuilder();
-    proxyFactoryBuilder.intercept(any(), any(), counter);
-    ProxyFactory proxyFactory = proxyFactoryBuilder.create();
 
-    ContainerBuilder containerBuilder = new ContainerBuilder(proxyFactory);
+    ContainerBuilder containerBuilder = new ContainerBuilder();
     containerBuilder.bind(Foo.class);
+    containerBuilder.intercept(any(), any(), counter);
     Container container = containerBuilder.create(false);
 
     Foo foo = container.getFactory(Key.get(Foo.class)).get();
diff --git a/test/com/google/inject/intercept/ProxyFactoryTest.java b/test/com/google/inject/ProxyFactoryTest.java
similarity index 97%
rename from test/com/google/inject/intercept/ProxyFactoryTest.java
rename to test/com/google/inject/ProxyFactoryTest.java
index ca64b01..b2d66ec 100644
--- a/test/com/google/inject/intercept/ProxyFactoryTest.java
+++ b/test/com/google/inject/ProxyFactoryTest.java
@@ -14,10 +14,10 @@
  * limitations under the License.
  */
 
-package com.google.inject.intercept;
+package com.google.inject;
 
-import com.google.inject.spi.ConstructionProxy;
-import static com.google.inject.intercept.Queries.*;
+import com.google.inject.ConstructionProxy;
+import static com.google.inject.query.Queries.*;
 import com.google.inject.Factory;
 
 import junit.framework.TestCase;
diff --git a/test/com/google/inject/intercept/QueryTest.java b/test/com/google/inject/query/QueryTest.java
similarity index 96%
rename from test/com/google/inject/intercept/QueryTest.java
rename to test/com/google/inject/query/QueryTest.java
index 2a91d97..3b90e33 100644
--- a/test/com/google/inject/intercept/QueryTest.java
+++ b/test/com/google/inject/query/QueryTest.java
@@ -14,9 +14,9 @@
  * limitations under the License.
  */
 
-package com.google.inject.intercept;
+package com.google.inject.query;
 
-import static com.google.inject.intercept.Queries.*;
+import static com.google.inject.query.Queries.*;
 
 import junit.framework.TestCase;