master:lib weinanli$ pwd /Users/weinanli/projs/apache-tomcat-7.0.54/lib master:lib weinanli$ ls tomcat7-store* tomcat7-storeconfig-0.0.1.Alpha3-redhat-5.jar
From JBoss EWS 2.1.0, the Tomcat6 and 7 will include a component called StoreConfig. It is a component that can let users to persist the server runtime configuration changes into configuration files. JBoss JON uses it to manage Tomcat servers. In this article, I'd like to give a brief introduction to StoreConfig.
The StoreConfig component is jar that will be placed into Tomcat's 'lib' directory:
master:lib weinanli$ pwd /Users/weinanli/projs/apache-tomcat-7.0.54/lib master:lib weinanli$ ls tomcat7-store* tomcat7-storeconfig-0.0.1.Alpha3-redhat-5.jar
Please note Tomcat 6 and Tomcat 7 are using different versions of storeconfig. Because their configurations to be dealt with have differences. As the bash command show above, in Tomcat 7 it's using tomcat7-storeconfig-0.0.1.Alpha3-redhat-5.jar.
To enable StoreConfig, we need to add a line in server.xml:
<Server port="8005" shutdown="SHUTDOWN"> ... <Listener className="org.apache.catalina.storeconfig.StoreConfigLifecycleListener" /> ...
The StoreConfig is exposed as MBeans at runtime, so to use it, we need to enable JMX during Tomcat startup. To achieve this, we need to set 'CATALINA_OPTS' properly:
export CATALINA_OPTS='-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=6667 -Dcom.sun.management.jmxremote.authenticate=false'
Then we can start Tomcat server:
master:bin weinanli$ pwd /Users/weinanli/projs/apache-tomcat-7.0.54/bin master:bin weinanli$ ./startup.sh Using CATALINA_BASE: /Users/weinanli/projs/apache-tomcat-7.0.54 Using CATALINA_HOME: /Users/weinanli/projs/apache-tomcat-7.0.54 Using CATALINA_TMPDIR: /Users/weinanli/projs/apache-tomcat-7.0.54/temp Using JRE_HOME: /Library/Java/JavaVirtualMachines/jdk1.7.0_25.jdk/Contents/Home Using CLASSPATH: /Users/weinanli/projs/apache-tomcat-7.0.54/bin/bootstrap.jar:/Users/weinanli/projs/apache-tomcat-7.0.54/bin/tomcat-juli.jar Tomcat started.
To access StoreConfig, we can use 'jconsole' to connect to Tomcat JMX port:
From JConsole, we can see the StoreConfig is in 'Catalina:StoreConfig' category, and it contains multiple operations:
We can try to click 'storeConfig' operation and it's like this:
And from the tomcat log file 'catalina.out' we can see the StoreConfig actions:
Jul 09, 2014 4:55:31 PM org.apache.catalina.storeconfig.StandardContextSF storeWithBackup INFO: Store Context /manager separate with backup (at file /Users/weinanli/projs/apache-tomcat-7.0.54/webapps/manager/META-INF/context.xml.2014-07-09.16-55-31 ) Jul 09, 2014 4:55:31 PM org.apache.catalina.storeconfig.StandardContextSF storeWithBackup INFO: Store Context separate with backup (at file /Users/weinanli/projs/apache-tomcat-7.0.54/conf/Catalina/localhost/ROOT.xml.2014-07-09.16-55-31 ) Jul 09, 2014 4:55:31 PM org.apache.catalina.storeconfig.StandardContextSF storeWithBackup INFO: Store Context /docs separate with backup (at file /Users/weinanli/projs/apache-tomcat-7.0.54/conf/Catalina/localhost/docs.xml.2014-07-09.16-55-31 ) Jul 09, 2014 4:55:31 PM org.apache.catalina.storeconfig.StandardContextSF storeWithBackup INFO: Store Context /examples separate with backup (at file /Users/weinanli/projs/apache-tomcat-7.0.54/conf/Catalina/localhost/examples.xml.2014-07-09.16-55-31 ) Jul 09, 2014 4:55:31 PM org.apache.catalina.storeconfig.StandardContextSF storeWithBackup INFO: Store Context /host-manager separate with backup (at file /Users/weinanli/projs/apache-tomcat-7.0.54/webapps/host-manager/META-INF/context.xml.2014-07-09.16-55-31 ) Jul 09, 2014 4:55:37 PM org.apache.catalina.startup.HostConfig undeploy INFO: Undeploying context [/host-manager] Jul 09, 2014 4:55:38 PM org.apache.catalina.startup.HostConfig undeploy INFO: Undeploying context [/docs] Jul 09, 2014 4:55:38 PM org.apache.catalina.startup.HostConfig undeploy INFO: Undeploying context [] Jul 09, 2014 4:55:38 PM org.apache.catalina.startup.HostConfig undeploy INFO: Undeploying context [/examples] Jul 09, 2014 4:55:39 PM org.apache.catalina.startup.HostConfig undeploy INFO: Undeploying context [/manager] Jul 09, 2014 4:55:39 PM org.apache.catalina.startup.HostConfig deployDescriptor INFO: Deploying configuration descriptor /Users/weinanli/projs/apache-tomcat-7.0.54/conf/Catalina/localhost/docs.xml Jul 09, 2014 4:55:39 PM org.apache.catalina.startup.HostConfig deployDescriptor INFO: Deployment of configuration descriptor /Users/weinanli/projs/apache-tomcat-7.0.54/conf/Catalina/localhost/docs.xml has finished in 154 ms Jul 09, 2014 4:55:39 PM org.apache.catalina.startup.HostConfig deployDescriptor INFO: Deploying configuration descriptor /Users/weinanli/projs/apache-tomcat-7.0.54/conf/Catalina/localhost/examples.xml Jul 09, 2014 4:55:40 PM org.apache.catalina.startup.HostConfig deployDescriptor INFO: Deployment of configuration descriptor /Users/weinanli/projs/apache-tomcat-7.0.54/conf/Catalina/localhost/examples.xml has finished in 331 ms Jul 09, 2014 4:55:40 PM org.apache.catalina.startup.HostConfig deployDescriptor INFO: Deploying configuration descriptor /Users/weinanli/projs/apache-tomcat-7.0.54/conf/Catalina/localhost/ROOT.xml Jul 09, 2014 4:55:40 PM org.apache.catalina.startup.HostConfig deployDescriptor INFO: Deployment of configuration descriptor /Users/weinanli/projs/apache-tomcat-7.0.54/conf/Catalina/localhost/ROOT.xml has finished in 147 ms Jul 09, 2014 4:55:40 PM org.apache.catalina.startup.HostConfig deployDirectory INFO: Deploying web application directory /Users/weinanli/projs/apache-tomcat-7.0.54/webapps/host-manager Jul 09, 2014 4:55:40 PM org.apache.catalina.startup.HostConfig deployDirectory INFO: Deployment of web application directory /Users/weinanli/projs/apache-tomcat-7.0.54/webapps/host-manager has finished in 167 ms Jul 09, 2014 4:55:40 PM org.apache.catalina.startup.HostConfig deployDirectory INFO: Deploying web application directory /Users/weinanli/projs/apache-tomcat-7.0.54/webapps/manager Jul 09, 2014 4:55:40 PM org.apache.catalina.startup.HostConfig deployDirectory INFO: Deployment of web application directory /Users/weinanli/projs/apache-tomcat-7.0.54/webapps/manager has finished in 210 ms
From the log we can see the server configuration files are saved.
Now we can have a look at the design of Storeconfig. The Storeconfig is written as MBeans and registered into Tomcat as a Listener in Tomcat's configuration file 'server.xml':
<Listener className="org.apache.catalina.storeconfig.StoreConfigLifecycleListener" />
We can have a look at the source code of StoreConfigLifecycleListener to see how it's implemented:
public class StoreConfigLifecycleListener implements LifecycleListener { private static Log log = LogFactory .getLog(StoreConfigLifecycleListener.class); IStoreConfig storeConfig; private String storeConfigClass = "org.apache.catalina.storeconfig.StoreConfig"; private String storeRegistry = null; /* * register StoreRegistry after Start the complete Server * * @see org.apache.catalina.LifecycleListener#lifecycleEvent(org.apache.catalina.LifecycleEvent) */ public void lifecycleEvent(LifecycleEvent event) { if (Lifecycle.AFTER_START_EVENT.equals(event.getType())) { if (event.getSource() instanceof StandardServer) { createMBean((StandardServer) event.getSource()); } } } /** * create StoreConfig MBean and load StoreRgistry MBeans name is * <i>Catalina:type=StoreConfig </i> */ protected void createMBean(StandardServer server) { StoreLoader loader = new StoreLoader(); try { Class clazz = Class.forName(getStoreConfigClass(), true, this .getClass().getClassLoader()); storeConfig = (IStoreConfig) clazz.newInstance(); if (null == getStoreRegistry()) // default Loading loader.load(); else // load a spezial file registry (url) loader.load(getStoreRegistry()); // use the loader Registry storeConfig.setRegistry(loader.getRegistry()); storeConfig.setServer(server); } catch (Exception e) { log.error("createMBean load", e); return; } MBeanServer mserver = MBeanUtils.createServer(); InputStream descriptor = null; try { ObjectName objectName = new ObjectName("Catalina:type=StoreConfig" ); if (!mserver.isRegistered(objectName)) { descriptor = this.getClass().getResourceAsStream( "mbeans-descriptors.xml"); Registry registry = MBeanUtils.createRegistry(); registry.loadMetadata(descriptor); mserver.registerMBean(getManagedBean(storeConfig), objectName); } } catch (Exception ex) { log.error("createMBean register MBean", ex); } finally { if (descriptor != null) { try { descriptor.close(); descriptor = null; } catch (Exception ex) { log.error("createMBean register MBean", ex); } } } } }
I've trimmed the code a little bit, the import method is 'createMBean' which will register Storeconfig mbeans into Tomcat. We can see this part of code:
ObjectName objectName = new ObjectName("Catalina:type=StoreConfig" ); if (!mserver.isRegistered(objectName)) { descriptor = this.getClass().getResourceAsStream( "mbeans-descriptors.xml"); Registry registry = MBeanUtils.createRegistry(); registry.loadMetadata(descriptor); mserver.registerMBean(getManagedBean(storeConfig), objectName);
This is the process how the mbeans are registered into 'Catalina:type=StoreConfig" category. The descriptions of mbeans for StoreConfig are in the mbeans-descriptor.xml file:
<?xml version="1.0"?> <!-- Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. --> <mbeans-descriptors> <mbean name="StoreConfig" description="Implementation of a store server.xml config" domain="Catalina" group="StoreConfig" type="org.apache.catalina.storeconfig.StoreConfig"> <operation name="storeConfig" description="Store Server" impact="ACTION" returnType="void" /> <operation name="storeServer" description="Store Server from ObjectName" impact="ACTION" returnType="void" > <parameter name="objectname" description="Objectname from Server" type="java.lang.String" default="Catalina:type=Server"/> <parameter name="backup" description="store Context with backup" type="boolean"/> <parameter name="externalAllowed" description="store all Context external that have a configFile" type="boolean"/> </operation> <!-- Catalina:j2eeType=WebModule,name=//localhost/manager,J2EEApplication=none,J2EEServer=none --> <operation name="storeContext" description="Store Context from ObjectName" impact="ACTION" returnType="void" > <parameter name="objectname" description="ObjectName from Context" type="java.lang.String"/> <parameter name="backup" description="store with Backup" type="boolean"/> <parameter name="externalAllowed" description="store all or store only internal server.xml context (configFile == null)" type="boolean"/> </operation> <operation name="store" description="Store Server" impact="ACTION" returnType="void" > <parameter name="server" description="Server" type="org.apache.catalina.Server" /> </operation> <operation name="store" description="Store Context" impact="ACTION" returnType="void" > <parameter name="context" description="Context" type="org.apache.catalina.context"/> </operation> <operation name="store" description="Store Host" impact="ACTION" returnType="void" > <parameter name="host" description="Host" type="org.apache.catalina.Host"/> </operation> <operation name="store" description="Store Service" impact="ACTION" returnType="void" > <parameter name="service" description="service" type="org.apache.catalina.Service"/> </operation> </mbean> </mbeans-descriptors>
This is the scheme used by Tomcat. There is a reference document to introduce 'mbeans-descriptor.xml' on Tomcat website: MBean Descriptor How To
This is how StoreConfig registered into Tomcat. If you are interested in how to write MBeans for Tomcat, here is an excellent introduction article: MBeans and Tomcat: A HOWTO Guide for Managing YOUR Application
Now let's dig a little bit in Tomcat source code to see how it support Storeconfig:
public synchronized void storeConfig() throws Exception { ObjectName sname = new ObjectName("Catalina:type=StoreConfig"); mserver.invoke(sname, "storeConfig", null, null); } public synchronized void storeContext(Context context) throws Exception { ObjectName sname = null; try { sname = new ObjectName("Catalina:type=StoreConfig"); if(mserver.isRegistered(sname)) { mserver.invoke(sname, "store", new Object[] {context}, new String [] { "java.lang.String"}); } else log.error("StoreConfig mbean not registered" + sname); } catch (Throwable t) { ExceptionUtils.handleThrowable(t); log.error(t); } }
Above are codes in org.apache.catalina.core.StandardServer if Tomcat 7.0.54. We can see the Tomcat server will use Storeconfig to store its configurations if StoreConfig is present.