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 7 years ago
parent f94e86df1d
commit 67d93b91a2

@ -19,6 +19,30 @@
<target>1.8</target>
</configuration>
</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>
</build>
@ -43,7 +67,7 @@
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-core</artifactId>
<artifactId>hamcrest-all</artifactId>
<version>1.3</version>
<scope>test</scope>
</dependency>

@ -19,6 +19,20 @@ import java.util.stream.Collectors;
import org.slf4j.Logger;
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 {
private static final Logger LOG = LoggerFactory.getLogger(IntegrityCheck.class);
@ -151,7 +165,7 @@ class IntegrityCheck {
}
static boolean isServiceName(String name) {
return name.matches("service\\.[^.]+");
return name.matches("^service\\.[^.]+$");
}
List<ServiceDefinition> getDefinitions() {

@ -10,9 +10,28 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.stream.Collectors;
import org.slf4j.Logger;
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 {
private static final Logger LOG = LoggerFactory.getLogger(SdiContainer.class);
@ -25,11 +44,22 @@ public class SdiContainer implements SdiContainerInterface {
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")
public static SdiContainer load() {
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")
public static SdiContainer load(String filename) {
Properties props = new Properties();
@ -57,6 +87,16 @@ public class SdiContainer implements SdiContainerInterface {
.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
public <T> T getInstance(Class<T> 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) {
if (definition.isSingleton()) {
singletons.put(definition, instance);

@ -12,7 +12,7 @@ public class IntegrityCheckTest {
assertThat(IntegrityCheck.isServiceName("dingens"), 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));
}

@ -1,6 +1,16 @@
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.ServiceInterface;
import java.util.List;
import org.junit.Test;
public class SimpleDependenciesTest {
@ -17,4 +27,21 @@ public class SimpleDependenciesTest {
SdiContainer container = SdiContainer.load(FILENAME);
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;
public class DependsOnNoDependencies {
public class DependsOnNoDependencies implements ServiceInterface {
private final NoDependencies b;

@ -1,4 +1,4 @@
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