You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by ma...@apache.org on 2019/11/14 09:35:50 UTC
[tomcat] 02/05: Back-port FindBugs fixes from 9.0.x
This is an automated email from the ASF dual-hosted git repository.
markt pushed a commit to branch 8.5.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git
commit 3fe5742da6d2b803b4f71242982ce34045d5f396
Author: Mark Thomas <ma...@apache.org>
AuthorDate: Wed Nov 13 19:22:42 2019 +0000
Back-port FindBugs fixes from 9.0.x
(and a few obvious alignment fixes spotted along the way)
---
.../catalina/core/ApplicationFilterConfig.java | 2 +-
.../apache/catalina/ha/tcp/ReplicationValve.java | 46 +++++----
.../catalina/manager/LocalStrings.properties | 1 +
.../catalina/manager/LocalStrings_fr.properties | 1 +
.../catalina/manager/LocalStrings_ja.properties | 1 +
.../catalina/manager/LocalStrings_ko.properties | 1 +
.../catalina/manager/LocalStrings_ru.properties | 1 +
.../catalina/manager/LocalStrings_zh_CN.properties | 1 +
.../apache/catalina/manager/ManagerServlet.java | 8 +-
.../catalina/manager/StatusManagerServlet.java | 7 --
.../mbeans/JmxRemoteLifecycleListener.java | 27 +++++
java/org/apache/catalina/tribes/Member.java | 4 +-
res/findbugs/filter-false-positives.xml | 115 +++++++++++++++++++--
13 files changed, 176 insertions(+), 39 deletions(-)
diff --git a/java/org/apache/catalina/core/ApplicationFilterConfig.java b/java/org/apache/catalina/core/ApplicationFilterConfig.java
index a8f960c..dc146fc 100644
--- a/java/org/apache/catalina/core/ApplicationFilterConfig.java
+++ b/java/org/apache/catalina/core/ApplicationFilterConfig.java
@@ -62,7 +62,7 @@ public final class ApplicationFilterConfig implements FilterConfig, Serializable
static final StringManager sm =
StringManager.getManager(Constants.Package);
- private final Log log = LogFactory.getLog(ApplicationFilterConfig.class); // must not be static
+ private transient Log log = LogFactory.getLog(ApplicationFilterConfig.class); // must not be static
/**
* Empty String collection to serve as the basis for empty enumerations.
diff --git a/java/org/apache/catalina/ha/tcp/ReplicationValve.java b/java/org/apache/catalina/ha/tcp/ReplicationValve.java
index 8fac6b2..30308b5 100644
--- a/java/org/apache/catalina/ha/tcp/ReplicationValve.java
+++ b/java/org/apache/catalina/ha/tcp/ReplicationValve.java
@@ -93,13 +93,21 @@ public class ReplicationValve
*/
protected boolean doProcessingStats = false;
- protected long totalRequestTime = 0;
- protected long totalSendTime = 0;
- protected long nrOfRequests = 0;
- protected long lastSendTime = 0;
- protected long nrOfFilterRequests = 0;
- protected long nrOfSendRequests = 0;
- protected long nrOfCrossContextSendRequests = 0;
+ /*
+ * Note: The statistics are volatile to ensure the concurrent updates do not
+ * corrupt them but it is still possible that:
+ * - some updates may be lost;
+ * - the individual statistics may not be consistent which each other.
+ * This is a deliberate design choice to reduce the requirement for
+ * synchronization.
+ */
+ protected volatile long totalRequestTime = 0;
+ protected volatile long totalSendTime = 0;
+ protected volatile long nrOfRequests = 0;
+ protected volatile long lastSendTime = 0;
+ protected volatile long nrOfFilterRequests = 0;
+ protected volatile long nrOfSendRequests = 0;
+ protected volatile long nrOfCrossContextSendRequests = 0;
/**
* must primary change indicator set
@@ -354,11 +362,11 @@ public class ReplicationValve
* reset the active statistics
*/
public void resetStatistics() {
- totalRequestTime = 0 ;
- totalSendTime = 0 ;
- lastSendTime = 0 ;
- nrOfFilterRequests = 0 ;
- nrOfRequests = 0 ;
+ totalRequestTime = 0;
+ totalSendTime = 0;
+ lastSendTime = 0;
+ nrOfFilterRequests = 0;
+ nrOfRequests = 0;
nrOfSendRequests = 0;
nrOfCrossContextSendRequests = 0;
}
@@ -422,8 +430,7 @@ public class ReplicationValve
protected void sendCrossContextSession() {
List<DeltaSession> sessions = crossContextSessions.get();
if(sessions != null && sessions.size() >0) {
- for(Iterator<DeltaSession> iter = sessions.iterator(); iter.hasNext() ;) {
- Session session = iter.next();
+ for (DeltaSession session : sessions) {
if(log.isDebugEnabled()) {
log.debug(sm.getString("ReplicationValve.crossContext.sendDelta",
session.getManager().getContext().getName() ));
@@ -564,12 +571,11 @@ public class ReplicationValve
protected void updateStats(long requestTime, long clusterTime) {
// TODO: Async requests may trigger multiple replication requests. How,
// if at all, should the stats handle this?
- synchronized(this) {
- lastSendTime=System.currentTimeMillis();
- totalSendTime+=lastSendTime - clusterTime;
- totalRequestTime+=lastSendTime - requestTime;
- nrOfRequests++;
- }
+ long currentTime = System.currentTimeMillis();
+ lastSendTime = currentTime;
+ totalSendTime += currentTime - clusterTime;
+ totalRequestTime += currentTime - requestTime;
+ nrOfRequests++;
if(log.isInfoEnabled()) {
if ( (nrOfRequests % 100) == 0 ) {
log.info(sm.getString("ReplicationValve.stats",
diff --git a/java/org/apache/catalina/manager/LocalStrings.properties b/java/org/apache/catalina/manager/LocalStrings.properties
index fac641c..556e0c1 100644
--- a/java/org/apache/catalina/manager/LocalStrings.properties
+++ b/java/org/apache/catalina/manager/LocalStrings.properties
@@ -141,6 +141,7 @@ managerServlet.notSslConnector=SSL is not enabled for this connector
managerServlet.objectNameFail=FAIL - Unable to register object name [{0}] for Manager Servlet
managerServlet.postCommand=FAIL - Tried to use command [{0}] via a GET request but POST is required
managerServlet.reloaded=OK - Reloaded application at context path [{0}]
+managerServlet.renameFail=FAIL - Unable to rename [{0}] to [{1}]. This may cause problems for future deployments.
managerServlet.resourcesAll=OK - Listed global resources of all types
managerServlet.resourcesType=OK - Listed global resources of type [{0}]
managerServlet.saveFail=FAIL - Configuration save failed: [{0}]
diff --git a/java/org/apache/catalina/manager/LocalStrings_fr.properties b/java/org/apache/catalina/manager/LocalStrings_fr.properties
index 03ce4d1..fc2baef 100644
--- a/java/org/apache/catalina/manager/LocalStrings_fr.properties
+++ b/java/org/apache/catalina/manager/LocalStrings_fr.properties
@@ -139,6 +139,7 @@ managerServlet.notSslConnector=SSL n'est pas activé pour ce connecteur
managerServlet.objectNameFail=ECHEC - Le nom d''objet [{0}] n''a pas pu être enregistré pour le Servlet de Gestion
managerServlet.postCommand=ECHEC - Tentative d''utilisation de la commande [{0}] via une requête GET, mais POST est requis
managerServlet.reloaded=OK - L''application associée au chemin de contexte [{0}] a été rechargée
+managerServlet.renameFail=ECHEC - N''a pas pu renommer [{0}] vers [{1}], cela pourrait causer des problèmes pour de prochains déploiements
managerServlet.resourcesAll=OK - Liste des ressources globales de tout type
managerServlet.resourcesType=OK - Liste des ressources globales de type [{0}]
managerServlet.saveFail=ECHEC - La sauvegarde de la configuration a échoué: [{0}]
diff --git a/java/org/apache/catalina/manager/LocalStrings_ja.properties b/java/org/apache/catalina/manager/LocalStrings_ja.properties
index 8007ddf..662e25a 100644
--- a/java/org/apache/catalina/manager/LocalStrings_ja.properties
+++ b/java/org/apache/catalina/manager/LocalStrings_ja.properties
@@ -141,6 +141,7 @@ managerServlet.notSslConnector=このコネクターでは SSL が有効化さ
managerServlet.objectNameFail=FAIL - オブジェクト名 [{0}] を ManagerServlet として登録できません。
managerServlet.postCommand=FAIL - コマンド [{0}] を GET リクエストで実行しようとしましたが、POST リクエストでなければなりません。
managerServlet.reloaded=OK - コンテキストパス [{0}] のアプリケーションを再ロードしました
+managerServlet.renameFail=FAIL - [{0}]の名前を[{1}]に変更できません。 これにより、今後の展開で問題が発生する可能性があります。
managerServlet.resourcesAll=OK - すべてのタイプのグローバルリソースを列挙しました
managerServlet.resourcesType=OK - タイプ [{0}] のグローバルリソースを列挙しました
managerServlet.saveFail=FAIL - 設定の保存に失敗しました: [{0}]
diff --git a/java/org/apache/catalina/manager/LocalStrings_ko.properties b/java/org/apache/catalina/manager/LocalStrings_ko.properties
index 9d34ac1..bab468d 100644
--- a/java/org/apache/catalina/manager/LocalStrings_ko.properties
+++ b/java/org/apache/catalina/manager/LocalStrings_ko.properties
@@ -139,6 +139,7 @@ managerServlet.notSslConnector=SSL이 이 connector를 위해 사용 가능 상
managerServlet.objectNameFail=실패 - 매니저 서블릿을 위한 객체 이름 [{0}]을(를) 등록할 수 없습니다.
managerServlet.postCommand=실패 - GET 요청을 통해 명령 [{0}]을(를) 사용하려 시도했으나, POST 요청이 요구됩니다.
managerServlet.reloaded=OK - 컨텍스트 경로 [{0}]의 애플리케이션을 다시 로드했습니다.
+managerServlet.renameFail=실패 - [{0}]을(를) [{1}](으)로 이름을 변경할 수 없습니다. 이는 이후의 배치 작업들에서 문제들을 일으킬 수 있습니다.
managerServlet.resourcesAll=OK - 모든 타입들의 글로벌 리소스들이 목록으로 표시되었습니다.
managerServlet.resourcesType=OK - 타입이 [{0}]인 글로벌 리소스들의 목록을 표시했습니다.
managerServlet.saveFail=실패 - 설정을 저장하지 못했습니다: [{0}]
diff --git a/java/org/apache/catalina/manager/LocalStrings_ru.properties b/java/org/apache/catalina/manager/LocalStrings_ru.properties
index 4f0ab23..02bb71e 100644
--- a/java/org/apache/catalina/manager/LocalStrings_ru.properties
+++ b/java/org/apache/catalina/manager/LocalStrings_ru.properties
@@ -141,6 +141,7 @@ managerServlet.notSslConnector=Протокол SSL/TLS для этого кон
managerServlet.objectNameFail=ОШИБКА - Не удалось зарегистрировать имя объекта [{0}] для Manager Servlet
managerServlet.postCommand=ОШИБКА - Попытка использовать команду [{0}] через запрос GET но требуется POST
managerServlet.reloaded=OK - Приложение по пути контекста [{0}] было перезагружено
+managerServlet.renameFail=ОШИБКА - Невозможно переименовать [{0}] в [{1}]. Это может вызвать проблемы для будущих развертываний.
managerServlet.resourcesAll=OK - Перечислены глобальные ресурсы всех видов
managerServlet.resourcesType=OK - Перечислены глобальные ресурсы вида [{0}]
managerServlet.saveFail=ОШИБКА - Не удалось сохранить настройки: [{0}]
diff --git a/java/org/apache/catalina/manager/LocalStrings_zh_CN.properties b/java/org/apache/catalina/manager/LocalStrings_zh_CN.properties
index 93d1a00..b3b1a40 100644
--- a/java/org/apache/catalina/manager/LocalStrings_zh_CN.properties
+++ b/java/org/apache/catalina/manager/LocalStrings_zh_CN.properties
@@ -138,6 +138,7 @@ managerServlet.notSslConnector=不允许SSL连接
managerServlet.objectNameFail=FAIL - 不能将为Manager Servlet 注册 object name [{0}]
managerServlet.postCommand=失败 - 尝试通过GET请求使用命令[{0}],但需要POST
managerServlet.reloaded=OK - 上下文路径[{0}]重新加载应用程序
+managerServlet.renameFail=失败 - 无法重命名[{0}]为[{1}],这可能这未来部署时出现问题。
managerServlet.resourcesAll=OK - 列出所有类型的全部资源
managerServlet.resourcesType=OK - [{0}]类型全局资源列入清单
managerServlet.saveFail=失败 - 配置保存失败:[{0}]
diff --git a/java/org/apache/catalina/manager/ManagerServlet.java b/java/org/apache/catalina/manager/ManagerServlet.java
index b3d84b0..73f3593 100644
--- a/java/org/apache/catalina/manager/ManagerServlet.java
+++ b/java/org/apache/catalina/manager/ManagerServlet.java
@@ -690,7 +690,6 @@ public class ManagerServlet extends HttpServlet implements ContainerServlet {
log("managerServlet.storeConfig", e);
writer.println(smClient.getString("managerServlet.exception",
e.toString()));
- return;
}
} else {
String contextPath = path;
@@ -713,7 +712,6 @@ public class ManagerServlet extends HttpServlet implements ContainerServlet {
log("managerServlet.save[" + path + "]", e);
writer.println(smClient.getString("managerServlet.exception",
e.toString()));
- return;
}
}
}
@@ -802,7 +800,11 @@ public class ManagerServlet extends HttpServlet implements ContainerServlet {
return;
}
// Rename uploaded WAR file
- uploadedWar.renameTo(deployedWar);
+ if (!uploadedWar.renameTo(deployedWar)) {
+ writer.println(smClient.getString("managerServlet.renameFail",
+ uploadedWar, deployedWar));
+ return;
+ }
}
if (tag != null) {
// Copy WAR to the host's appBase
diff --git a/java/org/apache/catalina/manager/StatusManagerServlet.java b/java/org/apache/catalina/manager/StatusManagerServlet.java
index 71806b9..1fc7be2 100644
--- a/java/org/apache/catalina/manager/StatusManagerServlet.java
+++ b/java/org/apache/catalina/manager/StatusManagerServlet.java
@@ -401,14 +401,7 @@ public class StatusManagerServlet
requestProcessors.removeElement(objectName);
}
}
- String j2eeType = objectName.getKeyProperty("j2eeType");
- if (j2eeType != null) {
-
- }
}
}
-
}
-
-
}
diff --git a/java/org/apache/catalina/mbeans/JmxRemoteLifecycleListener.java b/java/org/apache/catalina/mbeans/JmxRemoteLifecycleListener.java
index 5795ec5..f3aefde 100644
--- a/java/org/apache/catalina/mbeans/JmxRemoteLifecycleListener.java
+++ b/java/org/apache/catalina/mbeans/JmxRemoteLifecycleListener.java
@@ -464,5 +464,32 @@ public class JmxRemoteLifecycleListener implements LifecycleListener {
sslServerSocket.setNeedClientAuth(getNeedClientAuth());
return sslServerSocket;
}
+
+ // Super class defines hashCode() and equals(). Probably not used in
+ // Tomcat but for safety, override them here.
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = super.hashCode();
+ result = prime * result + ((bindAddress == null) ? 0 : bindAddress.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (!super.equals(obj))
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ SslRmiServerBindSocketFactory other = (SslRmiServerBindSocketFactory) obj;
+ if (bindAddress == null) {
+ if (other.bindAddress != null)
+ return false;
+ } else if (!bindAddress.equals(other.bindAddress))
+ return false;
+ return true;
+ }
}
}
diff --git a/java/org/apache/catalina/tribes/Member.java b/java/org/apache/catalina/tribes/Member.java
index 11484eb..638abf8 100644
--- a/java/org/apache/catalina/tribes/Member.java
+++ b/java/org/apache/catalina/tribes/Member.java
@@ -17,6 +17,8 @@
package org.apache.catalina.tribes;
+import java.io.Serializable;
+
/**
* The Member interface, defines a member in the group.
* Each member can carry a set of properties, defined by the actual implementation.<BR>
@@ -27,7 +29,7 @@ package org.apache.catalina.tribes;
* since a member that has crashed and the starts up again on the same port/host is
* not guaranteed to be the same member, so no state transfers will ever be confused
*/
-public interface Member {
+public interface Member extends Serializable {
/**
* When a member leaves the cluster, the payload of the memberDisappeared member
diff --git a/res/findbugs/filter-false-positives.xml b/res/findbugs/filter-false-positives.xml
index f441fdf..659f87d 100644
--- a/res/findbugs/filter-false-positives.xml
+++ b/res/findbugs/filter-false-positives.xml
@@ -83,12 +83,7 @@
<Bug code="REC"/>
</Match>
<Match>
- <Class name="org.apache.catalina.ant.jmx.JMXAccessorCondition"/>
- <Method name="accessJMXValue"/>
- <Bug code="REC"/>
- </Match>
- <Match>
- <Class name="org.apache.catalina.ant.jmx.JMXAccessorEqualsCondition"/>
+ <Class name="org.apache.catalina.ant.jmx.JMXAccessorConditionBase"/>
<Method name="accessJMXValue"/>
<Bug code="REC"/>
</Match>
@@ -127,6 +122,12 @@
<Bug code="RCN"/>
</Match>
<Match>
+ <!-- False positive. It is lifecycle state that is being protected -->
+ <Class name="org.apache.catalina.authenticator.SingleSignOn" />
+ <Field name="engine" />
+ <Bug pattern="IS2_INCONSISTENT_SYNC" />
+ </Match>
+ <Match>
<!-- req.getRemoteUser(), req.getAuthType(), request.getQueryString() can
return null because o.a.t.util.buf.MessageBytes.toString() can return NULL
-->
@@ -173,12 +174,53 @@
<Bug code="RCN"/>
</Match>
<Match>
+ <!-- Exception caught deliberately -->
+ <Class name="org.apache.catalina.core.NamingContextListener" />
+ <Method name="constructEnvEntry" />
+ <Bug pattern="REC_CATCH_EXCEPTION" />
+ </Match>
+ <Match>
+ <!-- Code uses same approach as CopyOnWriteArrayList -->
+ <Class name="org.apache.catalina.core.StandardContext" />
+ <Field name="constraints" />
+ <Bug pattern="VO_VOLATILE_REFERENCE_TO_ARRAY" />
+ </Match>
+ <Match>
+ <!-- Sync is for lifecycle state, not CookieProcessor -->
+ <Class name="org.apache.catalina.core.StandardContext" />
+ <Field name="cookieProcessor" />
+ <Bug pattern="IS2_INCONSISTENT_SYNC" />
+ </Match>
+ <Match>
+ <!-- Calling sleep while holding a lock is deliberate -->
+ <Class name="org.apache.catalina.core.StandardContext" />
+ <Method name="stopInternal" />
+ <Bug pattern="SWL_SLEEP_WITH_LOCK_HELD" />
+ </Match>
+ <Match>
<!-- Have to trigger GC for leak detection to work. Clearly documented -->
<Class name="org.apache.catalina.core.StandardHost" />
<Method name="findReloadedContextMemoryLeaks" />
<Bug code="Dm" />
</Match>
<Match>
+ <!-- Sync not targeting these fields -->
+ <Class name="org.apache.catalina.core.StandardWrapper" />
+ <Or>
+ <Field name="multipartConfigElement" />
+ <Field name="servletClass" />
+ <Field name="swallowOutput" />
+ <Field name="unloadDelay" />
+ </Or>
+ <Bug pattern="IS2_INCONSISTENT_SYNC" />
+ </Match>
+ <Match>
+ <!-- There is only a single wait condition -->
+ <Class name="org.apache.catalina.core.StandardWrapper" />
+ <Method name="deallocate" />
+ <Bug pattern="NO_NOTIFY_NOT_NOTIFYALL" />
+ </Match>
+ <Match>
<!-- Sleep is of short duration and lock is required -->
<Class name="org.apache.catalina.core.StandardWrapper" />
<Method name="unload" />
@@ -204,6 +246,12 @@
<Bug code="DE" />
</Match>
<Match>
+ <!-- False positive. It is lifecycle state that is being protected -->
+ <Class name="org.apache.catalina.ha.authenticator.ClusterSingleSignOn" />
+ <Field name="cluster" />
+ <Bug pattern="IS2_INCONSISTENT_SYNC" />
+ </Match>
+ <Match>
<!-- shost will not be null in normal usage -->
<Class name="org.apache.catalina.ha.backend.CollectedInfo" />
<Method name="init" />
@@ -225,6 +273,29 @@
<Bug code="DE" />
</Match>
<Match>
+ <!-- False positive caused by additional method syncs -->
+ <Class name="org.apache.catalina.ha.session.DeltaManager" />
+ <Field name="receiverQueue" />
+ <Pattern code="IS2_INCONSISTENT_SYNC" />
+ </Match>
+ <Match>
+ <!-- False positive caused by method syncs -->
+ <Class name="org.apache.catalina.ha.session.JvmRouteBinderValve" />
+ <Field name="cluster" />
+ <Pattern code="IS2_INCONSISTENT_SYNC" />
+ </Match>
+ <Match>
+ <!-- Design choice to reduce need for syncs -->
+ <Class name="org.apache.catalina.ha.tcp.ReplicationValve" />
+ <Or>
+ <Field name="nrOfCrossContextSendRequests" />
+ <Field name="nrOfFilterRequests" />
+ <Field name="nrOfRequests" />
+ <Field name="nrOfSendRequests" />
+ </Or>
+ <Pattern code="VO_VOLATILE_INCREMENT" />
+ </Match>
+ <Match>
<!-- Field is only modified during Servlet load -->
<Class name="org.apache.catalina.manager.host.HostManagerServlet" />
<Bug code="MSF" />
@@ -236,6 +307,19 @@
<Bug code="REC" />
</Match>
<Match>
+ <!-- The fields are only set in setWrapper() which Tomcat calls once during
+ initialisation. All other accesses are reads. -->
+ <Class name="org.apache.catalina.manager.ManagerServlet" />
+ <Or>
+ <Field name="context" />
+ <Field name="host" />
+ <Field name="mBeanServer" />
+ <Field name="oname" />
+ <Field name="wrapper" />
+ </Or>
+ <Bug pattern="MSF_MUTABLE_SERVLET_FIELD" />
+ </Match>
+ <Match>
<!-- SQL construction is safe since it is from trusted config -->
<Or>
<Class name="org.apache.catalina.realm.DataSourceRealm" />
@@ -243,6 +327,8 @@
</Or>
<Or>
<Method name="credentials" />
+ <Method name="getPassword" />
+ <Method name="getRoles" />
<Method name="roles" />
</Or>
<Bug pattern="SQL_PREPARED_STATEMENT_GENERATED_FROM_NONCONSTANT_STRING" />
@@ -253,6 +339,15 @@
<Bug code="IS" />
</Match>
<Match>
+ <!-- Sync is protecting preparedRoles, not these fields -->
+ <Class name="org.apache.catalina.realm.JDBCRealm" />
+ <Or>
+ <Field name="roleNameCol" />
+ <Field name="userRoleTable" />
+ </Or>
+ <Bug pattern="IS2_INCONSISTENT_SYNC " />
+ </Match>
+ <Match>
<!-- roles will be initialized in addAttributeValues -->
<Class name="org.apache.catalina.realm.JNDIRealm" />
<Or>
@@ -262,6 +357,12 @@
<Bug code="NP" />
</Match>
<Match>
+ <!-- Sync is protecting authenticate90, not this field -->
+ <Class name="org.apache.catalina.realm.JNDIRealm" />
+ <Field name="userPatternFormatArray" />
+ <Bug pattern="IS2_INCONSISTENT_SYNC " />
+ </Match>
+ <Match>
<!-- request.getRequestPathMB(), request.getQueryString() can be null because
o.a.t.util.buf.MessageBytes.toString() can return NULL -->
<Class name="org.apache.catalina.realm.RealmBase"/>
@@ -1320,4 +1421,4 @@
</Or>
<Bug pattern="DLS_DEAD_LOCAL_STORE"/>
</Match>
-</FindBugsFilter>
\ No newline at end of file
+</FindBugsFilter>
---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org