Added Spring and JNDI integration.

git-svn-id: https://google-guice.googlecode.com/svn/trunk@258 d779f126-a31b-0410-b53b-1d3aecad763e
diff --git a/build.properties b/build.properties
index 6bfac26..92d4f20 100644
--- a/build.properties
+++ b/build.properties
@@ -1,4 +1,3 @@
-version=1.0rc2
 lib.dir=lib
 src.dir=src
 test.dir=test
@@ -6,5 +5,5 @@
 build.dir=build
 javadoc.packagenames=com.google.inject,com.google.inject.spi,\
   com.google.inject.matcher,com.google.inject.servlet,com.google.inject.name,\
-  com.google.inject.tools.jmx,com.google.inject.binder
+  com.google.inject.tools.jmx,com.google.inject.binder,com.google.inject.jndi
 test.class=com.google.inject.AllTests
diff --git a/build.xml b/build.xml
index 61729c4..919cc80 100644
--- a/build.xml
+++ b/build.xml
@@ -26,12 +26,16 @@
   <target name="dist" depends="jar, javadoc"
        description="Build entire distribution.">
     <ant antfile="servlet/build.xml" target="jar" inheritAll="false"/>
+    <ant antfile="spring/build.xml" target="jar" inheritAll="false"/>
     <ant antfile="struts2/plugin/build.xml" target="jar" inheritAll="false"/>
    
     <copy toDir="${build.dir}/dist"> 
       <fileset dir="servlet/build" includes="*.jar"/>
     </copy>
     <copy toDir="${build.dir}/dist"> 
+      <fileset dir="spring/build" includes="*.jar"/>
+    </copy>
+    <copy toDir="${build.dir}/dist"> 
       <fileset dir="struts2/plugin/build" includes="*.jar"/>
     </copy>
     
@@ -95,6 +99,7 @@
       depends="clean"
       description="Remove generated files.">
     <ant dir="servlet" antfile="build.xml" target="clean"/>
+    <ant dir="spring" antfile="build.xml" target="clean"/>
     <ant dir="struts2/plugin" antfile="build.xml" target="clean"/>
   </target>
   
diff --git a/common.xml b/common.xml
index 8343c6a..68ed220 100644
--- a/common.xml
+++ b/common.xml
@@ -3,7 +3,8 @@
 <project name="common">
 
   <property file="build.properties"/>
-
+  <property name="version" value="1.0rc3"/>
+  
   <target name="compile" description="Compile Java source.">
     <mkdir dir="${build.dir}/classes"/>
     <javac srcdir="${src.dir}"
diff --git a/guice.ipr b/guice.ipr
index a911ed0..9f97b57 100644
--- a/guice.ipr
+++ b/guice.ipr
@@ -347,6 +347,7 @@
     <modules>
       <module fileurl="file://$PROJECT_DIR$/guice.iml" filepath="$PROJECT_DIR$/guice.iml" />
       <module fileurl="file://$PROJECT_DIR$/servlet/servlet.iml" filepath="$PROJECT_DIR$/servlet/servlet.iml" />
+      <module fileurl="file://$PROJECT_DIR$/spring/spring.iml" filepath="$PROJECT_DIR$/spring/spring.iml" />
       <module fileurl="file://$PROJECT_DIR$/struts2/example/struts2-example.iml" filepath="$PROJECT_DIR$/struts2/example/struts2-example.iml" />
       <module fileurl="file://$PROJECT_DIR$/struts2/plugin/struts2-plugin.iml" filepath="$PROJECT_DIR$/struts2/plugin/struts2-plugin.iml" />
     </modules>
diff --git a/servlet/build.properties b/servlet/build.properties
index 56546f0..9722f8b 100644
--- a/servlet/build.properties
+++ b/servlet/build.properties
@@ -1,4 +1,3 @@
-version=1.0rc2
 lib.dir=lib
 src.dir=src
 test.dir=test
diff --git a/spring/build.properties b/spring/build.properties
new file mode 100644
index 0000000..2ee6533
--- /dev/null
+++ b/spring/build.properties
@@ -0,0 +1,5 @@
+src.dir=src
+test.dir=test
+build.dir=build
+test.class=com.google.inject.spring.SpringTest
+
diff --git a/spring/build.xml b/spring/build.xml
new file mode 100644
index 0000000..b331ba7
--- /dev/null
+++ b/spring/build.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0"?>
+
+<project name="guice-spring" basedir="." default="jar">
+
+  <import file="../common.xml"/>
+  
+  <path id="compile.classpath">
+    <fileset dir="../lib" includes="*.jar"/>
+    <fileset dir="../lib/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/spring/spring.iml b/spring/spring.iml
new file mode 100644
index 0000000..c6fae4c
--- /dev/null
+++ b/spring/spring.iml
@@ -0,0 +1,52 @@
+<?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/spring-beans.jar!/" />
+        </CLASSES>
+        <JAVADOC />
+        <SOURCES />
+      </library>
+    </orderEntry>
+    <orderEntry type="module-library">
+      <library>
+        <CLASSES>
+          <root url="jar://$MODULE_DIR$/../lib/build/spring-core.jar!/" />
+        </CLASSES>
+        <JAVADOC />
+        <SOURCES />
+      </library>
+    </orderEntry>
+    <orderEntry type="module-library">
+      <library>
+        <CLASSES>
+          <root url="jar://$MODULE_DIR$/../lib/build/junit.jar!/" />
+        </CLASSES>
+        <JAVADOC />
+        <SOURCES />
+      </library>
+    </orderEntry>
+    <orderEntry type="module-library">
+      <library>
+        <CLASSES>
+          <root url="jar://$MODULE_DIR$/../lib/build/commons-logging-1.0.4.jar!/" />
+        </CLASSES>
+        <JAVADOC />
+        <SOURCES />
+      </library>
+    </orderEntry>
+    <orderEntryProperties />
+  </component>
+</module>
+
diff --git a/spring/src/com/google/inject/spring/SpringIntegration.java b/spring/src/com/google/inject/spring/SpringIntegration.java
new file mode 100644
index 0000000..f791ae8
--- /dev/null
+++ b/spring/src/com/google/inject/spring/SpringIntegration.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.spring;
+
+import com.google.inject.Provider;
+import com.google.inject.Inject;
+import org.springframework.beans.factory.BeanFactory;
+
+/**
+ * Integrates Guice with Spring. Requires a binding to
+ * {@link org.springframework.beans.factory.BeanFactory}.
+ *
+ * @author crazybob@google.com (Bob Lee)
+ */
+public class SpringIntegration {
+
+  private SpringIntegration() {}
+
+  /**
+   * Creates a provider which looks up objects from Spring using the given name.
+   * Example usage:
+   *
+   * <pre>
+   * bind(DataSource.class).toProvider(fromSpring(DataSource.class, "dataSource"));
+   * </pre>
+   */
+  public static <T> Provider<T> fromSpring(Class<T> type, String name) {
+    return new SpringProvider<T>(type, name);
+  }
+
+  static class SpringProvider<T> implements Provider<T> {
+
+    BeanFactory beanFactory;
+    boolean singleton;
+    final Class<T> type;
+    final String name;
+
+    public SpringProvider(Class<T> type, String name) {
+      this.type = type;
+      this.name = name;
+    }
+
+    @Inject
+    void initialize(BeanFactory beanFactory) {
+      this.beanFactory = beanFactory;
+      if (!beanFactory.isTypeMatch(name, type)) {
+        throw new ClassCastException("Spring bean named '" + name
+            + "' does not implement " + type.getName() + ".");
+      }
+      singleton = beanFactory.isSingleton(name);
+    }
+
+    public T get() {
+      return singleton ? getSingleton() : type.cast(beanFactory.getBean(name));
+    }
+
+    volatile T instance;
+
+    private T getSingleton() {
+      if (instance == null) {
+        instance = type.cast(beanFactory.getBean(name));
+      }
+      return instance;
+    }
+  }
+}
diff --git a/spring/test/com/google/inject/spring/SpringIntegrationTest.java b/spring/test/com/google/inject/spring/SpringIntegrationTest.java
new file mode 100644
index 0000000..048c050
--- /dev/null
+++ b/spring/test/com/google/inject/spring/SpringIntegrationTest.java
@@ -0,0 +1,69 @@
+/**
+ * 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.spring;
+
+import junit.framework.TestCase;
+import org.springframework.beans.factory.support.DefaultListableBeanFactory;
+import org.springframework.beans.factory.support.RootBeanDefinition;
+import org.springframework.beans.factory.config.ConstructorArgumentValues;
+import org.springframework.beans.factory.BeanFactory;
+import com.google.inject.PerformanceComparison.TeeImpl;
+import com.google.inject.Injector;
+import com.google.inject.Guice;
+import com.google.inject.AbstractModule;
+import com.google.inject.CreationException;
+import static com.google.inject.spring.SpringIntegration.*;
+
+/**
+ * @author crazybob@google.com (Bob Lee)
+ */
+public class SpringIntegrationTest extends TestCase {
+
+  public void testSpringIntegration() throws CreationException {
+    final DefaultListableBeanFactory beanFactory
+        = new DefaultListableBeanFactory();
+
+    RootBeanDefinition singleton
+        = new RootBeanDefinition(Singleton.class);
+    beanFactory.registerBeanDefinition("singleton", singleton);
+
+    RootBeanDefinition prototype
+        = new RootBeanDefinition(Prototype.class, false);
+    beanFactory.registerBeanDefinition("prototype", prototype);
+
+    Injector injector = Guice.createInjector(new AbstractModule() {
+      protected void configure() {
+        bind(BeanFactory.class).toInstance(beanFactory);
+        bind(Singleton.class)
+            .toProvider(fromSpring(Singleton.class, "singleton"));
+        bind(Prototype.class)
+            .toProvider(fromSpring(Prototype.class, "prototype"));
+      }
+    });
+
+    assertNotNull(injector.getInstance(Singleton.class));
+    assertSame(injector.getInstance(Singleton.class),
+        injector.getInstance(Singleton.class));
+
+    assertNotNull(injector.getInstance(Prototype.class));
+    assertNotSame(injector.getInstance(Prototype.class),
+        injector.getInstance(Prototype.class));
+  }
+
+  static class Singleton {}
+  static class Prototype {}
+}
diff --git a/src/com/google/inject/jndi/JndiIntegration.java b/src/com/google/inject/jndi/JndiIntegration.java
new file mode 100644
index 0000000..fea706b
--- /dev/null
+++ b/src/com/google/inject/jndi/JndiIntegration.java
@@ -0,0 +1,66 @@
+/**
+ * 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.jndi;
+
+import com.google.inject.Provider;
+import com.google.inject.Inject;
+import javax.naming.Context;
+import javax.naming.NamingException;
+
+/**
+ * Integrates Guice with JNDI. Requires a binding to 
+ * {@link javax.naming.Context}.
+ *
+ * @author crazybob@google.com (Bob Lee)
+ */
+public class JndiIntegration {
+
+  private JndiIntegration() {}
+
+  /**
+   * Creates a provider which looks up objects in JNDI using the given name.
+   * Example usage:
+   *
+   * <pre>
+   * bind(DataSource.class).toProvider(fromJndi(DataSource.class, "java:..."));
+   * </pre>
+   */
+  public static <T> Provider<T> fromJndi(Class<T> type, String name) {
+    return new JndiProvider<T>(type, name);
+  }
+
+  static class JndiProvider<T> implements Provider<T> {
+
+    @Inject Context context;
+    final Class<T> type;
+    final String name;
+
+    public JndiProvider(Class<T> type, String name) {
+      this.type = type;
+      this.name = name;
+    }
+
+    public T get() {
+      try {
+        return type.cast(context.lookup(name));
+      }
+      catch (NamingException e) {
+        throw new RuntimeException(e);
+      }
+    }
+  }
+}
diff --git a/struts2/plugin/build.properties b/struts2/plugin/build.properties
index 31ee68d..a155282 100644
--- a/struts2/plugin/build.properties
+++ b/struts2/plugin/build.properties
@@ -1,4 +1,3 @@
-version=1.0rc2
 lib.dir=../lib
 src.dir=src
 build.dir=build