/*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (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.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
* License for the specific language governing rights and limitations under
* the License.
*
* The Original Code is OpenELIS code.
*
* Copyright (C) ITECH, University of Washington, Seattle WA. All Rights Reserved.
*/
package us.mn.state.health.lims.plugin;
import org.apache.commons.io.IOUtils;
import org.dom4j.*;
import us.mn.state.health.lims.common.exception.LIMSException;
import us.mn.state.health.lims.common.log.LogEvent;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
public class PluginLoader {
private static final String PLUGIN_ANALYZER = "plugin" + File.separator;
private static final String VERSION = "version";
private static final String SUPPORTED_VERSION = "1.0";
private static final String PATH = "path";
private static final String ANALYZER_IMPORTER = "analyzerImporter";
private static final String MENU = "menu";
private static final String PERMISSION = "permission";
private static final String EXTENSION_POINT = "extension_point";
private static final String EXTENSION = "extension";
private static final String DESCRIPTION = "description";
private static final String VALUE = "value";
private int JDK_VERSION_MAJOR;
private int JDK_VERSION_MINOR;
private ServletContext context;
private static List<String> currentPlugins;
private static void registerPluginNames(List<String> listOfPlugins) {
if (currentPlugins == null) {
currentPlugins = listOfPlugins;
}
}
public PluginLoader(ServletContextEvent event) {
context = event.getServletContext();
}
public void load() {
File pluginDir = new File(context.getRealPath(PLUGIN_ANALYZER));
loadDirectory( pluginDir );
}
private void loadDirectory( File pluginDir ){
String[] version = System.getProperty("java.version").split("\\.");
JDK_VERSION_MAJOR = Integer.parseInt(version[0]);
JDK_VERSION_MINOR = Integer.parseInt(version[1]);
List<String> pluginList = new ArrayList<String>();
File[] files = pluginDir.listFiles();
if (files != null) {
for (File file : files) {
if (file.getName().endsWith("jar")) {
loadPlugin(file);
pluginList.add(file.getName());
}else if(file.isDirectory()){
System.out.println("Checking plugin subfolder: " + file.getName());
loadDirectory( file );
}
}
}
registerPluginNames(pluginList);
}
private void loadPlugin(File pluginFile) {
try {
JarFile jar = new JarFile(pluginFile);
if (!checkJDKVersions(pluginFile.getName(), jar)) return;
final Enumeration<JarEntry> entries = jar.entries();
while (entries.hasMoreElements()) {
final JarEntry entry = entries.nextElement();
if (entry.getName().contains(".xml")) {
boolean valid = loadFromXML(jar, entry);
if (valid) {
break;
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
private boolean checkJDKVersions(String fileName, JarFile jar) throws IOException {
Manifest manifest = jar.getManifest();
if( manifest == null){
LogEvent.logError("PluginLoader", "check jdk version", "Manifest file not in jar file, unable to check jdk versions");
System.out.println("Manifest file not in jar file, unable to check jdk versions");
return true;
}
String createdBy = manifest.getMainAttributes().getValue("Created-By");
if( createdBy == null){
LogEvent.logError("PluginLoader", "check jdk version", "JDK version not found in manifest file, unable to check jdk versions");
System.out.println("JDK version not found in manifest file, unable to check jdk versions");
return true;
}
String[] jarVersion = createdBy.split("\\.");
int jarVersionMajor = Integer.parseInt(jarVersion[0]);
int jarVersionMinor = Integer.parseInt( jarVersion[1]);
if( jarVersionMajor > JDK_VERSION_MAJOR || ( jarVersionMajor == JDK_VERSION_MAJOR && jarVersionMinor > JDK_VERSION_MINOR )){
LogEvent.logError("PluginLoader", "check jdk version", "The plugin " + fileName + " was compiled with a higher JDK version (" + getVersion(jarVersionMajor, jarVersionMinor) + ") than the runtime JDK (" + getVersion(JDK_VERSION_MAJOR, JDK_VERSION_MINOR) + ")");
System.out.println("The plugin " + fileName + " was compiled with a higher JDK version (" + getVersion(jarVersionMajor, jarVersionMinor) + ") than the runtime JDK (" + getVersion(JDK_VERSION_MAJOR, JDK_VERSION_MINOR) + ")");
return false;
}
return true;
}
private String getVersion(int major, int minor) {
return major + "." + minor;
}
private boolean loadFromXML(JarFile jar, JarEntry entry) {
Attribute description = null;
try {
URL url = new URL("jar:file:///" + jar.getName() + "!/");
InputStream input = jar.getInputStream(entry);
String xml = IOUtils.toString(input, "UTF-8");
//System.out.println(xml);
Document doc = DocumentHelper.parseText(xml);
Element versionElement = doc.getRootElement().element(VERSION);
if( versionElement == null){
LogEvent.logError("PluginLoader", "load", "Missing version number in plugin");
System.out.println("Missing version number in plugin");
return false;
}
if (!SUPPORTED_VERSION.equals(versionElement.getData())) {
LogEvent.logError("PluginLoader", "load", "Unsupported version number. Expected " + SUPPORTED_VERSION + " got " + versionElement.getData());
System.out.println("Unsupported version number. Expected " + SUPPORTED_VERSION + " got " + versionElement.getData());
return false;
}
Element analyzerImporter = doc.getRootElement().element(ANALYZER_IMPORTER);
if (analyzerImporter != null) {
description = analyzerImporter.element(EXTENSION_POINT).element(DESCRIPTION).attribute(VALUE);
Attribute path = analyzerImporter.element(EXTENSION_POINT).element(EXTENSION).attribute(PATH);
loadActualPlugin(url, path.getValue());
System.out.println("Loaded: " + description.getValue());
}
Element menu = doc.getRootElement().element(MENU);
if (menu != null) {
description = menu.element(EXTENSION_POINT).element(DESCRIPTION).attribute(VALUE);
Attribute path = menu.element(EXTENSION_POINT).element(EXTENSION).attribute(PATH);
loadActualPlugin(url, path.getValue());
System.out.println("Loaded: " + description.getValue());
}
Element permissions = doc.getRootElement().element(PERMISSION);
if (permissions != null) {
description = permissions.element(EXTENSION_POINT).element(DESCRIPTION).attribute(VALUE);
Attribute path = permissions.element(EXTENSION_POINT).element(EXTENSION).attribute(PATH);
loadActualPlugin(url, path.getValue());
System.out.println( "Loaded: " + description.getValue());
}
} catch (MalformedURLException e) {
e.printStackTrace();
return false;
} catch (IOException e) {
e.printStackTrace();
return false;
} catch (DocumentException e) {
e.printStackTrace();
return false;
} catch (LIMSException e){
if( description != null) {
LogEvent.logError("PluginLoader", "load", "Failed Loading: " + description.getValue());
System.out.println("Failed Loading: " + description.getValue());
}
return false;
}
return true;
}
@SuppressWarnings("unchecked")
private void loadActualPlugin(URL url, String classPath) throws LIMSException {
try {
URL[] urls = {url};
ClassLoader classLoader = new URLClassLoader(urls, this.getClass().getClassLoader());
Class<APlugin> aClass = (Class<APlugin>) classLoader.loadClass(classPath);
APlugin instance = aClass.newInstance();
instance.connect();
} catch (ClassNotFoundException e) {
e.printStackTrace();
throw new LIMSException("See previous stack trace");
} catch (InstantiationException e) {
e.printStackTrace();
throw new LIMSException("See previous stack trace");
} catch (IllegalAccessException e) {
e.printStackTrace();
throw new LIMSException("See previous stack trace");
}
}
public static List<String> getCurrentPlugins() {
return currentPlugins;
}
}