add method to fetch all imlementations of an interface or class

add tests
add config to pom to generate sources and javadoc
master
Josha von Gizycki 10 years ago
parent f94e86df1d
commit 67d93b91a2

@ -19,6 +19,30 @@
<target>1.8</target> <target>1.8</target>
</configuration> </configuration>
</plugin> </plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<executions>
<execution>
<id>attach-javadocs</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins> </plugins>
</build> </build>
@ -43,7 +67,7 @@
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.hamcrest</groupId> <groupId>org.hamcrest</groupId>
<artifactId>hamcrest-core</artifactId> <artifactId>hamcrest-all</artifactId>
<version>1.3</version> <version>1.3</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>

@ -19,6 +19,20 @@ import java.util.stream.Collectors;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
/**
* Takes a loaded Properties file, analyzes the {@link ServiceDefinition}s and
* checks them for sanity and integrity
* <p>
* These Checks are performed:
* <ol>
* <li>duplicated FQCNs</li>
* <li>dependency cycles</li>
* <li>dependency availability</li>
* <li>class availability</li>
* <li>constructor visibility</li>
* <li>constructor count</li>
* </ol>
*/
class IntegrityCheck { class IntegrityCheck {
private static final Logger LOG = LoggerFactory.getLogger(IntegrityCheck.class); private static final Logger LOG = LoggerFactory.getLogger(IntegrityCheck.class);
@ -151,7 +165,7 @@ class IntegrityCheck {
} }
static boolean isServiceName(String name) { static boolean isServiceName(String name) {
return name.matches("service\\.[^.]+"); return name.matches("^service\\.[^.]+$");
} }
List<ServiceDefinition> getDefinitions() { List<ServiceDefinition> getDefinitions() {

@ -10,9 +10,28 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Properties; import java.util.Properties;
import java.util.stream.Collectors;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
/**
* The main container that loads a specific properties file
* and creates instances of defined services
* <p>
* Services are given arbitrary names and can be defined as singletons:
* <code><pre>
* service.servicename: tld.vendor.project.ServiceClass
* service.servicename.singleton: true
* </pre></code>
* Service names must match the regular expression <code>^service\.[^.]+$</code>
* <p>
* Services declare their dependencies via their constructor. Only one
* constructor per class is allowed.
* All dependencies must be declared in the same container as the declaring
* service.
* <p>
* After loading the properties file, an {@link IntegrityCheck} will be performed.
*/
public class SdiContainer implements SdiContainerInterface { public class SdiContainer implements SdiContainerInterface {
private static final Logger LOG = LoggerFactory.getLogger(SdiContainer.class); private static final Logger LOG = LoggerFactory.getLogger(SdiContainer.class);
@ -25,11 +44,22 @@ public class SdiContainer implements SdiContainerInterface {
this.singletons = new HashMap<>(); this.singletons = new HashMap<>();
} }
/**
* Creates a Container using the file <code>sdic.properties</code> from the classpath.
*
* @return the loaded and integrity checked container
*/
@SuppressWarnings("unused") @SuppressWarnings("unused")
public static SdiContainer load() { public static SdiContainer load() {
return load("sdic.properties"); return load("sdic.properties");
} }
/**
* Creates a Container using the properties file at the designated location
*
* @param filename the filename that shall be loaded
* @return the loaded and integrity checked container
*/
@SuppressWarnings("WeakerAccess") @SuppressWarnings("WeakerAccess")
public static SdiContainer load(String filename) { public static SdiContainer load(String filename) {
Properties props = new Properties(); Properties props = new Properties();
@ -57,6 +87,16 @@ public class SdiContainer implements SdiContainerInterface {
.orElse(null); .orElse(null);
} }
/**
* Creates and returns a service instance of the given class
* <p>
* Dependency services are automatically created. Services marked
* as singletons will be only created once, either as transient
* or direct dependency.
*
* @param clz the type which shall be created
* @return the created type with declared dependencies fulfilled
*/
@Override @Override
public <T> T getInstance(Class<T> clz) { public <T> T getInstance(Class<T> clz) {
LOG.trace("instance ordered: ", clz); LOG.trace("instance ordered: ", clz);
@ -80,6 +120,22 @@ public class SdiContainer implements SdiContainerInterface {
} }
} }
/**
* returns all registered services that implement or extend the given class
*
* @param clz parent class or interface
* @return the list with instances, or an empty list if nothing is found
*/
@SuppressWarnings("WeakerAccess")
public <T> List<T> getInstancesThatImplement(Class<T> clz) {
LOG.trace("instances of interface {} ordered", clz);
return definitions.stream()
.filter(d -> clz.isAssignableFrom(d.getClz()))
.map(ServiceDefinition::getClz)
.map(c -> clz.cast(getInstance(c)))
.collect(Collectors.toList());
}
private <T> void handleSingleton(ServiceDefinition definition, T instance) { private <T> void handleSingleton(ServiceDefinition definition, T instance) {
if (definition.isSingleton()) { if (definition.isSingleton()) {
singletons.put(definition, instance); singletons.put(definition, instance);

@ -12,7 +12,7 @@ public class IntegrityCheckTest {
assertThat(IntegrityCheck.isServiceName("dingens"), is(false)); assertThat(IntegrityCheck.isServiceName("dingens"), is(false));
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.aad-s"), is(true));
assertThat(IntegrityCheck.isServiceName("service.a.singleton"), is(false)); assertThat(IntegrityCheck.isServiceName("service.a.singleton"), is(false));
} }

@ -1,6 +1,16 @@
package de.joshavg.simpledic; package de.joshavg.simpledic;
import static org.hamcrest.CoreMatchers.allOf;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.nullValue;
import static org.hamcrest.collection.IsCollectionWithSize.hasSize;
import static org.hamcrest.core.IsInstanceOf.instanceOf;
import de.joshavg.simpledic.services.DependsOnNoDependencies; import de.joshavg.simpledic.services.DependsOnNoDependencies;
import de.joshavg.simpledic.services.ServiceInterface;
import java.util.List;
import org.junit.Test; import org.junit.Test;
public class SimpleDependenciesTest { public class SimpleDependenciesTest {
@ -17,4 +27,21 @@ public class SimpleDependenciesTest {
SdiContainer container = SdiContainer.load(FILENAME); SdiContainer container = SdiContainer.load(FILENAME);
container.getInstance(DependsOnNoDependencies.class); container.getInstance(DependsOnNoDependencies.class);
} }
@Test
public void serviceImplementationsAreReturned() {
SdiContainer container = SdiContainer.load(FILENAME);
List<ServiceInterface> list = container.getInstancesThatImplement(ServiceInterface.class);
assertThat(list, hasSize(2));
list.forEach(s -> assertThat(s, instanceOf(ServiceInterface.class)));
}
@Test
public void emptyListIsReturnedOnUnknownInterface() {
SdiContainer container = SdiContainer.load(FILENAME);
List<List> list = container.getInstancesThatImplement(List.class);
assertThat(list, allOf(empty(), not(nullValue())));
}
} }

@ -1,6 +1,6 @@
package de.joshavg.simpledic.services; package de.joshavg.simpledic.services;
public class DependsOnNoDependencies { public class DependsOnNoDependencies implements ServiceInterface {
private final NoDependencies b; private final NoDependencies b;

@ -1,4 +1,4 @@
package de.joshavg.simpledic.services; package de.joshavg.simpledic.services;
public class NoDependencies { public class NoDependencies implements ServiceInterface {
} }

@ -0,0 +1,5 @@
package de.joshavg.simpledic.services;
public interface ServiceInterface {
}
Loading…
Cancel
Save