You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cactus-dev@jakarta.apache.org by vm...@apache.org on 2003/10/19 20:08:43 UTC
cvs commit: jakarta-cactus/integration/ant/src/java/org/apache/cactus/integration/ant/container AbstractJavaContainer.java ContainerRunner.java ContainerWrapper.java Container.java AbstractContainer.java
vmassol 2003/10/19 11:08:43
Modified: integration/ant/src/java/org/apache/cactus/integration/ant/container/resin
Resin2xContainer.java
integration/ant/src/test/org/apache/cactus/integration/ant/container
MockContainer.java
integration/ant/src/java/org/apache/cactus/integration/ant
CactusTask.java
integration/ant/src/java/org/apache/cactus/integration/ant/container
AbstractJavaContainer.java ContainerRunner.java
ContainerWrapper.java Container.java
AbstractContainer.java
Added: integration/ant/src/java/org/apache/cactus/integration/ant/util
PropertySet.java
Log:
Make logging system really work. Tested with JDK 1.4, Log4j and simple logging. Also tested with several app server
Revision Changes Path
1.7 +2 -2 jakarta-cactus/integration/ant/src/java/org/apache/cactus/integration/ant/container/resin/Resin2xContainer.java
Index: Resin2xContainer.java
===================================================================
RCS file: /home/cvs/jakarta-cactus/integration/ant/src/java/org/apache/cactus/integration/ant/container/resin/Resin2xContainer.java,v
retrieving revision 1.6
retrieving revision 1.7
diff -u -r1.6 -r1.7
--- Resin2xContainer.java 7 Sep 2003 19:54:37 -0000 1.6
+++ Resin2xContainer.java 19 Oct 2003 18:08:43 -0000 1.7
@@ -185,7 +185,7 @@
prepare("cactus/resin2x");
// invoke the main class
- Java java = createJavaForStartUp();
+ Java java = createJavaForStartUp();
java.addSysproperty(createSysProperty("resin.home", this.tmpDir));
Path classpath = java.createClasspath();
classpath.createPathElement().setLocation(
1.8 +17 -1 jakarta-cactus/integration/ant/src/test/org/apache/cactus/integration/ant/container/MockContainer.java
Index: MockContainer.java
===================================================================
RCS file: /home/cvs/jakarta-cactus/integration/ant/src/test/org/apache/cactus/integration/ant/container/MockContainer.java,v
retrieving revision 1.7
retrieving revision 1.8
diff -u -r1.7 -r1.8
--- MockContainer.java 7 Sep 2003 19:54:37 -0000 1.7
+++ MockContainer.java 19 Oct 2003 18:08:43 -0000 1.8
@@ -62,6 +62,7 @@
import org.apache.cactus.integration.ant.util.AntTaskFactory;
import org.apache.commons.logging.Log;
+import org.apache.tools.ant.types.Environment.Variable;
/**
* Mock implementation of the {@link Container} interface.
@@ -259,4 +260,19 @@
}
}
+ /**
+ * @see Container#setSystemProperties(Variable[])
+ */
+ public void setSystemProperties(Variable[] theProperties)
+ {
+ // do nothing
+ }
+
+ /**
+ * @see Container#getSystemProperties()
+ */
+ public Variable[] getSystemProperties()
+ {
+ throw new RuntimeException("not implemented");
+ }
}
1.1 jakarta-cactus/integration/ant/src/java/org/apache/cactus/integration/ant/util/PropertySet.java
Index: PropertySet.java
===================================================================
/*
* ====================================================================
*
* The Apache Software License, Version 1.1
*
* Copyright (c) 2003 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Cactus" and "Apache Software
* Foundation" must not be used to endorse or promote products
* derived from this software without prior written permission. For
* written permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.cactus.integration.ant.util;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.PropertyResourceBundle;
import java.util.ResourceBundle;
import org.apache.tools.ant.BuildException;
/**
* Ant element used to tell the Cactus task to load a properties file
* and passed its properties to the client side or server side JVMs.
*
* @author <a href="mailto:vmassol@apache.org">Vincent Massol</a>
*
* @version $Id: PropertySet.java,v 1.1 2003/10/19 18:08:43 vmassol Exp $
*/
public class PropertySet
{
/**
* Properties file to load.
*/
private File propertiesFile;
/**
* Are the properties for the Cactus server side JVM?
*/
private boolean isServer;
/**
* @param thePropertiesFile the properties file to load
*/
public void setPropertiesFile(File thePropertiesFile)
{
this.propertiesFile = thePropertiesFile;
}
/**
* @param isServer if true the properties will be passed to the
* Cactus server side JVM
*/
public void setServer(boolean isServer)
{
this.isServer = isServer;
}
/**
* @return true if the properties are to be passed to the Cactus
* server side JVM, false otherwise
*/
public boolean isServer()
{
return this.isServer;
}
/**
* @return the properties loaded from the proeprties file
*/
public ResourceBundle readProperties()
{
if (this.propertiesFile == null)
{
throw new BuildException("Missing 'propertiesFiles' attribute");
}
ResourceBundle bundle;
try
{
bundle = new PropertyResourceBundle(
new FileInputStream(this.propertiesFile));
}
catch (IOException e)
{
throw new BuildException("Failed to load properties "
+ "file [" + this.propertiesFile + "]");
}
return bundle;
}
}
1.23 +103 -13 jakarta-cactus/integration/ant/src/java/org/apache/cactus/integration/ant/CactusTask.java
Index: CactusTask.java
===================================================================
RCS file: /home/cvs/jakarta-cactus/integration/ant/src/java/org/apache/cactus/integration/ant/CactusTask.java,v
retrieving revision 1.22
retrieving revision 1.23
diff -u -r1.22 -r1.23
--- CactusTask.java 7 Sep 2003 20:13:24 -0000 1.22
+++ CactusTask.java 19 Oct 2003 18:08:43 -0000 1.23
@@ -59,7 +59,10 @@
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
+import java.util.ArrayList;
import java.util.Enumeration;
+import java.util.List;
+import java.util.ResourceBundle;
import org.apache.cactus.integration.ant.container.Container;
import org.apache.cactus.integration.ant.container.ContainerRunner;
@@ -68,11 +71,13 @@
import org.apache.cactus.integration.ant.container.WarDeployableFile;
import org.apache.cactus.integration.ant.util.AntLog;
import org.apache.cactus.integration.ant.util.AntTaskFactory;
+import org.apache.cactus.integration.ant.util.PropertySet;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.taskdefs.optional.junit.JUnitTask;
import org.apache.tools.ant.taskdefs.optional.junit.JUnitTest;
+import org.apache.tools.ant.types.Environment;
import org.apache.tools.ant.types.Environment.Variable;
/**
@@ -80,12 +85,12 @@
* in-container testing.
*
* @author <a href="mailto:cmlenz@apache.org">Christopher Lenz</a>
+ * @author <a href="mailto:vmassol@apache.org">Vincent Massol</a>
*
* @version $Id$
*/
public class CactusTask extends JUnitTask
{
-
// Instance Variables ------------------------------------------------------
/**
@@ -103,6 +108,11 @@
* The archive that contains the web-app that is ready to be tested.
*/
private File warFile;
+
+ /**
+ * System properties that will be set in the container JVM.
+ */
+ private List systemProperties = new ArrayList();
/**
* The factory for creating ant tasks that is passed to the containers.
@@ -181,7 +191,7 @@
}
addRedirectorNameProperties(deployableFile);
-
+
if (this.containerSet == null)
{
log("No containers specified, tests will run locally",
@@ -199,6 +209,9 @@
containers[i].setAntTaskFactory(this.antTaskFactory);
containers[i].setLog(new AntLog(this));
containers[i].setDeployableFile(deployableFile);
+ containers[i].setSystemProperties(
+ (Variable[]) this.systemProperties.toArray(
+ new Variable[0]));
if (containers[i].isEnabled())
{
containers[i].init();
@@ -267,25 +280,102 @@
this.warFile = theWarFile;
}
+ /**
+ * Adds a system property. Note that we can't reuse the JUnitTask
+ * sysproperty element as there is no getter to get them.
+ *
+ * @see JUnitTask#addSysproperty(Environment.Variable)
+ */
+ public void addSysproperty(Environment.Variable theProperty)
+ {
+ addCactusServerProperty(theProperty);
+ super.addSysproperty(theProperty);
+ }
+
+ /**
+ * Called by Ant when the Variable object has been properly initialized.
+ *
+ * @param theProperty the system property to set
+ */
+ public void addConfiguredSysproperty(Environment.Variable theProperty)
+ {
+ addSysproperty(theProperty);
+ }
+
+ /**
+ * Adds a set of properties that will be used as system properties
+ * either on the client side or on the server side.
+ *
+ * @param thePropertySet the set of properties to be added
+ */
+ public void addConfiguredCactusproperty(PropertySet thePropertySet)
+ {
+ // Add all properties from the properties file
+ ResourceBundle bundle = thePropertySet.readProperties();
+ Enumeration keys = bundle.getKeys();
+ while (keys.hasMoreElements())
+ {
+ String key = (String) keys.nextElement();
+ Variable var = new Variable();
+ var.setKey(key);
+ var.setValue(bundle.getString(key));
+ if (thePropertySet.isServer())
+ {
+ addCactusServerProperty(var);
+ }
+ else
+ {
+ super.addSysproperty(var);
+ }
+ }
+ }
+
// Private Methods ---------------------------------------------------------
/**
- * Adds a Cactus system property.
+ * Adds a Cactus system property for the client side JVM.
*
- * @param theKey The property name (not including the 'cactus.' prefix)
+ * @param theKey The property name
* @param theValue The property value
*/
- private void addCactusProperty(String theKey, String theValue)
+ private void addCactusClientProperty(String theKey, String theValue)
{
- log("Adding Cactus system property 'cactus." + theKey + "' with value '"
- + theValue + "'", Project.MSG_VERBOSE);
+ log("Adding Cactus client system property [" + theKey
+ + "] with value [" + theValue + "]", Project.MSG_VERBOSE);
Variable sysProperty = new Variable();
- sysProperty.setKey("cactus." + theKey);
+ sysProperty.setKey(theKey);
sysProperty.setValue(theValue);
- addSysproperty(sysProperty);
+ super.addSysproperty(sysProperty);
+ }
+
+ /**
+ * Adds a Cactus system property for the server side JVM.
+ *
+ * @param theProperty The system property to set in the container JVM
+ */
+ private void addCactusServerProperty(Variable theProperty)
+ {
+ log("Adding Cactus server system property [cactus."
+ + theProperty.getKey() + "] with value ["
+ + theProperty.getValue() + "]", Project.MSG_VERBOSE);
+ this.systemProperties.add(theProperty);
}
/**
+ * Adds a Cactus system property for the server side JVM.
+ *
+ * @param theKey The property name
+ * @param theValue The property value
+ */
+ private void addCactusServerProperty(String theKey, String theValue)
+ {
+ Variable property = new Variable();
+ property.setKey(theKey);
+ property.setValue(theValue);
+ addCactusServerProperty(property);
+ }
+
+ /**
* Extracts the redirector mappings from the deployment descriptor and sets
* the corresponding system properties.
*
@@ -297,7 +387,7 @@
theFile.getFilterRedirectorMapping();
if (filterRedirectorMapping != null)
{
- addCactusProperty("filterRedirectorName",
+ addCactusClientProperty("cactus.filterRedirectorName",
filterRedirectorMapping.substring(1));
}
else
@@ -310,7 +400,7 @@
theFile.getJspRedirectorMapping();
if (jspRedirectorMapping != null)
{
- addCactusProperty("jspRedirectorName",
+ addCactusClientProperty("cactus.jspRedirectorName",
jspRedirectorMapping.substring(1));
}
else
@@ -323,7 +413,7 @@
theFile.getServletRedirectorMapping();
if (servletRedirectorMapping != null)
{
- addCactusProperty("servletRedirectorName",
+ addCactusClientProperty("cactus.servletRedirectorName",
servletRedirectorMapping.substring(1));
}
else
1.3 +13 -4 jakarta-cactus/integration/ant/src/java/org/apache/cactus/integration/ant/container/AbstractJavaContainer.java
Index: AbstractJavaContainer.java
===================================================================
RCS file: /home/cvs/jakarta-cactus/integration/ant/src/java/org/apache/cactus/integration/ant/container/AbstractJavaContainer.java,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -r1.2 -r1.3
--- AbstractJavaContainer.java 12 May 2003 09:03:51 -0000 1.2
+++ AbstractJavaContainer.java 19 Oct 2003 18:08:43 -0000 1.3
@@ -73,7 +73,6 @@
*/
public abstract class AbstractJavaContainer extends AbstractContainer
{
-
// Instance Variables ------------------------------------------------------
/**
@@ -137,6 +136,15 @@
java.setFork(true);
java.setOutput(this.output);
java.setAppend(this.append);
+
+ // Add Cactus properties for the server side
+ for (int i = 0; i < getSystemProperties().length; i++)
+ {
+ java.addSysproperty(
+ createSysProperty(getSystemProperties()[i].getKey(),
+ getSystemProperties()[i].getValue()));
+ }
+
return java;
}
@@ -198,12 +206,13 @@
throws FileNotFoundException
{
String javaHome = System.getProperty("java.home");
+ // TODO: Fix this as it fails on Max OSX (which doesn't have a
+ // tools.jar file).
File toolsJar = new File(javaHome + "/../lib/tools.jar");
if (!toolsJar.isFile())
{
throw new FileNotFoundException(toolsJar.getAbsolutePath());
}
return toolsJar;
- }
-
+ }
}
1.5 +42 -19 jakarta-cactus/integration/ant/src/java/org/apache/cactus/integration/ant/container/ContainerRunner.java
Index: ContainerRunner.java
===================================================================
RCS file: /home/cvs/jakarta-cactus/integration/ant/src/java/org/apache/cactus/integration/ant/container/ContainerRunner.java,v
retrieving revision 1.4
retrieving revision 1.5
diff -u -r1.4 -r1.5
--- ContainerRunner.java 9 Jul 2003 14:22:41 -0000 1.4
+++ ContainerRunner.java 19 Oct 2003 18:08:43 -0000 1.5
@@ -75,7 +75,6 @@
*/
public final class ContainerRunner
{
-
// Instance Variables ------------------------------------------------------
/**
@@ -168,7 +167,7 @@
// Try connecting in case the server is already running. If so, does
// nothing
- this.alreadyRunning = isAvailable(this.url);
+ this.alreadyRunning = isAvailable(testConnectivity(this.url));
if (this.alreadyRunning)
{
// Server is already running. Record this information so that we
@@ -190,16 +189,21 @@
// Continuously try calling the test URL until it succeeds or
// until a timeout is reached (we then throw a build exception).
long startTime = System.currentTimeMillis();
+ int responseCode = -1;
do
{
if ((System.currentTimeMillis() - startTime) > this.timeout)
{
throw new BuildException("Failed to start the container after "
- + "more than [" + this.timeout + "] ms.");
+ + "more than [" + this.timeout + "] ms. Trying to connect "
+ + "to the [" + this.url + "] test URL yielded a ["
+ + responseCode + "] error code. Please run in debug mode "
+ + "for more details about the error.");
}
sleep(this.checkInterval);
this.log.debug("Checking if server is up ...");
- } while (!isAvailable(this.url));
+ responseCode = testConnectivity(this.url);
+ } while (!isAvailable(responseCode));
// Wait a few ms more (just to be sure !)
sleep(this.startUpWait);
@@ -229,7 +233,7 @@
return;
}
- if (!isAvailable(this.url))
+ if (!isAvailable(testConnectivity(this.url)))
{
this.log.debug("Server isn't running!");
return;
@@ -250,7 +254,7 @@
do
{
sleep(this.checkInterval);
- } while (isAvailable(this.url));
+ } while (isAvailable(testConnectivity(this.url)));
// sleep a bit longer to be sure the container has terminated
sleep(this.shutDownWait);
@@ -335,16 +339,15 @@
/**
* Tests whether we are able to connect to the HTTP server identified by the
- * specified URL, and whether it responds with a HTTP status code indicating
- * success (200 up to but excluding 300) for the requested resource.
+ * specified URL.
*
* @param theUrl The URL to check
- * @return <code>true</code> if the test URL could be called without error,
- * <code>false</code> otherwise
+ * @return the HTTP response code or -1 if no connection could be
+ * established
*/
- private boolean isAvailable(URL theUrl)
+ private int testConnectivity(URL theUrl)
{
- boolean retVal = false;
+ int code;
try
{
HttpURLConnection connection =
@@ -353,17 +356,37 @@
connection.connect();
readFully(connection);
connection.disconnect();
- if ((connection.getResponseCode() != -1)
- && (connection.getResponseCode() < 300))
- {
- retVal = true;
- }
+ code = connection.getResponseCode();
}
catch (IOException e)
{
this.log.debug("Failed to connect to " + theUrl, e);
+ code = -1;
+ }
+ return code;
+ }
+
+
+ /**
+ * Tests whether an HTTP return code corresponds to a valid connection
+ * to the test URL or not. Success is 200 up to but excluding 300.
+ *
+ * @param theCode the HTTP response code to verify
+ * @return <code>true</code> if the test URL could be called without error,
+ * <code>false</code> otherwise
+ */
+ private boolean isAvailable(int theCode)
+ {
+ boolean result;
+ if ((theCode != -1) && (theCode < 300))
+ {
+ result = true;
+ }
+ else
+ {
+ result = false;
}
- return retVal;
+ return result;
}
/**
1.7 +17 -1 jakarta-cactus/integration/ant/src/java/org/apache/cactus/integration/ant/container/ContainerWrapper.java
Index: ContainerWrapper.java
===================================================================
RCS file: /home/cvs/jakarta-cactus/integration/ant/src/java/org/apache/cactus/integration/ant/container/ContainerWrapper.java,v
retrieving revision 1.6
retrieving revision 1.7
diff -u -r1.6 -r1.7
--- ContainerWrapper.java 7 Sep 2003 19:54:37 -0000 1.6
+++ ContainerWrapper.java 19 Oct 2003 18:08:43 -0000 1.7
@@ -60,6 +60,7 @@
import org.apache.cactus.integration.ant.util.AntTaskFactory;
import org.apache.commons.logging.Log;
+import org.apache.tools.ant.types.Environment.Variable;
/**
* Class that wraps around an implementation of the <code>Container</code>
@@ -122,6 +123,14 @@
}
/**
+ * @see Container#getSystemProperties
+ */
+ public Variable[] getSystemProperties()
+ {
+ return this.container.getSystemProperties();
+ }
+
+ /**
* @see Container#init
*/
public void init()
@@ -185,4 +194,11 @@
this.container.setDeployableFile(theWarFile);
}
+ /**
+ * @see Container#setSystemProperties
+ */
+ public void setSystemProperties(Variable[] theProperties)
+ {
+ this.container.setSystemProperties(theProperties);
+ }
}
1.6 +17 -1 jakarta-cactus/integration/ant/src/java/org/apache/cactus/integration/ant/container/Container.java
Index: Container.java
===================================================================
RCS file: /home/cvs/jakarta-cactus/integration/ant/src/java/org/apache/cactus/integration/ant/container/Container.java,v
retrieving revision 1.5
retrieving revision 1.6
diff -u -r1.5 -r1.6
--- Container.java 7 Sep 2003 19:54:37 -0000 1.5
+++ Container.java 19 Oct 2003 18:08:43 -0000 1.6
@@ -60,6 +60,7 @@
import org.apache.cactus.integration.ant.util.AntTaskFactory;
import org.apache.commons.logging.Log;
+import org.apache.tools.ant.types.Environment.Variable;
/**
* Interface for classes that can be used as nested elements in the
@@ -97,6 +98,12 @@
File getToDir();
/**
+ * @return the list of system properties that will be set in the container
+ * JVM
+ */
+ Variable[] getSystemProperties();
+
+ /**
* Subclasses should implement this method to perform any initialization
* that may be necessary. This method is called before any of the accessors
* or the methods {@link AbstractContainer#startUp} and
@@ -145,6 +152,15 @@
* @param theDeployableFile The file to deploy
*/
void setDeployableFile(DeployableFile theDeployableFile);
+
+ /**
+ * Sets the system properties that will be passed to the JVM in which the
+ * server will execute.
+ *
+ * @param theProperties the list of system properties to set in the
+ * container JVM
+ */
+ void setSystemProperties(Variable[] theProperties);
/**
* Subclasses must implement this method to perform the actual task of
1.9 +23 -1 jakarta-cactus/integration/ant/src/java/org/apache/cactus/integration/ant/container/AbstractContainer.java
Index: AbstractContainer.java
===================================================================
RCS file: /home/cvs/jakarta-cactus/integration/ant/src/java/org/apache/cactus/integration/ant/container/AbstractContainer.java,v
retrieving revision 1.8
retrieving revision 1.9
diff -u -r1.8 -r1.9
--- AbstractContainer.java 7 Sep 2003 19:54:37 -0000 1.8
+++ AbstractContainer.java 19 Oct 2003 18:08:43 -0000 1.9
@@ -70,6 +70,7 @@
import org.apache.tools.ant.types.FileSet;
import org.apache.tools.ant.types.FilterChain;
import org.apache.tools.ant.types.PatternSet;
+import org.apache.tools.ant.types.Environment.Variable;
import org.apache.tools.ant.types.selectors.SelectorUtils;
/**
@@ -131,6 +132,11 @@
*/
private Log log = AntLog.NULL;
+ /**
+ * List of system properties to set in the container JVM.
+ */
+ private Variable[] systemProperties;
+
// Public Methods ----------------------------------------------------------
/**
@@ -265,6 +271,22 @@
this.log = theLog;
}
+ /**
+ * @see Container#setSystemProperties
+ */
+ public void setSystemProperties(Variable[] theProperties)
+ {
+ this.systemProperties = theProperties;
+ }
+
+ /**
+ * @see Container#getSystemProperties
+ */
+ public Variable[] getSystemProperties()
+ {
+ return this.systemProperties;
+ }
+
// Protected Methods -------------------------------------------------------
/**
---------------------------------------------------------------------
To unsubscribe, e-mail: cactus-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: cactus-dev-help@jakarta.apache.org