package org.ukiuni.pacifista;
import java.io.File;
import java.io.FileFilter;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.net.HttpURLConnection;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.jar.Attributes;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.ukiuni.pacifista.util.HttpUtil;
import org.ukiuni.pacifista.util.HttpUtil.HttpMethod;
import org.ukiuni.pacifista.util.IOUtil;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
public class PluginLoader {
private static final String PACIFISTA_PLUGIN_ATTRIBUTE_NAME = "PacifistaPluginName";
private static final String PACIFISTA_PLUGIN_ATTRIBUTE_CLASS = "PacifistaPluginClass";
private static final String PACIFISTA_PLUGIN_ATTRIBUTE_VERSION = "PacifistaPluginVirsion";
private static final String PACIFISTA_PLUGIN_ATTRIBUTE_DESCRIPTION = "PacifistaPluginDescription";
private static final String PACIFISTA_PLUGIN_ATTRIBUTE_PLUGINSDEPENDSON = "PacifistaPluginDependsOn";
private static final String PACIFISTA_PLUGIN_HOST_URL = "http://pacifista.ukiuni.org/plugins/xml/";
private String pluginHostUrl = PACIFISTA_PLUGIN_HOST_URL;
public void downloadPluginIfNotHave(File baseDir, List<Plugin> aleadyExistsPlugin, String name, String version, String proxyHost, int proxyPort, String proxyUser, String proxyPassword) throws IOException {
Set<PluginDownloadInfo> loadPluginInfos = loadPluginInfo(name, version, true, proxyHost, proxyPort, proxyUser, proxyPassword);
for (PluginDownloadInfo pluginDownloadInfo : loadPluginInfos) {
if (!hasAleady(aleadyExistsPlugin, pluginDownloadInfo)) {
List<File> downloadedFiles = new ArrayList<File>();
for (DownloadFile downloadFile : pluginDownloadInfo.getDownloadFiles()) {
File file = new File(baseDir, downloadFile.downloadTo);
file.getParentFile().mkdirs();
FileOutputStream out = new FileOutputStream(file);
try {
HttpUtil.download(downloadFile.downloadFrom, out, proxyHost, proxyPort, proxyUser, proxyPassword);
downloadedFiles.add(file);
} catch (IOException e) {
IOUtil.close(out);
if (null != file) {
file.delete();
}
for (File downloadedFile : downloadedFiles) {
downloadedFile.delete();
}
throw e;
} finally {
IOUtil.close(out);
}
}
}
}
}
public void deletePlugin(File baseDir, String name, String version, String proxyHost, int proxyPort, String proxyUser, String proxyPassword) throws IOException {
Set<PluginDownloadInfo> loadPluginInfos = loadPluginInfo(name, version, false, proxyHost, proxyPort, proxyUser, proxyPassword);
for (PluginDownloadInfo pluginDownloadInfo : loadPluginInfos) {
for (DownloadFile downloadFile : pluginDownloadInfo.getDownloadFiles()) {
new File(baseDir, downloadFile.downloadTo).delete();
}
}
}
private boolean hasAleady(List<Plugin> plugins, PluginDownloadInfo pluginDownloadInfo) {
for (Plugin plugin : plugins) {
if (plugin.equals(pluginDownloadInfo.name)) {
if (plugin.version == null && pluginDownloadInfo.version == null) {
return true;
} else if (pluginDownloadInfo.version != null && pluginDownloadInfo.version.equals(plugin.version)) {
return true;
}
}
}
return false;
}
public Set<PluginDownloadInfo> loadPluginInfo(String name, String version, boolean loadDependsOn, String proxyHost, int proxyPort, String proxyUser, String proxyPassword) throws IOException {
Set<PluginDownloadInfo> pluginDownloadInfos = new HashSet<PluginDownloadInfo>();
loadPluginInfo(name, version, pluginDownloadInfos, loadDependsOn, proxyHost, proxyPort, proxyUser, proxyPassword);
return pluginDownloadInfos;
}
public void loadPluginInfo(String name, String version, Set<PluginDownloadInfo> pluginDownloadInfos, boolean loadDependsOn, String proxyHost, int proxyPort, String proxyUser, String proxyPassword) throws IOException {
String urlPath = getPluginHostUrl() + name;
if (null != version) {
urlPath = urlPath + "?version=" + version;
}
loadPluginInfo(urlPath, pluginDownloadInfos, loadDependsOn, proxyHost, proxyPort, proxyUser, proxyPassword);
}
public void loadPluginInfo(String urlPath, Set<PluginDownloadInfo> pluginDownloadInfos, boolean loadDependsOn, String proxyHost, int proxyPort, String proxyUser, String proxyPassword) throws IOException {
URLConnection connection = HttpUtil.openConnection(urlPath, HttpMethod.GET, null, null, proxyHost, proxyPort, proxyUser, proxyPassword);
if (connection instanceof HttpURLConnection && 300 <= ((HttpURLConnection) connection).getResponseCode()) {
throw new IOException("response code is " + ((HttpURLConnection) connection).getResponseCode());
}
InputStream in = connection.getInputStream();
loadPluginInfo(in, pluginDownloadInfos, loadDependsOn, proxyHost, proxyPort, proxyUser, proxyPassword);
}
public void loadPluginInfo(InputStream in, Set<PluginDownloadInfo> pluginDownloadInfos, boolean loadDependsOn, String proxyHost, int proxyPort, String proxyUser, String proxyPassword) throws IOException {
SAXParserFactory spfactory = SAXParserFactory.newInstance();
PluginHandler handler = new PluginHandler();
try {
SAXParser parser = spfactory.newSAXParser();
parser.parse(in, handler);
} catch (Exception e) {
throw new IOException(e);
}
in.close();
PluginDownloadInfo pluginDownloadInfo = handler.getPluginDownloadInfo();
pluginDownloadInfos.add(pluginDownloadInfo);
for (Plugin dependsOnPlugin : pluginDownloadInfo.getDependsOns()) {
if (loadDependsOn && !isAleadyHasDependency(pluginDownloadInfos, dependsOnPlugin)) {
loadPluginInfo(dependsOnPlugin.name, dependsOnPlugin.version, pluginDownloadInfos, loadDependsOn, proxyHost, proxyPort, proxyUser, proxyPassword);
}
}
}
public List<PluginDownloadInfo> loadAllPluginInfos(String proxyHost, int proxyPort, String proxyUser, String proxyPassword) throws IOException {
URLConnection connection = HttpUtil.openConnection(getPluginHostUrl(), HttpMethod.GET, null, null, proxyHost, proxyPort, proxyUser, proxyPassword);
if (connection instanceof HttpURLConnection && 300 <= ((HttpURLConnection) connection).getResponseCode()) {
throw new IOException("response code is " + ((HttpURLConnection) connection).getResponseCode());
}
InputStream in = connection.getInputStream();
return loadAllPluginInfos(in);
}
public List<PluginDownloadInfo> loadAllPluginInfos(InputStream in) throws IOException {
SAXParserFactory spfactory = SAXParserFactory.newInstance();
PluginListHandler handler = new PluginListHandler();
try {
SAXParser parser = spfactory.newSAXParser();
parser.parse(in, handler);
} catch (Exception e) {
throw new IOException(e);
}
in.close();
return handler.getPluginDownloadInfos();
}
private boolean isAleadyHasDependency(Set<PluginDownloadInfo> pluginDownloadInfos, Plugin plugin) {
for (PluginDownloadInfo dependsPluginDownloadInfo : pluginDownloadInfos) {
if (plugin.name.equals(dependsPluginDownloadInfo.name)) {
if (plugin.version == null) {
return true;
}
if (plugin.version.equals(dependsPluginDownloadInfo.version)) {
return true;
}
}
}
return false;
}
public List<Plugin> loadAllPluginFromDirectory(File directory) throws IOException, IllegalArgumentException, SecurityException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException, ClassNotFoundException {
List<Plugin> plugins = new ArrayList<PluginLoader.Plugin>();
File[] jarFiles = directory.listFiles(new FileFilter() {
@Override
public boolean accept(File file) {
return file.getName().endsWith(".jar") || file.getName().endsWith(".zip");
}
});
for (File file : jarFiles) {
plugins.addAll(loadPlugin(file));
}
return plugins;
}
public List<Plugin> loadPlugin(File file) throws IOException, IllegalArgumentException, SecurityException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException, ClassNotFoundException {
JarFile jarFile = new JarFile(file);
Manifest manifest = jarFile.getManifest();
if (null == manifest) {
return Collections.emptyList();
}
Attributes attributes = manifest.getMainAttributes();
Pattern pattern = Pattern.compile("^" + PACIFISTA_PLUGIN_ATTRIBUTE_NAME + "([0-9]+)$");
List<Plugin> plugins = new ArrayList<Plugin>();
for (Object key : attributes.keySet()) {
Matcher matcher = pattern.matcher(key.toString());
if (matcher.find()) {
String number = matcher.group(1);
String className = attributes.getValue(PACIFISTA_PLUGIN_ATTRIBUTE_CLASS + number);
if (null != className) {
Plugin plugin = new Plugin();
plugin.setName(attributes.getValue(key.toString()));
plugin.setInstance(Class.forName(className.trim()).getConstructor().newInstance());
plugins.add(plugin);
String version = attributes.getValue(PACIFISTA_PLUGIN_ATTRIBUTE_VERSION + number);
plugin.setVersion(version);
String description = attributes.getValue(PACIFISTA_PLUGIN_ATTRIBUTE_DESCRIPTION + number);
plugin.setDescription(description);
String pluginsDependsOnConcated = attributes.getValue(PACIFISTA_PLUGIN_ATTRIBUTE_PLUGINSDEPENDSON + number);
if (null != pluginsDependsOnConcated) {
String[] pluginsDependsOn = splitAndTrim(pluginsDependsOnConcated);
plugin.setPluginsDependsOn(pluginsDependsOn);
}
}
}
}
return plugins;
}
private String[] splitAndTrim(String src) {
String[] pluginsDependsOn = src.split(",");
for (int i = 0; i < pluginsDependsOn.length; i++) {
pluginsDependsOn[i] = pluginsDependsOn[i].trim();
}
return pluginsDependsOn;
}
public String getPluginHostUrl() {
return pluginHostUrl;
}
public void setPluginHostUrl(String pluginHostUrl) {
this.pluginHostUrl = pluginHostUrl;
}
public static class Plugin {
private String name;
private Object instance;
private String version;
private String description;
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String[] getPluginsDependsOn() {
return pluginsDependsOn;
}
public void setPluginsDependsOn(String[] pluginsDependsOn) {
this.pluginsDependsOn = pluginsDependsOn;
}
private String[] pluginsDependsOn;
public Object getInstance() {
return instance;
}
public void setInstance(Object instance) {
this.instance = instance;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
private class PluginHandler extends DefaultHandler {
private String currentValue;
private PluginDownloadInfo downloadInfo;
private DownloadFile currentDownloadFile;
private Plugin currentPlugin;
private boolean inDependsOnPlugin;
@Override
public void startDocument() throws SAXException {
downloadInfo = new PluginDownloadInfo();
downloadInfo.downloadFiles = new ArrayList<DownloadFile>();
downloadInfo.dependsOns = new ArrayList<Plugin>();
}
@Override
public void characters(char[] ch, int offset, int length) {
currentValue = new String(ch, offset, length).trim();
}
@Override
public void startElement(String uri, String localName, String qName, org.xml.sax.Attributes attributes) throws SAXException {
if (qName.equals("downloadFile")) {
currentDownloadFile = new DownloadFile();
} else if (qName.equals("dependsOnPlugin")) {
currentPlugin = new Plugin();
inDependsOnPlugin = true;
}
}
@Override
public void endElement(String uri, String localName, String qName) {
if (inDependsOnPlugin) {
if (qName.equals("name")) {
currentPlugin.setName(currentValue);
} else if (qName.equals("version")) {
currentPlugin.setVersion(currentValue);
} else if (qName.equals("dependsOnPlugin")) {
downloadInfo.dependsOns.add(currentPlugin);
currentPlugin = null;
inDependsOnPlugin = false;
}
} else if (qName.equals("name")) {
downloadInfo.name = currentValue;
} else if (qName.equals("version")) {
downloadInfo.version = currentValue;
} else if (qName.equals("description")) {
downloadInfo.description = currentValue;
} else if (qName.equals("from")) {
currentDownloadFile.downloadFrom = currentValue;
} else if (qName.equals("to")) {
currentDownloadFile.downloadTo = currentValue;
} else if (qName.equals("downloadFile")) {
downloadInfo.downloadFiles.add(currentDownloadFile);
currentDownloadFile = null;
}
}
public PluginDownloadInfo getPluginDownloadInfo() {
return downloadInfo;
}
}
private class PluginListHandler extends DefaultHandler {
private String currentValue;
private List<PluginDownloadInfo> downloadInfos;
private PluginDownloadInfo currentPlugin;
@Override
public void startDocument() throws SAXException {
downloadInfos = new ArrayList<PluginDownloadInfo>();
}
@Override
public void characters(char[] ch, int offset, int length) {
currentValue = new String(ch, offset, length).trim();
}
@Override
public void startElement(String uri, String localName, String qName, org.xml.sax.Attributes attributes) throws SAXException {
if (qName.equals("plugin")) {
currentPlugin = new PluginDownloadInfo();
}
}
@Override
public void endElement(String uri, String localName, String qName) {
if (qName.equals("name")) {
currentPlugin.setName(currentValue);
} else if (qName.equals("version")) {
currentPlugin.setVersion(currentValue);
} else if (qName.equals("description")) {
currentPlugin.setDescription(currentValue);
} else if (qName.equals("plugin")) {
downloadInfos.add(currentPlugin);
}
}
public List<PluginDownloadInfo> getPluginDownloadInfos() {
return downloadInfos;
}
}
public class PluginDownloadInfo {
public String name;
public String version;
public String description;
public List<DownloadFile> downloadFiles;
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public List<Plugin> dependsOns;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
public List<DownloadFile> getDownloadFiles() {
return downloadFiles;
}
public void setDownloadFiles(List<DownloadFile> downloadFiles) {
this.downloadFiles = downloadFiles;
}
public List<Plugin> getDependsOns() {
return dependsOns;
}
public void setDependsOns(List<Plugin> dependsOns) {
this.dependsOns = dependsOns;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof PluginDownloadInfo) {
PluginDownloadInfo pluginDownloadInfo = (PluginDownloadInfo) obj;
if (this.name.equals(pluginDownloadInfo.name)) {
if (this.version == null && pluginDownloadInfo.version == null) {
return true;
} else if (this.version != null && this.version.equals(pluginDownloadInfo.version)) {
return true;
}
}
}
return false;
}
@Override
public int hashCode() {
return (this.name + ":" + this.version).hashCode();
}
}
public class DownloadFile {
public String downloadFrom;
public String downloadTo;
}
}