add singletons

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

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

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

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

@ -6,22 +6,27 @@ import de.joshavg.simpledic.exception.SdicInstantiationException;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Properties;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SdiContainer {
public class SdiContainer implements SdiContainerInterface {
private static final Logger LOG = LoggerFactory.getLogger(SdiContainer.class);
private final Properties props;
private final List<ServiceDefinition> definitions;
private final Map<Class<?>, Object> singletons;
private SdiContainer(Properties props, List<ServiceDefinition> definitions) {
this.props = props;
this.definitions = definitions;
this.singletons = new HashMap<>();
}
public static SdiContainer load() {
@ -51,21 +56,40 @@ public class SdiContainer {
return definitions.stream().map(ServiceDefinition::getClz).collect(Collectors.toList());
}
public <T> T createInstance(Class<T> clz) {
if (clz == null) {
throw new NullPointerException();
}
@Override
public <T> T getInstance(Class<T> clz) {
LOG.trace("instance ordered: ", clz);
if (!serviceClasses().contains(clz)) {
throw new ClassNotRegistered(clz);
}
if (isStoredAsSingleton(clz)) {
return clz.cast(singletons.get(clz));
}
try {
return new Instantiator<>(clz).createInstance();
T instance = new Instantiator<>(clz, this).createInstance();
handleSingleton(clz, instance);
return instance;
} catch (IllegalAccessException | InvocationTargetException | InstantiationException 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.ContainerInitException;
import de.joshavg.simpledic.exception.SdicInstantiationException;
import de.joshavg.simpledic.exception.integrity.DependencyCycleDetected;
import de.joshavg.simpledic.exception.integrity.DependencyNotSatisfied;
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.PrivateConstructor;
import java.util.Map;
@ -20,27 +20,22 @@ public class ErrorTests {
@Test(expected = DependencyCycleDetected.class)
public void testCycleDetection() {
SdiContainer.load("cycle.properties").createInstance(Depends1.class);
SdiContainer.load("cycle.properties").getInstance(Depends1.class);
}
@Test(expected = ClassNotRegistered.class)
public void requestUnknownClass() {
SdiContainer.load("almostsane.properties").createInstance(Map.class);
SdiContainer.load("sane.properties").getInstance(Map.class);
}
@Test(expected = DependencyNotSatisfied.class)
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() {
SdiContainer.load("almostsane.properties").createInstance(PrivateConstructor.class);
}
@Test(expected = NullPointerException.class)
public void requestNull() {
SdiContainer.load("almostsane.properties").createInstance(null);
SdiContainer.load("invisible.properties").getInstance(PrivateConstructor.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.a"), is(true));
assertThat(IntegrityCheck.isServiceName("service.a.singleton"), is(false));
}
}

@ -5,7 +5,7 @@ import org.junit.Test;
public class SimpleDependenciesTest {
private static final String FILENAME = "almostsane.properties";
private static final String FILENAME = "sane.properties";
@Test
public void testIntegrityCheck() {
@ -15,6 +15,6 @@ public class SimpleDependenciesTest {
@Test
public void createOneDependencyService() {
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.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