/*
* Copyright 2014-2016 Red Hat, Inc, and individual contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.projectodd.wunderboss;
import org.slf4j.ILoggerFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicLong;
public class WunderBoss {
static {
init();
}
private static void init() {
classLoader = new DynamicClassLoader(WunderBoss.class.getClassLoader());
locator = new ClassPathLocator(classLoader);
options = new Options<>();
options.put("root", ".");
configureLogback();
}
private WunderBoss() {}
public static <T extends Component> T findComponent(Class<T> clazz) {
return findComponent(clazz, null);
}
public static <T extends Component> T findComponent(Class<T> clazz, String name) {
return (T) components.get(fullComponentName(clazz, name));
}
public static <T extends Component> T findOrCreateComponent(Class<T> clazz) {
return findOrCreateComponent(clazz, null, null);
}
public static <T extends Component> T findOrCreateComponent(Class<T> clazz, String name, Map<Object, Object> options) {
if (name == null) {
name = "default";
}
T component = findComponent(clazz, name);
String fullName = fullComponentName(clazz, name);
if (component != null) {
log.debug("Returning existing component for " + fullName + ", ignoring options.");
} else {
component = getComponentProvider(clazz, true).create(name, new Options<>(options));
components.put(fullName, component);
}
return component;
}
private static String fullComponentName(Class clazz, String name) {
return clazz.getName() + ":" + name;
}
public static Language findLanguage(String name) {
return findLanguage(name, true);
}
private static Language findLanguage(String name, boolean throwIfMissing) {
Language language = languages.get(name);
if (language == null &&
(language = locator.findLanguage(name)) != null) {
registerLanguage(name, language);
}
if (throwIfMissing &&
language == null) {
throw new IllegalArgumentException("Unknown language: " + name);
}
return language;
}
public static void registerLanguage(String languageName, Language language) {
language.initialize();
languages.put(languageName, language);
}
public static boolean providesLanguage(String name) {
return (findLanguage(name, false) != null);
}
public static void registerComponentProvider(Class iface, ComponentProvider<?> provider) {
componentProviders.put(iface, provider);
}
public static boolean providesComponent(Class<? extends Component> clazz) {
return getComponentProvider(clazz, false) != null;
}
public static void shutdownAndReset() throws Exception {
for (Runnable action : shutdownActions) {
action.run();
}
shutdownActions.clear();
for (Language language : languages.values()) {
language.shutdown();
}
languages.clear();
for (Component component : components.values()) {
component.stop();
}
components.clear();
shutdownWorkerPool();
}
public synchronized static void addShutdownAction(Runnable action) {
if (!shutdownHook) {
if (!inContainer()) {
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
public void run() {
try {
shutdownAndReset();
} catch (Throwable t) {
log.warn("Error in WunderBoss shutdown hook", t);
}
}
}));
}
shutdownHook = true;
}
shutdownActions.add(action);
}
private static <T extends Component> ComponentProvider<T> getComponentProvider(Class<T> iface, boolean throwIfMissing) {
ComponentProvider<T> provider = componentProviders.get(iface);
if (provider == null &&
(provider = locator.findComponentProvider(iface)) != null) {
registerComponentProvider(iface, provider);
}
if (throwIfMissing &&
provider == null) {
throw new IllegalArgumentException("Unknown component: " + iface.getName());
}
return provider;
}
public static boolean inContainer() {
return inWildFly();
}
public static boolean inWildFly() {
return options().containsKey("service-registry");
}
public static Logger logger(String name) {
return LoggerFactory.getLogger(name);
}
public static Logger logger(Class clazz) {
return LoggerFactory.getLogger(clazz);
}
public static void setLogLevel(String level) {
Logger logger = logger(org.slf4j.Logger.ROOT_LOGGER_NAME);
try {
logger.getClass().getClassLoader().loadClass("ch.qos.logback.classic.Logger");
LogbackUtil.setLogLevel(logger, level);
} catch (ClassNotFoundException e) {
log.error("Failed to change root logging level - only supported when using logback");
}
}
private static void configureLogback() {
ILoggerFactory loggerFactory = LoggerFactory.getILoggerFactory();
ClassLoader cl = loggerFactory.getClass().getClassLoader();
try {
cl.loadClass("ch.qos.logback.classic.LoggerContext");
LogbackUtil.configureLogback(loggerFactory);
} catch (ClassNotFoundException ignored) {
// we're not using logback
}
}
public static void updateClassPath(List<URL> classpath) {
for(URL each : classpath) {
classLoader.addURL(each);
}
}
public static void updateClassPath(URL url) {
classLoader.addURL(url);
}
public static ClassLoader classLoader() {
return classLoader;
}
public static Locator locator() {
return locator;
}
public static void setLocator(Locator loc) {
locator = loc;
}
public static synchronized Options options() {
return options;
}
public static synchronized void putOption(String k, Object v) {
options.put(k, v);
}
public static synchronized void mergeOptions(Options<String> other) {
options = options.merge(other);
}
public static synchronized ExecutorService workerPool() {
if (workerExecutor == null) {
final String deploymentName = options().getString("deployment-name", "wunderboss");
workerExecutor = Executors.newCachedThreadPool(new ThreadFactory() {
private final AtomicLong counter = new AtomicLong(0);
@Override
public Thread newThread(Runnable r) {
return new Thread(r, String.format("%s-worker-%d",
deploymentName, counter.getAndIncrement()));
}
});
}
return workerExecutor;
}
public static synchronized void shutdownWorkerPool() {
if (workerExecutor != null) {
workerExecutor.shutdown();
workerExecutor = null;
}
}
private static Locator locator;
private static Options<String> options;
private static final Map<String, Language> languages = new HashMap<>();
private static final Map<Class, ComponentProvider> componentProviders = new HashMap<>();
private static final Map<String, Component> components = new HashMap<>();
private static final List<Runnable> shutdownActions = new ArrayList<>();
private static DynamicClassLoader classLoader;
private static ExecutorService workerExecutor;
private static final Logger log = logger(WunderBoss.class);
private static boolean shutdownHook = false;
}