package PluginLoader.Implementation;
import EnvironmentPluginAPI.Contract.*;
import EnvironmentPluginAPI.Exceptions.CorruptConfigurationFileException;
import EnvironmentPluginAPI.Exceptions.TechnicalException;
import EnvironmentPluginAPI.CustomNetworkMessages.IEnvironmentStateMessage;
import EnvironmentPluginAPI.CustomNetworkMessages.NetworkMessage;
import EnvironmentPluginAPI.Service.AbstractVisualizeReplayPanel;
import EnvironmentPluginAPI.Service.ICycleStatisticsSaver;
import EnvironmentPluginAPI.Service.IEnvironmentConfiguration;
import EnvironmentPluginAPI.Service.IVisualizeReplay;
import NetworkAdapter.Interface.IServerNetworkAdapter;
import NetworkAdapter.Messages.DefaultEnvironmentStateMessage;
import PluginLoader.Interface.Exceptions.PluginNotReadableException;
import PluginLoader.Interface.IEnvironmentPluginLoader;
import ZeroTypes.Settings.AppSettings;
import ZeroTypes.Settings.SettingException;
import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.Hashtable;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
/**
* This class implements all logic affiliated with the loading of environment plugins.
*/
public class EnvironmentPluginLoaderUseCase implements IEnvironmentPluginLoader {
private final IServerNetworkAdapter serverNetworkAdapter;
private PluginHelper pluginHelper;
private Map<TEnvironmentDescription, File> environmentPluginPaths;
private Constructor customEnvironmentStateMessage;
private Class customAbstractVisualization;
private Class customInterfaceVisualization;
private IEnvironmentPluginDescriptor loadedEnvironmentDescriptor = null;
private ClassLoader usedClassLoader;
public EnvironmentPluginLoaderUseCase(IServerNetworkAdapter serverNetworkAdapter) throws TechnicalException, SettingException, PluginNotReadableException {
this.serverNetworkAdapter = serverNetworkAdapter;
pluginHelper = new PluginHelper();
listAvailableEnvironments();
}
public List<TEnvironmentDescription> listAvailableEnvironments() throws TechnicalException, PluginNotReadableException, SettingException {
pluginHelper = new PluginHelper();
environmentPluginPaths = new Hashtable<TEnvironmentDescription, File>();
List<TEnvironmentDescription> result = new LinkedList<TEnvironmentDescription>();
try {
// Load all plugins recursively from the directory specified in the config file.
List<String> allJars = pluginHelper.findJarsRecursively(AppSettings.getString("environmentPluginsFolder"));
TEnvironmentDescription tmp = null;
for (String jarPath : allJars) {
//look for the first class that abides the contract that is not the interface itself
for (Class c : pluginHelper.listClassesFromJar(jarPath)) {
if (IEnvironmentPluginDescriptor.class != c
&& IEnvironmentPluginDescriptor.class.isAssignableFrom(c)) {
//save available AgentSystemDescriptor description and it's path
tmp = ((IEnvironmentPluginDescriptor) c.newInstance()).getDescription();
result.add(tmp);
environmentPluginPaths.put(tmp, new File(jarPath));
}
}
}
} catch (InstantiationException e) {
throw new TechnicalException("Unable to load Class from plugin. Reason: \n\n" + e);
} catch (IllegalAccessException e) {
throw new TechnicalException("Unable to load Class from plugin. Reason: \n\n" + e);
}
return result;
}
public void loadEnvironmentPlugin(TEnvironmentDescription environment) throws TechnicalException, PluginNotReadableException {
customEnvironmentStateMessage = null;
customAbstractVisualization = null;
customInterfaceVisualization = null;
try {
// Load all classes from the jar where this plugin is located
Plugin plugin = pluginHelper.loadJar(environmentPluginPaths.get(environment).getPath());
//search for the first class that abides the contract that is not the interface itself
for (Class c : plugin) {
if (!IEnvironmentPluginDescriptor.class.equals(c)
&& IEnvironmentPluginDescriptor.class.isAssignableFrom(c)) {
//save available AgentSystemDescriptor description and it's directory
loadedEnvironmentDescriptor = (IEnvironmentPluginDescriptor) c.newInstance();
continue;
}
//now that this time the plugin is really loaded, get the types of all needed classes for use
//in the factory methods
if (!IEnvironmentStateMessage.class.equals(c)
&& IEnvironmentStateMessage.class.isAssignableFrom(c)) {
customEnvironmentStateMessage = pluginHelper.findSuitableConstructor(c, int.class, IEnvironmentState.class);
if (customEnvironmentStateMessage == null) {
throw new PluginNotReadableException("A custom environment state message was found, but didn't provide the needed constructor => (int clientId, E extends IEnvironmentState state).", environmentPluginPaths.get(environment).getPath());
}
continue;
}
if (IVisualizeReplay.class.isAssignableFrom(c)
&& !IVisualizeReplay.class.equals(c)
&& !AbstractVisualizeReplayPanel.class.equals(c)) {
if (AbstractVisualizeReplayPanel.class.isAssignableFrom(c)) {
customAbstractVisualization = c;
} else {
customInterfaceVisualization = c;
}
}
}
if (loadedEnvironmentDescriptor != null) {
usedClassLoader = plugin.getClassLoader();
if(serverNetworkAdapter != null) {
serverNetworkAdapter.setContextClassLoader(plugin.getClassLoader());
}
} else {
throw new PluginNotReadableException("plugin didn't provide a descriptor.", environmentPluginPaths.get(environment).getPath());
}
} catch (InstantiationException e) {
throw new TechnicalException("Unable to load Class from '" + environmentPluginPaths.get(environment) + "' Reason: \n\n" + e);
} catch (IllegalAccessException e) {
throw new TechnicalException("Unable to load Class from '" + environmentPluginPaths.get(environment) + "' Reason: \n\n" + e);
}
}
@Override
public ClassLoader getUsedClassLoader() {
return usedClassLoader;
}
@Override
public List<IEnvironmentConfiguration> getAvailableConfigurations() throws CorruptConfigurationFileException, TechnicalException {
return loadedEnvironmentDescriptor.getAvailableConfigurations();
}
@Override
public void saveConfiguration(IEnvironmentConfiguration environmentConfiguration) throws TechnicalException {
loadedEnvironmentDescriptor.saveConfiguration(environmentConfiguration);
}
/**
* Returns the file handle for the directory, where the plugin jar is located at.
*
* @param environmentDescription a description of an existing environment != null
* @return null, if environment was not found
* @throws EnvironmentPluginAPI.Exceptions.TechnicalException
* if technical errors prevent the component from loading the plugin specified
* @throws PluginLoader.Interface.Exceptions.PluginNotReadableException
* if the plugin is not readable, for example if no TEnvironmentDescription is provided
*/
public File getEnvironmentPluginPath(TEnvironmentDescription environmentDescription) throws TechnicalException, PluginNotReadableException {
if (environmentPluginPaths != null) {
throw new UnsupportedOperationException("protocol violated: listAvailableEnvironments must be called before this method.");
}
return environmentPluginPaths.get(environmentDescription);
}
/**
* Creates an environment state message. If the environment provides a custom implementation, it will be used.
* Otherwise a default message is used.
*
* @param environmentState the environment state to send
* @param targetClientId the client instance the message is targeted to, != null
* @return not null
*/
public NetworkMessage createEnvironmentStateMessage(IEnvironmentState environmentState, int targetClientId) {
if (customEnvironmentStateMessage != null) {
try { //TODO: needs better exception handling
NetworkMessage message;
message = (NetworkMessage) customEnvironmentStateMessage.newInstance(targetClientId, environmentState);
return message;
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
return new DefaultEnvironmentStateMessage(targetClientId, environmentState);
}
public IVisualizeReplay getReplayVisualization() {
try { //TODO: needs better exception handling
if (customInterfaceVisualization != null) {
return (IVisualizeReplay) customInterfaceVisualization.newInstance();
}
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
public AbstractVisualizeReplayPanel getReplayVisualizationForSwing() {
try { //TODO: needs better exception handling
if (customAbstractVisualization != null) {
return (AbstractVisualizeReplayPanel) customAbstractVisualization.newInstance();
}
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
public IEnvironment createEnvironmentInstance(ICycleStatisticsSaver saveGameStatistics) throws TechnicalException {
if (loadedEnvironmentDescriptor == null) {
throw new UnsupportedOperationException("No plugin was loaded!");
}
return loadedEnvironmentDescriptor.getInstance(saveGameStatistics);
}
@Override
public NetworkMessage createEnvironmentStateMessage(int clientId, IEnvironmentState environmentState) {
return null;
}
}