add singletons

master
Josha von Gizycki 8 years ago
parent e616a53540
commit 71a4e70ae2

@ -33,6 +33,7 @@
<groupId>org.slf4j</groupId> <groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId> <artifactId>slf4j-simple</artifactId>
<version>1.7.21</version> <version>1.7.21</version>
<scope>provided</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>junit</groupId> <groupId>junit</groupId>
@ -46,11 +47,6 @@
<version>1.3</version> <version>1.3</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>com.google.collections</groupId>
<artifactId>google-collections</artifactId>
<version>1.0</version>
</dependency>
</dependencies> </dependencies>

@ -6,9 +6,11 @@ import java.lang.reflect.InvocationTargetException;
class Instantiator<T> { class Instantiator<T> {
private final Class<T> clz; private final Class<T> clz;
private final SdiContainerInterface container;
Instantiator(Class<T> clz) { Instantiator(Class<T> clz, SdiContainerInterface container) {
this.clz = clz; this.clz = clz;
this.container = container;
} }
T createInstance() T createInstance()
@ -22,10 +24,14 @@ class Instantiator<T> {
for (int i = 0; i < parameterTypes.length; ++i) { for (int i = 0; i < parameterTypes.length; ++i) {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
Class<Object> paramClz = (Class<Object>) parameterTypes[i]; Class<Object> paramClz = (Class<Object>) parameterTypes[i];
parameters[i] = new Instantiator<>(paramClz).createInstance(); parameters[i] = container.getInstance(paramClz);
} }
return constructor.newInstance(parameters); return constructor.newInstance(parameters);
} }
T createSingleton() {
return null;
}
} }

@ -1,13 +1,14 @@
package de.joshavg.simpledic; package de.joshavg.simpledic;
import com.google.common.annotations.VisibleForTesting;
import de.joshavg.simpledic.exception.ClassNotRegistered; import de.joshavg.simpledic.exception.ClassNotRegistered;
import de.joshavg.simpledic.exception.integrity.DependencyCycleDetected; import de.joshavg.simpledic.exception.integrity.DependencyCycleDetected;
import de.joshavg.simpledic.exception.integrity.DependencyNotSatisfied; import de.joshavg.simpledic.exception.integrity.DependencyNotSatisfied;
import de.joshavg.simpledic.exception.integrity.DuplicatedServiceClassesFound; import de.joshavg.simpledic.exception.integrity.DuplicatedServiceClassesFound;
import de.joshavg.simpledic.exception.integrity.MoreThanOneConstructor; import de.joshavg.simpledic.exception.integrity.MoreThanOneConstructor;
import de.joshavg.simpledic.exception.integrity.NoVisibleConstructor;
import de.joshavg.simpledic.exception.integrity.SdicClassNotFound; import de.joshavg.simpledic.exception.integrity.SdicClassNotFound;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
@ -91,10 +92,19 @@ class IntegrityCheck {
} }
private void collectConstructors(ArrayList<Constructor> l, Constructor<?>[] arr) { private void collectConstructors(ArrayList<Constructor> l, Constructor<?>[] arr) {
if(arr.length == 0) {
return;
}
Class<?> clz = arr[0].getDeclaringClass(); Class<?> clz = arr[0].getDeclaringClass();
if (arr.length > 1) { if (arr.length > 1) {
throw new MoreThanOneConstructor(clz); throw new MoreThanOneConstructor(clz);
} }
if(!Modifier.isPublic(arr[0].getModifiers())) {
throw new NoVisibleConstructor(clz);
}
l.add(arr[0]); l.add(arr[0]);
findDefinition(clz).setConstructor(arr[0]); findDefinition(clz).setConstructor(arr[0]);
} }
@ -131,9 +141,8 @@ class IntegrityCheck {
} }
} }
@VisibleForTesting
static boolean isServiceName(String name) { static boolean isServiceName(String name) {
return !"service.".equals(name) && name.startsWith("service."); return name.matches("service\\.[^.]+");
} }
List<ServiceDefinition> getDefinitions() { List<ServiceDefinition> getDefinitions() {

@ -6,22 +6,27 @@ import de.joshavg.simpledic.exception.SdicInstantiationException;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Properties; import java.util.Properties;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
public class SdiContainer { public class SdiContainer implements SdiContainerInterface {
private static final Logger LOG = LoggerFactory.getLogger(SdiContainer.class); private static final Logger LOG = LoggerFactory.getLogger(SdiContainer.class);
private final Properties props; private final Properties props;
private final List<ServiceDefinition> definitions; private final List<ServiceDefinition> definitions;
private final Map<Class<?>, Object> singletons;
private SdiContainer(Properties props, List<ServiceDefinition> definitions) { private SdiContainer(Properties props, List<ServiceDefinition> definitions) {
this.props = props; this.props = props;
this.definitions = definitions; this.definitions = definitions;
this.singletons = new HashMap<>();
} }
public static SdiContainer load() { public static SdiContainer load() {
@ -51,21 +56,40 @@ public class SdiContainer {
return definitions.stream().map(ServiceDefinition::getClz).collect(Collectors.toList()); return definitions.stream().map(ServiceDefinition::getClz).collect(Collectors.toList());
} }
public <T> T createInstance(Class<T> clz) { @Override
if (clz == null) { public <T> T getInstance(Class<T> clz) {
throw new NullPointerException();
}
LOG.trace("instance ordered: ", clz); LOG.trace("instance ordered: ", clz);
if (!serviceClasses().contains(clz)) { if (!serviceClasses().contains(clz)) {
throw new ClassNotRegistered(clz); throw new ClassNotRegistered(clz);
} }
if (isStoredAsSingleton(clz)) {
return clz.cast(singletons.get(clz));
}
try { try {
return new Instantiator<>(clz).createInstance(); T instance = new Instantiator<>(clz, this).createInstance();
handleSingleton(clz, instance);
return instance;
} catch (IllegalAccessException | InvocationTargetException | InstantiationException e) { } catch (IllegalAccessException | InvocationTargetException | InstantiationException e) {
throw new SdicInstantiationException(e); throw new SdicInstantiationException(e);
} }
} }
private <T> void handleSingleton(Class<T> clz, T instance) {
String key = props.entrySet().stream()
.filter(e -> Objects.equals(clz.getName(), String.valueOf(e.getValue())))
.map(e -> e.getKey().toString())
.findFirst()
.orElse("");
if (props.containsKey(key + ".singleton") && "true".equals(props.get(key + ".singleton"))) {
singletons.put(clz, instance);
}
}
private <T> boolean isStoredAsSingleton(Class<T> clz) {
return singletons.containsKey(clz);
}
} }

@ -0,0 +1,6 @@
package de.joshavg.simpledic;
public interface SdiContainerInterface {
<T> T getInstance(Class<T> clz);
}

@ -0,0 +1,8 @@
package de.joshavg.simpledic.exception.integrity;
public class NoVisibleConstructor extends RuntimeException {
public NoVisibleConstructor(Class<?> clz) {
super(String.format("%s has no visible constructor", clz));
}
}

@ -2,10 +2,10 @@ package de.joshavg.simpledic;
import de.joshavg.simpledic.exception.ClassNotRegistered; import de.joshavg.simpledic.exception.ClassNotRegistered;
import de.joshavg.simpledic.exception.ContainerInitException; import de.joshavg.simpledic.exception.ContainerInitException;
import de.joshavg.simpledic.exception.SdicInstantiationException;
import de.joshavg.simpledic.exception.integrity.DependencyCycleDetected; import de.joshavg.simpledic.exception.integrity.DependencyCycleDetected;
import de.joshavg.simpledic.exception.integrity.DependencyNotSatisfied; import de.joshavg.simpledic.exception.integrity.DependencyNotSatisfied;
import de.joshavg.simpledic.exception.integrity.DuplicatedServiceClassesFound; import de.joshavg.simpledic.exception.integrity.DuplicatedServiceClassesFound;
import de.joshavg.simpledic.exception.integrity.NoVisibleConstructor;
import de.joshavg.simpledic.services.Depends1; import de.joshavg.simpledic.services.Depends1;
import de.joshavg.simpledic.services.PrivateConstructor; import de.joshavg.simpledic.services.PrivateConstructor;
import java.util.Map; import java.util.Map;
@ -20,27 +20,22 @@ public class ErrorTests {
@Test(expected = DependencyCycleDetected.class) @Test(expected = DependencyCycleDetected.class)
public void testCycleDetection() { public void testCycleDetection() {
SdiContainer.load("cycle.properties").createInstance(Depends1.class); SdiContainer.load("cycle.properties").getInstance(Depends1.class);
} }
@Test(expected = ClassNotRegistered.class) @Test(expected = ClassNotRegistered.class)
public void requestUnknownClass() { public void requestUnknownClass() {
SdiContainer.load("almostsane.properties").createInstance(Map.class); SdiContainer.load("sane.properties").getInstance(Map.class);
} }
@Test(expected = DependencyNotSatisfied.class) @Test(expected = DependencyNotSatisfied.class)
public void requestServiceWithUnregisteredDependency() { public void requestServiceWithUnregisteredDependency() {
SdiContainer.load("unknowndep.properties").createInstance(Depends1.class); SdiContainer.load("unknowndep.properties").getInstance(Depends1.class);
} }
@Test(expected = SdicInstantiationException.class) @Test(expected = NoVisibleConstructor.class)
public void privateConstructor() { public void privateConstructor() {
SdiContainer.load("almostsane.properties").createInstance(PrivateConstructor.class); SdiContainer.load("invisible.properties").getInstance(PrivateConstructor.class);
}
@Test(expected = NullPointerException.class)
public void requestNull() {
SdiContainer.load("almostsane.properties").createInstance(null);
} }
@Test(expected = ContainerInitException.class) @Test(expected = ContainerInitException.class)

@ -13,6 +13,7 @@ public class IntegrityCheckTest {
assertThat(IntegrityCheck.isServiceName("service"), is(false)); assertThat(IntegrityCheck.isServiceName("service"), is(false));
assertThat(IntegrityCheck.isServiceName("service."), is(false)); assertThat(IntegrityCheck.isServiceName("service."), is(false));
assertThat(IntegrityCheck.isServiceName("service.a"), is(true)); assertThat(IntegrityCheck.isServiceName("service.a"), is(true));
assertThat(IntegrityCheck.isServiceName("service.a.singleton"), is(false));
} }
} }

@ -5,7 +5,7 @@ import org.junit.Test;
public class SimpleDependenciesTest { public class SimpleDependenciesTest {
private static final String FILENAME = "almostsane.properties"; private static final String FILENAME = "sane.properties";
@Test @Test
public void testIntegrityCheck() { public void testIntegrityCheck() {
@ -15,6 +15,6 @@ public class SimpleDependenciesTest {
@Test @Test
public void createOneDependencyService() { public void createOneDependencyService() {
SdiContainer container = SdiContainer.load(FILENAME); SdiContainer container = SdiContainer.load(FILENAME);
container.createInstance(DependsOnNoDependencies.class); container.getInstance(DependsOnNoDependencies.class);
} }
} }

@ -0,0 +1,18 @@
package de.joshavg.simpledic;
import static org.hamcrest.CoreMatchers.sameInstance;
import static org.hamcrest.MatcherAssert.assertThat;
import de.joshavg.simpledic.services.NoDependencies;
import org.junit.Test;
public class SingletonTest {
@Test
public void sameInstances() {
SdiContainer container = SdiContainer.load("singleton.properties");
assertThat(container.getInstance(NoDependencies.class),
sameInstance(container.getInstance(NoDependencies.class)));
}
}

@ -0,0 +1 @@
service.private: de.joshavg.simpledic.services.PrivateConstructor

@ -1,3 +1,2 @@
service.one: de.joshavg.simpledic.services.DependsOnNoDependencies service.one: de.joshavg.simpledic.services.DependsOnNoDependencies
service.nodeps: de.joshavg.simpledic.services.NoDependencies service.nodeps: de.joshavg.simpledic.services.NoDependencies
service.private: de.joshavg.simpledic.services.PrivateConstructor

@ -0,0 +1,2 @@
service.one: de.joshavg.simpledic.services.NoDependencies
service.one.singleton: true
Loading…
Cancel
Save