/*
* $Id$
*
* Copyright (C) 2003-2015 JNode.org
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library 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 Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; If not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.jnode.build.packager;
import java.io.File;
import java.io.FileFilter;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.types.Path;
import org.jnode.build.BuildException;
import org.jnode.build.PluginTask;
import org.jnode.build.AbstractPluginTask.LibAlias;
/**
* Class building new jnode plugins from third party jars/resources.
*
* @author fabien
*
*/
public class PluginBuilder extends PackagerTask {
private final Task parent;
/**
* List of user plugin ids.
*/
private StringBuilder userPluginIds = new StringBuilder();
/**
* {@link Path} to third party jars for compilation purpose.
*/
private Path path;
/**
* Construct a PluginBuilder from the given {@link Task},
* which will be used as a delegate to access ant context.
*
* @param parent
*/
public PluginBuilder(Task parent) {
this.parent = parent;
}
/**
* Define the path reference for compilation.
* @param pathRefId
*/
public void setPathRefId(String pathRefId) {
this.path = (Path) parent.getProject().getReference(pathRefId);
}
/**
* Main method for build the jnode plugin.
*
* @param executor
* @param descriptors
*/
public void execute(ThreadPoolExecutor executor, final Map<String, File> descriptors) {
if (isEnabled()) {
if (path == null) {
throw new BuildException("pathRefId is mandatory");
}
File[] userJars = userApplicationsDir.listFiles(new FileFilter() {
@Override
public boolean accept(File pathname) {
return pathname.getName().endsWith(".jar") || pathname.isDirectory();
}
});
for (File userJar : userJars) {
processUserJar(executor, descriptors, userJar, userPluginIds);
}
}
}
/**
* Do finalization tasks. For instance, it's writing the plugin ids to the properties file
*/
public void finish() {
if (isEnabled()) {
if ((userPluginIds.length() > 0) && (userPluginIds.charAt(userPluginIds.length() - 1) == ',')) {
userPluginIds.deleteCharAt(userPluginIds.length() - 1);
}
// write properties
Properties properties = getProperties();
properties.put(USER_PLUGIN_IDS, userPluginIds.toString());
FileOutputStream fos = null;
try {
fos = new FileOutputStream(getPropertiesFile());
properties.store(fos, "File automatically generated by JNode Packager");
} catch (IOException e) {
throw new BuildException("failed to write properties file", e);
} finally {
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
throw new BuildException("failed to close properties file", e);
}
}
}
}
}
/**
* Attention : userPluginList must be a StringBuilder because it's accessed from multiple threads.
* @param executor
* @param descriptors
* @param userJar
* @param userPluginList
*/
private void processUserJar(ExecutorService executor, final Map<String, File> descriptors, final File userJar,
final StringBuilder userPluginList) {
final PluginTask task = (PluginTask) parent;
executor.execute(new Runnable() {
public void run() {
final String jarName = userJar.getName();
final String pluginId;
if (userJar.isFile()) {
pluginId = jarName.substring(0, jarName.length() - 4); // remove ".jar"
} else {
pluginId = jarName; // use directory name as plugin id
}
userPluginList.append(pluginId + ",");
// replace ".jar" by ".xml"
final String pluginDesc = pluginId + ".xml";
path.createPathElement().setLocation(userJar);
// create the lib alias
final String alias = pluginId + ".jar";
LibAlias libAlias = task.createLibAlias();
libAlias.setName(alias);
libAlias.setAlias(userJar);
final File descriptorFile = new File(userJar.getParent(), pluginDesc);
if (!descriptorFile.exists()) {
// build the descriptor from scratch
buildDescriptor(userJar, descriptorFile, pluginId, alias);
}
if (userJar.isDirectory()) {
ScriptBuilder.build(userJar, getProperties());
}
task.buildPlugin(descriptors, descriptorFile);
}
});
}
/**
* Build the plugin descriptor.
*
* @param userJar
* @param descriptorFile
* @param pluginId
* @param alias
*/
private void buildDescriptor(File userJar, File descriptorFile, String pluginId, String alias) {
PrintStream out = null;
boolean success = false;
try {
out = new PrintStream(descriptorFile);
out.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
out.println("<!DOCTYPE plugin SYSTEM \"jnode.dtd\">");
out.println("<plugin id=\"" + pluginId + "\"");
out.println(" name=\"" + pluginId + "\"");
out.println(" version=\"\"");
out.println(" class=\"org.jnode.plugin.AutoUnzipPlugin\"");
out.println(" auto-start=\"true\"");
out.println(" license-name=\"unspecified\">");
out.println(" <runtime>");
out.println(" <library name=\"" + alias + "\">");
out.println(" <export name=\"*\"/>");
out.println(" </library>");
out.println(" </runtime>");
if (userJar.isFile()) {
List<String> mainClasses = MainFinder.searchMain(userJar);
if (!mainClasses.isEmpty()) {
out.println(" <extension point=\"org.jnode.shell.aliases\">");
for (String mainClass : mainClasses) {
int idx = mainClass.lastIndexOf('.');
String name = (idx < 0) ? mainClass : mainClass.substring(idx + 1);
out.println(" <alias name=\"" + name + "\" class=\"" + mainClass + "\"/>");
log(pluginId + " : added alias " + name + " for class " + mainClass, Project.MSG_INFO);
}
out.println(" </extension>");
} else {
log("no main found for plugin " + pluginId, Project.MSG_WARN);
}
}
out.println(" <!-- FIXME : use more restricted permissions -->");
out.println(" <extension point=\"org.jnode.security.permissions\">");
out.println(" <permission class=\"java.security.AllPermission\" />");
out.println(" </extension>");
out.println("</plugin>");
success = true;
} catch (IOException ioe) {
throw new BuildException("failed to write plugin descriptor", ioe);
} finally {
if (out != null) {
out.close();
}
if (!success) {
// in case of failure, delete the incomplete descriptor file
descriptorFile.delete();
}
}
}
}