remm 2005/03/02 10:30:45 Modified: . build.xml catalina/etc bootstrap.MF Added: juli/src/conf logging.properties juli/src/java/org/apache/juli ClassLoaderLogManager.java FileHandler.java juli build.xml Log: - Add JULI, a Java Util Logging Implementation. - It supports per classloader configuration, using standard JDK properties files, with optional extensions to be able to flexibly assign handlers to loggers. - The package includes a rotating handler, since zillions of people have lamented its departure in 5.5.x (even if it didn't contain anything useful anymore). - This builds a small JAR, which is then added to the classpath by bootstrap.jar. - I may need to fix packaging a little after that change; sorry for the trouble. - I'll add comments and docs soon. - This is based on code subimmted by Lachlan O'Dea in bug 33143, and is likely to move to commons (once I get comfortable enough with svn). Revision Changes Path 1.1 jakarta-tomcat-connectors/juli/src/conf/logging.properties Index: logging.properties =================================================================== handlers = org.apache.juli.FileHandler java.util.logging.ConsoleHandler ############################################################ # Handler specific properties. # Describes specific configuration info for Handlers. ############################################################ org.apache.juli.FileHandler.level = FINE org.apache.juli.FileHandler.directory = ${catalina.base}/logs java.util.logging.ConsoleHandler.level = FINE java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter ############################################################ # Facility specific properties. # Provides extra control for each logger. ############################################################ # For example, set the com.xyz.foo logger to only log SEVERE # messages: #org.apache.catalina.startup.ContextConfig.level = FINE #org.apache.catalina.startup.HostConfig.level = FINE #org.apache.catalina.session.ManagerBase.level = FINE 1.223 +23 -0 jakarta-tomcat-5/build.xml Index: build.xml =================================================================== RCS file: /home/cvs/jakarta-tomcat-5/build.xml,v retrieving revision 1.222 retrieving revision 1.223 diff -u -r1.222 -r1.223 --- build.xml 20 Jan 2005 12:00:20 -0000 1.222 +++ build.xml 2 Mar 2005 18:30:45 -0000 1.223 @@ -288,6 +288,27 @@ </target> + <target name="build-juli" + unless="tomcatjuli.build.notrequired" + depends="init" description="builds j-t-c/juli"> + <echo>========== Building: tomcat-juli </echo> + + <ant dir="${jtc.home}/juli" target="compile-only"> + <property name="build.home" value="${tomcat.build}"/> + </ant> + + <!-- Java.util.logging Implementation --> + <jar jarfile="${tomcat.build}/bin/tomcat-juli.jar" index="true"> + <fileset dir="${tomcat.build}/classes"> + <include name="org/apache/juli/**" /> + <!-- Javadoc and i18n exclusions --> + <exclude name="**/package.html" /> + <exclude name="**/LocalStrings_*" /> + </fileset> + </jar> + + </target> + <target name="build-jasper" unless="jasper.build.notrequired" depends="init" description="build jasper"> @@ -513,6 +534,8 @@ <antcall target="build-tomcathttp11"/> + <antcall target="build-juli"/> + <antcall target="build-jasper"/> <antcall target="build-i18n"/> 1.6 +1 -1 jakarta-tomcat-catalina/catalina/etc/bootstrap.MF Index: bootstrap.MF =================================================================== RCS file: /home/cvs/jakarta-tomcat-catalina/catalina/etc/bootstrap.MF,v retrieving revision 1.5 retrieving revision 1.6 diff -u -r1.5 -r1.6 --- bootstrap.MF 2 Mar 2004 12:32:19 -0000 1.5 +++ bootstrap.MF 2 Mar 2005 18:30:45 -0000 1.6 @@ -1,5 +1,5 @@ Manifest-Version: 1.0 Main-Class: org.apache.catalina.startup.Bootstrap -Class-Path: jmx.jar commons-daemon.jar commons-logging-api.jar +Class-Path: jmx.jar commons-daemon.jar commons-logging-api.jar tomcat-juli.jar Specification-Title: Catalina Specification-Version: 1.0 \ No newline at end of file 1.1 jakarta-tomcat-connectors/juli/src/java/org/apache/juli/ClassLoaderLogManager.java Index: ClassLoaderLogManager.java =================================================================== /* * Copyright 1999,2004 The Apache Software Foundation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.juli; import java.util.logging.*; import java.util.*; import java.io.IOException; import java.io.InputStream; import java.net.URLClassLoader; import java.security.AccessController; import java.security.PrivilegedAction; /** * Per classloader LogManager implementation. */ public final class ClassLoaderLogManager extends LogManager { private static void doSetParentLogger(final Logger logger, final Logger parent) { if (System.getSecurityManager() != null) { AccessController.doPrivileged(new PrivilegedAction() { public Object run() { logger.setParent(parent); return null; } }); } else { logger.setParent(parent); } } private final Map classLoaderLoggers = new WeakHashMap(); private Logger rootLogger; private Logger globalLogger; private ThreadLocal prefix = new ThreadLocal(); public synchronized boolean addLogger(final Logger logger) { final String loggerName = logger.getName(); if ("".equals(loggerName)) { final boolean unset = rootLogger == null; if (unset) { rootLogger = logger; if (globalLogger != null) { doSetParentLogger(globalLogger, rootLogger); } } return unset; } if ("global".equals(loggerName)) { final boolean unset = globalLogger == null; if (unset) { globalLogger = logger; if (rootLogger != null) { doSetParentLogger(globalLogger, rootLogger); } } return unset; } final ClassLoader classLoader = Thread.currentThread() .getContextClassLoader(); final ClassLoaderLogInfo info = getClassLoaderInfo(classLoader); if (info.loggers.containsKey(loggerName)) { return false; } info.loggers.put(loggerName, logger); // apply initial level for new logger final String levelString = getProperty(loggerName + ".level"); final Level level; if (levelString != null) { Level parsedLevel = null; try { parsedLevel = Level.parse(levelString.trim()); } catch (IllegalArgumentException e) { // leave level set to null } level = parsedLevel; } else { level = null; } if (level != null) { if (System.getSecurityManager() == null) { logger.setLevel(level); } else { AccessController.doPrivileged(new PrivilegedAction() { public Object run() { logger.setLevel(level); return null; } }); } } // if any parent loggers have levels definied, make sure they are // instantiated int dotIndex = loggerName.lastIndexOf('.'); while (dotIndex >= 0) { final String parentName = loggerName.substring(0, dotIndex); if (getProperty(parentName + ".level") != null) { Logger.getLogger(parentName); break; } dotIndex = loggerName.lastIndexOf('.', dotIndex - 1); } // find node final LogNode node = info.rootNode.findNode(loggerName); node.logger = logger; // set parent logger final Logger parentLogger = node.findParentLogger(); if (parentLogger != null) { doSetParentLogger(logger, parentLogger); } // tell children we are their new parent node.setParentLogger(logger); // Add associated handlers, if any are defined using the .handlers property. // In this case, handlers of the parent logger(s) will not be used final String handlers = getProperty(loggerName + ".handlers"); if (handlers != null) { logger.setUseParentHandlers(false); StringTokenizer tok = new StringTokenizer(handlers); while (tok.hasMoreTokens()) { String handlerName = (tok.nextToken()); Handler handler = (Handler) info.handlers.get(handlerName); if (handler != null) { logger.addHandler(handler); } } } return true; } public synchronized Logger getLogger(final String name) { if (rootLogger == null && globalLogger == null) { // this ends up being called during initialization, we don't // want do anything unless the root logger has been set up. return null; } if (name == null || name.length() == 0) { return rootLogger; } if ("global".equals(name)) { return globalLogger; } final ClassLoader classLoader = Thread.currentThread() .getContextClassLoader(); final Map loggers = getClassLoaderInfo(classLoader).loggers; /* * Debug: display handler list Logger logger = (Logger) loggers.get(name); System.out.println(name); if (logger != null) { Handler[] handlers = logger.getHandlers(); for (int i = 0; i < handlers.length; i++) { System.out.println("H" + i + ":" + handlers[i]); } } */ return (Logger) loggers.get(name); } public synchronized Enumeration getLoggerNames() { if (rootLogger == null && globalLogger == null) { // this ends up being called during initialization, we don't // want do anything unless the root logger has been set up. return Collections.enumeration(Collections.EMPTY_LIST); } final ClassLoader classLoader = Thread.currentThread() .getContextClassLoader(); final Map loggers = getClassLoaderInfo(classLoader).loggers; return Collections.enumeration(loggers.keySet()); } /** * Get the value of the specified property in the current classloader * context. */ public String getProperty(String name) { String prefix = (String) this.prefix.get(); if (prefix != null) { name = prefix + name; } final ClassLoader classLoader = Thread.currentThread() .getContextClassLoader(); String result = getClassLoaderInfo(classLoader).props.getProperty(name); if (result == null) { // FIXME: Look in parent classloader ? Probably not. result = super.getProperty(name); } // Simple property replacement (mostly for folder names) if (result != null) { result = result.trim(); if (result.startsWith("${")) { int pos = result.indexOf('}'); if (pos != -1) { String propName = result.substring(2, pos); String replacement = System.getProperty(propName); if (replacement != null) { result = replacement + result.substring(pos + 1); } } } } return result; } private ClassLoaderLogInfo getClassLoaderInfo(final ClassLoader classLoader) { ClassLoaderLogInfo info = (ClassLoaderLogInfo) classLoaderLoggers .get(classLoader); if (info == null) { InputStream is = null; // Special case for URL classloaders which are used in containers: // only look in the local repositories to avoid redefining loggers 20 times if ((classLoader instanceof URLClassLoader) && (((URLClassLoader) classLoader).findResource("logging.properties") != null)) { is = classLoader.getResourceAsStream("logging.properties"); } Logger localRootLogger = null; if (is != null) { localRootLogger = new RootLogger(); } else { // Retrieve the root logger of the parent classloader instead if (classLoader.getParent() != null) { localRootLogger = getClassLoaderInfo(classLoader.getParent()).rootNode.logger; } else { localRootLogger = rootLogger; } } info = new ClassLoaderLogInfo(new LogNode(null, localRootLogger)); info.loggers.put("", localRootLogger); classLoaderLoggers.put(classLoader, info); if (is != null) { try { info.props.load(is); } catch (IOException e) { // FIXME: Report this using the main logger ? e.printStackTrace(); } finally { try { is.close(); } catch (Throwable t) {} } // Create handlers for the root logger of this classloader String rootHandlers = info.props.getProperty(".handlers"); String handlers = info.props.getProperty("handlers"); if (handlers != null) { StringTokenizer tok = new StringTokenizer(handlers); while (tok.hasMoreTokens()) { String handlerName = (tok.nextToken()); String handlerClassName = handlerName; String prefix = ""; if (handlerClassName.length() <= 0) { continue; } // Parse and remove a prefix (prefix start with a digit, such as // "10WebappFooHanlder.") if (Character.isDigit(handlerClassName.charAt(0))) { int pos = handlerClassName.indexOf('.'); if (pos >= 0) { prefix = handlerClassName.substring(0, pos + 1); handlerClassName = handlerClassName.substring(pos + 1); } } try { this.prefix.set(prefix); Handler handler = (Handler) classLoader.loadClass(handlerClassName).newInstance(); this.prefix.set(null); String handlerLevel = info.props.getProperty(handlerName + ".level"); if (handlerLevel != null) { handler.setLevel(Level.parse(handlerLevel.trim())); } info.handlers.put(handlerName, handler); if (rootHandlers == null) { localRootLogger.addHandler(handler); } } catch (Exception e) { // FIXME: Report this using the main logger ? e.printStackTrace(); } } // Add handlers to the root logger, if any are defined using the .handlers property. if (rootHandlers != null) { StringTokenizer tok2 = new StringTokenizer(rootHandlers); while (tok2.hasMoreTokens()) { String handlerName = (tok2.nextToken()); Handler handler = (Handler) info.handlers.get(handlerName); if (handler != null) { localRootLogger.addHandler(handler); } } } } } } return info; } private static final class LogNode { Logger logger; private final Map children = new HashMap(); private final LogNode parent; LogNode(final LogNode parent, final Logger logger) { this.parent = parent; this.logger = logger; } LogNode(final LogNode parent) { this(parent, null); } LogNode findNode(String name) { assert name != null && name.length() > 0; LogNode currentNode = this; while (name != null) { final int dotIndex = name.indexOf('.'); final String nextName; if (dotIndex < 0) { nextName = name; name = null; } else { nextName = name.substring(0, dotIndex); name = name.substring(dotIndex + 1); } LogNode childNode = (LogNode) currentNode.children .get(nextName); if (childNode == null) { childNode = new LogNode(currentNode); currentNode.children.put(nextName, childNode); } currentNode = childNode; } return currentNode; } Logger findParentLogger() { Logger logger = null; LogNode node = parent; while (node != null && logger == null) { logger = node.logger; node = node.parent; } assert logger != null; return logger; } void setParentLogger(final Logger parent) { for (final Iterator iter = children.values().iterator(); iter .hasNext();) { final LogNode childNode = (LogNode) iter.next(); if (childNode.logger == null) { childNode.setParentLogger(parent); } else { doSetParentLogger(childNode.logger, parent); } } } } private static final class ClassLoaderLogInfo { final LogNode rootNode; final Map loggers = new HashMap(); final Map handlers = new HashMap(); final Properties props = new Properties(); ClassLoaderLogInfo(final LogNode rootNode) { this.rootNode = rootNode; } } /** * This is needed to instantiate the root of each per classloader hierarchy. */ private class RootLogger extends Logger { public RootLogger() { super("", null); } } } 1.1 jakarta-tomcat-connectors/juli/src/java/org/apache/juli/FileHandler.java Index: FileHandler.java =================================================================== /* * Copyright 1999,2004 The Apache Software Foundation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.juli; import java.io.File; import java.io.FileWriter; import java.io.PrintWriter; import java.io.UnsupportedEncodingException; import java.sql.Timestamp; import java.util.logging.ErrorManager; import java.util.logging.Handler; import java.util.logging.LogManager; import java.util.logging.LogRecord; import java.util.logging.SimpleFormatter; /** * Implementation of <b>Handler</b> that appends log messages to a file * named {prefix}.{date}.{suffix} in a configured directory, with an * optional preceding timestamp. * * @version $Revision: 1.1 $ $Date: 2005/03/02 18:30:45 $ */ public class FileHandler extends Handler { // ------------------------------------------------------------ Constructor public FileHandler() { configure(); open(); } // ----------------------------------------------------- Instance Variables /** * The as-of date for the currently open log file, or a zero-length * string if there is no open log file. */ private String date = ""; /** * The directory in which log files are created. */ private String directory = null; /** * The prefix that is added to log file filenames. */ private String prefix = null; /** * The suffix that is added to log file filenames. */ private String suffix = null; /** * The PrintWriter to which we are currently logging, if any. */ private PrintWriter writer = null; // ------------------------------------------------------------- Properties /** * Return the directory in which we create log files. public String getDirectory() { return (directory); } */ /** * Set the directory in which we create log files. * * @param directory The new log file directory public void setDirectory(String directory) { this.directory = directory; } */ /** * Return the log file prefix. public String getPrefix() { return (prefix); } */ /** * Set the log file prefix. * * @param prefix The new log file prefix public void setPrefix(String prefix) { this.prefix = prefix; } */ /** * Return the log file suffix. public String getSuffix() { return (suffix); } */ /** * Set the log file suffix. * * @param suffix The new log file suffix public void setSuffix(String suffix) { this.suffix = suffix; } */ // --------------------------------------------------------- Public Methods /** * Format and publish a <tt>LogRecord</tt>. * * @param record description of the log event */ public void publish(LogRecord record) { if (!isLoggable(record)) { return; } // Construct the timestamp we will use, if requested Timestamp ts = new Timestamp(System.currentTimeMillis()); String tsString = ts.toString().substring(0, 19); String tsDate = tsString.substring(0, 10); // If the date has changed, switch log files if (!date.equals(tsDate)) { synchronized (this) { if (!date.equals(tsDate)) { close(); date = tsDate; open(); } } } String result = null; try { result = getFormatter().format(record); } catch (Exception e) { reportError(null, e, ErrorManager.FORMAT_FAILURE); return; } try { writer.write(result); writer.flush(); } catch (Exception e) { reportError(null, e, ErrorManager.WRITE_FAILURE); return; } } // -------------------------------------------------------- Private Methods /** * Close the currently open log file (if any) */ public void close() { try { if (writer == null) return; writer.write(getFormatter().getTail(this)); writer.flush(); writer.close(); writer = null; date = ""; } catch (Exception e) { reportError(null, e, ErrorManager.CLOSE_FAILURE); } } /** * FLush */ public void flush() { try { writer.flush(); } catch (Exception e) { reportError(null, e, ErrorManager.FLUSH_FAILURE); } } /** * Configure from <code>LogManager</code> properties. */ private void configure() { Timestamp ts = new Timestamp(System.currentTimeMillis()); String tsString = ts.toString().substring(0, 19); date = tsString.substring(0, 10); LogManager manager = LogManager.getLogManager(); String className = FileHandler.class.getName(); // Retrieve configuration of logging file name directory = getProperty(className + ".directory", "logs"); prefix = getProperty(className + ".prefix", "juli."); suffix = getProperty(className + ".suffix", ".log"); // FIXME: Add filter configuration in LogManager ? //setFilter(manager.getFilterProperty(className + ".filter", null)); // FIXME: Add formatter configuration in LogManager ? //setFormatter(manager.getFormatterProperty(className + ".formatter", new SimpleFormatter())); // Hardcode for now a SimpleFormatter setFormatter(new SimpleFormatter()); // FIXME: Add encoding configuration in LogManager ? try { setEncoding(manager.getProperty(className + ".encoding")); } catch (UnsupportedEncodingException e) { try { setEncoding(null); } catch (Exception ex) { } } setErrorManager(new ErrorManager()); } private String getProperty(String name, String defaultValue) { String value = LogManager.getLogManager().getProperty(name); if (value == null) { value = defaultValue; } return value.trim(); } /** * Open the new log file for the date specified by <code>date</code>. */ private void open() { // Create the directory if necessary File dir = new File(directory); dir.mkdirs(); // Open the current log file try { String pathname = dir.getAbsolutePath() + File.separator + prefix + date + suffix; writer = new PrintWriter(new FileWriter(pathname, true), true); writer.write(getFormatter().getHead(this)); } catch (Exception e) { reportError(null, e, ErrorManager.OPEN_FAILURE); writer = null; } } } 1.1 jakarta-tomcat-connectors/juli/build.xml Index: build.xml =================================================================== <project name="Juli" default="compile" basedir="."> <!-- "Coyote" connector framework for Jakarta Tomcat $Id: build.xml,v 1.1 2005/03/02 18:30:45 remm Exp $ --> <!-- ========== Initialize Properties ===================================== --> <property file="build.properties"/> <!-- Component local --> <property file="../build.properties"/> <!-- Commons local --> <property file="${user.home}/build.properties"/> <!-- User local --> <!-- ========== External Dependencies ===================================== --> <!-- The directories corresponding to your necessary dependencies --> <property name="junit.home" value="/usr/local/junit3.5"/> <!-- ========== Derived Values ============================================ --> <property name="junit.jar" value="${junit.home}/junit.jar"/> <property name="jmx.jar" location="../lib/mx4j.jar" /> <!-- ========== Component Declarations ==================================== --> <!-- The name of this component --> <property name="component.name" value="juli"/> <!-- The title of this component --> <property name="component.title" value="Java Util Logging Implementation"/> <!-- The current version number of this component --> <property name="component.version" value="1.0-dev"/> <!-- The base directory for compilation targets --> <property name="build.home" value="build"/> <!-- The base directory for component configuration files --> <property name="conf.home" value="src/conf"/> <!-- The base directory for component sources --> <property name="source.home" value="src/java"/> <!-- The base directory for unit test sources --> <property name="test.home" value="src/test"/> <!-- ========== Compiler Defaults ========================================= --> <!-- Should Java compilations set the 'debug' compiler option? --> <property name="compile.debug" value="true"/> <!-- Should Java compilations set the 'deprecation' compiler option? --> <property name="compile.deprecation" value="false"/> <!-- Should Java compilations set the 'optimize' compiler option? --> <property name="compile.optimize" value="true"/> <!-- Construct compile classpath --> <path id="compile.classpath"> <pathelement location="${build.home}/classes"/> <pathelement location="${jmx.jar}"/> </path> <!-- ========== Test Execution Defaults =================================== --> <!-- Construct unit test classpath --> <path id="test.classpath"> <pathelement location="${build.home}/classes"/> <pathelement location="${build.home}/tests"/> <pathelement location="${jmx.jar}"/> <pathelement location="${junit.jar}"/> </path> <!-- Should all tests fail if one does? --> <property name="test.failonerror" value="true"/> <!-- ========== Executable Targets ======================================== --> <target name="init" description="Initialize and evaluate conditionals"> <echo message="-------- ${component.title} ${component.version} --------"/> <filter token="name" value="${component.name}"/> <filter token="version" value="${component.version}"/> </target> <target name="prepare" depends="init" description="Prepare build directory"> <mkdir dir="${build.home}"/> <mkdir dir="${build.home}/classes"/> <mkdir dir="${build.home}/conf"/> <mkdir dir="${build.home}/lib"/> <mkdir dir="${build.home}/docs"/> <mkdir dir="${build.home}/docs/api"/> <mkdir dir="${build.home}/tests"/> </target> <target name="static" depends="prepare" description="Copy static files to build directory"> <tstamp/> <copy todir="${build.home}/conf" filtering="on"> <fileset dir="${conf.home}" includes="*.MF"/> </copy> </target> <target name="javadoc" unless="docs-uptodate" description="Create component Javadoc documentation"> <mkdir dir="${build.home}/docs/api"/> <javadoc sourcepath="${source.home}" destdir="${build.home}/docs/api" packagenames="org.apache.coyote.*" author="true" private="true" version="true" doctitle="<h1>${component.title}</h1>" windowtitle="${component.title} (Version ${component.version})" bottom="Copyright (c) 2005 - Apache Software Foundation"> <classpath refid="compile.classpath"/> </javadoc> </target> <target name="compile-only" description="Compile shareable components"> <javac srcdir="${source.home}" destdir="${build.home}/classes" debug="${compile.debug}" deprecation="${compile.deprecation}" source="1.4" optimize="${compile.optimize}"> <classpath refid="compile.classpath"/> </javac> <copy todir="${build.home}/classes" filtering="on"> <fileset dir="${source.home}" excludes="**/*.java"/> </copy> <property name="tomcat-juli.jar" value="${build.home}/lib/tomcat-${component.name}.jar"/> <jar jarfile="${tomcat-juli.jar}" index="true" basedir="${build.home}/classes" manifest="${build.home}/conf/MANIFEST.MF"> <include name="org/apache/juli/**"/> </jar> </target> <target name="compile" depends="static,compile-only" description="Compile shareable components"> </target> <target name="compile.tests" depends="compile" description="Compile unit test cases"> <javac srcdir="${test.home}/java" destdir="${build.home}/tests" debug="${compile.debug}" deprecation="${compile.deprecation}" optimize="${compile.optimize}"> <classpath refid="test.classpath"/> </javac> <copy todir="${build.home}/tests" filtering="on"> <fileset dir="${test.home}" excludes="**/*.java"/> </copy> </target> <target name="clean" description="Clean build and distribution directories"> <delete dir="${build.home}"/> <delete dir="${dist.home}"/> </target> <target name="all" depends="clean,compile,compile.tests" description="Clean and compile all components"/> <!-- ========== Unit Test Targets ========================================= --> <target name="test" depends="compile.tests" if="test.entry" description="Run all unit test cases"> <!-- <junit printsummary="yes" fork="on" haltonfailure="yes"> <formatter type="plain" usefile="false"/> <test name="${test.entry}"/> <classpath refid="test.classpath"/> </junit> <java classname="${test.runner}" fork="yes" failonerror="${test.failonerror}"> <jvmarg value="${java.protocol.handler.pkgs}"/> <arg value="${test.entry}"/> <classpath refid="test.classpath"/> </java> --> </target> </project>
--------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]