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:52 UTC

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

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