Externalize Tomcat configuration

后端 未结 4 1416
轻奢々
轻奢々 2020-12-06 10:20

I have a DataSource configuration in context.xml. Is it possible not to hard-code database parameters in that file? For example, use an external properties file, and load th

相关标签:
4条回答
  • 2020-12-06 10:29

    If this is Tomcat 7, you can write your own org.apache.tomcat.util.IntrospectionUtils.PropertySource implementation read variables written like "${...}" in context.xml. You'll need to set the system property org.apache.tomcat.util.digester.PROPERTY_SOURCE to point to your PropertySource implementation.

    0 讨论(0)
  • 2020-12-06 10:30

    It is easy with context deploy descriptors, which look like:

    <Context docBase="${basedir}/src/main/webapp"
             reloadable="true">
        <!-- http://tomcat.apache.org/tomcat-7.0-doc/config/context.html -->
        <Resources className="org.apache.naming.resources.VirtualDirContext"
                   extraResourcePaths="/WEB-INF/classes=${basedir}/target/classes,/WEB-INF/lib=${basedir}/target/${project.build.finalName}/WEB-INF/lib"/>
        <Loader className="org.apache.catalina.loader.VirtualWebappLoader"
                virtualClasspath="${basedir}/target/classes;${basedir}/target/${project.build.finalName}/WEB-INF/lib"/>
        <JarScanner scanAllDirectories="true"/>
    
        <Parameter name="min" value="dev"/>
        <Environment name="app.devel.ldap" value="USER" type="java.lang.String" override="true"/>
        <Environment name="app.devel.permitAll" value="true" type="java.lang.String" override="true"/>
    </Context>
    

    There are several places where you can put this config, for my opinion best option is $CATALINA_BASE/conf/[enginename]/[hostname]/$APP.xml

    In above XML Context can hold custom Loader org.apache.catalina.loader.VirtualWebappLoader (available in modern Tomcat 7, you can add own separate classpath per application to your .properties files), Parameter (accessed via FilterConfig.getServletContext().getInitParameter(name)) and Environment (accessed via new InitialContext().lookup("java:comp/env").lookup("name"))

    See discussion at:

    • Adding a directory to tomcat classpath
    • Can I create a custom classpath on a per application basis in Tomcat
    • How to read a properties file outside my webapp context in Tomcat
    • Configure Tomcat to use properties file to load DB connection information
    • Should you set up database connection properties in server.xml or context.xml

    UPDATE Tomcat 8 change syntax for <Resources> and <Loader> elements, corresponding part now look like:

    <Resources>
        <PostResources className="org.apache.catalina.webresources.DirResourceSet"
                       webAppMount="/WEB-INF/classes" base="${basedir}/target/classes" />
        <PostResources className="org.apache.catalina.webresources.DirResourceSet"
                       webAppMount="/WEB-INF/lib" base="${basedir}/target/${project.build.finalName}/WEB-INF/lib" />
    </Resources>
    
    0 讨论(0)
  • 2020-12-06 10:47

    Sure, this is possible. You have to register ServletContextListener to your web.xml like this:

    <!-- at the beginning of web.xml -->
    
    <listener>
        <listener-class>com.mycompany.servlets.ApplicationListener</listener-class>
    </listener>
    

    Source of com.mycompany.servlets.ApplicationListener:

    package com.mycompany.servlets;
    
    public class ApplicationListener implements ServletContextListener {
    
        @Override
        public void contextInitialized(ServletContextEvent servletContextEvent) {
            // this method is invoked once when web-application is deployed (started)
    
            // reading properties file
            FileInputStream fis = null;
            Properties properties = new Properties();
            try {
                fis = new FileInputStream("path/to/db.properties")    
                properties.load(fis);
            } catch(IOException ex) {
                throw new RuntimeException(ex);
            } finally {
                try {
                    if(fis != null) {
                        fis.close();
                    }
                } catch(IOException e) {
                    throw new RuntimeException(e);
                }
            }
    
            // creating data source instance
            SomeDataSourceImpl dataSource = new SomeDataSourceImpl();
            dataSource.setJdbcUrl(properties.getProperty("db.url"));
            dataSource.setUser(properties.getProperty("db.user"));
            dataSource.setPassword(properties.getProperty("db.pwd"));
    
            // storing reference to dataSource in ServletContext attributes map
            // there is only one instance of ServletContext per web-application, which can be accessed from almost anywhere in web application(servlets, filters, listeners etc)
            final ServletContext servletContext = servletContextEvent.getServletContext();
            servletContext.setAttribute("some-data-source-alias", dataSource);
        }
    
        @Override
        public void contextDestroyed(ServletContextEvent servletContextEvent) {
            // this method is invoked once when web-application is undeployed (stopped) - here one can (should) implement resource cleanup etc
        }
    
    }
    

    And then, somewhere in web-application code to access dataSource:

    ServletContext servletContext = ...; // as mentioned above, it should be accessible from almost anywhere
    DataSource dataSource = (DataSource) servletContext.getAttribute("some-data-source-alias");
    // use dataSource
    

    SomeDataSourceImpl is some concrete implementation of javax.sql.DataSource. Please advise if you doesn't use specific DataSources (like ComboPooledDataSource for connection pooling) and don't know how to obtain it - I will post how to bypass this.

    some-data-source-alias - is just String alias(key) for your DataSource instance in ServletContext attribute map. Good practice is to give aliases prepended with package name like com.mycompany.mywebapp.dataSource.

    Hope this helps...

    0 讨论(0)
  • 2020-12-06 10:50

    As stated here, you could do this in the following way.

    1.Download tomcat library to get the interface definition, for instance by defining maven dependency:

        <dependency>
            <groupId>org.apache.tomcat</groupId>
            <artifactId>tomcat-coyote</artifactId>
            <version>7.0.47</version>
        </dependency>
    

    2.Next step is to create a com.mycompany.MyPropertyDecoder in the following way:

    import org.apache.tomcat.util.IntrospectionUtils;
    public class MyPropertyDecoder implements IntrospectionUtils.PropertySource  {
        @Override
        public String getProperty(String arg0) {
            //TODO read properties here
            return null;
        }
    }
    

    3.Put MyPropertyDecoder.class into tomcat7/lib folder
    4.Define org.apache.tomcat.util.digester. PROPERTY_SOURCE property at tomcat7/conf/catalina.properties as following:

    org.apache.tomcat.util.digester.PROPERTY_SOURCE=com.mycompany.MyPropertyDecoder
    

    5.Update your context.xml with properties vars

    <Resource name="jdbc/TestDB"
               auth="Container"
               type="javax.sql.DataSource"
               username="root"
               password="${db.password}"
               driverClassName="com.mysql.jdbc.Driver"
               url="jdbc:mysql://localhost:3306/mysql?autoReconnect=true"
               ...  
    

    6.Put application.properties file somewhere in your project/container
    7.Make sure MyPropertyDecoder correctly reads application.properties
    8.Enjoy!

    PS Also there is a similar approach described for tc Server.

    0 讨论(0)
提交回复
热议问题