From: <fd...@us...> - 2008-08-24 19:35:03
|
Revision: 4497 http://jnode.svn.sourceforge.net/jnode/?rev=4497&view=rev Author: fduminy Date: 2008-08-24 19:34:59 +0000 (Sun, 24 Aug 2008) Log Message: ----------- first version of jar packager tool. It allows easy testing of third party applications Modified Paths: -------------- trunk/all/build.xml 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 Modified: trunk/all/build.xml =================================================================== --- trunk/all/build.xml 2008-08-24 16:08:36 UTC (rev 4496) +++ trunk/all/build.xml 2008-08-24 19:34:59 UTC (rev 4497) @@ -223,7 +223,8 @@ <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}"> + <plugin todir="${plugins.dir}" tmpdir="${build.dir}/tmp/plugins" pluginDir="${descriptors.dir}" + userApplicationsDir="${user.applications.dir}" userApplicationsProperty="userApplications"> <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}"/> @@ -294,7 +295,8 @@ <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"> + systemPluginList="${basedir}/conf/system-plugin-list.xml" + userPlugins="${userApplications}"> <fileset dir="${basedir}/conf"> <exclude name="system-plugin-list.xml"/> <include name="*plugin-list.xml"/> Modified: trunk/builder/src/builder/org/jnode/build/InitJarBuilder.java =================================================================== --- trunk/builder/src/builder/org/jnode/build/InitJarBuilder.java 2008-08-24 16:08:36 UTC (rev 4496) +++ trunk/builder/src/builder/org/jnode/build/InitJarBuilder.java 2008-08-24 19:34:59 UTC (rev 4497) @@ -47,6 +47,8 @@ private File destDir; private File destFile; + + private String userPlugins; public void execute() throws BuildException { @@ -57,6 +59,11 @@ final long lmPI; try { piList = getPluginList(); + + if ((userPlugins != null) && !userPlugins.isEmpty()) { + piList.processUserPlugins(userPlugins); + } + systemPluginList = getSystemPluginList(); if ((destFile == null) && (destDir != null)) { destFile = new File(destDir, piList.getName() + ".jgz"); @@ -281,4 +288,8 @@ public final void setDestDir(File destDir) { this.destDir = destDir; } + + public void setUserPlugins(String userPlugins) { + this.userPlugins = userPlugins; + } } Modified: trunk/builder/src/builder/org/jnode/build/InitJarsBuilder.java =================================================================== --- trunk/builder/src/builder/org/jnode/build/InitJarsBuilder.java 2008-08-24 16:08:36 UTC (rev 4496) +++ trunk/builder/src/builder/org/jnode/build/InitJarsBuilder.java 2008-08-24 19:34:59 UTC (rev 4497) @@ -42,7 +42,9 @@ private final ArrayList<FileSet> fileSets = new ArrayList<FileSet>(); private File pluginDir; private File systemPluginListFile; - + + private String userPlugins; + /** * Add a fileset to this task. * @@ -72,6 +74,11 @@ builder.setSystemPluginList(systemPluginListFile); 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); + } builder.execute(); } @@ -120,5 +127,8 @@ public final void setSystemPluginList(File systemPluginListFile) { this.systemPluginListFile = systemPluginListFile; } - + + public void setUserPlugins(String userPlugins) { + this.userPlugins = userPlugins; + } } Modified: trunk/builder/src/builder/org/jnode/build/PluginList.java =================================================================== --- trunk/builder/src/builder/org/jnode/build/PluginList.java 2008-08-24 16:08:36 UTC (rev 4496) +++ trunk/builder/src/builder/org/jnode/build/PluginList.java 2008-08-24 19:34:59 UTC (rev 4497) @@ -33,9 +33,10 @@ import java.util.Hashtable; import java.util.Iterator; import java.util.List; -import org.jnode.nanoxml.XMLElement; + import org.apache.tools.ant.taskdefs.Manifest; import org.apache.tools.ant.taskdefs.ManifestException; +import org.jnode.nanoxml.XMLElement; import org.jnode.plugin.PluginException; /** @@ -43,12 +44,12 @@ */ public final class PluginList { - private final URL[] descrList; + private final List<URL> descrList; - private final URL[] pluginList; + private final List<URL> pluginList; private final String name; - + private Manifest manifest; private List<PluginList> includes = new ArrayList<PluginList>(); @@ -58,8 +59,8 @@ public PluginList(File file, File defaultDir, String targetArch) throws PluginException, MalformedURLException { this.defaultDir = defaultDir; - final ArrayList<URL> descrList = new ArrayList<URL>(); - final ArrayList<URL> pluginList = new ArrayList<URL>(); + descrList = new ArrayList<URL>(); + pluginList = new ArrayList<URL>(); final XMLElement root = new XMLElement(new Hashtable(), true, false); try { final FileReader r = new FileReader(file); @@ -86,24 +87,12 @@ if (e.getName().equals("plugin")) { final String id = e.getStringAttribute("id"); - final URL descrUrl; - final URL pluginUrl; - if (id != null) { - File f = findPlugin(defaultDir, id); - pluginUrl = f.toURL(); - descrUrl = new URL("jar:" + pluginUrl + "!/plugin.xml"); - } else { + if (id == null) { throw new PluginException("id attribute expected on " + e.getName()); } - if (pluginList.contains(pluginUrl)) { - throw new PluginException("can't use the same id(" + id - + ") for multiple plugins"); - } - - descrList.add(descrUrl); - pluginList.add(pluginUrl); + addPlugin(descrList, pluginList, id); } else if (e.getName().equals("manifest")) { manifest = parseManifest(e); } else if (e.getName().equals("include")) { @@ -113,11 +102,22 @@ throw new PluginException("Unknown element " + e.getName()); } } - this.descrList = descrList.toArray(new URL[descrList.size()]); - this.pluginList = pluginList - .toArray(new URL[pluginList.size()]); } + + private void addPlugin(List<URL> descrList, List<URL> pluginList, String id) + throws MalformedURLException, PluginException { + final File f = findPlugin(defaultDir, id); + final URL pluginUrl = f.toURL(); + final URL descrUrl = new URL("jar:" + pluginUrl + "!/plugin.xml"); + if (pluginList.contains(pluginUrl)) { + throw new PluginException("can't use the same id(" + id + ") for multiple plugins"); + } + + descrList.add(descrUrl); + pluginList.add(pluginUrl); + } + private File findPlugin(File dir, final String id) { // System.out.println("Find " + id + " in " + dir); String[] names = dir.list(new FilenameFilter() { @@ -181,8 +181,8 @@ } } - descrList.addAll(Arrays.asList(inc.descrList)); - pluginList.addAll(Arrays.asList(inc.pluginList)); + descrList.addAll(inc.descrList); + pluginList.addAll(inc.pluginList); } /** @@ -193,8 +193,8 @@ */ public long lastModified() throws IOException { long max = 0; - for (int i = 0; i < descrList.length; i++) { - final URLConnection conn2 = pluginList[i].openConnection(); + for (URL url : descrList) { + final URLConnection conn2 = url.openConnection(); max = Math.max(max, conn2.getLastModified()); } for (PluginList inc : includes) { @@ -209,7 +209,7 @@ * @return URL[] */ public URL[] getDescriptorUrlList() { - return descrList; + return descrList.toArray(new URL[descrList.size()]); } /** @@ -218,7 +218,7 @@ * @return URL[] */ public URL[] getPluginList() { - return pluginList; + return pluginList.toArray(new URL[pluginList.size()]); } /** @@ -245,4 +245,17 @@ 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-24 16:08:36 UTC (rev 4496) +++ trunk/builder/src/builder/org/jnode/build/PluginTask.java 2008-08-24 19:34:59 UTC (rev 4497) @@ -24,15 +24,27 @@ 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; @@ -40,6 +52,7 @@ 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; @@ -57,6 +70,9 @@ private File todir; private File tmpDir = new File(System.getProperty("java.io.tmpdir")); + private File userApplicationsDir; + private String userApplicationsProperty; + public ZipFileSet createDescriptors() { final ZipFileSet fs = new ZipFileSet(); descriptorSets.add(fs); @@ -86,6 +102,7 @@ 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, @@ -93,7 +110,6 @@ @Override protected void afterExecute(Runnable r, Throwable t) { if (t != null) { - // at least one plugin task failed failure.set(true); } } @@ -111,6 +127,21 @@ }); } } + 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); + } + } + executor.shutdown(); try { executor.awaitTermination(10, TimeUnit.MINUTES); @@ -121,8 +152,218 @@ if (failure.get()) { 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; + 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>"); + } catch (IOException ioe) { + throw new BuildException(ioe); + } finally { + if (out != null) { + out.close(); + } + } + } + + 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 = jarFile.getManifest().getMainAttributes().get(Attributes.Name.MAIN_CLASS); + if (value == null) { + String name = Attributes.Name.MAIN_CLASS.toString(); + value = jarFile.getManifest().getAttributes(name).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 @@ -239,4 +480,17 @@ this.tmpDir = tmpDir; } + /** + * @param file + */ + public void setUserApplicationsDir(File file) { + userApplicationsDir = file; + } + + /** + * @param file + */ + public void setUserApplicationsProperty(String name) { + userApplicationsProperty = name; + } } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |