/*
* LibGodshawk - General-purpose Java library
* Copyright (C) 2014-2015 godshawk
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.godshawk.lib.plugin;
import com.godshawk.lib.annotations.modularity.Plugin;
import com.godshawk.lib.exception.InitializationException;
import com.godshawk.lib.exception.TooManyPluginsException;
import com.godshawk.lib.interfaces.modularity.IPlugin;
import com.godshawk.lib.manager.AbstractManager;
import com.godshawk.lib.plugin.metadata.EnumPluginKeys;
import com.godshawk.lib.plugin.metadata.PluginMetadata;
import com.godshawk.lib.reflection.ClassEnumerator;
import java.awt.geom.IllegalPathStateException;
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
@SuppressWarnings("unused")
public final class PluginManager extends AbstractManager<IPlugin> {
/**
* Sanity limit on the number of plugins
*/
private static final int HARD_LIMIT = 128;
private final File pluginDir;
private String configExtension = "plugin";
public PluginManager(File pluginDir) {
super();
this.pluginDir = pluginDir;
if (!this.pluginDir.exists()) {
if (!this.pluginDir.mkdir()) {
throw new IllegalPathStateException("Unable to create plugin directory!");
}
}
}
@SuppressWarnings("unchecked")
private void loadPlugins() throws TooManyPluginsException {
List<Class<?>> classesInDir = ClassEnumerator.getClassesFromExternalDirectory(pluginDir);
List<Class<? extends IPlugin>> pluginClasses = new ArrayList<>();
classesInDir.stream().filter(IPlugin.class::isAssignableFrom).sequential()
.forEach(c -> pluginClasses.add((Class<? extends IPlugin>) c));
if (pluginClasses.size() + getObjects().size() > HARD_LIMIT) {
throw new TooManyPluginsException(pluginClasses.size(), HARD_LIMIT);
}
pluginClasses.forEach(this::internalPluginLoad);
}
private void setupPlugins() throws InitializationException {
for (IPlugin plugin : getObjects()) {
plugin.initialize();
}
}
@Override
public void initialize() throws InitializationException {
try {
loadPlugins();
setupPlugins();
} catch (TooManyPluginsException e) {
throw new RuntimeException(e);
}
}
public PluginMetadata generateMetadata(Class<? extends IPlugin> plugin) {
if (!plugin.isAnnotationPresent(Plugin.class)) {
throw new IllegalArgumentException(String.format("Plugin %s is not annotated!", plugin.getSimpleName()));
}
Plugin pl = plugin.getDeclaredAnnotation(Plugin.class);
return new PluginMetadata(pl.name(), pl.id(), pl.author(), pl.type());
}
public void internalPluginLoad(Class<? extends IPlugin> pluginClass) {
try {
PluginMetadata metadata = generateMetadata(pluginClass);
IPlugin plugin = pluginClass.getDeclaredConstructor(
PluginManager.class, File.class, File.class
).newInstance(
this, pluginDir, new File(
String.format("%s%s%s.%s",
pluginDir.getAbsolutePath(),
File.separator,
metadata.getKeyValue(EnumPluginKeys.PLUGIN_ID.getValue()),
configExtension
)
)
);
plugin.setMetadata(metadata);
addObject(plugin);
} catch (InstantiationException | IllegalAccessException | NoSuchMethodException
| InvocationTargetException e) {
e.printStackTrace();
}
}
public void setConfigExtension(String ext) {
configExtension = ext;
}
@SuppressWarnings("unchecked")
public <T extends IPlugin> T getPluginByPluginId(String id) {
for (IPlugin p : getObjects()) {
if (p.getMetadata().getKeyValue(EnumPluginKeys.PLUGIN_ID.getValue()).equals(id)) {
return (T) p;
}
}
return null;
}
}