|
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.
|