/* * $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(); } } } }