Added servlet scopes.
git-svn-id: https://google-guice.googlecode.com/svn/trunk@104 d779f126-a31b-0410-b53b-1d3aecad763e
diff --git a/build.properties b/build.properties
index 989e7b6..ea4aa7d 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
+javadoc.packagenames=com.google.inject,com.google.inject.spi,com.google.inject.intercept,com.google.inject.servlet
diff --git a/build.xml b/build.xml
index 345b44c..301f1e1 100644
--- a/build.xml
+++ b/build.xml
@@ -98,6 +98,8 @@
<pathelement location="${build.dir}/dist/guice-${version}.jar"/>
<pathelement location="lib/aopalliance.jar"/>
<pathelement location="lib/build/junit.jar"/>
+ <pathelement location="lib/build/servlet-api-2.5.jar"/>
+ <pathelement location="lib/build/easymock.jar"/>
</classpath>
<arg value="com.google.inject.AllTests"/>
</java>
diff --git a/guice.iml b/guice.iml
index 2881ec3..a86f9df 100644
--- a/guice.iml
+++ b/guice.iml
@@ -68,6 +68,24 @@
</SOURCES>
</library>
</orderEntry>
+ <orderEntry type="module-library">
+ <library>
+ <CLASSES>
+ <root url="jar://$MODULE_DIR$/lib/build/servlet-api-2.5.jar!/" />
+ </CLASSES>
+ <JAVADOC />
+ <SOURCES />
+ </library>
+ </orderEntry>
+ <orderEntry type="module-library">
+ <library>
+ <CLASSES>
+ <root url="jar://$MODULE_DIR$/lib/build/easymock.jar!/" />
+ </CLASSES>
+ <JAVADOC />
+ <SOURCES />
+ </library>
+ </orderEntry>
<orderEntryProperties />
</component>
</module>
diff --git a/guice.ipr b/guice.ipr
index 0bed8d0..b7a3645 100644
--- a/guice.ipr
+++ b/guice.ipr
@@ -6,11 +6,11 @@
<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="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>
diff --git a/lib/build/easymock.jar b/lib/build/easymock.jar
new file mode 100644
index 0000000..c4159f5
--- /dev/null
+++ b/lib/build/easymock.jar
Binary files differ
diff --git a/lib/build/servlet-api-2.5.jar b/lib/build/servlet-api-2.5.jar
new file mode 100644
index 0000000..fb52493
--- /dev/null
+++ b/lib/build/servlet-api-2.5.jar
Binary files differ
diff --git a/src/com/google/inject/ContainerScoped.java b/src/com/google/inject/ContainerScoped.java
index 6a8e338..7d1408e 100644
--- a/src/com/google/inject/ContainerScoped.java
+++ b/src/com/google/inject/ContainerScoped.java
@@ -22,8 +22,8 @@
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
- * Apply this to annotation classes when you want one instance per container,
- * as opposed to one instance per injection.
+ * Apply this to implementation classes when you want one instance per
+ * container, as opposed to one instance per injection.
*
* @author crazybob@google.com (Bob Lee)
*/
diff --git a/src/com/google/inject/servlet/GuiceFilter.java b/src/com/google/inject/servlet/GuiceFilter.java
new file mode 100644
index 0000000..6d880b5
--- /dev/null
+++ b/src/com/google/inject/servlet/GuiceFilter.java
@@ -0,0 +1,62 @@
+/**
+ * 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.servlet;
+
+import java.io.IOException;
+
+import javax.servlet.Filter;
+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;
+
+/**
+ * Apply this filter to all requests where you plan to use servlet scopes.
+ *
+ * @author crazybob@google.com (Bob Lee)
+ */
+public class GuiceFilter implements Filter {
+
+ static ThreadLocal<HttpServletRequest> localRequest =
+ new ThreadLocal<HttpServletRequest>();
+
+ public void doFilter(ServletRequest servletRequest,
+ ServletResponse servletResponse, FilterChain filterChain)
+ throws IOException, ServletException {
+ HttpServletRequest previous = localRequest.get();
+ try {
+ localRequest.set((HttpServletRequest) servletRequest);
+ filterChain.doFilter(servletRequest, servletResponse);
+ } finally {
+ localRequest.set(previous);
+ }
+ }
+
+ static HttpServletRequest getRequest() {
+ HttpServletRequest request = localRequest.get();
+ if (request == null) {
+ throw new RuntimeException("Please apply " + GuiceFilter.class.getName()
+ + " to any request which uses servlet scopes.");
+ }
+ return request;
+ }
+
+ public void init(FilterConfig filterConfig) throws ServletException {}
+ public void destroy() {}
+}
diff --git a/src/com/google/inject/servlet/RequestScoped.java b/src/com/google/inject/servlet/RequestScoped.java
new file mode 100644
index 0000000..c863fe5
--- /dev/null
+++ b/src/com/google/inject/servlet/RequestScoped.java
@@ -0,0 +1,34 @@
+/**
+ * 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.servlet;
+
+import com.google.inject.Scoped;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Apply this to implementation classes when you want one instance per request.
+ *
+ * @author crazybob@google.com (Bob Lee)
+ */
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+@Scoped(ServletScopes.REQUEST_NAME)
+public @interface RequestScoped {}
diff --git a/src/com/google/inject/servlet/ServletModule.java b/src/com/google/inject/servlet/ServletModule.java
new file mode 100644
index 0000000..c47bc92
--- /dev/null
+++ b/src/com/google/inject/servlet/ServletModule.java
@@ -0,0 +1,33 @@
+/**
+ * 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.servlet;
+
+import com.google.inject.AbstractModule;
+import static com.google.inject.servlet.ServletScopes.*;
+
+/**
+ * Configures the servlet scopes.
+ *
+ * @author crazybob@google.com (Bob Lee)
+ */
+public class ServletModule extends AbstractModule {
+
+ protected void configure() {
+ scope(REQUEST_NAME, REQUEST);
+ scope(SESSION_NAME, SESSION);
+ }
+}
diff --git a/src/com/google/inject/servlet/ServletScopes.java b/src/com/google/inject/servlet/ServletScopes.java
new file mode 100644
index 0000000..d8ece57
--- /dev/null
+++ b/src/com/google/inject/servlet/ServletScopes.java
@@ -0,0 +1,90 @@
+/**
+ * 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.servlet;
+
+import com.google.inject.Scope;
+import com.google.inject.Factory;
+import com.google.inject.Key;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+
+/**
+ * Servlet scopes.
+ *
+ * @author crazybob@google.com (Bob Lee)
+ */
+public class ServletScopes {
+
+ private ServletScopes() {}
+
+ /**
+ * Name of HTTP servlet request scope.
+ */
+ public static final String REQUEST_NAME = "REQUEST";
+
+ /**
+ * HTTP servlet request scope.
+ */
+ public static final Scope REQUEST = new Scope() {
+ public <T> Factory<T> scope(Key<T> key, final Factory<T> creator) {
+ final String name = key.toString();
+ return new Factory<T>() {
+ public T get() {
+ HttpServletRequest request = GuiceFilter.getRequest();
+ synchronized (request) {
+ @SuppressWarnings("unchecked")
+ T t = (T) request.getAttribute(name);
+ if (t == null) {
+ t = creator.get();
+ request.setAttribute(name, t);
+ }
+ return t;
+ }
+ }
+ };
+ }
+ };
+
+ /**
+ * Name of HTTP session scope.
+ */
+ public static final String SESSION_NAME = "SESSION";
+
+ /**
+ * HTTP session scope.
+ */
+ public static final Scope SESSION = new Scope() {
+ public <T> Factory<T> scope(Key<T> key, final Factory<T> creator) {
+ final String name = key.toString();
+ return new Factory<T>() {
+ public T get() {
+ HttpSession session = GuiceFilter.getRequest().getSession();
+ synchronized (session) {
+ @SuppressWarnings("unchecked")
+ T t = (T) session.getAttribute(name);
+ if (t == null) {
+ t = creator.get();
+ session.setAttribute(name, t);
+ }
+ return t;
+ }
+ }
+ };
+ }
+ };
+}
diff --git a/src/com/google/inject/servlet/SessionScoped.java b/src/com/google/inject/servlet/SessionScoped.java
new file mode 100644
index 0000000..2c931e6
--- /dev/null
+++ b/src/com/google/inject/servlet/SessionScoped.java
@@ -0,0 +1,34 @@
+/**
+ * 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.servlet;
+
+import com.google.inject.Scoped;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Apply this to implementation classes when you want one instance per session.
+ *
+ * @author crazybob@google.com (Bob Lee)
+ */
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+@Scoped(ServletScopes.SESSION_NAME)
+public @interface SessionScoped {}
diff --git a/test/com/google/inject/AllTests.java b/test/com/google/inject/AllTests.java
index ec1cea9..1071145 100644
--- a/test/com/google/inject/AllTests.java
+++ b/test/com/google/inject/AllTests.java
@@ -23,6 +23,7 @@
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;
@@ -52,6 +53,8 @@
suite.addTestSuite(ProxyFactoryTest.class);
suite.addTestSuite(IntegrationTest.class);
+ suite.addTestSuite(ServletTest.class);
+
suite.addTestSuite(FinalizableReferenceQueueTest.class);
suite.addTestSuite(ReferenceMapTest.class);
suite.addTest(ReferenceMapTestSuite.suite());
diff --git a/test/com/google/inject/servlet/ServletTest.java b/test/com/google/inject/servlet/ServletTest.java
new file mode 100644
index 0000000..a36a2d2
--- /dev/null
+++ b/test/com/google/inject/servlet/ServletTest.java
@@ -0,0 +1,80 @@
+/**
+ * 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.servlet;
+
+import com.google.inject.Container;
+import com.google.inject.ContainerBuilder;
+import com.google.inject.ContainerCreationException;
+import com.google.inject.Key;
+
+import junit.framework.TestCase;
+
+import static org.easymock.EasyMock.*;
+
+import java.io.IOException;
+
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * @author crazybob@google.com (Bob Lee)
+ */
+public class ServletTest extends TestCase {
+
+ public void testNewRequestObject()
+ throws ContainerCreationException, IOException, ServletException {
+ ContainerBuilder builder = new ContainerBuilder();
+ builder.install(new ServletModule());
+ builder.bind(InSession.class);
+ builder.bind(InRequest.class);
+ final Container container = builder.create(false);
+
+ GuiceFilter filter = new GuiceFilter();
+
+ final HttpServletRequest request = createMock(HttpServletRequest.class);
+
+ String requestName = Key.get(InRequest.class).toString();
+ expect(request.getAttribute(requestName)).andReturn(null);
+ request.setAttribute(eq(requestName), isA(InRequest.class));
+
+ final boolean[] invoked = new boolean[1];
+ FilterChain filterChain = new FilterChain() {
+ public void doFilter(ServletRequest servletRequest,
+ ServletResponse servletResponse) {
+ invoked[0] = true;
+ assertSame(request, servletRequest);
+ assertTrue(container.getInstance(InRequest.class) instanceof InRequest);
+ }
+ };
+
+ replay(request);
+
+ filter.doFilter(request, null, filterChain);
+
+ verify(request);
+ assertTrue(invoked[0]);
+ }
+
+ @SessionScoped
+ static class InSession {}
+
+ @RequestScoped
+ static class InRequest {}
+}