You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by re...@apache.org on 2020/06/23 11:41:48 UTC

[tomcat] branch 9.0.x updated (e358d0d -> 05ba3d4)

This is an automated email from the ASF dual-hosted git repository.

remm pushed a change to branch 9.0.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git.


    from e358d0d  Tab police
     new 55ef30d  Add a code generator for server.xml by taking advantage of the digester
     new 93a7a3b  Drop from merge
     new 1d10b19  Do the code generation tooling for context.xml files
     new 0306b6b  Various improvements to code generation
     new 05ba3d4  Fix IDE nags

The 5 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../apache/catalina/startup/AddPortOffsetRule.java |   7 +
 java/org/apache/catalina/startup/Catalina.java     | 264 +++++++++++++++------
 .../catalina/startup/CertificateCreateRule.java    |  11 +-
 .../catalina/startup/ConnectorCreateRule.java      |  32 ++-
 .../org/apache/catalina/startup/ContextConfig.java | 246 +++++++++++++++++--
 .../apache/catalina/startup/ContextRuleSet.java    |   2 +-
 .../startup/CopyParentClassLoaderRule.java         |   6 +
 .../catalina/startup/LifecycleListenerRule.java    |   6 +
 .../catalina/startup/ListenerCreateRule.java       |  12 +-
 .../catalina/startup/LocalStrings.properties       |   3 +-
 .../org/apache/catalina/startup/NamingRuleSet.java |  12 +-
 .../catalina/startup/SetAllPropertiesRule.java     |  53 +----
 .../catalina/startup/SetContextPropertiesRule.java |  43 +---
 .../apache/catalina/startup/SetNextNamingRule.java |  13 +
 java/org/apache/catalina/startup/Tomcat.java       |  39 ++-
 .../org/apache/tomcat/util/IntrospectionUtils.java |  58 ++++-
 .../tomcat/util/descriptor/web/ContextEjb.java     |   1 -
 .../util/descriptor/web/ContextEnvironment.java    |   1 +
 .../tomcat/util/digester/CallMethodRule.java       |  18 ++
 java/org/apache/tomcat/util/digester/Digester.java |  47 ++++
 .../tomcat/util/digester/ObjectCreateRule.java     |  11 +
 .../apache/tomcat/util/digester/SetNextRule.java   |   6 +
 .../tomcat/util/digester/SetPropertiesRule.java    |  45 +++-
 webapps/docs/changelog.xml                         |   4 +
 24 files changed, 743 insertions(+), 197 deletions(-)


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org


[tomcat] 05/05: Fix IDE nags

Posted by re...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

remm pushed a commit to branch 9.0.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git

commit 05ba3d4d1ca6261ec12967b1010d34ba7adc6ccb
Author: Mark Thomas <ma...@apache.org>
AuthorDate: Tue Jun 23 12:08:20 2020 +0100

    Fix IDE nags
---
 java/org/apache/catalina/startup/Catalina.java                 | 4 ++--
 java/org/apache/catalina/startup/SetAllPropertiesRule.java     | 1 +
 java/org/apache/catalina/startup/SetContextPropertiesRule.java | 1 +
 3 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/java/org/apache/catalina/startup/Catalina.java b/java/org/apache/catalina/startup/Catalina.java
index d2456d6..215f179 100644
--- a/java/org/apache/catalina/startup/Catalina.java
+++ b/java/org/apache/catalina/startup/Catalina.java
@@ -697,7 +697,7 @@ public class Catalina {
         }
 
         if(log.isInfoEnabled()) {
-            log.info(sm.getString("catalina.init", TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - t1)));
+            log.info(sm.getString("catalina.init", Long.toString(TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - t1))));
         }
     }
 
@@ -747,7 +747,7 @@ public class Catalina {
         }
 
         if (log.isInfoEnabled()) {
-            log.info(sm.getString("catalina.startup", TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - t1)));
+            log.info(sm.getString("catalina.startup", Long.toString(TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - t1))));
         }
 
         // Register shutdown hook
diff --git a/java/org/apache/catalina/startup/SetAllPropertiesRule.java b/java/org/apache/catalina/startup/SetAllPropertiesRule.java
index 93d59e9..7288fe6 100644
--- a/java/org/apache/catalina/startup/SetAllPropertiesRule.java
+++ b/java/org/apache/catalina/startup/SetAllPropertiesRule.java
@@ -26,6 +26,7 @@ import org.apache.tomcat.util.digester.SetPropertiesRule;
  * @author Remy Maucherat
  * @deprecated This will be removed in Tomcat 10
  */
+@Deprecated
 public class SetAllPropertiesRule extends SetPropertiesRule {
 
 
diff --git a/java/org/apache/catalina/startup/SetContextPropertiesRule.java b/java/org/apache/catalina/startup/SetContextPropertiesRule.java
index fda8742..36ad6a9 100644
--- a/java/org/apache/catalina/startup/SetContextPropertiesRule.java
+++ b/java/org/apache/catalina/startup/SetContextPropertiesRule.java
@@ -27,6 +27,7 @@ import org.apache.tomcat.util.digester.SetPropertiesRule;
  * @author Remy Maucherat
  * @deprecated This will be removed in Tomcat 10
  */
+@Deprecated
 public class SetContextPropertiesRule extends SetPropertiesRule {
 
 


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org


[tomcat] 01/05: Add a code generator for server.xml by taking advantage of the digester

Posted by re...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

remm pushed a commit to branch 9.0.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git

commit 55ef30d459f859d92a562eed9bd1b7d080a510b4
Author: remm <re...@apache.org>
AuthorDate: Thu Jun 18 15:07:39 2020 +0200

    Add a code generator for server.xml by taking advantage of the digester
    
    This produces a Tomcat embedded out of server.xml. Only the rules used
    by server.xml have code generation at the moment. The code is generated
    in the work directory.
    Also minor refactorings and simplifications, like for the properties
    related rules which could use the main one.
    For some reason, it is only marginally faster than actually using the
    digester and regular XML parsing. I expected better results overall and
    it could be useful to investigate.
    However this still has benefits:
    - It is better for AOT compilation; I will likely be moving the Tomcat
    standalone "blueprint" for AOT compilation use from res/tomcat-maven to
    a new module since this will eventually expand to include all the extra
    steps that can benefit AOT
    - It can be educational about how to use the Tomcat embedded API
    - People using Tomcat embedded could use it to generate code for complex
    configurations, and this guarantees server.xml equivalence
    - Maybe there will be benefits if the technique is applied to the larger
    and more numerous web.xml files (but it is possible other methods are
    better there, I will investigate)
---
 .../apache/catalina/startup/AddPortOffsetRule.java |   7 +
 java/org/apache/catalina/startup/Catalina.java     | 144 +++++++++++++++------
 .../catalina/startup/CertificateCreateRule.java    |  11 +-
 .../catalina/startup/ConnectorCreateRule.java      |  31 ++++-
 .../apache/catalina/startup/ContextRuleSet.java    |   2 +-
 .../startup/CopyParentClassLoaderRule.java         |   6 +
 .../catalina/startup/LifecycleListenerRule.java    |   6 +
 .../org/apache/catalina/startup/NamingRuleSet.java |  12 +-
 .../catalina/startup/SetAllPropertiesRule.java     |  52 +-------
 .../catalina/startup/SetContextPropertiesRule.java |  42 +-----
 .../apache/catalina/startup/SetNextNamingRule.java |   7 +
 .../org/apache/tomcat/util/IntrospectionUtils.java |  31 ++++-
 .../tomcat/util/descriptor/web/ContextEjb.java     |   4 +
 .../util/descriptor/web/ContextEnvironment.java    |   4 +
 .../tomcat/util/descriptor/web/ContextHandler.java |   4 +
 .../util/descriptor/web/ContextLocalEjb.java       |   4 +
 .../util/descriptor/web/ContextResource.java       |   4 +
 .../util/descriptor/web/ContextResourceEnvRef.java |   4 +
 .../util/descriptor/web/ContextResourceLink.java   |   4 +
 .../tomcat/util/descriptor/web/ContextService.java |   4 +
 .../util/descriptor/web/MessageDestination.java    |   4 +
 .../util/descriptor/web/MessageDestinationRef.java |   4 +
 .../tomcat/util/descriptor/web/ResourceBase.java   |  10 ++
 .../tomcat/util/digester/CallMethodRule.java       |  14 ++
 java/org/apache/tomcat/util/digester/Digester.java |  27 ++++
 .../tomcat/util/digester/ObjectCreateRule.java     |  10 ++
 .../apache/tomcat/util/digester/SetNextRule.java   |   6 +
 .../tomcat/util/digester/SetPropertiesRule.java    |  48 ++++++-
 28 files changed, 362 insertions(+), 144 deletions(-)

diff --git a/java/org/apache/catalina/startup/AddPortOffsetRule.java b/java/org/apache/catalina/startup/AddPortOffsetRule.java
index 9b961cb..50f3fc0 100644
--- a/java/org/apache/catalina/startup/AddPortOffsetRule.java
+++ b/java/org/apache/catalina/startup/AddPortOffsetRule.java
@@ -32,5 +32,12 @@ public class AddPortOffsetRule extends Rule {
 
         int portOffset = server.getPortOffset();
         conn.setPortOffset(portOffset);
+
+        StringBuilder code = digester.getGeneratedCode();
+        if (code != null) {
+            code.append(digester.toVariableName(conn)).append(".setPortOffset(");
+            code.append(digester.toVariableName(server)).append(".getPortOffset());");
+            code.append(System.lineSeparator());
+        }
     }
 }
\ No newline at end of file
diff --git a/java/org/apache/catalina/startup/Catalina.java b/java/org/apache/catalina/startup/Catalina.java
index 00c820e..ecf197f 100644
--- a/java/org/apache/catalina/startup/Catalina.java
+++ b/java/org/apache/catalina/startup/Catalina.java
@@ -19,6 +19,7 @@ package org.apache.catalina.startup;
 
 import java.io.File;
 import java.io.FileInputStream;
+import java.io.FileWriter;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
@@ -29,6 +30,7 @@ import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.TimeUnit;
 import java.util.logging.LogManager;
 
 import org.apache.catalina.Container;
@@ -132,6 +134,19 @@ public class Catalina {
     protected boolean loaded = false;
 
 
+    /**
+     * Rethrow exceptions on init failure.
+     */
+    protected boolean throwOnInitFailure =
+            Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE");
+
+
+    /**
+     * Generate Tomcat embedded code from server.xml.
+     */
+    protected boolean generateCode = false;
+
+
     // ----------------------------------------------------------- Constructors
 
     public Catalina() {
@@ -237,6 +252,8 @@ public class Catalina {
                 isConfig = false;
             } else if (arg.equals("-config")) {
                 isConfig = true;
+            } else if (arg.equals("-generateCode")) {
+                generateCode = true;
             } else if (arg.equals("-nonaming")) {
                 setUseNaming(false);
             } else if (arg.equals("-help")) {
@@ -278,7 +295,6 @@ public class Catalina {
      * @return the main digester to parse server.xml
      */
     protected Digester createStartDigester() {
-        long t1=System.currentTimeMillis();
         // Initialize the digester
         Digester digester = new Digester();
         digester.setValidating(false);
@@ -348,11 +364,10 @@ public class Catalina {
                             "addExecutor",
                             "org.apache.catalina.Executor");
 
-
         digester.addRule("Server/Service/Connector",
                          new ConnectorCreateRule());
-        digester.addRule("Server/Service/Connector", new SetAllPropertiesRule(
-                new String[]{"executor", "sslImplementationName", "protocol"}));
+        digester.addSetProperties("Server/Service/Connector",
+                new String[]{"executor", "sslImplementationName", "protocol"});
         digester.addSetNext("Server/Service/Connector",
                             "addConnector",
                             "org.apache.catalina.connector.Connector");
@@ -368,8 +383,7 @@ public class Catalina {
 
         digester.addRule("Server/Service/Connector/SSLHostConfig/Certificate",
                          new CertificateCreateRule());
-        digester.addRule("Server/Service/Connector/SSLHostConfig/Certificate",
-                         new SetAllPropertiesRule(new String[]{"type"}));
+        digester.addSetProperties("Server/Service/Connector/SSLHostConfig/Certificate", new String[]{"type"});
         digester.addSetNext("Server/Service/Connector/SSLHostConfig/Certificate",
                             "addCertificate",
                             "org.apache.tomcat.util.net.SSLHostConfigCertificate");
@@ -417,10 +431,6 @@ public class Catalina {
                          new SetParentClassLoaderRule(parentClassLoader));
         addClusterRuleSet(digester, "Server/Service/Engine/Cluster/");
 
-        long t2=System.currentTimeMillis();
-        if (log.isDebugEnabled()) {
-            log.debug("Digester for server.xml created " + ( t2-t1 ));
-        }
         return digester;
 
     }
@@ -555,15 +565,44 @@ public class Catalina {
         ConfigFileLoader.setSource(new CatalinaBaseConfigurationSource(Bootstrap.getCatalinaBaseFile(), getConfigFile()));
         File file = configFile();
 
-        // Create and execute our Digester
-        Digester digester = createStartDigester();
-
         try (ConfigurationSource.Resource resource = ConfigFileLoader.getSource().getServerXml()) {
-            InputStream inputStream = resource.getInputStream();
-            InputSource inputSource = new InputSource(resource.getURI().toURL().toString());
-            inputSource.setByteStream(inputStream);
-            digester.push(this);
-            digester.parse(inputSource);
+            String serverXmlId = String.valueOf(resource.getLastModified());
+            String serverXmlClassName = "catalina.ServerXml_" + serverXmlId;
+            ServerXml serverXml = null;
+            try {
+                serverXml = (ServerXml) Catalina.class.getClassLoader().loadClass(serverXmlClassName).newInstance();
+            } catch (ClassNotFoundException e) {
+                // Ignore, no generated code found
+            }
+            if (serverXml != null) {
+                serverXml.load(this);
+            } else {
+                // Create and execute our Digester
+                Digester digester = createStartDigester();
+
+                InputStream inputStream = resource.getInputStream();
+                InputSource inputSource = new InputSource(resource.getURI().toURL().toString());
+                inputSource.setByteStream(inputStream);
+                digester.push(this);
+                if (generateCode) {
+                    digester.startGeneratingCode();
+                    generateClassHeader(digester, String.valueOf(resource.getLastModified()));
+                }
+                digester.parse(inputSource);
+                if (generateCode) {
+                    generateClassFooter(digester);
+                    File generatedSourceFolder = new File(new File(Bootstrap.getCatalinaHomeFile(), "work"), "catalina");
+                    if (generatedSourceFolder.isDirectory() || generatedSourceFolder.mkdirs()) {
+                        File generatedSourceFile = new File(generatedSourceFolder,
+                                "ServerXml_" + serverXmlId + ".java");
+                        if (!generatedSourceFile.exists()) {
+                            try (FileWriter writer = new FileWriter(generatedSourceFile)) {
+                                writer.write(digester.getGeneratedCode().toString());
+                            }
+                        }
+                    }
+                }
+            }
         } catch (Exception e) {
             log.warn(sm.getString("catalina.configFail", file.getAbsolutePath()), e);
             if (file.exists() && !file.canRead()) {
@@ -590,9 +629,8 @@ public class Catalina {
             }
         }
 
-        long t2 = System.nanoTime();
         if(log.isInfoEnabled()) {
-            log.info(sm.getString("catalina.init", Long.valueOf((t2 - t1) / 1000000)));
+            log.info(sm.getString("catalina.init", TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - t1)));
         }
     }
 
@@ -788,6 +826,28 @@ public class Catalina {
     }
 
 
+    protected void generateClassHeader(Digester digester, String time) {
+        StringBuilder code = digester.getGeneratedCode();
+        code.append("package catalina;").append(System.lineSeparator());
+        code.append("public class ServerXml_").append(time).append(" implements ");
+        code.append(ServerXml.class.getName().replace('$', '.')).append(" {").append(System.lineSeparator());
+        code.append("public void load(").append(Catalina.class.getName());
+        code.append(" ").append(digester.toVariableName(this)).append(") {").append(System.lineSeparator());
+    }
+
+
+    protected void generateClassFooter(Digester digester) {
+        StringBuilder code = digester.getGeneratedCode();
+        code.append("}").append(System.lineSeparator());
+        code.append("}").append(System.lineSeparator());
+    }
+
+
+    public interface ServerXml {
+        public void load(Catalina catalina);
+    }
+
+
     // --------------------------------------- CatalinaShutdownHook Inner Class
 
     // XXX Should be moved to embedded !
@@ -819,39 +879,41 @@ public class Catalina {
 
     private static final Log log = LogFactory.getLog(Catalina.class);
 
-}
 
+    /**
+     * Rule that sets the parent class loader for the top object on the stack,
+     * which must be a <code>Container</code>.
+     */
 
-// ------------------------------------------------------------ Private Classes
-
+    final class SetParentClassLoaderRule extends Rule {
 
-/**
- * Rule that sets the parent class loader for the top object on the stack,
- * which must be a <code>Container</code>.
- */
+        public SetParentClassLoaderRule(ClassLoader parentClassLoader) {
 
-final class SetParentClassLoaderRule extends Rule {
+            this.parentClassLoader = parentClassLoader;
 
-    public SetParentClassLoaderRule(ClassLoader parentClassLoader) {
+        }
 
-        this.parentClassLoader = parentClassLoader;
+        ClassLoader parentClassLoader = null;
 
-    }
+        @Override
+        public void begin(String namespace, String name, Attributes attributes)
+            throws Exception {
 
-    ClassLoader parentClassLoader = null;
+            if (digester.getLogger().isDebugEnabled()) {
+                digester.getLogger().debug("Setting parent class loader");
+            }
 
-    @Override
-    public void begin(String namespace, String name, Attributes attributes)
-        throws Exception {
+            Container top = (Container) digester.peek();
+            top.setParentClassLoader(parentClassLoader);
 
-        if (digester.getLogger().isDebugEnabled()) {
-            digester.getLogger().debug("Setting parent class loader");
+            StringBuilder code = digester.getGeneratedCode();
+            if (code != null) {
+                code.append(digester.toVariableName(top)).append(".setParentClassLoader(");
+                code.append(digester.toVariableName(Catalina.this)).append(".getParentClassLoader());");
+                code.append(System.lineSeparator());
+            }
         }
 
-        Container top = (Container) digester.peek();
-        top.setParentClassLoader(parentClassLoader);
-
     }
 
-
 }
diff --git a/java/org/apache/catalina/startup/CertificateCreateRule.java b/java/org/apache/catalina/startup/CertificateCreateRule.java
index 1db3c70..f41c001 100644
--- a/java/org/apache/catalina/startup/CertificateCreateRule.java
+++ b/java/org/apache/catalina/startup/CertificateCreateRule.java
@@ -29,7 +29,7 @@ public class CertificateCreateRule extends Rule {
 
     @Override
     public void begin(String namespace, String name, Attributes attributes) throws Exception {
-        SSLHostConfig sslHostConfig = (SSLHostConfig)digester.peek();
+        SSLHostConfig sslHostConfig = (SSLHostConfig) digester.peek();
 
         Type type;
         String typeValue = attributes.getValue("type");
@@ -42,6 +42,15 @@ public class CertificateCreateRule extends Rule {
         SSLHostConfigCertificate certificate = new SSLHostConfigCertificate(sslHostConfig, type);
 
         digester.push(certificate);
+
+        StringBuilder code = digester.getGeneratedCode();
+        if (code != null) {
+            code.append(SSLHostConfigCertificate.class.getName()).append(" ").append(digester.toVariableName(certificate));
+            code.append(" = new ").append(SSLHostConfigCertificate.class.getName());
+            code.append("(").append(digester.toVariableName(sslHostConfig));
+            code.append(", ").append(Type.class.getName().replace('$', '.')).append(".").append(type).append(");");
+            code.append(System.lineSeparator());
+        }
     }
 
 
diff --git a/java/org/apache/catalina/startup/ConnectorCreateRule.java b/java/org/apache/catalina/startup/ConnectorCreateRule.java
index 83e488c..a1b53d9 100644
--- a/java/org/apache/catalina/startup/ConnectorCreateRule.java
+++ b/java/org/apache/catalina/startup/ConnectorCreateRule.java
@@ -24,6 +24,7 @@ import java.lang.reflect.Method;
 import org.apache.catalina.Executor;
 import org.apache.catalina.Service;
 import org.apache.catalina.connector.Connector;
+import org.apache.coyote.http11.AbstractHttp11JsseProtocol;
 import org.apache.juli.logging.Log;
 import org.apache.juli.logging.LogFactory;
 import org.apache.tomcat.util.IntrospectionUtils;
@@ -56,12 +57,14 @@ public class ConnectorCreateRule extends Rule {
     @Override
     public void begin(String namespace, String name, Attributes attributes)
             throws Exception {
-        Service svc = (Service)digester.peek();
+        Service svc = (Service) digester.peek();
         Executor ex = null;
-        if ( attributes.getValue("executor")!=null ) {
-            ex = svc.getExecutor(attributes.getValue("executor"));
+        String executorName = attributes.getValue("executor");
+        if (executorName != null ) {
+            ex = svc.getExecutor(executorName);
         }
-        Connector con = new Connector(attributes.getValue("protocol"));
+        String protocolName = attributes.getValue("protocol");
+        Connector con = new Connector(protocolName);
         if (ex != null) {
             setExecutor(con, ex);
         }
@@ -70,6 +73,26 @@ public class ConnectorCreateRule extends Rule {
             setSSLImplementationName(con, sslImplementationName);
         }
         digester.push(con);
+
+        StringBuilder code = digester.getGeneratedCode();
+        if (code != null) {
+            code.append(Connector.class.getName()).append(" ").append(digester.toVariableName(con));
+            code.append(" = new ").append(Connector.class.getName());
+            code.append("(new ").append(con.getProtocolHandlerClassName()).append("());");
+            code.append(System.lineSeparator());
+            if (ex != null) {
+                code.append(digester.toVariableName(con)).append(".getProtocolHandler().setExecutor(");
+                code.append(digester.toVariableName(svc)).append(".getExecutor(").append(executorName);
+                code.append("));");
+                code.append(System.lineSeparator());
+            }
+            if (sslImplementationName != null) {
+                code.append("((").append(AbstractHttp11JsseProtocol.class.getName()).append("<?>) ");
+                code.append(digester.toVariableName(con)).append(".getProtocolHandler()).setSslImplementationName(\"");
+                code.append(sslImplementationName).append("\");");
+                code.append(System.lineSeparator());
+            }
+        }
     }
 
     private static void setExecutor(Connector con, Executor ex) throws Exception {
diff --git a/java/org/apache/catalina/startup/ContextRuleSet.java b/java/org/apache/catalina/startup/ContextRuleSet.java
index 3476317..e230d54 100644
--- a/java/org/apache/catalina/startup/ContextRuleSet.java
+++ b/java/org/apache/catalina/startup/ContextRuleSet.java
@@ -98,7 +98,7 @@ public class ContextRuleSet implements RuleSet {
                     "org.apache.catalina.core.StandardContext", "className");
             digester.addSetProperties(prefix + "Context");
         } else {
-            digester.addRule(prefix + "Context", new SetContextPropertiesRule());
+            digester.addSetProperties(prefix + "Context", new String[]{"path", "docBase"});
         }
 
         if (create) {
diff --git a/java/org/apache/catalina/startup/CopyParentClassLoaderRule.java b/java/org/apache/catalina/startup/CopyParentClassLoaderRule.java
index 8bf60c6..20a8c8a 100644
--- a/java/org/apache/catalina/startup/CopyParentClassLoaderRule.java
+++ b/java/org/apache/catalina/startup/CopyParentClassLoaderRule.java
@@ -71,6 +71,12 @@ public class CopyParentClassLoaderRule extends Rule {
             (ClassLoader) method.invoke(parent, new Object[0]);
         child.setParentClassLoader(classLoader);
 
+        StringBuilder code = digester.getGeneratedCode();
+        if (code != null) {
+            code.append(digester.toVariableName(child)).append(".setParentClassLoader(");
+            code.append(digester.toVariableName(parent)).append(".getParentClassLoader());");
+            code.append(System.lineSeparator());
+        }
     }
 
 
diff --git a/java/org/apache/catalina/startup/LifecycleListenerRule.java b/java/org/apache/catalina/startup/LifecycleListenerRule.java
index f70a7b2..d65bf59 100644
--- a/java/org/apache/catalina/startup/LifecycleListenerRule.java
+++ b/java/org/apache/catalina/startup/LifecycleListenerRule.java
@@ -128,6 +128,12 @@ public class LifecycleListenerRule extends Rule {
 
         // Add this LifecycleListener to our associated component
         c.addLifecycleListener(listener);
+
+        StringBuilder code = digester.getGeneratedCode();
+        if (code != null) {
+            code.append(digester.toVariableName(c)).append(".addLifecycleListener(");
+            code.append("new ").append(className).append("());").append(System.lineSeparator());
+        }
     }
 
 
diff --git a/java/org/apache/catalina/startup/NamingRuleSet.java b/java/org/apache/catalina/startup/NamingRuleSet.java
index dc5fbb3..b2a3baa 100644
--- a/java/org/apache/catalina/startup/NamingRuleSet.java
+++ b/java/org/apache/catalina/startup/NamingRuleSet.java
@@ -75,7 +75,7 @@ public class NamingRuleSet implements RuleSet {
 
         digester.addObjectCreate(prefix + "Ejb",
                                  "org.apache.tomcat.util.descriptor.web.ContextEjb");
-        digester.addRule(prefix + "Ejb", new SetAllPropertiesRule());
+        digester.addSetProperties(prefix + "Ejb");
         digester.addRule(prefix + "Ejb",
                 new SetNextNamingRule("addEjb",
                             "org.apache.tomcat.util.descriptor.web.ContextEjb"));
@@ -89,35 +89,35 @@ public class NamingRuleSet implements RuleSet {
 
         digester.addObjectCreate(prefix + "LocalEjb",
                                  "org.apache.tomcat.util.descriptor.web.ContextLocalEjb");
-        digester.addRule(prefix + "LocalEjb", new SetAllPropertiesRule());
+        digester.addSetProperties(prefix + "LocalEjb");
         digester.addRule(prefix + "LocalEjb",
                 new SetNextNamingRule("addLocalEjb",
                             "org.apache.tomcat.util.descriptor.web.ContextLocalEjb"));
 
         digester.addObjectCreate(prefix + "Resource",
                                  "org.apache.tomcat.util.descriptor.web.ContextResource");
-        digester.addRule(prefix + "Resource", new SetAllPropertiesRule());
+        digester.addSetProperties(prefix + "Resource");
         digester.addRule(prefix + "Resource",
                 new SetNextNamingRule("addResource",
                             "org.apache.tomcat.util.descriptor.web.ContextResource"));
 
         digester.addObjectCreate(prefix + "ResourceEnvRef",
             "org.apache.tomcat.util.descriptor.web.ContextResourceEnvRef");
-        digester.addRule(prefix + "ResourceEnvRef", new SetAllPropertiesRule());
+        digester.addSetProperties(prefix + "ResourceEnvRef");
         digester.addRule(prefix + "ResourceEnvRef",
                 new SetNextNamingRule("addResourceEnvRef",
                             "org.apache.tomcat.util.descriptor.web.ContextResourceEnvRef"));
 
         digester.addObjectCreate(prefix + "ServiceRef",
             "org.apache.tomcat.util.descriptor.web.ContextService");
-        digester.addRule(prefix + "ServiceRef", new SetAllPropertiesRule());
+        digester.addSetProperties(prefix + "ServiceRef");
         digester.addRule(prefix + "ServiceRef",
                 new SetNextNamingRule("addService",
                             "org.apache.tomcat.util.descriptor.web.ContextService"));
 
         digester.addObjectCreate(prefix + "Transaction",
             "org.apache.tomcat.util.descriptor.web.ContextTransaction");
-        digester.addRule(prefix + "Transaction", new SetAllPropertiesRule());
+        digester.addSetProperties(prefix + "Transaction");
         digester.addRule(prefix + "Transaction",
                 new SetNextNamingRule("setTransaction",
                             "org.apache.tomcat.util.descriptor.web.ContextTransaction"));
diff --git a/java/org/apache/catalina/startup/SetAllPropertiesRule.java b/java/org/apache/catalina/startup/SetAllPropertiesRule.java
index 5115e32..93d59e9 100644
--- a/java/org/apache/catalina/startup/SetAllPropertiesRule.java
+++ b/java/org/apache/catalina/startup/SetAllPropertiesRule.java
@@ -18,62 +18,24 @@
 
 package org.apache.catalina.startup;
 
-import java.util.HashMap;
-
-import org.apache.tomcat.util.IntrospectionUtils;
-import org.apache.tomcat.util.digester.Rule;
-import org.xml.sax.Attributes;
+import org.apache.tomcat.util.digester.SetPropertiesRule;
 
 /**
  * Rule that uses the introspection utils to set properties.
  *
  * @author Remy Maucherat
+ * @deprecated This will be removed in Tomcat 10
  */
-public class SetAllPropertiesRule extends Rule {
+public class SetAllPropertiesRule extends SetPropertiesRule {
 
 
     // ----------------------------------------------------------- Constructors
-    public SetAllPropertiesRule() {}
-
-    public SetAllPropertiesRule(String[] exclude) {
-        for (String s : exclude) if (s != null) this.excludes.put(s, s);
+    public SetAllPropertiesRule() {
+        super();
     }
 
-    // ----------------------------------------------------- Instance Variables
-    protected final HashMap<String,String> excludes = new HashMap<>();
-
-    // --------------------------------------------------------- Public Methods
-
-
-    /**
-     * Handle the beginning of an XML element.
-     *
-     * @param attributes The attributes of this element
-     *
-     * @exception Exception if a processing error occurs
-     */
-    @Override
-    public void begin(String namespace, String nameX, Attributes attributes)
-        throws Exception {
-
-        for (int i = 0; i < attributes.getLength(); i++) {
-            String name = attributes.getLocalName(i);
-            if ("".equals(name)) {
-                name = attributes.getQName(i);
-            }
-            String value = attributes.getValue(i);
-            if ( !excludes.containsKey(name)) {
-                if (!digester.isFakeAttribute(digester.peek(), name)
-                        && !IntrospectionUtils.setProperty(digester.peek(), name, value)
-                        && digester.getRulesValidation()) {
-                    digester.getLogger().warn("[SetAllPropertiesRule]{" + digester.getMatch() +
-                            "} Setting property '" + name + "' to '" +
-                            value + "' did not find a matching property.");
-                }
-            }
-        }
-
+    public SetAllPropertiesRule(String[] exclude) {
+        super(exclude);
     }
 
-
 }
diff --git a/java/org/apache/catalina/startup/SetContextPropertiesRule.java b/java/org/apache/catalina/startup/SetContextPropertiesRule.java
index c9e58a6..fda8742 100644
--- a/java/org/apache/catalina/startup/SetContextPropertiesRule.java
+++ b/java/org/apache/catalina/startup/SetContextPropertiesRule.java
@@ -18,21 +18,23 @@
 
 package org.apache.catalina.startup;
 
-import org.apache.tomcat.util.IntrospectionUtils;
-import org.apache.tomcat.util.digester.Rule;
-import org.xml.sax.Attributes;
+import org.apache.tomcat.util.digester.SetPropertiesRule;
 
 /**
  * Rule that uses the introspection utils to set properties of a context
  * (everything except "path").
  *
  * @author Remy Maucherat
+ * @deprecated This will be removed in Tomcat 10
  */
-public class SetContextPropertiesRule extends Rule {
+public class SetContextPropertiesRule extends SetPropertiesRule {
 
 
     // ----------------------------------------------------------- Constructors
 
+    public SetContextPropertiesRule() {
+        super(new String[]{"path", "docBase"});
+    }
 
     // ----------------------------------------------------- Instance Variables
 
@@ -40,36 +42,4 @@ public class SetContextPropertiesRule extends Rule {
     // --------------------------------------------------------- Public Methods
 
 
-    /**
-     * Handle the beginning of an XML element.
-     *
-     * @param attributes The attributes of this element
-     *
-     * @exception Exception if a processing error occurs
-     */
-    @Override
-    public void begin(String namespace, String nameX, Attributes attributes)
-        throws Exception {
-
-        for (int i = 0; i < attributes.getLength(); i++) {
-            String name = attributes.getLocalName(i);
-            if ("".equals(name)) {
-                name = attributes.getQName(i);
-            }
-            if ("path".equals(name) || "docBase".equals(name)) {
-                continue;
-            }
-            String value = attributes.getValue(i);
-            if (!digester.isFakeAttribute(digester.peek(), name)
-                    && !IntrospectionUtils.setProperty(digester.peek(), name, value)
-                    && digester.getRulesValidation()) {
-                digester.getLogger().warn("[SetContextPropertiesRule]{" + digester.getMatch() +
-                        "} Setting property '" + name + "' to '" +
-                        value + "' did not find a matching property.");
-            }
-        }
-
-    }
-
-
 }
diff --git a/java/org/apache/catalina/startup/SetNextNamingRule.java b/java/org/apache/catalina/startup/SetNextNamingRule.java
index c82fe06..cf94b7c 100644
--- a/java/org/apache/catalina/startup/SetNextNamingRule.java
+++ b/java/org/apache/catalina/startup/SetNextNamingRule.java
@@ -21,6 +21,7 @@ package org.apache.catalina.startup;
 import org.apache.catalina.Context;
 import org.apache.catalina.deploy.NamingResourcesImpl;
 import org.apache.tomcat.util.IntrospectionUtils;
+import org.apache.tomcat.util.descriptor.web.ResourceBase;
 import org.apache.tomcat.util.digester.Rule;
 
 
@@ -104,6 +105,12 @@ public class SetNextNamingRule extends Rule {
         IntrospectionUtils.callMethod1(namingResources, methodName,
                 child, paramType, digester.getClassLoader());
 
+        StringBuilder code = digester.getGeneratedCode();
+        if (code != null) {
+            code.append(digester.toVariableName(namingResources)).append(".").append(methodName).append("(");
+            code.append(digester.toVariableName(child, ((ResourceBase) child).getInitialHashCode())).append(");");
+            code.append(System.lineSeparator());
+        }
     }
 
 
diff --git a/java/org/apache/tomcat/util/IntrospectionUtils.java b/java/org/apache/tomcat/util/IntrospectionUtils.java
index 22bf92a..4355882 100644
--- a/java/org/apache/tomcat/util/IntrospectionUtils.java
+++ b/java/org/apache/tomcat/util/IntrospectionUtils.java
@@ -45,12 +45,17 @@ public final class IntrospectionUtils {
      * @return <code>true</code> if operation was successful
      */
     public static boolean setProperty(Object o, String name, String value) {
-        return setProperty(o,name,value,true);
+        return setProperty(o, name, value, true, null);
     }
 
-    @SuppressWarnings("null") // setPropertyMethodVoid is not null when used
     public static boolean setProperty(Object o, String name, String value,
             boolean invokeSetProperty) {
+        return setProperty(o, name, value, invokeSetProperty, null);
+    }
+
+    @SuppressWarnings("null") // setPropertyMethodVoid is not null when used
+    public static boolean setProperty(Object o, String name, String value,
+            boolean invokeSetProperty, StringBuilder actualMethod) {
         if (log.isDebugEnabled())
             log.debug("IntrospectionUtils: setProperty(" +
                     o.getClass() + " " + name + "=" + value + ")");
@@ -67,8 +72,10 @@ public final class IntrospectionUtils {
                 Class<?> paramT[] = item.getParameterTypes();
                 if (setter.equals(item.getName()) && paramT.length == 1
                         && "java.lang.String".equals(paramT[0].getName())) {
-
                     item.invoke(o, new Object[]{value});
+                    if (actualMethod != null) {
+                        actualMethod.append(item.getName()).append("(\"").append(value).append("\")");
+                    }
                     return true;
                 }
             }
@@ -91,6 +98,9 @@ public final class IntrospectionUtils {
                         } catch (NumberFormatException ex) {
                             ok = false;
                         }
+                        if (actualMethod != null) {
+                            actualMethod.append(method.getName()).append("(Integer.valueOf(\"").append(value).append("\"))");
+                        }
                         // Try a setFoo ( long )
                     } else if ("java.lang.Long".equals(paramType.getName())
                             || "long".equals(paramType.getName())) {
@@ -99,12 +109,16 @@ public final class IntrospectionUtils {
                         } catch (NumberFormatException ex) {
                             ok = false;
                         }
-
+                        if (actualMethod != null) {
+                            actualMethod.append(method.getName()).append("(Long.valueOf(\"").append(value).append("\"))");
+                        }
                         // Try a setFoo ( boolean )
                     } else if ("java.lang.Boolean".equals(paramType.getName())
                             || "boolean".equals(paramType.getName())) {
                         params[0] = Boolean.valueOf(value);
-
+                        if (actualMethod != null) {
+                            actualMethod.append(method.getName()).append("(Boolean.valueOf(\"").append(value).append("\"))");
+                        }
                         // Try a setFoo ( InetAddress )
                     } else if ("java.net.InetAddress".equals(paramType
                             .getName())) {
@@ -115,7 +129,9 @@ public final class IntrospectionUtils {
                                 log.debug("IntrospectionUtils: Unable to resolve host name:" + value);
                             ok = false;
                         }
-
+                        if (actualMethod != null) {
+                            actualMethod.append(method.getName()).append("(InetAddress.getByName(\"").append(value).append("\"))");
+                        }
                         // Unknown type
                     } else {
                         if (log.isDebugEnabled())
@@ -143,6 +159,9 @@ public final class IntrospectionUtils {
             // Ok, no setXXX found, try a setProperty("name", "value")
             if (invokeSetProperty && (setPropertyMethodBool != null ||
                     setPropertyMethodVoid != null)) {
+                if (actualMethod != null) {
+                    actualMethod.append("setProperty(\"").append(name).append("\", \"").append(value).append("\")");
+                }
                 Object params[] = new Object[2];
                 params[0] = name;
                 params[1] = value;
diff --git a/java/org/apache/tomcat/util/descriptor/web/ContextEjb.java b/java/org/apache/tomcat/util/descriptor/web/ContextEjb.java
index 4190253..8e2b177 100644
--- a/java/org/apache/tomcat/util/descriptor/web/ContextEjb.java
+++ b/java/org/apache/tomcat/util/descriptor/web/ContextEjb.java
@@ -30,6 +30,10 @@ public class ContextEjb extends ResourceBase {
 
     private static final long serialVersionUID = 1L;
 
+    public ContextEjb() {
+        initialHashCode = hashCode();
+    }
+
     // ------------------------------------------------------------- Properties
 
 
diff --git a/java/org/apache/tomcat/util/descriptor/web/ContextEnvironment.java b/java/org/apache/tomcat/util/descriptor/web/ContextEnvironment.java
index f51b250..b606961 100644
--- a/java/org/apache/tomcat/util/descriptor/web/ContextEnvironment.java
+++ b/java/org/apache/tomcat/util/descriptor/web/ContextEnvironment.java
@@ -28,6 +28,10 @@ public class ContextEnvironment extends ResourceBase {
 
     private static final long serialVersionUID = 1L;
 
+    public ContextEnvironment() {
+        initialHashCode = hashCode();
+    }
+
     // ------------------------------------------------------------- Properties
 
 
diff --git a/java/org/apache/tomcat/util/descriptor/web/ContextHandler.java b/java/org/apache/tomcat/util/descriptor/web/ContextHandler.java
index c0ee25c..7b124aa 100644
--- a/java/org/apache/tomcat/util/descriptor/web/ContextHandler.java
+++ b/java/org/apache/tomcat/util/descriptor/web/ContextHandler.java
@@ -34,6 +34,10 @@ public class ContextHandler extends ResourceBase {
 
     private static final long serialVersionUID = 1L;
 
+    public ContextHandler() {
+        initialHashCode = hashCode();
+    }
+
     // ------------------------------------------------------------- Properties
 
 
diff --git a/java/org/apache/tomcat/util/descriptor/web/ContextLocalEjb.java b/java/org/apache/tomcat/util/descriptor/web/ContextLocalEjb.java
index 29e8d5e..ecfc1e3 100644
--- a/java/org/apache/tomcat/util/descriptor/web/ContextLocalEjb.java
+++ b/java/org/apache/tomcat/util/descriptor/web/ContextLocalEjb.java
@@ -30,6 +30,10 @@ public class ContextLocalEjb extends ResourceBase {
 
     private static final long serialVersionUID = 1L;
 
+    public ContextLocalEjb() {
+        initialHashCode = hashCode();
+    }
+
     // ------------------------------------------------------------- Properties
 
     /**
diff --git a/java/org/apache/tomcat/util/descriptor/web/ContextResource.java b/java/org/apache/tomcat/util/descriptor/web/ContextResource.java
index dd3de30..b4115bd 100644
--- a/java/org/apache/tomcat/util/descriptor/web/ContextResource.java
+++ b/java/org/apache/tomcat/util/descriptor/web/ContextResource.java
@@ -30,6 +30,10 @@ public class ContextResource extends ResourceBase {
 
     private static final long serialVersionUID = 1L;
 
+    public ContextResource() {
+        initialHashCode = hashCode();
+    }
+
     // ------------------------------------------------------------- Properties
 
 
diff --git a/java/org/apache/tomcat/util/descriptor/web/ContextResourceEnvRef.java b/java/org/apache/tomcat/util/descriptor/web/ContextResourceEnvRef.java
index ba3169c..81b6aa6 100644
--- a/java/org/apache/tomcat/util/descriptor/web/ContextResourceEnvRef.java
+++ b/java/org/apache/tomcat/util/descriptor/web/ContextResourceEnvRef.java
@@ -29,6 +29,10 @@ public class ContextResourceEnvRef extends ResourceBase {
 
     private static final long serialVersionUID = 1L;
 
+    public ContextResourceEnvRef() {
+        initialHashCode = hashCode();
+    }
+
     // ------------------------------------------------------------- Properties
 
     /**
diff --git a/java/org/apache/tomcat/util/descriptor/web/ContextResourceLink.java b/java/org/apache/tomcat/util/descriptor/web/ContextResourceLink.java
index e50dcf9..6b88c07 100644
--- a/java/org/apache/tomcat/util/descriptor/web/ContextResourceLink.java
+++ b/java/org/apache/tomcat/util/descriptor/web/ContextResourceLink.java
@@ -30,6 +30,10 @@ public class ContextResourceLink extends ResourceBase {
 
     private static final long serialVersionUID = 1L;
 
+    public ContextResourceLink() {
+        initialHashCode = hashCode();
+    }
+
     // ------------------------------------------------------------- Properties
 
    /**
diff --git a/java/org/apache/tomcat/util/descriptor/web/ContextService.java b/java/org/apache/tomcat/util/descriptor/web/ContextService.java
index cefdc56..a8e2331 100644
--- a/java/org/apache/tomcat/util/descriptor/web/ContextService.java
+++ b/java/org/apache/tomcat/util/descriptor/web/ContextService.java
@@ -33,6 +33,10 @@ public class ContextService extends ResourceBase {
 
     private static final long serialVersionUID = 1L;
 
+    public ContextService() {
+        initialHashCode = hashCode();
+    }
+
     // ------------------------------------------------------------- Properties
 
 
diff --git a/java/org/apache/tomcat/util/descriptor/web/MessageDestination.java b/java/org/apache/tomcat/util/descriptor/web/MessageDestination.java
index be9fcfb..b4a97f2 100644
--- a/java/org/apache/tomcat/util/descriptor/web/MessageDestination.java
+++ b/java/org/apache/tomcat/util/descriptor/web/MessageDestination.java
@@ -29,6 +29,10 @@ public class MessageDestination extends ResourceBase {
 
     private static final long serialVersionUID = 1L;
 
+    public MessageDestination() {
+        initialHashCode = hashCode();
+    }
+
     // ------------------------------------------------------------- Properties
 
 
diff --git a/java/org/apache/tomcat/util/descriptor/web/MessageDestinationRef.java b/java/org/apache/tomcat/util/descriptor/web/MessageDestinationRef.java
index 25d9f67..be8b44f 100644
--- a/java/org/apache/tomcat/util/descriptor/web/MessageDestinationRef.java
+++ b/java/org/apache/tomcat/util/descriptor/web/MessageDestinationRef.java
@@ -29,6 +29,10 @@ public class MessageDestinationRef extends ResourceBase {
 
     private static final long serialVersionUID = 1L;
 
+    public MessageDestinationRef() {
+        initialHashCode = hashCode();
+    }
+
     // ------------------------------------------------------------- Properties
 
 
diff --git a/java/org/apache/tomcat/util/descriptor/web/ResourceBase.java b/java/org/apache/tomcat/util/descriptor/web/ResourceBase.java
index 37ec8fb..e191b46 100644
--- a/java/org/apache/tomcat/util/descriptor/web/ResourceBase.java
+++ b/java/org/apache/tomcat/util/descriptor/web/ResourceBase.java
@@ -96,6 +96,16 @@ public class ResourceBase implements Serializable, Injectable {
 
 
     /**
+     * Store the initial hash code of the object, as the override
+     * can mess some operations.
+     */
+    protected int initialHashCode = 0;
+
+    public int getInitialHashCode() {
+        return initialHashCode;
+    }
+
+    /**
      * Holder for our configured properties.
      */
     private final Map<String, Object> properties = new HashMap<>();
diff --git a/java/org/apache/tomcat/util/digester/CallMethodRule.java b/java/org/apache/tomcat/util/digester/CallMethodRule.java
index 288312e..0d8c468 100644
--- a/java/org/apache/tomcat/util/digester/CallMethodRule.java
+++ b/java/org/apache/tomcat/util/digester/CallMethodRule.java
@@ -383,6 +383,20 @@ public class CallMethodRule extends Rule {
         Object result = IntrospectionUtils.callMethodN(target, methodName,
                 paramValues, paramTypes);
         processMethodCallResult(result);
+
+        StringBuilder code = digester.getGeneratedCode();
+        if (code != null) {
+            code.append(digester.toVariableName(target)).append(".").append(methodName);
+            code.append("(");
+            for (int i = 0; i < paramValues.length; i++) {
+                if (i > 0) {
+                    code.append(",");
+                }
+                code.append(digester.toVariableName(paramValues[i]));
+            }
+            code.append(");");
+            code.append(System.lineSeparator());
+        }
     }
 
 
diff --git a/java/org/apache/tomcat/util/digester/Digester.java b/java/org/apache/tomcat/util/digester/Digester.java
index 6af109d..9ef59a4 100644
--- a/java/org/apache/tomcat/util/digester/Digester.java
+++ b/java/org/apache/tomcat/util/digester/Digester.java
@@ -352,6 +352,10 @@ public class Digester extends DefaultHandler2 {
      */
     protected Log saxLog = LogFactory.getLog("org.apache.tomcat.util.digester.Digester.sax");
 
+    /**
+     * Generated code.
+     */
+    protected StringBuilder code = null;
 
     public Digester() {
         propertySourcesSet = true;
@@ -386,6 +390,22 @@ public class Digester extends DefaultHandler2 {
     }
 
 
+    public void startGeneratingCode() {
+        code = new StringBuilder();
+    }
+
+    public StringBuilder getGeneratedCode() {
+        return code;
+    }
+
+    public String toVariableName(Object object) {
+        return toVariableName(object, object.hashCode());
+    }
+
+    public String toVariableName(Object object, int hashCode) {
+        return "tc_" + object.getClass().getSimpleName() + "_" + String.valueOf(Math.abs(hashCode));
+    }
+
     // ------------------------------------------------------------- Properties
 
     /**
@@ -1678,6 +1698,13 @@ public class Digester extends DefaultHandler2 {
     }
 
 
+    public void addSetProperties(String pattern, String[] excludes) {
+
+        addRule(pattern, new SetPropertiesRule(excludes));
+
+    }
+
+
     // --------------------------------------------------- Object Stack Methods
 
 
diff --git a/java/org/apache/tomcat/util/digester/ObjectCreateRule.java b/java/org/apache/tomcat/util/digester/ObjectCreateRule.java
index 21d0f11..18e1e0b 100644
--- a/java/org/apache/tomcat/util/digester/ObjectCreateRule.java
+++ b/java/org/apache/tomcat/util/digester/ObjectCreateRule.java
@@ -104,6 +104,12 @@ public class ObjectCreateRule extends Rule {
         Class<?> clazz = digester.getClassLoader().loadClass(realClassName);
         Object instance = clazz.getConstructor().newInstance();
         digester.push(instance);
+
+        StringBuilder code = digester.getGeneratedCode();
+        if (code != null) {
+            code.append(realClassName).append(" ").append(digester.toVariableName(instance)).append(" = new ");
+            code.append(realClassName).append("();").append(System.lineSeparator());
+        }
     }
 
 
@@ -143,6 +149,10 @@ public class ObjectCreateRule extends Rule {
                     "} Pop " + top.getClass().getName());
         }
 
+        StringBuilder code = digester.getGeneratedCode();
+        if (code != null) {
+            code.append(System.lineSeparator());
+        }
     }
 
 
diff --git a/java/org/apache/tomcat/util/digester/SetNextRule.java b/java/org/apache/tomcat/util/digester/SetNextRule.java
index bb8eb9f..1fa14f3 100644
--- a/java/org/apache/tomcat/util/digester/SetNextRule.java
+++ b/java/org/apache/tomcat/util/digester/SetNextRule.java
@@ -145,6 +145,12 @@ public class SetNextRule extends Rule {
         IntrospectionUtils.callMethod1(parent, methodName,
                 child, paramType, digester.getClassLoader());
 
+        StringBuilder code = digester.getGeneratedCode();
+        if (code != null) {
+            code.append(digester.toVariableName(parent)).append(".");
+            code.append(methodName).append("(").append(digester.toVariableName(child)).append(");");
+            code.append(System.lineSeparator());
+        }
     }
 
 
diff --git a/java/org/apache/tomcat/util/digester/SetPropertiesRule.java b/java/org/apache/tomcat/util/digester/SetPropertiesRule.java
index 78c976c..23a1e1a 100644
--- a/java/org/apache/tomcat/util/digester/SetPropertiesRule.java
+++ b/java/org/apache/tomcat/util/digester/SetPropertiesRule.java
@@ -19,6 +19,8 @@
 package org.apache.tomcat.util.digester;
 
 
+import java.util.HashMap;
+
 import org.apache.tomcat.util.IntrospectionUtils;
 import org.xml.sax.Attributes;
 
@@ -34,6 +36,21 @@ public class SetPropertiesRule extends Rule {
         void endSetPropertiesRule();
     }
 
+    protected final HashMap<String,String> excludes;
+
+    public SetPropertiesRule() {
+        excludes = null;
+    }
+
+    public SetPropertiesRule(String[] exclude) {
+        excludes = new HashMap<>();
+        for (String s : exclude) {
+            if (s != null) {
+                this.excludes.put(s, s);
+            }
+        }
+    }
+
     /**
      * Process the beginning of this element.
      *
@@ -60,6 +77,11 @@ public class SetPropertiesRule extends Rule {
                                    "} Set NULL properties");
             }
         }
+        StringBuilder code = digester.getGeneratedCode();
+        String variableName = null;
+        if (code != null) {
+            variableName = digester.toVariableName(top);
+        }
 
         for (int i = 0; i < attributes.getLength(); i++) {
             String name = attributes.getLocalName(i);
@@ -74,18 +96,36 @@ public class SetPropertiesRule extends Rule {
                         value + "'");
             }
             if (!digester.isFakeAttribute(top, name)
-                    && !IntrospectionUtils.setProperty(top, name, value)
-                    && digester.getRulesValidation()) {
-                if (!"optional".equals(name)) {
-                    digester.log.warn(sm.getString("rule.noProperty", digester.match, name, value));
+                    && (excludes == null || (excludes != null && !excludes.containsKey(name)))) {
+                StringBuilder actualMethod = null;
+                if (code != null) {
+                    actualMethod = new StringBuilder();
+                }
+                if (!IntrospectionUtils.setProperty(top, name, value, true, actualMethod)) {
+                    if (digester.getRulesValidation() && !"optional".equals(name)) {
+                        digester.log.warn(sm.getString("rule.noProperty", digester.match, name, value));
+                    }
+                } else {
+                    if (code != null) {
+                        code.append(variableName).append(".").append(actualMethod).append(";");
+                        code.append(System.lineSeparator());
+                    }
                 }
             }
         }
 
         if (top instanceof Listener) {
             ((Listener) top).endSetPropertiesRule();
+            if (code != null) {
+                code.append("((org.apache.tomcat.util.digester.SetPropertiesRule.Listener) ");
+                code.append(variableName).append(").endSetPropertiesRule();");
+                code.append(System.lineSeparator());
+            }
         }
 
+        if (code != null) {
+            code.append(System.lineSeparator());
+        }
     }
 
 


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org


[tomcat] 02/05: Drop from merge

Posted by re...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

remm pushed a commit to branch 9.0.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git

commit 93a7a3bbc94c8afc56c7ea65fce17f2ac7c6c038
Author: remm <re...@apache.org>
AuthorDate: Tue Jun 23 13:28:50 2020 +0200

    Drop from merge
---
 java/org/apache/catalina/startup/Catalina.java | 7 -------
 1 file changed, 7 deletions(-)

diff --git a/java/org/apache/catalina/startup/Catalina.java b/java/org/apache/catalina/startup/Catalina.java
index ecf197f..b03d4ce 100644
--- a/java/org/apache/catalina/startup/Catalina.java
+++ b/java/org/apache/catalina/startup/Catalina.java
@@ -135,13 +135,6 @@ public class Catalina {
 
 
     /**
-     * Rethrow exceptions on init failure.
-     */
-    protected boolean throwOnInitFailure =
-            Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE");
-
-
-    /**
      * Generate Tomcat embedded code from server.xml.
      */
     protected boolean generateCode = false;


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org


[tomcat] 04/05: Various improvements to code generation

Posted by re...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

remm pushed a commit to branch 9.0.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git

commit 0306b6b0eda5d8225c725af932af441d0299f40e
Author: remm <re...@apache.org>
AuthorDate: Tue Jun 23 12:02:48 2020 +0200

    Various improvements to code generation
    
    Add a configuration for the location of the generated sources.
    Catalina start and stop will now use the same code. As a result, stop
    will now use the configuration source and will also generate stop code.
    If the use generated flag is enabled, ContextConfig will not look at the
    real files at all, this is just simpler (and there could be 100s of
    webapps too).
    Pass arguments from Tomcat to Catalina, since in addition to educational
    uses, the feature would be useful with AOT compilation.
---
 java/org/apache/catalina/startup/Catalina.java     | 177 +++++++++++++--------
 .../catalina/startup/ConnectorCreateRule.java      |   1 +
 .../org/apache/catalina/startup/ContextConfig.java |  39 +++--
 .../catalina/startup/LocalStrings.properties       |   3 +-
 java/org/apache/catalina/startup/Tomcat.java       |  39 ++++-
 .../tomcat/util/digester/ObjectCreateRule.java     |   1 +
 .../tomcat/util/digester/SetPropertiesRule.java    |   3 -
 webapps/docs/changelog.xml                         |   4 +
 8 files changed, 182 insertions(+), 85 deletions(-)

diff --git a/java/org/apache/catalina/startup/Catalina.java b/java/org/apache/catalina/startup/Catalina.java
index 5e6bb12..d2456d6 100644
--- a/java/org/apache/catalina/startup/Catalina.java
+++ b/java/org/apache/catalina/startup/Catalina.java
@@ -18,7 +18,6 @@ package org.apache.catalina.startup;
 
 
 import java.io.File;
-import java.io.FileInputStream;
 import java.io.FileWriter;
 import java.io.IOException;
 import java.io.InputStream;
@@ -141,6 +140,18 @@ public class Catalina {
 
 
     /**
+     * Location of generated sources.
+     */
+    protected File generatedCodeLocation = null;
+
+
+    /**
+     * Value of the argument.
+     */
+    protected String generatedCodeLocationParameter = null;
+
+
+    /**
      * Use generated code as a replacement for configuration files.
      */
     protected boolean useGeneratedCode = false;
@@ -196,6 +207,16 @@ public class Catalina {
     }
 
 
+    public File getGeneratedCodeLocation() {
+        return this.generatedCodeLocation;
+    }
+
+
+    public void setGeneratedCodeLocation(File generatedCodeLocation) {
+        this.generatedCodeLocation = generatedCodeLocation;
+    }
+
+
     /**
      * Set the shared extensions class loader.
      *
@@ -259,6 +280,7 @@ public class Catalina {
     protected boolean arguments(String args[]) {
 
         boolean isConfig = false;
+        boolean isGenerateCode = false;
 
         if (args.length < 1) {
             usage();
@@ -272,20 +294,29 @@ public class Catalina {
             } else if (arg.equals("-config")) {
                 isConfig = true;
             } else if (arg.equals("-generateCode")) {
-                generateCode = true;
+                setGenerateCode(true);
+                isGenerateCode = true;
             } else if (arg.equals("-useGeneratedCode")) {
-                useGeneratedCode = true;
+                setUseGeneratedCode(true);
+                isGenerateCode = false;
             } else if (arg.equals("-nonaming")) {
                 setUseNaming(false);
+                isGenerateCode = false;
             } else if (arg.equals("-help")) {
                 usage();
                 return false;
             } else if (arg.equals("start")) {
+                isGenerateCode = false;
                 // NOOP
             } else if (arg.equals("configtest")) {
+                isGenerateCode = false;
                 // NOOP
             } else if (arg.equals("stop")) {
+                isGenerateCode = false;
                 // NOOP
+            } else if (isGenerateCode) {
+                generatedCodeLocationParameter = arg;
+                isGenerateCode = false;
             } else {
                 usage();
                 return false;
@@ -502,6 +533,72 @@ public class Catalina {
     }
 
 
+    protected void parseServerXml(boolean start) {
+        // Set configuration source
+        ConfigFileLoader.setSource(new CatalinaBaseConfigurationSource(Bootstrap.getCatalinaBaseFile(), getConfigFile()));
+        File file = configFile();
+
+        // Init source location
+        File serverXmlLocation = null;
+        if (generateCode) {
+            if (generatedCodeLocationParameter != null) {
+                generatedCodeLocation = new File(generatedCodeLocationParameter);
+                if (!generatedCodeLocation.isAbsolute()) {
+                    generatedCodeLocation = new File(Bootstrap.getCatalinaHomeFile(), generatedCodeLocationParameter);
+                }
+            } else {
+                generatedCodeLocation = new File(Bootstrap.getCatalinaHomeFile(), "work");
+            }
+            serverXmlLocation = new File(generatedCodeLocation, "catalina");
+            if (!serverXmlLocation.isDirectory() && !serverXmlLocation.mkdirs()) {
+                log.warn(sm.getString("catalina.generatedCodeLocationError", generatedCodeLocation.getAbsolutePath()));
+                // Disable code generation
+                generateCode = false;
+            }
+        }
+
+        ServerXml serverXml = null;
+        if (useGeneratedCode) {
+            String xmlClassName = start ? "catalina.ServerXml" : "catalina.ServerXmlStop";
+            try {
+                serverXml = (ServerXml) Catalina.class.getClassLoader().loadClass(xmlClassName).newInstance();
+            } catch (Exception e) {
+                // Ignore, no generated code found
+            }
+        }
+
+        if (serverXml != null) {
+            serverXml.load(this);
+        } else {
+            try (ConfigurationSource.Resource resource = ConfigFileLoader.getSource().getServerXml()) {
+                // Create and execute our Digester
+                Digester digester = start ? createStartDigester() : createStopDigester();
+                InputStream inputStream = resource.getInputStream();
+                InputSource inputSource = new InputSource(resource.getURI().toURL().toString());
+                inputSource.setByteStream(inputStream);
+                digester.push(this);
+                if (generateCode) {
+                    digester.startGeneratingCode();
+                    generateClassHeader(digester, start);
+                }
+                digester.parse(inputSource);
+                if (generateCode) {
+                    generateClassFooter(digester);
+                    try (FileWriter writer = new FileWriter(new File(serverXmlLocation,
+                            start ? "ServerXml.java" : "ServerXmlStop.java"))) {
+                        writer.write(digester.getGeneratedCode().toString());
+                    }
+                    digester.endGeneratingCode();
+                }
+            } catch (Exception e) {
+                log.warn(sm.getString("catalina.configFail", file.getAbsolutePath()), e);
+                if (file.exists() && !file.canRead()) {
+                    log.warn(sm.getString("catalina.incorrectPermissions"));
+                }
+            }
+        }
+    }
+
     public void stopServer() {
         stopServer(null);
     }
@@ -514,17 +611,9 @@ public class Catalina {
 
         Server s = getServer();
         if (s == null) {
-            // Create and execute our Digester
-            Digester digester = createStopDigester();
-            File file = configFile();
-            try (FileInputStream fis = new FileInputStream(file)) {
-                InputSource is =
-                    new InputSource(file.toURI().toURL().toString());
-                is.setByteStream(fis);
-                digester.push(this);
-                digester.parse(is);
-            } catch (Exception e) {
-                log.error(sm.getString("catalina.stopError"), e);
+            parseServerXml(false);
+            if (getServer() == null) {
+                log.error(sm.getString("catalina.stopError"));
                 System.exit(1);
             }
         } else {
@@ -582,53 +671,11 @@ public class Catalina {
         // Before digester - it may be needed
         initNaming();
 
-        // Set configuration source
-        ConfigFileLoader.setSource(new CatalinaBaseConfigurationSource(Bootstrap.getCatalinaBaseFile(), getConfigFile()));
-        File file = configFile();
-
-        ServerXml serverXml = null;
-        if (useGeneratedCode) {
-            String xmlClassName = "catalina.ServerXml";
-            try {
-                serverXml = (ServerXml) Catalina.class.getClassLoader().loadClass(xmlClassName).newInstance();
-            } catch (Exception e) {
-                // Ignore, no generated code found
-            }
-        }
-
-        if (serverXml != null) {
-            serverXml.load(this);
-        } else {
-            try (ConfigurationSource.Resource resource = ConfigFileLoader.getSource().getServerXml()) {
-                // Create and execute our Digester
-                Digester digester = createStartDigester();
-                InputStream inputStream = resource.getInputStream();
-                InputSource inputSource = new InputSource(resource.getURI().toURL().toString());
-                inputSource.setByteStream(inputStream);
-                digester.push(this);
-                if (generateCode) {
-                    digester.startGeneratingCode();
-                    generateClassHeader(digester);
-                }
-                digester.parse(inputSource);
-                if (generateCode) {
-                    generateClassFooter(digester);
-                    File generatedSourceFolder = new File(new File(Bootstrap.getCatalinaHomeFile(), "work"), "catalina");
-                    if (generatedSourceFolder.isDirectory() || generatedSourceFolder.mkdirs()) {
-                        File generatedSourceFile = new File(generatedSourceFolder, "ServerXml.java");
-                        try (FileWriter writer = new FileWriter(generatedSourceFile)) {
-                            writer.write(digester.getGeneratedCode().toString());
-                        }
-                    }
-                    digester.endGeneratingCode();
-                }
-            } catch (Exception e) {
-                log.warn(sm.getString("catalina.configFail", file.getAbsolutePath()), e);
-                if (file.exists() && !file.canRead()) {
-                    log.warn(sm.getString("catalina.incorrectPermissions"));
-                }
-                return;
-            }
+        // Parse main server.xml
+        parseServerXml(true);
+        Server s = getServer();
+        if (s == null) {
+            return;
         }
 
         getServer().setCatalina(this);
@@ -845,10 +892,14 @@ public class Catalina {
     }
 
 
-    protected void generateClassHeader(Digester digester) {
+    protected void generateClassHeader(Digester digester, boolean start) {
         StringBuilder code = digester.getGeneratedCode();
         code.append("package catalina;").append(System.lineSeparator());
-        code.append("public class ServerXml implements ");
+        code.append("public class ServerXml");
+        if (!start) {
+            code.append("Stop");
+        }
+        code.append(" implements ");
         code.append(ServerXml.class.getName().replace('$', '.')).append(" {").append(System.lineSeparator());
         code.append("public void load(").append(Catalina.class.getName());
         code.append(" ").append(digester.toVariableName(this)).append(") {").append(System.lineSeparator());
diff --git a/java/org/apache/catalina/startup/ConnectorCreateRule.java b/java/org/apache/catalina/startup/ConnectorCreateRule.java
index a1b53d9..875fea7 100644
--- a/java/org/apache/catalina/startup/ConnectorCreateRule.java
+++ b/java/org/apache/catalina/startup/ConnectorCreateRule.java
@@ -76,6 +76,7 @@ public class ConnectorCreateRule extends Rule {
 
         StringBuilder code = digester.getGeneratedCode();
         if (code != null) {
+            code.append(System.lineSeparator());
             code.append(Connector.class.getName()).append(" ").append(digester.toVariableName(con));
             code.append(" = new ").append(Connector.class.getName());
             code.append("(new ").append(con.getProtocolHandlerClassName()).append("());");
diff --git a/java/org/apache/catalina/startup/ContextConfig.java b/java/org/apache/catalina/startup/ContextConfig.java
index 1d4c0e2..aa67c03 100644
--- a/java/org/apache/catalina/startup/ContextConfig.java
+++ b/java/org/apache/catalina/startup/ContextConfig.java
@@ -463,6 +463,17 @@ public class ContextConfig implements LifecycleListener {
     }
 
 
+    protected File getGeneratedCodeLocation() {
+        Catalina catalina = Container.getService(context).getServer().getCatalina();
+        if (catalina != null) {
+            return catalina.getGeneratedCodeLocation();
+        } else {
+            // Cannot happen
+            return null;
+        }
+    }
+
+
     protected static String getContextXmlPackageName(Container container) {
         StringBuffer result = new StringBuffer();
         Container host = null;
@@ -492,14 +503,12 @@ public class ContextConfig implements LifecycleListener {
     }
 
 
-    protected static File getContextXmlJavaSource(String contextXmlPackageName, String contextXmlSimpleClassName) {
-        File generatedSourceFolder = new File(Bootstrap.getCatalinaHomeFile(), "work");
-        if (generatedSourceFolder.isDirectory() || generatedSourceFolder.mkdirs()) {
-            String path = contextXmlPackageName.replace('.', File.separatorChar);
-            File packageFolder = new File(generatedSourceFolder, path);
-            if (packageFolder.isDirectory() || packageFolder.mkdirs()) {
-                return new File(packageFolder, contextXmlSimpleClassName + ".java");
-            }
+    protected File getContextXmlJavaSource(String contextXmlPackageName, String contextXmlSimpleClassName) {
+        File generatedSourceFolder = getGeneratedCodeLocation();
+        String path = contextXmlPackageName.replace('.', File.separatorChar);
+        File packageFolder = new File(generatedSourceFolder, path);
+        if (packageFolder.isDirectory() || packageFolder.mkdirs()) {
+            return new File(packageFolder, contextXmlSimpleClassName + ".java");
         }
         return null;
     }
@@ -579,7 +588,7 @@ public class ContextConfig implements LifecycleListener {
             if (contextXml != null) {
                 contextXml.load(context);
                 contextXml = null;
-            } else {
+            } else if (!useGeneratedCode) {
                 try (ConfigurationSource.Resource contextXmlResource =
                         ConfigFileLoader.getSource().getResource(defaultContextXml)) {
                     if (generateCode) {
@@ -618,7 +627,7 @@ public class ContextConfig implements LifecycleListener {
             if (contextXml != null) {
                 contextXml.load(context);
                 contextXml = null;
-            } else {
+            } else if (!useGeneratedCode) {
                 String hostContextFile = Container.getConfigPath(context, Constants.HostContextXml);
                 try (ConfigurationSource.Resource contextXmlResource =
                         ConfigFileLoader.getSource().getResource(hostContextFile)) {
@@ -660,7 +669,7 @@ public class ContextConfig implements LifecycleListener {
             if (contextXml != null) {
                 contextXml.load(context);
                 contextXml = null;
-            } else {
+            } else if (!useGeneratedCode) {
                 if (generateCode) {
                     contextXmlJavaSource = getContextXmlJavaSource(contextXmlPackageName, contextXmlSimpleClassName);
                     digester.startGeneratingCode();
@@ -922,9 +931,11 @@ public class ContextConfig implements LifecycleListener {
     protected synchronized void init() {
         // Called from StandardContext.init()
 
-        // FIXME: Try to avoid creation of the parser if not needed due to code generation
-        Digester contextDigester = createContextDigester();
-        contextDigester.getParser();
+        Digester contextDigester = null;
+        if (!getUseGeneratedCode()) {
+            contextDigester = createContextDigester();
+            contextDigester.getParser();
+        }
 
         if (log.isDebugEnabled()) {
             log.debug(sm.getString("contextConfig.init"));
diff --git a/java/org/apache/catalina/startup/LocalStrings.properties b/java/org/apache/catalina/startup/LocalStrings.properties
index 1c897e7..2d242a0 100644
--- a/java/org/apache/catalina/startup/LocalStrings.properties
+++ b/java/org/apache/catalina/startup/LocalStrings.properties
@@ -14,6 +14,7 @@
 # limitations under the License.
 
 catalina.configFail=Unable to load server configuration from [{0}]
+catalina.generatedCodeLocationError=Error using configured location for generated Tomcat embedded code [{0}]
 catalina.incorrectPermissions=Permissions incorrect, read permission is not allowed on the file
 catalina.init=Server initialization in [{0}] milliseconds
 catalina.initError=Error initializing Catalina
@@ -26,7 +27,7 @@ catalina.startup=Server startup in [{0}] milliseconds
 catalina.stopError=Error stopping Catalina
 catalina.stopServer=No shutdown port configured. Shut down server through OS signal. Server not shut down.
 catalina.stopServer.connectException=Could not contact [{0}:{1}] (base port [{2}] and offset [{3}]). Tomcat may not be running.
-catalina.usage=usage: java org.apache.catalina.startup.Catalina [ -config {pathname} ] [ -nonaming ] { -help | start | stop }
+catalina.usage=usage: java org.apache.catalina.startup.Catalina [ -config {pathname} ] [ -nonaming ] [ -generateCode [ {pathname} ] ] [ -useGeneratedCode ] { -help | start | stop }
 
 catalinaConfigurationSource.cannotObtainURL=Cannot obtain URL for the relative path [{0}]. Check that catalina.base is set.
 
diff --git a/java/org/apache/catalina/startup/Tomcat.java b/java/org/apache/catalina/startup/Tomcat.java
index bfc0353..af895ed 100644
--- a/java/org/apache/catalina/startup/Tomcat.java
+++ b/java/org/apache/catalina/startup/Tomcat.java
@@ -436,12 +436,30 @@ public class Tomcat {
      * @param source The configuration source
      */
     public void init(ConfigurationSource source) {
+        init(source, null);
+    }
+
+    /**
+     * Initialize the server given the specified configuration source.
+     * The server will be loaded according to the Tomcat configuration
+     * files contained in the source (server.xml, web.xml, context.xml,
+     * SSL certificates, etc).
+     * If no configuration source is specified, it will use the default
+     * locations for these files.
+     * @param source The configuration source
+     * @param catalinaArguments The arguments that should be passed to Catalina
+     */
+    public void init(ConfigurationSource source, String[] catalinaArguments) {
         ConfigFileLoader.setSource(source);
         addDefaultWebXmlToWebapp = false;
         Catalina catalina = new Catalina();
         // Load the Catalina instance with the regular configuration files
         // from specified source
-        catalina.load();
+        if (catalinaArguments == null) {
+            catalina.load();
+        } else {
+            catalina.load(catalinaArguments);
+        }
         // Retrieve and set the server
         server = catalina.getServer();
     }
@@ -1289,9 +1307,19 @@ public class Tomcat {
      */
     public static void main(String[] args) throws Exception {
         // Process some command line parameters
-        for (String arg : args) {
-            if (arg.equals("--no-jmx")) {
+        String[] catalinaArguments = null;
+        for (int i = 0; i < args.length; i++) {
+            if (args[i].equals("--no-jmx")) {
                 Registry.disableRegistry();
+            } else if (args[i].equals("--catalina")) {
+                // This was already processed before
+                // Skip the rest of the arguments as they are for Catalina
+                ArrayList<String> result = new ArrayList<>();
+                for (int j = i + 1; j < args.length; j++) {
+                    result.add(args[j]);
+                }
+                catalinaArguments = result.toArray(new String[0]);
+                break;
             }
         }
         SecurityClassLoad.securityClassLoad(Thread.currentThread().getContextClassLoader());
@@ -1299,7 +1327,7 @@ public class Tomcat {
         // Create a Catalina instance and let it parse the configuration files
         // It will also set a shutdown hook to stop the Server when needed
         // Use the default configuration source
-        tomcat.init(null);
+        tomcat.init(null, catalinaArguments);
         boolean await = false;
         String path = "";
         // Process command line parameters
@@ -1319,6 +1347,9 @@ public class Tomcat {
                 await = true;
             } else if (args[i].equals("--no-jmx")) {
                 // This was already processed before
+            } else if (args[i].equals("--catalina")) {
+                // This was already processed before
+                // Skip the rest of the arguments as they are for Catalina
             } else {
                 throw new IllegalArgumentException(sm.getString("tomcat.invalidCommandLine", args[i]));
             }
diff --git a/java/org/apache/tomcat/util/digester/ObjectCreateRule.java b/java/org/apache/tomcat/util/digester/ObjectCreateRule.java
index 18e1e0b..ffcf4ba 100644
--- a/java/org/apache/tomcat/util/digester/ObjectCreateRule.java
+++ b/java/org/apache/tomcat/util/digester/ObjectCreateRule.java
@@ -107,6 +107,7 @@ public class ObjectCreateRule extends Rule {
 
         StringBuilder code = digester.getGeneratedCode();
         if (code != null) {
+            code.append(System.lineSeparator());
             code.append(realClassName).append(" ").append(digester.toVariableName(instance)).append(" = new ");
             code.append(realClassName).append("();").append(System.lineSeparator());
         }
diff --git a/java/org/apache/tomcat/util/digester/SetPropertiesRule.java b/java/org/apache/tomcat/util/digester/SetPropertiesRule.java
index 23a1e1a..284bc2f 100644
--- a/java/org/apache/tomcat/util/digester/SetPropertiesRule.java
+++ b/java/org/apache/tomcat/util/digester/SetPropertiesRule.java
@@ -123,9 +123,6 @@ public class SetPropertiesRule extends Rule {
             }
         }
 
-        if (code != null) {
-            code.append(System.lineSeparator());
-        }
     }
 
 
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index 6ec6c69..43a2485 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -71,6 +71,10 @@
         response is returned for an HTTP/2 request if the mapped servlet does
         implement the requested method rather than the more general
         <code>400</code> response. (markt)
+      <add>
+        Add generated classes using Tomcat embedded as an optional replacement
+        for the Catalina configuration files. (remm)
+      </add>
       </fix>
     </changelog>
   </subsection>


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org


[tomcat] 03/05: Do the code generation tooling for context.xml files

Posted by re...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

remm pushed a commit to branch 9.0.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git

commit 1d10b19c7ced08d6f81e8c93a67d321a1307c47f
Author: remm <re...@apache.org>
AuthorDate: Mon Jun 22 17:20:24 2020 +0200

    Do the code generation tooling for context.xml files
    
    Drop the hack that used the hash code to create variable names since it
    does not work, instead a list of objects used had to be kept. The names
    are more readable so it's not a bad move anyway.
    Using the generated code (beyond looking for a Tomcat embedded example)
    during load will be done only when using an explicit flag.
    I will document this tooling.
    Most likely some additional escaping and fixes are needed.
---
 java/org/apache/catalina/startup/Catalina.java     |  84 +++++---
 .../org/apache/catalina/startup/ContextConfig.java | 231 +++++++++++++++++++--
 .../catalina/startup/ListenerCreateRule.java       |  12 +-
 .../apache/catalina/startup/SetNextNamingRule.java |  12 +-
 .../org/apache/tomcat/util/IntrospectionUtils.java |  31 ++-
 .../tomcat/util/descriptor/web/ContextEjb.java     |   5 -
 .../util/descriptor/web/ContextEnvironment.java    |   3 -
 .../tomcat/util/descriptor/web/ContextHandler.java |   4 -
 .../util/descriptor/web/ContextLocalEjb.java       |   4 -
 .../util/descriptor/web/ContextResource.java       |   4 -
 .../util/descriptor/web/ContextResourceEnvRef.java |   4 -
 .../util/descriptor/web/ContextResourceLink.java   |   4 -
 .../tomcat/util/descriptor/web/ContextService.java |   4 -
 .../util/descriptor/web/MessageDestination.java    |   4 -
 .../util/descriptor/web/MessageDestinationRef.java |   4 -
 .../tomcat/util/descriptor/web/ResourceBase.java   |  10 -
 .../tomcat/util/digester/CallMethodRule.java       |   6 +-
 java/org/apache/tomcat/util/digester/Digester.java |  30 ++-
 18 files changed, 346 insertions(+), 110 deletions(-)

diff --git a/java/org/apache/catalina/startup/Catalina.java b/java/org/apache/catalina/startup/Catalina.java
index b03d4ce..5e6bb12 100644
--- a/java/org/apache/catalina/startup/Catalina.java
+++ b/java/org/apache/catalina/startup/Catalina.java
@@ -135,11 +135,17 @@ public class Catalina {
 
 
     /**
-     * Generate Tomcat embedded code from server.xml.
+     * Generate Tomcat embedded code from configuration files.
      */
     protected boolean generateCode = false;
 
 
+    /**
+     * Use generated code as a replacement for configuration files.
+     */
+    protected boolean useGeneratedCode = false;
+
+
     // ----------------------------------------------------------- Constructors
 
     public Catalina() {
@@ -170,6 +176,26 @@ public class Catalina {
     }
 
 
+    public boolean getGenerateCode() {
+        return this.generateCode;
+    }
+
+
+    public void setGenerateCode(boolean generateCode) {
+        this.generateCode = generateCode;
+    }
+
+
+    public boolean getUseGeneratedCode() {
+        return this.useGeneratedCode;
+    }
+
+
+    public void setUseGeneratedCode(boolean useGeneratedCode) {
+        this.useGeneratedCode = useGeneratedCode;
+    }
+
+
     /**
      * Set the shared extensions class loader.
      *
@@ -247,6 +273,8 @@ public class Catalina {
                 isConfig = true;
             } else if (arg.equals("-generateCode")) {
                 generateCode = true;
+            } else if (arg.equals("-useGeneratedCode")) {
+                useGeneratedCode = true;
             } else if (arg.equals("-nonaming")) {
                 setUseNaming(false);
             } else if (arg.equals("-help")) {
@@ -558,50 +586,49 @@ public class Catalina {
         ConfigFileLoader.setSource(new CatalinaBaseConfigurationSource(Bootstrap.getCatalinaBaseFile(), getConfigFile()));
         File file = configFile();
 
-        try (ConfigurationSource.Resource resource = ConfigFileLoader.getSource().getServerXml()) {
-            String serverXmlId = String.valueOf(resource.getLastModified());
-            String serverXmlClassName = "catalina.ServerXml_" + serverXmlId;
-            ServerXml serverXml = null;
+        ServerXml serverXml = null;
+        if (useGeneratedCode) {
+            String xmlClassName = "catalina.ServerXml";
             try {
-                serverXml = (ServerXml) Catalina.class.getClassLoader().loadClass(serverXmlClassName).newInstance();
-            } catch (ClassNotFoundException e) {
+                serverXml = (ServerXml) Catalina.class.getClassLoader().loadClass(xmlClassName).newInstance();
+            } catch (Exception e) {
                 // Ignore, no generated code found
             }
-            if (serverXml != null) {
-                serverXml.load(this);
-            } else {
+        }
+
+        if (serverXml != null) {
+            serverXml.load(this);
+        } else {
+            try (ConfigurationSource.Resource resource = ConfigFileLoader.getSource().getServerXml()) {
                 // Create and execute our Digester
                 Digester digester = createStartDigester();
-
                 InputStream inputStream = resource.getInputStream();
                 InputSource inputSource = new InputSource(resource.getURI().toURL().toString());
                 inputSource.setByteStream(inputStream);
                 digester.push(this);
                 if (generateCode) {
                     digester.startGeneratingCode();
-                    generateClassHeader(digester, String.valueOf(resource.getLastModified()));
+                    generateClassHeader(digester);
                 }
                 digester.parse(inputSource);
                 if (generateCode) {
                     generateClassFooter(digester);
                     File generatedSourceFolder = new File(new File(Bootstrap.getCatalinaHomeFile(), "work"), "catalina");
                     if (generatedSourceFolder.isDirectory() || generatedSourceFolder.mkdirs()) {
-                        File generatedSourceFile = new File(generatedSourceFolder,
-                                "ServerXml_" + serverXmlId + ".java");
-                        if (!generatedSourceFile.exists()) {
-                            try (FileWriter writer = new FileWriter(generatedSourceFile)) {
-                                writer.write(digester.getGeneratedCode().toString());
-                            }
+                        File generatedSourceFile = new File(generatedSourceFolder, "ServerXml.java");
+                        try (FileWriter writer = new FileWriter(generatedSourceFile)) {
+                            writer.write(digester.getGeneratedCode().toString());
                         }
                     }
+                    digester.endGeneratingCode();
                 }
+            } catch (Exception e) {
+                log.warn(sm.getString("catalina.configFail", file.getAbsolutePath()), e);
+                if (file.exists() && !file.canRead()) {
+                    log.warn(sm.getString("catalina.incorrectPermissions"));
+                }
+                return;
             }
-        } catch (Exception e) {
-            log.warn(sm.getString("catalina.configFail", file.getAbsolutePath()), e);
-            if (file.exists() && !file.canRead()) {
-                log.warn(sm.getString("catalina.incorrectPermissions"));
-            }
-            return;
         }
 
         getServer().setCatalina(this);
@@ -672,9 +699,8 @@ public class Catalina {
             return;
         }
 
-        long t2 = System.nanoTime();
-        if(log.isInfoEnabled()) {
-            log.info(sm.getString("catalina.startup", Long.valueOf((t2 - t1) / 1000000)));
+        if (log.isInfoEnabled()) {
+            log.info(sm.getString("catalina.startup", TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - t1)));
         }
 
         // Register shutdown hook
@@ -819,10 +845,10 @@ public class Catalina {
     }
 
 
-    protected void generateClassHeader(Digester digester, String time) {
+    protected void generateClassHeader(Digester digester) {
         StringBuilder code = digester.getGeneratedCode();
         code.append("package catalina;").append(System.lineSeparator());
-        code.append("public class ServerXml_").append(time).append(" implements ");
+        code.append("public class ServerXml implements ");
         code.append(ServerXml.class.getName().replace('$', '.')).append(" {").append(System.lineSeparator());
         code.append("public void load(").append(Catalina.class.getName());
         code.append(" ").append(digester.toVariableName(this)).append(") {").append(System.lineSeparator());
diff --git a/java/org/apache/catalina/startup/ContextConfig.java b/java/org/apache/catalina/startup/ContextConfig.java
index 2c09a15..1d4c0e2 100644
--- a/java/org/apache/catalina/startup/ContextConfig.java
+++ b/java/org/apache/catalina/startup/ContextConfig.java
@@ -19,6 +19,7 @@ package org.apache.catalina.startup;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
+import java.io.FileWriter;
 import java.io.IOException;
 import java.io.InputStream;
 import java.net.MalformedURLException;
@@ -442,6 +443,98 @@ public class ContextConfig implements LifecycleListener {
     }
 
 
+    protected boolean getGenerateCode() {
+        Catalina catalina = Container.getService(context).getServer().getCatalina();
+        if (catalina != null) {
+            return catalina.getGenerateCode();
+        } else {
+            return false;
+        }
+    }
+
+
+    protected boolean getUseGeneratedCode() {
+        Catalina catalina = Container.getService(context).getServer().getCatalina();
+        if (catalina != null) {
+            return catalina.getUseGeneratedCode();
+        } else {
+            return false;
+        }
+    }
+
+
+    protected static String getContextXmlPackageName(Container container) {
+        StringBuffer result = new StringBuffer();
+        Container host = null;
+        Container engine = null;
+        while (container != null) {
+            if (container instanceof Host) {
+                host = container;
+            } else if (container instanceof Engine) {
+                engine = container;
+            }
+            container = container.getParent();
+        }
+        result.append("catalina");
+        if (engine != null) {
+            result.append('.');
+        }
+        if (engine != null) {
+            result.append(engine.getName());
+            if (host != null) {
+                result.append('.');
+            }
+        }
+        if (host != null) {
+            result.append(host.getName());
+        }
+        return result.toString();
+    }
+
+
+    protected static File getContextXmlJavaSource(String contextXmlPackageName, String contextXmlSimpleClassName) {
+        File generatedSourceFolder = new File(Bootstrap.getCatalinaHomeFile(), "work");
+        if (generatedSourceFolder.isDirectory() || generatedSourceFolder.mkdirs()) {
+            String path = contextXmlPackageName.replace('.', File.separatorChar);
+            File packageFolder = new File(generatedSourceFolder, path);
+            if (packageFolder.isDirectory() || packageFolder.mkdirs()) {
+                return new File(packageFolder, contextXmlSimpleClassName + ".java");
+            }
+        }
+        return null;
+    }
+
+
+    protected void generateClassHeader(Digester digester, String packageName, String resourceName) {
+        StringBuilder code = digester.getGeneratedCode();
+        code.append("package ").append(packageName).append(";").append(System.lineSeparator());
+        code.append("public class ").append(resourceName).append(" implements ");
+        code.append(ContextXml.class.getName().replace('$', '.'));
+        code.append(" {").append(System.lineSeparator());
+        code.append("public void load(");
+        code.append(Context.class.getName());
+        String contextArgument = digester.toVariableName(context);
+        code.append(" ").append(contextArgument).append(") {").append(System.lineSeparator());
+        // Create a new variable with the concrete type
+        digester.setKnown(context);
+        code.append(context.getClass().getName()).append(" ").append(digester.toVariableName(context));
+        code.append(" = (").append(context.getClass().getName()).append(") ").append(contextArgument);
+        code.append(";").append(System.lineSeparator());
+    }
+
+
+    protected void generateClassFooter(Digester digester) {
+        StringBuilder code = digester.getGeneratedCode();
+        code.append("}").append(System.lineSeparator());
+        code.append("}").append(System.lineSeparator());
+    }
+
+
+    public interface ContextXml {
+        public void load(Context context);
+    }
+
+
     /**
      * Process the default configuration file, if it exists.
      * @param digester The digester that will be used for XML parsing
@@ -450,6 +543,14 @@ public class ContextConfig implements LifecycleListener {
 
         String defaultContextXml = null;
 
+        boolean generateCode = getGenerateCode();
+        boolean useGeneratedCode = getUseGeneratedCode();
+
+        String contextXmlPackageName = null;
+        String contextXmlSimpleClassName = null;
+        String contextXmlClassName = null;
+        File contextXmlJavaSource = null;
+
         // Open the default context.xml file, if it exists
         if (context instanceof StandardContext) {
             defaultContextXml = ((StandardContext)context).getDefaultContextXml();
@@ -459,30 +560,123 @@ public class ContextConfig implements LifecycleListener {
             defaultContextXml = Constants.DefaultContextXml;
         }
 
+        ContextXml contextXml = null;
+
         if (!context.getOverride()) {
-            try (ConfigurationSource.Resource contextXmlResource =
-                    ConfigFileLoader.getSource().getResource(defaultContextXml)) {
-                URL defaultContextUrl = contextXmlResource.getURI().toURL();
-                processContextConfig(digester, defaultContextUrl, contextXmlResource.getInputStream());
-            } catch (MalformedURLException e) {
-                log.error(sm.getString("contextConfig.badUrl", defaultContextXml), e);
-            } catch (IOException e) {
-                // Not found
+
+            if (useGeneratedCode || generateCode) {
+                contextXmlPackageName = "catalina";
+                contextXmlSimpleClassName = "ContextXmlDefault";
+                contextXmlClassName = contextXmlPackageName + "." + contextXmlSimpleClassName;
+            }
+            if (useGeneratedCode) {
+                try {
+                    contextXml = (ContextXml) Catalina.class.getClassLoader().loadClass(contextXmlClassName).newInstance();
+                } catch (Exception e) {
+                    // Ignore, no generated code found
+                }
+            }
+            if (contextXml != null) {
+                contextXml.load(context);
+                contextXml = null;
+            } else {
+                try (ConfigurationSource.Resource contextXmlResource =
+                        ConfigFileLoader.getSource().getResource(defaultContextXml)) {
+                    if (generateCode) {
+                        contextXmlJavaSource = getContextXmlJavaSource(contextXmlPackageName, contextXmlSimpleClassName);
+                        digester.startGeneratingCode();
+                        generateClassHeader(digester, contextXmlPackageName, contextXmlSimpleClassName);
+                    }
+                    URL defaultContextUrl = contextXmlResource.getURI().toURL();
+                    processContextConfig(digester, defaultContextUrl, contextXmlResource.getInputStream());
+                    if (generateCode) {
+                        generateClassFooter(digester);
+                        try (FileWriter writer = new FileWriter(contextXmlJavaSource)) {
+                            writer.write(digester.getGeneratedCode().toString());
+                        }
+                        digester.endGeneratingCode();
+                    }
+                } catch (MalformedURLException e) {
+                    log.error(sm.getString("contextConfig.badUrl", defaultContextXml), e);
+                } catch (IOException e) {
+                    // Not found
+                }
             }
 
-            String hostContextFile = Container.getConfigPath(context, Constants.HostContextXml);
-            try (ConfigurationSource.Resource contextXmlResource =
-                    ConfigFileLoader.getSource().getResource(hostContextFile)) {
-                URL defaultContextUrl = contextXmlResource.getURI().toURL();
-                processContextConfig(digester, defaultContextUrl, contextXmlResource.getInputStream());
-            } catch (MalformedURLException e) {
-                log.error(sm.getString("contextConfig.badUrl", hostContextFile), e);
-            } catch (IOException e) {
-                // Not found
+            if (useGeneratedCode || generateCode) {
+                contextXmlPackageName = getContextXmlPackageName(context);
+                contextXmlSimpleClassName = "ContextXmlDefault";
+                contextXmlClassName = contextXmlPackageName + "." + contextXmlSimpleClassName;
+            }
+            if (useGeneratedCode) {
+                try {
+                    contextXml = (ContextXml) Catalina.class.getClassLoader().loadClass(contextXmlClassName).newInstance();
+                } catch (Exception e) {
+                    // Ignore, no generated code found
+                }
+            }
+            if (contextXml != null) {
+                contextXml.load(context);
+                contextXml = null;
+            } else {
+                String hostContextFile = Container.getConfigPath(context, Constants.HostContextXml);
+                try (ConfigurationSource.Resource contextXmlResource =
+                        ConfigFileLoader.getSource().getResource(hostContextFile)) {
+                    if (generateCode) {
+                        contextXmlJavaSource = getContextXmlJavaSource(contextXmlPackageName, contextXmlSimpleClassName);
+                        digester.startGeneratingCode();
+                        generateClassHeader(digester, contextXmlPackageName, contextXmlSimpleClassName);
+                    }
+                    URL defaultContextUrl = contextXmlResource.getURI().toURL();
+                    processContextConfig(digester, defaultContextUrl, contextXmlResource.getInputStream());
+                    if (generateCode) {
+                        generateClassFooter(digester);
+                        try (FileWriter writer = new FileWriter(contextXmlJavaSource)) {
+                            writer.write(digester.getGeneratedCode().toString());
+                        }
+                        digester.endGeneratingCode();
+                    }
+                } catch (MalformedURLException e) {
+                    log.error(sm.getString("contextConfig.badUrl", hostContextFile), e);
+                } catch (IOException e) {
+                    // Not found
+                }
             }
         }
+
         if (context.getConfigFile() != null) {
-            processContextConfig(digester, context.getConfigFile(), null);
+            if (useGeneratedCode || generateCode) {
+                contextXmlPackageName = getContextXmlPackageName(context);
+                contextXmlSimpleClassName = "ContextXml_" + context.getName().replace('/', '_').replace("-", "__");
+                contextXmlClassName = contextXmlPackageName + "." + contextXmlSimpleClassName;
+            }
+            if (useGeneratedCode) {
+                try {
+                    contextXml = (ContextXml) Catalina.class.getClassLoader().loadClass(contextXmlClassName).newInstance();
+                } catch (Exception e) {
+                    // Ignore, no generated code found
+                }
+            }
+            if (contextXml != null) {
+                contextXml.load(context);
+                contextXml = null;
+            } else {
+                if (generateCode) {
+                    contextXmlJavaSource = getContextXmlJavaSource(contextXmlPackageName, contextXmlSimpleClassName);
+                    digester.startGeneratingCode();
+                    generateClassHeader(digester, contextXmlPackageName, contextXmlSimpleClassName);
+                }
+                processContextConfig(digester, context.getConfigFile(), null);
+                if (generateCode) {
+                    generateClassFooter(digester);
+                    try (FileWriter writer = new FileWriter(contextXmlJavaSource)) {
+                        writer.write(digester.getGeneratedCode().toString());
+                    } catch (IOException e) {
+                        // Ignore
+                    }
+                    digester.endGeneratingCode();
+                }
+            }
         }
 
     }
@@ -728,6 +922,7 @@ public class ContextConfig implements LifecycleListener {
     protected synchronized void init() {
         // Called from StandardContext.init()
 
+        // FIXME: Try to avoid creation of the parser if not needed due to code generation
         Digester contextDigester = createContextDigester();
         contextDigester.getParser();
 
diff --git a/java/org/apache/catalina/startup/ListenerCreateRule.java b/java/org/apache/catalina/startup/ListenerCreateRule.java
index 4a63952..9f1d5e1 100644
--- a/java/org/apache/catalina/startup/ListenerCreateRule.java
+++ b/java/org/apache/catalina/startup/ListenerCreateRule.java
@@ -56,14 +56,22 @@ public class ListenerCreateRule extends ObjectCreateRule {
                 } else {
                     log.info(sm.getString("listener.createFailed", className));
                 }
-                digester.push(new OptionalListener(className));
+                Object instance = new OptionalListener(className);
+                digester.push(instance);
+                StringBuilder code = digester.getGeneratedCode();
+                if (code != null) {
+                    code.append(OptionalListener.class.getName().replace('$', '.')).append(" ");
+                    code.append(digester.toVariableName(instance)).append(" = new ");
+                    code.append(OptionalListener.class.getName().replace('$', '.')).append("(\"").append(className).append("\");");
+                    code.append(System.lineSeparator());
+                }
             }
         } else {
             super.begin(namespace, name, attributes);
         }
     }
 
-    public class OptionalListener implements LifecycleListener {
+    public static class OptionalListener implements LifecycleListener {
         protected final String className;
         protected final HashMap<String, String> properties = new HashMap<>();
         public OptionalListener(String className) {
diff --git a/java/org/apache/catalina/startup/SetNextNamingRule.java b/java/org/apache/catalina/startup/SetNextNamingRule.java
index cf94b7c..20d7e3c 100644
--- a/java/org/apache/catalina/startup/SetNextNamingRule.java
+++ b/java/org/apache/catalina/startup/SetNextNamingRule.java
@@ -21,7 +21,6 @@ package org.apache.catalina.startup;
 import org.apache.catalina.Context;
 import org.apache.catalina.deploy.NamingResourcesImpl;
 import org.apache.tomcat.util.IntrospectionUtils;
-import org.apache.tomcat.util.descriptor.web.ResourceBase;
 import org.apache.tomcat.util.digester.Rule;
 
 
@@ -93,10 +92,12 @@ public class SetNextNamingRule extends Rule {
         // Identify the objects to be used
         Object child = digester.peek(0);
         Object parent = digester.peek(1);
+        boolean context = false;
 
         NamingResourcesImpl namingResources = null;
         if (parent instanceof Context) {
             namingResources = ((Context) parent).getNamingResources();
+            context = true;
         } else {
             namingResources = (NamingResourcesImpl) parent;
         }
@@ -107,8 +108,13 @@ public class SetNextNamingRule extends Rule {
 
         StringBuilder code = digester.getGeneratedCode();
         if (code != null) {
-            code.append(digester.toVariableName(namingResources)).append(".").append(methodName).append("(");
-            code.append(digester.toVariableName(child, ((ResourceBase) child).getInitialHashCode())).append(");");
+            if (context) {
+                code.append(digester.toVariableName(parent)).append(".getNamingResources()");
+            } else {
+                code.append(digester.toVariableName(namingResources));
+            }
+            code.append(".").append(methodName).append("(");
+            code.append(digester.toVariableName(child)).append(");");
             code.append(System.lineSeparator());
         }
     }
diff --git a/java/org/apache/tomcat/util/IntrospectionUtils.java b/java/org/apache/tomcat/util/IntrospectionUtils.java
index 4355882..1d830e1 100644
--- a/java/org/apache/tomcat/util/IntrospectionUtils.java
+++ b/java/org/apache/tomcat/util/IntrospectionUtils.java
@@ -74,7 +74,7 @@ public final class IntrospectionUtils {
                         && "java.lang.String".equals(paramT[0].getName())) {
                     item.invoke(o, new Object[]{value});
                     if (actualMethod != null) {
-                        actualMethod.append(item.getName()).append("(\"").append(value).append("\")");
+                        actualMethod.append(item.getName()).append("(\"").append(escape(value)).append("\")");
                     }
                     return true;
                 }
@@ -160,7 +160,7 @@ public final class IntrospectionUtils {
             if (invokeSetProperty && (setPropertyMethodBool != null ||
                     setPropertyMethodVoid != null)) {
                 if (actualMethod != null) {
-                    actualMethod.append("setProperty(\"").append(name).append("\", \"").append(value).append("\")");
+                    actualMethod.append("setProperty(\"").append(name).append("\", \"").append(escape(value)).append("\")");
                 }
                 Object params[] = new Object[2];
                 params[0] = name;
@@ -194,6 +194,33 @@ public final class IntrospectionUtils {
         return false;
     }
 
+    /**
+     * @param s
+     *            the input string
+     * @return escaped string, per Java rule
+     */
+    public static String escape(String s) {
+
+        if (s == null)
+            return "";
+
+        StringBuilder b = new StringBuilder();
+        for (int i = 0; i < s.length(); i++) {
+            char c = s.charAt(i);
+            if (c == '"')
+                b.append('\\').append('"');
+            else if (c == '\\')
+                b.append('\\').append('\\');
+            else if (c == '\n')
+                b.append('\\').append('n');
+            else if (c == '\r')
+                b.append('\\').append('r');
+            else
+                b.append(c);
+        }
+        return b.toString();
+    }
+
     public static Object getProperty(Object o, String name) {
         String getter = "get" + capitalize(name);
         String isGetter = "is" + capitalize(name);
diff --git a/java/org/apache/tomcat/util/descriptor/web/ContextEjb.java b/java/org/apache/tomcat/util/descriptor/web/ContextEjb.java
index 8e2b177..a650afe 100644
--- a/java/org/apache/tomcat/util/descriptor/web/ContextEjb.java
+++ b/java/org/apache/tomcat/util/descriptor/web/ContextEjb.java
@@ -30,14 +30,9 @@ public class ContextEjb extends ResourceBase {
 
     private static final long serialVersionUID = 1L;
 
-    public ContextEjb() {
-        initialHashCode = hashCode();
-    }
-
     // ------------------------------------------------------------- Properties
 
 
-
     /**
      * The name of the EJB home implementation class.
      */
diff --git a/java/org/apache/tomcat/util/descriptor/web/ContextEnvironment.java b/java/org/apache/tomcat/util/descriptor/web/ContextEnvironment.java
index b606961..43d110c 100644
--- a/java/org/apache/tomcat/util/descriptor/web/ContextEnvironment.java
+++ b/java/org/apache/tomcat/util/descriptor/web/ContextEnvironment.java
@@ -28,9 +28,6 @@ public class ContextEnvironment extends ResourceBase {
 
     private static final long serialVersionUID = 1L;
 
-    public ContextEnvironment() {
-        initialHashCode = hashCode();
-    }
 
     // ------------------------------------------------------------- Properties
 
diff --git a/java/org/apache/tomcat/util/descriptor/web/ContextHandler.java b/java/org/apache/tomcat/util/descriptor/web/ContextHandler.java
index 7b124aa..c0ee25c 100644
--- a/java/org/apache/tomcat/util/descriptor/web/ContextHandler.java
+++ b/java/org/apache/tomcat/util/descriptor/web/ContextHandler.java
@@ -34,10 +34,6 @@ public class ContextHandler extends ResourceBase {
 
     private static final long serialVersionUID = 1L;
 
-    public ContextHandler() {
-        initialHashCode = hashCode();
-    }
-
     // ------------------------------------------------------------- Properties
 
 
diff --git a/java/org/apache/tomcat/util/descriptor/web/ContextLocalEjb.java b/java/org/apache/tomcat/util/descriptor/web/ContextLocalEjb.java
index ecfc1e3..29e8d5e 100644
--- a/java/org/apache/tomcat/util/descriptor/web/ContextLocalEjb.java
+++ b/java/org/apache/tomcat/util/descriptor/web/ContextLocalEjb.java
@@ -30,10 +30,6 @@ public class ContextLocalEjb extends ResourceBase {
 
     private static final long serialVersionUID = 1L;
 
-    public ContextLocalEjb() {
-        initialHashCode = hashCode();
-    }
-
     // ------------------------------------------------------------- Properties
 
     /**
diff --git a/java/org/apache/tomcat/util/descriptor/web/ContextResource.java b/java/org/apache/tomcat/util/descriptor/web/ContextResource.java
index b4115bd..dd3de30 100644
--- a/java/org/apache/tomcat/util/descriptor/web/ContextResource.java
+++ b/java/org/apache/tomcat/util/descriptor/web/ContextResource.java
@@ -30,10 +30,6 @@ public class ContextResource extends ResourceBase {
 
     private static final long serialVersionUID = 1L;
 
-    public ContextResource() {
-        initialHashCode = hashCode();
-    }
-
     // ------------------------------------------------------------- Properties
 
 
diff --git a/java/org/apache/tomcat/util/descriptor/web/ContextResourceEnvRef.java b/java/org/apache/tomcat/util/descriptor/web/ContextResourceEnvRef.java
index 81b6aa6..ba3169c 100644
--- a/java/org/apache/tomcat/util/descriptor/web/ContextResourceEnvRef.java
+++ b/java/org/apache/tomcat/util/descriptor/web/ContextResourceEnvRef.java
@@ -29,10 +29,6 @@ public class ContextResourceEnvRef extends ResourceBase {
 
     private static final long serialVersionUID = 1L;
 
-    public ContextResourceEnvRef() {
-        initialHashCode = hashCode();
-    }
-
     // ------------------------------------------------------------- Properties
 
     /**
diff --git a/java/org/apache/tomcat/util/descriptor/web/ContextResourceLink.java b/java/org/apache/tomcat/util/descriptor/web/ContextResourceLink.java
index 6b88c07..e50dcf9 100644
--- a/java/org/apache/tomcat/util/descriptor/web/ContextResourceLink.java
+++ b/java/org/apache/tomcat/util/descriptor/web/ContextResourceLink.java
@@ -30,10 +30,6 @@ public class ContextResourceLink extends ResourceBase {
 
     private static final long serialVersionUID = 1L;
 
-    public ContextResourceLink() {
-        initialHashCode = hashCode();
-    }
-
     // ------------------------------------------------------------- Properties
 
    /**
diff --git a/java/org/apache/tomcat/util/descriptor/web/ContextService.java b/java/org/apache/tomcat/util/descriptor/web/ContextService.java
index a8e2331..cefdc56 100644
--- a/java/org/apache/tomcat/util/descriptor/web/ContextService.java
+++ b/java/org/apache/tomcat/util/descriptor/web/ContextService.java
@@ -33,10 +33,6 @@ public class ContextService extends ResourceBase {
 
     private static final long serialVersionUID = 1L;
 
-    public ContextService() {
-        initialHashCode = hashCode();
-    }
-
     // ------------------------------------------------------------- Properties
 
 
diff --git a/java/org/apache/tomcat/util/descriptor/web/MessageDestination.java b/java/org/apache/tomcat/util/descriptor/web/MessageDestination.java
index b4a97f2..be9fcfb 100644
--- a/java/org/apache/tomcat/util/descriptor/web/MessageDestination.java
+++ b/java/org/apache/tomcat/util/descriptor/web/MessageDestination.java
@@ -29,10 +29,6 @@ public class MessageDestination extends ResourceBase {
 
     private static final long serialVersionUID = 1L;
 
-    public MessageDestination() {
-        initialHashCode = hashCode();
-    }
-
     // ------------------------------------------------------------- Properties
 
 
diff --git a/java/org/apache/tomcat/util/descriptor/web/MessageDestinationRef.java b/java/org/apache/tomcat/util/descriptor/web/MessageDestinationRef.java
index be8b44f..25d9f67 100644
--- a/java/org/apache/tomcat/util/descriptor/web/MessageDestinationRef.java
+++ b/java/org/apache/tomcat/util/descriptor/web/MessageDestinationRef.java
@@ -29,10 +29,6 @@ public class MessageDestinationRef extends ResourceBase {
 
     private static final long serialVersionUID = 1L;
 
-    public MessageDestinationRef() {
-        initialHashCode = hashCode();
-    }
-
     // ------------------------------------------------------------- Properties
 
 
diff --git a/java/org/apache/tomcat/util/descriptor/web/ResourceBase.java b/java/org/apache/tomcat/util/descriptor/web/ResourceBase.java
index e191b46..37ec8fb 100644
--- a/java/org/apache/tomcat/util/descriptor/web/ResourceBase.java
+++ b/java/org/apache/tomcat/util/descriptor/web/ResourceBase.java
@@ -96,16 +96,6 @@ public class ResourceBase implements Serializable, Injectable {
 
 
     /**
-     * Store the initial hash code of the object, as the override
-     * can mess some operations.
-     */
-    protected int initialHashCode = 0;
-
-    public int getInitialHashCode() {
-        return initialHashCode;
-    }
-
-    /**
      * Holder for our configured properties.
      */
     private final Map<String, Object> properties = new HashMap<>();
diff --git a/java/org/apache/tomcat/util/digester/CallMethodRule.java b/java/org/apache/tomcat/util/digester/CallMethodRule.java
index 0d8c468..83aaf57 100644
--- a/java/org/apache/tomcat/util/digester/CallMethodRule.java
+++ b/java/org/apache/tomcat/util/digester/CallMethodRule.java
@@ -392,7 +392,11 @@ public class CallMethodRule extends Rule {
                 if (i > 0) {
                     code.append(",");
                 }
-                code.append(digester.toVariableName(paramValues[i]));
+                if (bodyText != null) {
+                    code.append("\"").append(bodyText).append("\"");
+                } else {
+                    code.append(digester.toVariableName(paramValues[i]));
+                }
             }
             code.append(");");
             code.append(System.lineSeparator());
diff --git a/java/org/apache/tomcat/util/digester/Digester.java b/java/org/apache/tomcat/util/digester/Digester.java
index 9ef59a4..7153b52 100644
--- a/java/org/apache/tomcat/util/digester/Digester.java
+++ b/java/org/apache/tomcat/util/digester/Digester.java
@@ -394,16 +394,36 @@ public class Digester extends DefaultHandler2 {
         code = new StringBuilder();
     }
 
+    public void endGeneratingCode() {
+        code = null;
+        known.clear();
+    }
+
     public StringBuilder getGeneratedCode() {
         return code;
     }
 
-    public String toVariableName(Object object) {
-        return toVariableName(object, object.hashCode());
+    protected ArrayList<Object> known = new ArrayList<>();
+    public void setKnown(Object object) {
+        known.add(object);
     }
-
-    public String toVariableName(Object object, int hashCode) {
-        return "tc_" + object.getClass().getSimpleName() + "_" + String.valueOf(Math.abs(hashCode));
+    public String toVariableName(Object object) {
+        boolean found = false;
+        int pos = 0;
+        if (known.size() > 0) {
+            for (int i = known.size() - 1; i >= 0; i--) {
+                if (known.get(i) == object) {
+                    pos = i;
+                    found = true;
+                    break;
+                }
+            }
+        }
+        if (!found) {
+            pos = known.size();
+            known.add(object);
+        }
+        return "tc_" + object.getClass().getSimpleName() + "_" + String.valueOf(pos);
     }
 
     // ------------------------------------------------------------- Properties


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org