--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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<String, Injector> injectors;
+
+ private InjectorFactory injectorFactory;
+
+ /**
+ * Constructs an empty cache.
+ *
+ * @param aMgr
+ * Bean manager.
+ */
+ public InjectorCache(InjectorFactory aInjectorFactory) {
+ injectorFactory = aInjectorFactory;
+ injectors = new ConcurrentHashMap<String, Injector>();
+ }
+
+ /**
+ * 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;
+ }
+}
--- /dev/null
+/*
+ * 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);
+
+}
--- /dev/null
+/*
+ * 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<InjectorFactory> factories = ServiceLoader
+ .load(InjectorFactory.class);
+ try {
+ return (InjectorFactory) factories.iterator().next();
+ } catch (NoSuchElementException e) {
+ throw new RuntimeException("Can not find InjectorFactory to use");
+ }
+ }
+}
--- /dev/null
+/*
+ * 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:
+ * <pre>
+ * class Pojo {
+ * @EJB
+ * private Service service;
+ *
+ * ...
+ * }
+ * </pre>
+ * injecting the EJB into a POJO using Contexts and Dependency Injection is accomplished as follows:
+ * <pre>
+ * Pojo pojo = new Pojo();
+ * SimpleInjector injector = new SimpleInjector(new CdiInjectorFactory());
+ * injector.inject(pojo);
+ * </pre>
+ *
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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());
+ }
+}
--- /dev/null
+/*
+ * 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());
+
+ }
+}
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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;
+ }
+}
--- /dev/null
+/*
+ * 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();
+ }
+
+}
--- /dev/null
+
+org.wamblee.inject.TestInjectorFactory