From: <fd...@us...> - 2008-08-31 18:12:27
|
Revision: 4522 http://jnode.svn.sourceforge.net/jnode/?rev=4522&view=rev Author: fduminy Date: 2008-08-31 18:12:16 +0000 (Sun, 31 Aug 2008) Log Message: ----------- jar packager tool : - refactored for better separation with regular build - now, each sub directory can be packaged as a jnode plugin, which will be automatically unzipped to a directory Modified Paths: -------------- trunk/all/build.xml trunk/builder/build.xml trunk/builder/src/builder/org/jnode/build/AbstractPluginsTask.java trunk/builder/src/builder/org/jnode/build/InitJarBuilder.java trunk/builder/src/builder/org/jnode/build/InitJarsBuilder.java trunk/builder/src/builder/org/jnode/build/PluginList.java trunk/builder/src/builder/org/jnode/build/PluginTask.java Added Paths: ----------- trunk/builder/src/builder/org/jnode/build/packager/ trunk/builder/src/builder/org/jnode/build/packager/PackagerTask.java trunk/builder/src/builder/org/jnode/build/packager/PluginBuilder.java trunk/builder/src/builder/org/jnode/build/packager/PluginListInsertor.java trunk/builder/src/builder/org/jnode/build/packager/plugins.properties Modified: trunk/all/build.xml =================================================================== --- trunk/all/build.xml 2008-08-31 16:58:22 UTC (rev 4521) +++ trunk/all/build.xml 2008-08-31 18:12:16 UTC (rev 4522) @@ -223,8 +223,9 @@ <target name="assemble-plugins" depends="assemble-projects,openjdk-annotate"> <!-- Now assemble all plugins --> <taskdef name="plugin" classname="org.jnode.build.PluginTask" classpathref="cp-jnode"/> - <plugin todir="${plugins.dir}" tmpdir="${build.dir}/tmp/plugins" pluginDir="${descriptors.dir}" - userApplicationsDir="${user.applications.dir}" userApplicationsProperty="userApplications"> + <plugin todir="${plugins.dir}" tmpdir="${build.dir}/tmp/plugins" pluginDir="${descriptors.dir}"> + <packager userApplicationsDir="${user.applications.dir}" pathRefId="cp"/> + <libalias name="jnode-core.jar" alias="${jnode-core.jar}"/> <libalias name="jnode-distr.jar" alias="${jnode-distr.jar}"/> <libalias name="jnode-fs.jar" alias="${jnode-fs.jar}"/> @@ -295,8 +296,9 @@ <taskdef name="initjars" classname="org.jnode.build.InitJarsBuilder" classpathref="cp-jnode"/> <initjars destdir="${initjars.dir}" pluginDir="${plugins.dir}" - systemPluginList="${basedir}/conf/system-plugin-list.xml" - userPlugins="${userApplications}"> + systemPluginList="${basedir}/conf/system-plugin-list.xml"> + <insert userApplicationsDir="${user.applications.dir}"/> + <fileset dir="${basedir}/conf"> <exclude name="system-plugin-list.xml"/> <include name="*plugin-list.xml"/> Modified: trunk/builder/build.xml =================================================================== --- trunk/builder/build.xml 2008-08-31 16:58:22 UTC (rev 4521) +++ trunk/builder/build.xml 2008-08-31 18:12:16 UTC (rev 4522) @@ -26,6 +26,10 @@ <mkdir dir="${my-classes.dir}"/> <mkdir dir="${jnasm-preprocessor-gen.dir}"/> <mkdir dir="${jnasm-assembler-gen.dir}"/> + + <copy todir="${my-classes.dir}"> + <fileset dir="${my-src.dir}/builder" includes="**/*.properties"/> + </copy> </target> <!-- Compile the Template ANT task needed for compiling the core --> Modified: trunk/builder/src/builder/org/jnode/build/AbstractPluginsTask.java =================================================================== --- trunk/builder/src/builder/org/jnode/build/AbstractPluginsTask.java 2008-08-31 16:58:22 UTC (rev 4521) +++ trunk/builder/src/builder/org/jnode/build/AbstractPluginsTask.java 2008-08-31 18:12:16 UTC (rev 4522) @@ -51,7 +51,7 @@ * @throws PluginException * @throws MalformedURLException */ - protected PluginList getPluginList() throws PluginException, + public PluginList getPluginList() throws PluginException, MalformedURLException { if (pluginList == null) { pluginList = new PluginList(pluginListFile, pluginDir, targetArch); Modified: trunk/builder/src/builder/org/jnode/build/InitJarBuilder.java =================================================================== --- trunk/builder/src/builder/org/jnode/build/InitJarBuilder.java 2008-08-31 16:58:22 UTC (rev 4521) +++ trunk/builder/src/builder/org/jnode/build/InitJarBuilder.java 2008-08-31 18:12:16 UTC (rev 4522) @@ -29,11 +29,13 @@ import java.util.Iterator; import java.util.List; import java.util.Set; + import org.apache.tools.ant.Project; import org.apache.tools.ant.taskdefs.GZip; import org.apache.tools.ant.taskdefs.Jar; import org.apache.tools.ant.taskdefs.Manifest; import org.apache.tools.ant.types.FileSet; +import org.jnode.build.packager.PluginListInsertor; import org.jnode.plugin.PluginDescriptor; import org.jnode.plugin.PluginException; import org.jnode.plugin.PluginPrerequisite; @@ -48,7 +50,7 @@ private File destFile; - private String userPlugins; + private PluginListInsertor insertor; public void execute() throws BuildException { @@ -59,11 +61,11 @@ final long lmPI; try { piList = getPluginList(); - - if ((userPlugins != null) && !userPlugins.isEmpty()) { - piList.processUserPlugins(userPlugins); + + if (insertor != null) { + insertor.insertInto(piList); } - + systemPluginList = getSystemPluginList(); if ((destFile == null) && (destDir != null)) { destFile = new File(destDir, piList.getName() + ".jgz"); @@ -289,7 +291,7 @@ this.destDir = destDir; } - public void setUserPlugins(String userPlugins) { - this.userPlugins = userPlugins; + public void setPackager(PluginListInsertor insertor) { + this.insertor = insertor; } } Modified: trunk/builder/src/builder/org/jnode/build/InitJarsBuilder.java =================================================================== --- trunk/builder/src/builder/org/jnode/build/InitJarsBuilder.java 2008-08-31 16:58:22 UTC (rev 4521) +++ trunk/builder/src/builder/org/jnode/build/InitJarsBuilder.java 2008-08-31 18:12:16 UTC (rev 4522) @@ -23,10 +23,12 @@ import java.io.File; import java.util.ArrayList; + import org.apache.tools.ant.BuildException; import org.apache.tools.ant.DirectoryScanner; import org.apache.tools.ant.Task; import org.apache.tools.ant.types.FileSet; +import org.jnode.build.packager.PluginListInsertor; /** * Task used to build several initjars. @@ -43,7 +45,7 @@ private File pluginDir; private File systemPluginListFile; - private String userPlugins; + private PluginListInsertor insertor; /** * Add a fileset to this task. @@ -75,9 +77,8 @@ builder.setPluginList(listFile); builder.setDestDir(getDestDir()); - // FIXME we should put the plugin list ("full-plugin-list.xml") outside - if (listFiles[j].equals("full-plugin-list.xml") && (userPlugins != null)) { - builder.setUserPlugins(userPlugins); + if (insertor != null) { + builder.setPackager(insertor); } builder.execute(); @@ -128,7 +129,9 @@ this.systemPluginListFile = systemPluginListFile; } - public void setUserPlugins(String userPlugins) { - this.userPlugins = userPlugins; + public PluginListInsertor createInsert() { + insertor = new PluginListInsertor(); + insertor.setProject(getProject()); + return insertor; } } Modified: trunk/builder/src/builder/org/jnode/build/PluginList.java =================================================================== --- trunk/builder/src/builder/org/jnode/build/PluginList.java 2008-08-31 16:58:22 UTC (rev 4521) +++ trunk/builder/src/builder/org/jnode/build/PluginList.java 2008-08-31 18:12:16 UTC (rev 4522) @@ -104,6 +104,10 @@ } } + public void addPlugin(String id) throws MalformedURLException, PluginException { + addPlugin(descrList, pluginList, id); + } + private void addPlugin(List<URL> descrList, List<URL> pluginList, String id) throws MalformedURLException, PluginException { final File f = findPlugin(defaultDir, id); @@ -245,17 +249,4 @@ throw new RuntimeException(e); } } - - /** - * Add user plugins to the list - * @param userPlugins - * @throws MalformedURLException - * @throws PluginException - */ - public void processUserPlugins(String userPlugins) throws MalformedURLException, PluginException { - for (String pluginId : userPlugins.split(",")) { - System.out.println("Adding user plugin " + pluginId); - addPlugin(descrList, pluginList, pluginId); - } - } } Modified: trunk/builder/src/builder/org/jnode/build/PluginTask.java =================================================================== --- trunk/builder/src/builder/org/jnode/build/PluginTask.java 2008-08-31 16:58:22 UTC (rev 4521) +++ trunk/builder/src/builder/org/jnode/build/PluginTask.java 2008-08-31 18:12:16 UTC (rev 4522) @@ -22,29 +22,14 @@ package org.jnode.build; import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileReader; -import java.io.FilenameFilter; import java.io.IOException; -import java.io.InputStream; -import java.io.PrintStream; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Enumeration; import java.util.HashMap; import java.util.LinkedList; -import java.util.List; import java.util.Map; import java.util.concurrent.ArrayBlockingQueue; -import java.util.concurrent.ExecutorService; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; -import java.util.jar.Attributes; -import java.util.jar.JarEntry; -import java.util.jar.JarFile; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.DirectoryScanner; @@ -52,11 +37,9 @@ import org.apache.tools.ant.taskdefs.Manifest; import org.apache.tools.ant.taskdefs.ManifestException; import org.apache.tools.ant.types.FileSet; -import org.apache.tools.ant.types.Path; import org.apache.tools.ant.types.ZipFileSet; import org.apache.tools.ant.util.FileUtils; -import org.jnode.nanoxml.XMLElement; -import org.jnode.nanoxml.XMLParseException; +import org.jnode.build.packager.PluginBuilder; import org.jnode.plugin.Library; import org.jnode.plugin.PluginDescriptor; import org.jnode.plugin.Runtime; @@ -70,8 +53,7 @@ private File todir; private File tmpDir = new File(System.getProperty("java.io.tmpdir")); - private File userApplicationsDir; - private String userApplicationsProperty; + private PluginBuilder packager; public ZipFileSet createDescriptors() { final ZipFileSet fs = new ZipFileSet(); @@ -102,7 +84,6 @@ int max_thread_count = 10; int max_plugin_count = 500; - StringBuilder userPlugins = new StringBuilder(); final AtomicBoolean failure = new AtomicBoolean(false); ThreadPoolExecutor executor = new ThreadPoolExecutor(max_thread_count, max_thread_count, 60, TimeUnit.SECONDS, @@ -127,19 +108,9 @@ }); } } - if ((userApplicationsDir != null) && userApplicationsDir.exists() && userApplicationsDir.isDirectory()) { - File[] userJars = userApplicationsDir.listFiles(new FilenameFilter() { - @Override - public boolean accept(File dir, String name) { - return name.endsWith(".jar"); - } - - }); - - for (File userJar : userJars) { - processUserJar(executor, descriptors, userJar, userPlugins); - } + if (packager != null) { + packager.execute(executor, descriptors); } executor.shutdown(); @@ -153,240 +124,18 @@ throw new RuntimeException("At least one plugin task failed : see above errors"); } - // that must be called after completion of all plugin tasks - if ((userPlugins.length() > 0) && (userPlugins.charAt(userPlugins.length() - 1) == ',')) { - userPlugins.deleteCharAt(userPlugins.length() - 1); - } - getProject().setProperty(userApplicationsProperty, userPlugins.toString()); - } - - /** - * 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) { - executor.execute(new Runnable() { - public void run() { - final String jarName = userJar.getName(); - final String pluginId = jarName.substring(0, jarName.length() - 4); // remove ".jar" - - userPluginList.append(pluginId + ","); - - // replace ".jar" by ".xml" - final String pluginDesc = pluginId + ".xml"; - - // FIXME remove the explicit reference to "cp" - // add user jar to path named "cp" (used in build.xml) - Path path = (Path) getProject().getReference("cp"); - path.createPathElement().setLocation(userJar); - - // create the lib alias - final String alias = pluginId + ".jar"; - LibAlias libAlias = 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); - } - - buildPlugin(descriptors, descriptorFile); - } - }); - } - - 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(" license-name=\"unspecified\">"); - - out.println(" <runtime>"); - out.println(" <library name=\"" + alias + "\">"); - out.println(" <export name=\"*\"/>"); - out.println(" </library>"); - out.println(" </runtime>"); - - List<String> mainClasses = 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 + "\"/>"); - System.out.println(pluginId + " : added alias " + name + " for class " + mainClass); - } - - out.println(" </extension>"); - } else { - System.err.println("WARNING : no main found for plugin " + pluginId); - } - - // FIXME using AllPermission is bad ! we must avoid that - 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(ioe); - } finally { - if (out != null) { - out.close(); - } - - if (!success) { - // in case of failure, remove the incomplete descriptor file - descriptorFile.delete(); - } + if (packager != null) { + // that must be called after completion of all plugin tasks + packager.finish(); } } - - private List<String> searchMain(File userJar) throws FileNotFoundException, IOException { - List<String> mainList = new ArrayList<String>(); - - JarFile jarFile = null; - try { - jarFile = new JarFile(userJar); - - // try to find the main class from the manifest - Object value = null; - - // do we have a manifest ? - if (jarFile.getManifest() != null) { - value = jarFile.getManifest().getMainAttributes().get(Attributes.Name.MAIN_CLASS); - if (value == null) { - String name = Attributes.Name.MAIN_CLASS.toString(); - final Attributes attr = jarFile.getManifest().getAttributes(name); - - // we have a manifest but do we have a main class defined inside ? - if (attr != null) { - value = attr.get(Attributes.Name.MAIN_CLASS); - } - } - } - - if (value != null) { - mainList.add(String.valueOf(value)); - } else { - // scan the jar to find the main classes - for (Enumeration<JarEntry> e = jarFile.entries(); e.hasMoreElements(); ) { - final JarEntry entry = e.nextElement(); - final String name = entry.getName(); - InputStream is = null; - - try { - if (name.endsWith(".class")) { - String className = name.substring(0, name.length() - ".class".length()); - className = className.replace('/', '.'); - is = jarFile.getInputStream(entry); - ClassLoader cl = new InputStreamLoader(is, (int) entry.getSize()); - Class<?> clazz = Class.forName(className, false, cl); - Method m = clazz.getMethod("main", new Class<?>[]{String[].class}); - if ((m.getModifiers() & Modifier.STATIC) == Modifier.STATIC) { - mainList.add(className); - } - } - } catch (ClassNotFoundException cnfe) { - cnfe.printStackTrace(); - // ignore - } catch (SecurityException se) { - se.printStackTrace(); - // ignore - } catch (NoSuchMethodException nsme) { - // such error is expected for non-main classes => ignore - } catch (Throwable t) { - t.printStackTrace(); - // ignore - } finally { - if (is != null) { - is.close(); - } - } - } - } - } finally { - if (jarFile != null) { - jarFile.close(); - } - } - - return mainList; - } - - private static class InputStreamLoader extends ClassLoader { - private InputStream inputStream; - private int size; - - public InputStreamLoader(InputStream inputStream, int size) { - this.inputStream = inputStream; - this.size = size; - } - - public Class loadClass(String className) throws ClassNotFoundException { - return loadClass(className, true); - } - - public synchronized Class loadClass(String className, boolean resolve) - throws ClassNotFoundException { - Class<?> result; - - try { - - result = super.findSystemClass(className); - - } catch (ClassNotFoundException e) { - byte[] classData = null; - - try { - classData = new byte[size]; - inputStream.read(classData); - } catch (IOException ioe) { - throw new ClassNotFoundException(className, ioe); - } - - if (classData == null) { - throw new ClassNotFoundException(className); - } - - result = defineClass(className, classData, 0, classData.length); - - if (result == null) { - throw new ClassFormatError(); - } - - if (resolve) { - resolveClass(result); - } - } - - return result; - } - } - - /** * @param descriptors map of fullPluginId to File descriptor * @param descriptor the plugin descriptor XML * @throws BuildException on failure */ - protected void buildPlugin(Map<String, File> descriptors, File descriptor) throws BuildException { + public void buildPlugin(Map<String, File> descriptors, File descriptor) throws BuildException { final PluginDescriptor descr = readDescriptor(descriptor); final String fullId = descr.getId() + "_" + descr.getVersion(); @@ -457,18 +206,6 @@ return mf; } - protected void addResourceList(File pluginDescr, Collection<ZipFileSet> resources) - throws XMLParseException, FileNotFoundException, IOException { - final XMLElement xml = new XMLElement(); - xml.parseFromReader(new FileReader(pluginDescr)); - -// XMLElement runtime = xml.g - - } - -// private final XMLElement getRuntimeElement(XMLElement xml) { -// } - /** * @return The destination directory */ @@ -497,17 +234,8 @@ this.tmpDir = tmpDir; } - /** - * @param file - */ - public void setUserApplicationsDir(File file) { - userApplicationsDir = file; + public PluginBuilder createPackager() { + packager = new PluginBuilder(this); + return packager; } - - /** - * @param file - */ - public void setUserApplicationsProperty(String name) { - userApplicationsProperty = name; - } } Added: trunk/builder/src/builder/org/jnode/build/packager/PackagerTask.java =================================================================== --- trunk/builder/src/builder/org/jnode/build/packager/PackagerTask.java (rev 0) +++ trunk/builder/src/builder/org/jnode/build/packager/PackagerTask.java 2008-08-31 18:12:16 UTC (rev 4522) @@ -0,0 +1,111 @@ +package org.jnode.build.packager; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.util.Properties; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Task; + +/** + * Abstract class used for common stuff among packager tasks + * + * @author fabien + * + */ +public class PackagerTask extends Task { + protected static final String PROPERTIES_FILE = "plugins.properties"; + + /** + * The default properties for the packager tool + */ + private static final Properties DEFAULT_PROPERTIES; + + static { + Properties props; + try { + props = readProperties(PackagerTask.class.getResourceAsStream(PROPERTIES_FILE), null); + } catch (Throwable t) { + t.printStackTrace(); + props = new Properties(); + } + DEFAULT_PROPERTIES = props; + } + + // properties names + protected static final String USER_PLUGIN_IDS = "user.plugin.ids"; + protected static final String PLUGIN_LIST_NAME = "plugin.list.name"; + + /** + * Directory for suer plugins/resources + */ + protected File userApplicationsDir = null; + + /** + * Define the directory where user put its own plugins/resources to add in jnode cdrom + * @param file + */ + public final void setUserApplicationsDir(File file) { + if ((file != null) && file.exists() && file.isDirectory()) { + userApplicationsDir = file; + } else { + userApplicationsDir = null; + } + } + + /** + * Is that task enabled ? + * @return + */ + protected final boolean isEnabled() { + return (userApplicationsDir != null); + } + + /** + * Get properties file used to configure the packager tool + * @return + */ + protected final File getPropertiesFile() { + return isEnabled() ? new File(userApplicationsDir, PROPERTIES_FILE) : null; + } + + /** + * Read the properties file used to configure the packager tool + * @return + */ + protected final Properties readProperties() { + try { + return readProperties(new FileInputStream(getPropertiesFile()), DEFAULT_PROPERTIES); + } catch (FileNotFoundException e) { + throw new BuildException("failed to read properties file", e); + } + } + + /** + * Read the properties from the given {@link InputStream} + * + * @param input + * @param defaultProps + * @return + */ + private static final Properties readProperties(InputStream input, Properties defaultProps) { + Properties properties = (defaultProps == null) ? new Properties() : new Properties(defaultProps); + + try { + properties.load(input); + } catch (IOException ioe) { + throw new BuildException("failed to read properties file", ioe); + } finally { + try { + input.close(); + } catch (IOException ioe) { + throw new BuildException("failed to close input stream", ioe); + } + } + + return properties; + } +} Added: trunk/builder/src/builder/org/jnode/build/packager/PluginBuilder.java =================================================================== --- trunk/builder/src/builder/org/jnode/build/packager/PluginBuilder.java (rev 0) +++ trunk/builder/src/builder/org/jnode/build/packager/PluginBuilder.java 2008-08-31 18:12:16 UTC (rev 4522) @@ -0,0 +1,369 @@ +package org.jnode.build.packager; + +import java.io.File; +import java.io.FileFilter; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.PrintStream; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.jar.Attributes; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; + +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; + + 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 = new Properties(); + properties.put(USER_PLUGIN_IDS, userPluginIds.toString()); + FileOutputStream fos = null; + try { + fos = new FileOutputStream(getPropertiesFile()); + properties.store(fos, ""); + } 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); + } + + 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 = 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, remove the incomplete descriptor file + descriptorFile.delete(); + } + } + } + + /** + * Search for the main classes in the jars/resources. + * Starts by looking in the jars manifests and, if nothing is found, + * then scans the jars/resources for main classes. + * + * @param userJar + * @return + * @throws FileNotFoundException + * @throws IOException + */ + private List<String> searchMain(File userJar) throws FileNotFoundException, IOException { + List<String> mainList = new ArrayList<String>(); + + JarFile jarFile = null; + try { + jarFile = new JarFile(userJar); + + // try to find the main class from the manifest + Object value = null; + + // do we have a manifest ? + if (jarFile.getManifest() != null) { + value = jarFile.getManifest().getMainAttributes().get(Attributes.Name.MAIN_CLASS); + if (value == null) { + String name = Attributes.Name.MAIN_CLASS.toString(); + final Attributes attr = jarFile.getManifest().getAttributes(name); + + // we have a manifest but do we have a main class defined inside ? + if (attr != null) { + value = attr.get(Attributes.Name.MAIN_CLASS); + } + } + } + + if (value != null) { + mainList.add(String.valueOf(value)); + } else { + // scan the jar to find the main classes + for (Enumeration<JarEntry> e = jarFile.entries(); e.hasMoreElements(); ) { + final JarEntry entry = e.nextElement(); + final String name = entry.getName(); + InputStream is = null; + + try { + if (name.endsWith(".class")) { + String className = name.substring(0, name.length() - ".class".length()); + className = className.replace('/', '.'); + + is = jarFile.getInputStream(entry); + ClassLoader cl = new InputStreamLoader(is, (int) entry.getSize()); + Class<?> clazz = Class.forName(className, false, cl); + Method m = clazz.getMethod("main", new Class<?>[]{String[].class}); + if ((m.getModifiers() & Modifier.STATIC) == Modifier.STATIC) { + mainList.add(className); + } + } + } catch (ClassNotFoundException cnfe) { + cnfe.printStackTrace(); + // ignore + } catch (SecurityException se) { + se.printStackTrace(); + // ignore + } catch (NoSuchMethodException nsme) { + // such error is expected for non-main classes => ignore + } catch (Throwable t) { + t.printStackTrace(); + // ignore + } finally { + if (is != null) { + is.close(); + } + } + } + } + } finally { + if (jarFile != null) { + jarFile.close(); + } + } + + return mainList; + } + + /** + * Custom {@link ClassLoader} used to load classes from an InputStream. + * It helps finding a main class in a jar file. + * @author fabien + * + */ + private static class InputStreamLoader extends ClassLoader { + private InputStream inputStream; + private int size; + + public InputStreamLoader(InputStream inputStream, int size) { + this.inputStream = inputStream; + this.size = size; + } + + public Class loadClass(String className) throws ClassNotFoundException { + return loadClass(className, true); + } + + public synchronized Class loadClass(String className, boolean resolve) + throws ClassNotFoundException { + Class<?> result; + + try { + + result = super.findSystemClass(className); + + } catch (ClassNotFoundException e) { + byte[] classData = null; + + try { + classData = new byte[size]; + inputStream.read(classData); + } catch (IOException ioe) { + throw new ClassNotFoundException(className, ioe); + } + + if (classData == null) { + throw new ClassNotFoundException(className); + } + + result = defineClass(className, classData, 0, classData.length); + + if (result == null) { + throw new ClassFormatError(); + } + + if (resolve) { + resolveClass(result); + } + } + + return result; + } + } +} Added: trunk/builder/src/builder/org/jnode/build/packager/PluginListInsertor.java =================================================================== --- trunk/builder/src/builder/org/jnode/build/packager/PluginListInsertor.java (rev 0) +++ trunk/builder/src/builder/org/jnode/build/packager/PluginListInsertor.java 2008-08-31 18:12:16 UTC (rev 4522) @@ -0,0 +1,66 @@ +package org.jnode.build.packager; + +import java.net.MalformedURLException; +import java.util.ArrayList; +import java.util.List; +import java.util.Properties; + +import org.apache.tools.ant.Project; +import org.jnode.build.PluginList; +import org.jnode.plugin.PluginException; + +/** + * Task that insert the user plugins into a plugin list + * without actually modifying the plugin list files. + * + * @author fabien + * + */ +public class PluginListInsertor extends PackagerTask { + + /** + * Main method of the task. + * + * @param list + * @throws MalformedURLException + * @throws PluginException + */ + public void insertInto(final PluginList list) throws MalformedURLException, PluginException { + if (isEnabled()) { + for (String pluginId : readPluginIds(list.getName())) { + log("Adding user plugin " + pluginId, Project.MSG_INFO); + list.addPlugin(pluginId); + } + } + } + + /** + * Read the user plugins ids from the properties file + * @param pluginListName + * @return + */ + private List<String> readPluginIds(String pluginListName) { + List<String> pluginIds = new ArrayList<String>(); + + final Properties properties = readProperties(); + final String targetName = properties.getProperty(PLUGIN_LIST_NAME, null); + if (targetName == null) { + log("property " + PLUGIN_LIST_NAME + " not specified in " + + getPropertiesFile().getAbsolutePath(), Project.MSG_ERR); + } else { + if (targetName.equals(pluginListName)) { + final String ids = properties.getProperty(USER_PLUGIN_IDS, null); + if ((ids == null) || ids.trim().isEmpty()) { + log("property " + USER_PLUGIN_IDS + " not specified in " + + getPropertiesFile().getAbsolutePath(), Project.MSG_ERR); + } else { + for (String pluginId : ids.split(",")) { + pluginIds.add(pluginId); + } + } + } + } + + return pluginIds; + } +} Added: trunk/builder/src/builder/org/jnode/build/packager/plugins.properties =================================================================== --- trunk/builder/src/builder/org/jnode/build/packager/plugins.properties (rev 0) +++ trunk/builder/src/builder/org/jnode/build/packager/plugins.properties 2008-08-31 18:12:16 UTC (rev 4522) @@ -0,0 +1,4 @@ +# +# Default values for the packager +# +plugin.list.name=full This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |