From 6edb8d1fb3e7f36d3fe9f53583fa6aac0ccd3f65 Mon Sep 17 00:00:00 2001 From: Erik Brakkee Date: Thu, 15 Jul 2010 13:50:11 +0000 Subject: [PATCH] --- .../java/org/wamblee/inject/Injectable.java | 45 ++++++++++++ .../java/org/wamblee/inject/Injector.java | 36 ++++++++++ .../org/wamblee/inject/InjectorCache.java | 67 +++++++++++++++++ .../org/wamblee/inject/InjectorFactory.java | 37 ++++++++++ .../inject/InjectorFactoryBuilder.java | 44 ++++++++++++ .../org/wamblee/inject/SimpleInjector.java | 71 +++++++++++++++++++ .../org/wamblee/inject/InjectableTest.java | 29 ++++++++ .../org/wamblee/inject/InjectorCacheTest.java | 62 ++++++++++++++++ .../inject/InjectorFactoryBuilderTest.java | 28 ++++++++ .../test/java/org/wamblee/inject/Pojo.java | 29 ++++++++ .../wamblee/inject/TestInjectorFactory.java | 35 +++++++++ .../org.wamblee.inject.InjectorFactory | 2 + 12 files changed, 485 insertions(+) create mode 100644 support/inject/src/main/java/org/wamblee/inject/Injectable.java create mode 100644 support/inject/src/main/java/org/wamblee/inject/Injector.java create mode 100644 support/inject/src/main/java/org/wamblee/inject/InjectorCache.java create mode 100644 support/inject/src/main/java/org/wamblee/inject/InjectorFactory.java create mode 100644 support/inject/src/main/java/org/wamblee/inject/InjectorFactoryBuilder.java create mode 100644 support/inject/src/main/java/org/wamblee/inject/SimpleInjector.java create mode 100644 support/inject/src/test/java/org/wamblee/inject/InjectableTest.java create mode 100644 support/inject/src/test/java/org/wamblee/inject/InjectorCacheTest.java create mode 100644 support/inject/src/test/java/org/wamblee/inject/InjectorFactoryBuilderTest.java create mode 100644 support/inject/src/test/java/org/wamblee/inject/Pojo.java create mode 100644 support/inject/src/test/java/org/wamblee/inject/TestInjectorFactory.java create mode 100644 support/inject/src/test/resources/META-INF/services/org.wamblee.inject.InjectorFactory diff --git a/support/inject/src/main/java/org/wamblee/inject/Injectable.java b/support/inject/src/main/java/org/wamblee/inject/Injectable.java new file mode 100644 index 00000000..0c881d9b --- /dev/null +++ b/support/inject/src/main/java/org/wamblee/inject/Injectable.java @@ -0,0 +1,45 @@ +/* + * Copyright 2005-2010 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.wamblee.inject; + +import java.util.ServiceLoader; + + +/** + * This abstract base class performs injection at construction. Be sure not to + * initialize fields of derived classes to null as these will override the + * initializations of this base class. + * + * Use this by subclassing through {@link #Injectable()). + * + * To use this class, the {@link ServiceLoader} mechanism must be used to locate + * a {@link InjectorFactory}. The first implementation that is found will be + * used for injection. + * + * @author Erik Brakkee + */ +public abstract class Injectable { + + private static final SimpleInjector INJECTOR = new SimpleInjector( + InjectorFactoryBuilder.getInjectorFactory()); + + /** + * Inheritance style constructor. + */ + protected Injectable() { + INJECTOR.inject(this); + } +} diff --git a/support/inject/src/main/java/org/wamblee/inject/Injector.java b/support/inject/src/main/java/org/wamblee/inject/Injector.java new file mode 100644 index 00000000..e42cc5f7 --- /dev/null +++ b/support/inject/src/main/java/org/wamblee/inject/Injector.java @@ -0,0 +1,36 @@ +/* + * Copyright 2005-2010 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.wamblee.inject; + +/** + * Interface used to perform injection with. An injector instance knows how to perform injection in + * one or more specific classes. + * + * @author Erik Brakkee + */ +public interface Injector { + + /** + * Injects beans into a given component of the required class. + * + * @param aComponent + * Component to inject into. + * @throws IllegalArgumentException In case the argument passed is not supported for injection + * @throws NullPointerException In case the argument passed is null. + */ + void inject(Object aComponent); + +} \ No newline at end of file diff --git a/support/inject/src/main/java/org/wamblee/inject/InjectorCache.java b/support/inject/src/main/java/org/wamblee/inject/InjectorCache.java new file mode 100644 index 00000000..9bf6e2df --- /dev/null +++ b/support/inject/src/main/java/org/wamblee/inject/InjectorCache.java @@ -0,0 +1,67 @@ +/* + * Copyright 2005-2010 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.wamblee.inject; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Cache of {@link CdiInjector}s for efficiency to avoid duplicate analysis of a + * given class. + * + * @author Erik Brakkee + */ +public class InjectorCache { + + private Map injectors; + + private InjectorFactory injectorFactory; + + /** + * Constructs an empty cache. + * + * @param aMgr + * Bean manager. + */ + public InjectorCache(InjectorFactory aInjectorFactory) { + injectorFactory = aInjectorFactory; + injectors = new ConcurrentHashMap(); + } + + /** + * Gets the injector for a given class. This returns a cached injector or + * creates a new injector and caches it. + * + * @param aClass + * Class to find injector for. + * @return Injector. + */ + public Injector getInjector(Class aClass) { + Injector injector = injectors.get(aClass.getName()); + if (injector == null) { + // create and add injector + // NOTE: in rare circumstances this will lead to parallel + // creation of + // an injector for the same class. However, only one of them + // will be the final one + // in the map. There are no side effects of this duplicate + // creation of injectors. + injector = injectorFactory.create(aClass); + injectors.put(aClass.getName(), injector); + } + return injector; + } +} diff --git a/support/inject/src/main/java/org/wamblee/inject/InjectorFactory.java b/support/inject/src/main/java/org/wamblee/inject/InjectorFactory.java new file mode 100644 index 00000000..84f7a66b --- /dev/null +++ b/support/inject/src/main/java/org/wamblee/inject/InjectorFactory.java @@ -0,0 +1,37 @@ +/* + * Copyright 2005-2010 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.wamblee.inject; + + +/** + * Injector factory used. This creates an injector that is appropriate for a certain class. + * May be subclassed for testing or other advanced usage (even replacing CDI with another + * injection framework). + * + * Implementations of this calss must have a default no-arg constructor. + * + * @author Erik Brakkee + */ +public interface InjectorFactory { + + /** + * Creates an injector. + * @return Injector fot he given class. + * @throws IllegalArgumentException In case the given class cannot be used. + */ + Injector create(Class aClass); + +} diff --git a/support/inject/src/main/java/org/wamblee/inject/InjectorFactoryBuilder.java b/support/inject/src/main/java/org/wamblee/inject/InjectorFactoryBuilder.java new file mode 100644 index 00000000..d6005005 --- /dev/null +++ b/support/inject/src/main/java/org/wamblee/inject/InjectorFactoryBuilder.java @@ -0,0 +1,44 @@ +/* + * Copyright 2005-2010 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.wamblee.inject; + +import java.util.NoSuchElementException; +import java.util.ServiceLoader; + +/** + * Utility for obtaining an implementation of the injector factory using + * {@link ServiceLoader}. + * + * @author Erik Brakkee + */ +public class InjectorFactoryBuilder { + + /** + * Gets the injector factory by using the first one found using + * {@link ServiceLoader}. + * + * @return InjectorFactory. + */ + public static InjectorFactory getInjectorFactory() { + ServiceLoader factories = ServiceLoader + .load(InjectorFactory.class); + try { + return (InjectorFactory) factories.iterator().next(); + } catch (NoSuchElementException e) { + throw new RuntimeException("Can not find InjectorFactory to use"); + } + } +} diff --git a/support/inject/src/main/java/org/wamblee/inject/SimpleInjector.java b/support/inject/src/main/java/org/wamblee/inject/SimpleInjector.java new file mode 100644 index 00000000..61bb5560 --- /dev/null +++ b/support/inject/src/main/java/org/wamblee/inject/SimpleInjector.java @@ -0,0 +1,71 @@ +/* + * Copyright 2005-2010 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.wamblee.inject; + +import java.util.ServiceLoader; + + +/** + * Singleton injector access. This should be used as main entry point for + * injection. A different {@link InjectorFactory} can be plugged in for testing. + * + * Given the following class: + *
 
+ * class Pojo {
+ *   @EJB
+ *   private Service service; 
+ *   
+ *   ...
+ * }
+ * 
+ * injecting the EJB into a POJO using Contexts and Dependency Injection is accomplished as follows: + *
+ *   Pojo pojo = new Pojo(); 
+ *   SimpleInjector injector = new SimpleInjector(new CdiInjectorFactory()); 
+ *   injector.inject(pojo);
+ * 
+ * + * Note that it is recommended to cache the injector because the injector does caching + * of the types that it injects into. Caching the injector makes sure that a class is not + * analysed again for annotation every time injection is used. + * + * The {@link InjectorFactory} to be used can also be found using a {@link ServiceLoader} discovery + * mechanism as provided by {@link InjectorFactoryBuilder}. + * + * @author Erik Brakkee + */ +public class SimpleInjector { + + private InjectorCache cache; + + /** + * Constructs the injector. + * @param aFactory Factory to use. + */ + public SimpleInjector(InjectorFactory aFactory) { + cache = new InjectorCache(aFactory); + } + + /** + * Injects into a given object. + * + * @param aObject + * Object to inject into. + */ + public void inject(Object aObject) { + cache.getInjector(aObject.getClass()).inject(aObject); + } +} diff --git a/support/inject/src/test/java/org/wamblee/inject/InjectableTest.java b/support/inject/src/test/java/org/wamblee/inject/InjectableTest.java new file mode 100644 index 00000000..150d4ad3 --- /dev/null +++ b/support/inject/src/test/java/org/wamblee/inject/InjectableTest.java @@ -0,0 +1,29 @@ +/* + * Copyright 2005-2010 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.wamblee.inject; + +import static junit.framework.Assert.*; + +import org.junit.Test; + +public class InjectableTest { + + @Test + public void testInjectInDerivedClass() { + Pojo pojo = new Pojo(); + assertEquals(TestInjectorFactory.MAGIC_VALUE, pojo.getValue()); + } +} diff --git a/support/inject/src/test/java/org/wamblee/inject/InjectorCacheTest.java b/support/inject/src/test/java/org/wamblee/inject/InjectorCacheTest.java new file mode 100644 index 00000000..8a5b2dee --- /dev/null +++ b/support/inject/src/test/java/org/wamblee/inject/InjectorCacheTest.java @@ -0,0 +1,62 @@ +/* + * Copyright 2005-2010 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.wamblee.inject; + +import static junit.framework.Assert.*; + +import org.junit.Test; + +public class InjectorCacheTest { + + private static class MyInjector implements Injector { + private String classname; + + public MyInjector(String aClassname) { + classname = aClassname; + } + @Override + public void inject(Object aComponent) { + throw new RuntimeException("not implemented"); + } + public String getClassname() { + return classname; + } + } + + @Test + public void testCache() { + InjectorFactory factory = new InjectorFactory() { + + @Override + public Injector create(Class aClass) { + return new MyInjector(aClass.getName()); + } + }; + InjectorCache cache = new InjectorCache(factory); + // Unrealistic exmaple, but sufficient for test. + + Injector injector = cache.getInjector(String.class); + assertEquals(String.class.getName(), ((MyInjector)injector).getClassname()); + + Injector injector2 = cache.getInjector(String.class); + assertSame(injector, injector2); + + // verify we get another one for another class. + Injector injector3 = cache.getInjector(Long.class); + assertEquals(Long.class.getName(), ((MyInjector)injector3).getClassname()); + + } +} diff --git a/support/inject/src/test/java/org/wamblee/inject/InjectorFactoryBuilderTest.java b/support/inject/src/test/java/org/wamblee/inject/InjectorFactoryBuilderTest.java new file mode 100644 index 00000000..1b1bf279 --- /dev/null +++ b/support/inject/src/test/java/org/wamblee/inject/InjectorFactoryBuilderTest.java @@ -0,0 +1,28 @@ +/* + * Copyright 2005-2010 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.wamblee.inject; + +import org.junit.Test; +import static junit.framework.TestCase.*; + +public class InjectorFactoryBuilderTest { + + @Test + public void testGetInjectorFactory() { + InjectorFactory factory = InjectorFactoryBuilder.getInjectorFactory(); + assertTrue(factory instanceof TestInjectorFactory); + } +} diff --git a/support/inject/src/test/java/org/wamblee/inject/Pojo.java b/support/inject/src/test/java/org/wamblee/inject/Pojo.java new file mode 100644 index 00000000..38afd7bb --- /dev/null +++ b/support/inject/src/test/java/org/wamblee/inject/Pojo.java @@ -0,0 +1,29 @@ +/* + * Copyright 2005-2010 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.wamblee.inject; + +public class Pojo extends Injectable { + + private String value; + + public void setValue(String aValue) { + value = aValue; + } + + public String getValue() { + return value; + } +} diff --git a/support/inject/src/test/java/org/wamblee/inject/TestInjectorFactory.java b/support/inject/src/test/java/org/wamblee/inject/TestInjectorFactory.java new file mode 100644 index 00000000..5e3d1cf2 --- /dev/null +++ b/support/inject/src/test/java/org/wamblee/inject/TestInjectorFactory.java @@ -0,0 +1,35 @@ +/* + * Copyright 2005-2010 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.wamblee.inject; + +public class TestInjectorFactory implements InjectorFactory { + + public static final String MAGIC_VALUE = "Hello"; + + public static class TestInjector implements Injector { + @Override + public void inject(Object aComponent) { + Pojo pojo = (Pojo) aComponent; + pojo.setValue(MAGIC_VALUE); + } + } + + @Override + public Injector create(Class aClass) { + return new TestInjector(); + } + +} diff --git a/support/inject/src/test/resources/META-INF/services/org.wamblee.inject.InjectorFactory b/support/inject/src/test/resources/META-INF/services/org.wamblee.inject.InjectorFactory new file mode 100644 index 00000000..43b1a974 --- /dev/null +++ b/support/inject/src/test/resources/META-INF/services/org.wamblee.inject.InjectorFactory @@ -0,0 +1,2 @@ + +org.wamblee.inject.TestInjectorFactory -- 2.31.1