You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

151 lines
5.1 KiB

package de.joshavg.simpledic;
import de.joshavg.simpledic.exception.ClassNotRegistered;
import de.joshavg.simpledic.exception.ContainerInitException;
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.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:
* <pre><code>
* service.servicename: tld.vendor.project.ServiceClass
* service.servicename.singleton: true
* </code></pre>
* 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);
private final List<ServiceDefinition> definitions;
private final Map<ServiceDefinition, Object> singletons;
private SdiContainer(List<ServiceDefinition> definitions) {
this.definitions = definitions;
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();
InputStream inputStream = SdiContainer.class.getClassLoader().getResourceAsStream(filename);
if (inputStream == null) {
throw new ContainerInitException("config file " + filename + " not found", null);
}
try {
props.load(inputStream);
} catch (IOException e) {
throw new ContainerInitException("failed loading properties", e);
}
IntegrityCheck integrityCheck = new IntegrityCheck(props);
integrityCheck.check();
return new SdiContainer(integrityCheck.getDefinitions());
}
private <T> ServiceDefinition getDefinition(Class<T> clz) {
return definitions.stream()
.filter(d -> d.getClz() == clz)
.findFirst()
.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);
ServiceDefinition definition = getDefinition(clz);
if (definition == null) {
throw new ClassNotRegistered(clz);
}
LOG.debug("service name is {}", definition.getName());
if (isStoredAsSingleton(definition)) {
return clz.cast(singletons.get(definition));
}
try {
T instance = new Instantiator<>(clz, this).createInstance();
handleSingleton(definition, instance);
return instance;
} catch (IllegalAccessException | InvocationTargetException | InstantiationException e) {
throw new SdicInstantiationException(e);
}
}
/**
* returns all registered services that implement or extend the given class
*
* @param clz parent class or interface
* @param <T> type of the requested service
* @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);
}
}
private boolean isStoredAsSingleton(ServiceDefinition def) {
return singletons.containsKey(def);
}
}