/*
* Copyright (C) 2006-2016 DLR, Germany
*
* All rights reserved
*
* http://www.rcenvironment.de/
*/
package de.rcenvironment.toolkit.core.internal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.apache.commons.logging.LogFactory;
import de.rcenvironment.toolkit.core.api.ImmutableServiceRegistry;
import de.rcenvironment.toolkit.core.api.Toolkit;
import de.rcenvironment.toolkit.core.api.ToolkitException;
import de.rcenvironment.toolkit.core.setup.ToolkitSetup;
import de.rcenvironment.toolkit.core.spi.module.DefaultClassFilter;
import de.rcenvironment.toolkit.core.spi.module.ObjectGraph;
import de.rcenvironment.toolkit.core.spi.module.ShutdownHookReceiver;
import de.rcenvironment.toolkit.core.spi.module.ToolkitModule;
/**
* Default {@link ToolkitSetup} implementation.
*
* @author Robert Mischke
*/
public final class ToolkitBuilderImpl implements ToolkitSetup, ShutdownHookReceiver {
private static final int MAX_DEPENDENCY_RESOLUTION_ATTEMPTS = 10;
private final ObjectGraph objectGraph;
private final DefaultClassFilter defaultClassFilter = new DefaultClassFilter();
private final Map<Class<?>, ToolkitModule<?>> moduleInstances = new HashMap<>();
private final List<Runnable> shutdownHooks = new ArrayList<>();
public ToolkitBuilderImpl(ObjectGraph objectGraphBuilder) {
this.objectGraph = objectGraphBuilder;
}
@Override
@SuppressWarnings("unchecked")
public <T extends ToolkitModule<TModuleConf>, TModuleConf> TModuleConf configureModule(Class<T> module) throws ToolkitException {
final T moduleInstance;
// ignore duplicate registrations; in that case, reuse the existing module and its configuration object
if (!moduleInstances.containsKey(module)) {
try {
moduleInstance = module.newInstance();
moduleInstances.put(module, moduleInstance);
} catch (InstantiationException | IllegalAccessException e) {
throw new ToolkitException(e);
}
} else {
moduleInstance = (T) moduleInstances.get(module);
}
return moduleInstance.getConfiguration();
}
/**
* Attempts to initialize all modules and construct the set of services.
*
* @return the assembled {@link Toolkit}
* @throws ToolkitException on instantiation errors
*/
public Toolkit create() throws ToolkitException {
for (Entry<Class<?>, ToolkitModule<?>> e : moduleInstances.entrySet()) {
final ToolkitModule<?> module = e.getValue();
registerModule(module);
}
for (int i = 0; i < MAX_DEPENDENCY_RESOLUTION_ATTEMPTS; i++) {
Set<Class<? extends ToolkitModule<?>>> missingModulesDependencies = new HashSet<>();
for (Entry<Class<?>, ToolkitModule<?>> e : moduleInstances.entrySet()) {
final ToolkitModule<?> module = e.getValue();
module.suggestMissingModuleDependencies(objectGraph, missingModulesDependencies);
}
if (missingModulesDependencies.isEmpty()) {
break; // all ok, leave the retry loop
}
LogFactory.getLog(getClass()).debug(
"Identified missing service dependencies, loading suggested default module(s) " + missingModulesDependencies);
for (Class<? extends ToolkitModule<?>> moduleClass : missingModulesDependencies) {
try {
registerModule(moduleClass.newInstance());
} catch (InstantiationException | IllegalAccessException e) {
throw new ToolkitException(e);
}
}
}
final ImmutableServiceRegistry serviceRegistry = objectGraph.instantiate();
for (ToolkitModule<?> module : moduleInstances.values()) {
module.registerShutdownHooks(serviceRegistry, this);
}
return new ToolkitImpl(serviceRegistry, Collections.unmodifiableList(shutdownHooks));
}
@Override
public void addShutdownHook(Runnable shutdownHook) {
shutdownHooks.add(shutdownHook);
}
private void registerModule(final ToolkitModule<?> module) {
objectGraph.setPublicInterfaceFilter(defaultClassFilter); // reset filter for each module
module.registerMembers(objectGraph);
}
}