Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
net.e6tech.elements.common.resources.plugin.PluginManager Maven / Gradle / Ivy
/*
* Copyright 2015-2019 Futeh Kao
*
* 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 net.e6tech.elements.common.resources.plugin;
import net.e6tech.elements.common.inject.Injector;
import net.e6tech.elements.common.inject.Module;
import net.e6tech.elements.common.inject.ModuleFactory;
import net.e6tech.elements.common.logging.Logger;
import net.e6tech.elements.common.reflection.Reflection;
import net.e6tech.elements.common.resources.*;
import net.e6tech.elements.common.util.SystemException;
import net.e6tech.elements.common.util.file.FileUtil;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Paths;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
/**
* Created by futeh.
*/
@SuppressWarnings({"squid:S134", "unchecked"})
public class PluginManager {
private static final String DEFAULT_PLUGIN = "defaultPlugin";
private static final Object NULL_OBJECT = new Object();
private PluginClassLoader classLoader;
private final ResourceManager resourceManager;
private Resources resources;
private Map, PluginEntry> plugins = new HashMap<>();
private Map, Object> defaultPlugins = new HashMap<>();
public PluginManager(ResourceManager resourceManager) {
this.resourceManager = resourceManager;
classLoader = new PluginClassLoader(resourceManager.getClass().getClassLoader());
}
public synchronized PluginManager from(Resources resources) {
PluginManager plugin = new PluginManager(resourceManager);
plugin.resources = resources;
plugin.defaultPlugins = defaultPlugins;
plugin.classLoader = classLoader;
plugin.plugins = plugins;
return plugin;
}
public Resources getResources() {
return resources;
}
public void setResources(Resources resources) {
this.resources = resources;
}
public synchronized void loadPlugins(String[] directories) {
for (String dir : directories) {
String[] paths;
try {
paths = FileUtil.listFiles(dir, ".jar");
} catch (IOException e) {
throw new SystemException(e);
}
for (String p : paths) {
String fullPath;
try {
fullPath = new File(p).getCanonicalPath();
} catch (IOException e) {
continue;
}
try {
classLoader.addURL(Paths.get(fullPath).toUri().toURL());
} catch (IOException e) {
throw new SystemException(e);
}
}
}
}
public URLClassLoader getPluginClassLoader() {
return classLoader;
}
@SuppressWarnings({"squid:S3824", "squid:S3776"})
protected synchronized Optional getDefaultPlugin(Class> type) {
Object lookup = defaultPlugins.get(type);
if (lookup == NULL_OBJECT)
return Optional.empty();
if (lookup == null) {
Class t = type;
while (t != null && !t.equals(Object.class)) {
try {
Field field = t.getField(DEFAULT_PLUGIN);
lookup = field.get(null);
defaultPlugins.put(type, lookup);
break;
} catch (NoSuchFieldException | IllegalAccessException e1) {
Logger.suppress(e1);
}
t = t.getSuperclass();
}
// no default static variable named defaultPlugin
// see if type implements AutoPlugin. If so, associate type with itself.
if (lookup == null
&& type != null
&& AutoPlugin.class.isAssignableFrom(type)) {
try {
PluginEntry.validateClass(type);
lookup = type;
defaultPlugins.put(type, lookup);
} catch (IllegalArgumentException e) {
// ok
}
}
if (lookup == null)
defaultPlugins.put(type, NULL_OBJECT);
}
return Optional.ofNullable(lookup);
}
public Map startsWith(PluginPath> path) {
return startsWith(PluginPaths.of(path));
}
public synchronized Map startsWith(PluginPaths> paths) {
Map map = new LinkedHashMap<>();
for (PluginPath> path : paths.getPaths()) {
for (Map.Entry, PluginEntry> entry : plugins.entrySet()) {
if (entry.getKey().startsWith(path) && !map.containsKey(entry.getKey()))
map.put(entry.getKey(), entry.getValue());
}
}
return map;
}
public synchronized Optional> getEntry(PluginPath path) {
return Optional.ofNullable(plugins.get(path));
}
public synchronized Optional> getEntry(PluginPaths paths) {
PluginEntry lookup = null;
// look up from paths
for (PluginPath path : paths.getPaths()) {
lookup = plugins.get(path);
if (lookup != null) {
break;
}
}
return Optional.ofNullable(lookup);
}
public Optional get(PluginPaths paths, Object... args) {
return lookup(paths, true, args);
}
public Optional lookup(PluginPaths paths) {
return lookup(paths, false);
}
private Optional lookup(PluginPaths paths, boolean inject, Object... args) {
PluginEntry lookup = getEntry(paths).orElse(null);
PluginPath pluginPath;
// if still null, look up from default plugin
Object instance = null;
if (lookup == null) {
pluginPath = PluginPath.of(paths.getType(), DEFAULT_PLUGIN);
} else {
pluginPath = lookup.getPath();
instance = lookup.getPlugin();
}
return Optional.ofNullable(createAndInject(pluginPath, instance, inject, args));
}
public T createInstance(PluginPath pluginPath, Object obj, Object... args) {
return createAndInject(pluginPath, obj, true, args);
}
private T createAndInject(PluginPath pluginPath, Object obj, boolean strictInject, Object... args) {
if (obj == null && pluginPath == null)
return null;
if (obj == null) {
// get default plugin
Optional defaultPlugin = getDefaultPlugin(pluginPath.getType());
if (!defaultPlugin.isPresent())
return null;
obj = defaultPlugin.get();
pluginPath = PluginPath.of(pluginPath.getType(), DEFAULT_PLUGIN);
}
T plugin;
boolean singleton = false;
if (obj instanceof Class) {
plugin = createFromClass((Class) obj);
} else if (obj instanceof PluginFactory) {
plugin = createFromFactory((PluginFactory) obj);
} else {
singleton = isSingleton(obj);
plugin = createFromInstance(obj);
}
if (!singleton) {
inject(plugin, strictInject, args);
plugin.initialize(pluginPath);
}
return plugin;
}
private T createFromClass(Class cls) {
try {
return (T) cls.getDeclaredConstructor().newInstance();
} catch (Exception e) {
throw new SystemException(e);
}
}
private T createFromFactory(PluginFactory factory) {
return factory.create(this);
}
private T createFromInstance(Object obj) {
T plugin;
T prototype = (T) obj;
if (prototype instanceof Prototype) {
plugin = (T) ((Prototype) prototype).newInstance();
} else if (prototype.isPrototype()) {
try {
plugin = (T) prototype.getClass().getDeclaredConstructor().newInstance();
Reflection.copyInstance(plugin, prototype);
} catch (Exception e) {
throw new SystemException(e);
}
} else {
// this is a singleton, no need to initialize and inject.
plugin = prototype;
}
return plugin;
}
private boolean isSingleton(Object obj) {
T prototype = (T) obj;
return !(prototype instanceof Prototype || prototype.isPrototype());
}
@SuppressWarnings("squid:S3776")
public void inject(Object instance, boolean strict, Object... args) {
if (instance != null && args != null && args.length > 0) {
ModuleFactory factory = (resources != null) ? resources.getModule().getFactory() :
resourceManager.getModule().getFactory();
Module module = factory.create();
for (Object arg : args) {
if (arg instanceof Binding) {
Binding binding = (Binding) arg;
if (binding.getName() != null) {
module.bindNamedInstance(binding.getBoundClass(), binding.getName(), binding.get());
} else {
module.bindInstance(binding.getBoundClass(), binding.get());
}
} else {
module.bindInstance(arg.getClass(), arg);
}
}
Injector injector = (resources != null) ?
module.build(resources.getModule(), resourceManager.getModule())
: module.build(resourceManager.getModule());
InjectionListener injectionListener = null;
ResourcePool resourcePool = (resources != null) ? resources : resourceManager;
if (instance instanceof InjectionListener) {
injectionListener = (InjectionListener) instance;
injectionListener.preInject(resourcePool);
}
injector.inject(instance, strict);
if (injectionListener != null)
injectionListener.injected(resourcePool);
} else if (instance != null && resources != null) {
resources.inject(instance, strict);
}
}
public Optional get(PluginPath path, Object... args) {
return get(PluginPaths.of(path), args);
}
public Optional lookup(PluginPath path) {
return lookup(PluginPaths.of(path));
}
public synchronized PluginEntry add(PluginPath path, Class cls) {
PluginEntry entry = new PluginEntry(path, cls);
plugins.put(path, entry);
return entry;
}
public synchronized PluginEntry add(PluginPath path, T singleton) {
PluginEntry entry = new PluginEntry(path, singleton);
plugins.put(path, entry);
resourceManager.inject(singleton, !singleton.isPrototype());
singleton.initialize(path);
return entry;
}
public synchronized Object remove(PluginPath path) {
PluginEntry entry = plugins.remove(path);
return entry == null ? null : entry.getPlugin();
}
public synchronized void addDefault(Class cls, U singleton) {
defaultPlugins.put(cls, singleton);
resourceManager.inject(singleton, !singleton.isPrototype());
singleton.initialize(PluginPath.of(cls, DEFAULT_PLUGIN));
}
public synchronized void addDefault(Class cls, Class implClass) {
defaultPlugins.put(cls, implClass);
}
public Object removeDefault(Class cls) {
return defaultPlugins.remove(cls);
}
public static class PluginClassLoader extends URLClassLoader {
private List pluginURLs = new ArrayList<>();
public PluginClassLoader(ClassLoader parent) {
super(new URL[0], parent);
}
@Override
public void addURL(URL url) {
super.addURL(url);
pluginURLs.add(url);
}
public URL[] getPluginURLs() {
return pluginURLs.toArray(new URL[0]);
}
}
}