View Javadoc

1   /**
2    * This file Copyright (c) 2003-2010 Magnolia International
3    * Ltd.  (http://www.magnolia-cms.com). All rights reserved.
4    *
5    *
6    * This file is dual-licensed under both the Magnolia
7    * Network Agreement and the GNU General Public License.
8    * You may elect to use one or the other of these licenses.
9    *
10   * This file is distributed in the hope that it will be
11   * useful, but AS-IS and WITHOUT ANY WARRANTY; without even the
12   * implied warranty of MERCHANTABILITY or FITNESS FOR A
13   * PARTICULAR PURPOSE, TITLE, or NONINFRINGEMENT.
14   * Redistribution, except as permitted by whichever of the GPL
15   * or MNA you select, is prohibited.
16   *
17   * 1. For the GPL license (GPL), you can redistribute and/or
18   * modify this file under the terms of the GNU General
19   * Public License, Version 3, as published by the Free Software
20   * Foundation.  You should have received a copy of the GNU
21   * General Public License, Version 3 along with this program;
22   * if not, write to the Free Software Foundation, Inc., 51
23   * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
24   *
25   * 2. For the Magnolia Network Agreement (MNA), this file
26   * and the accompanying materials are made available under the
27   * terms of the MNA which accompanies this distribution, and
28   * is available at http://www.magnolia-cms.com/mna.html
29   *
30   * Any modifications to this file must keep this entire header
31   * intact.
32   *
33   */
34  package info.magnolia.cms.servlets;
35  
36  import info.magnolia.cms.beans.config.ConfigLoader;
37  import info.magnolia.cms.beans.config.PropertiesInitializer;
38  import info.magnolia.cms.core.SystemProperty;
39  import info.magnolia.context.MgnlContext;
40  import info.magnolia.logging.Log4jConfigurer;
41  import info.magnolia.module.ModuleManager;
42  
43  import java.net.InetAddress;
44  import java.net.UnknownHostException;
45  
46  import javax.servlet.ServletContext;
47  import javax.servlet.ServletContextEvent;
48  import javax.servlet.ServletContextListener;
49  
50  import org.apache.commons.lang.BooleanUtils;
51  import org.apache.commons.lang.StringUtils;
52  import org.slf4j.Logger;
53  import org.slf4j.LoggerFactory;
54  
55  
56  /**
57   * <p>
58   * Magnolia main listener: reads initialization parameter from a properties file. The name of the file can be defined as
59   * a context parameter in web.xml. Multiple path, comma separated, are supported (the first existing file in the list
60   * will be used), and the following variables will be used:
61   * </p>
62   * <ul>
63   * <li><code>${servername}</code>: name of the server where the webapp is running, lowercase</li>
64   * <li><code>${webapp}</code>: the latest token in the webapp path (e.g. <code>magnoliaPublic</code> for a webapp
65   * deployed ad <code>tomcat/webapps/magnoliaPublic</code>)</li>
66   * </ul>
67   * <p>
68   * If no <code>magnolia.initialization.file</code> context parameter is set, the following default is assumed:
69   * </p>
70   *
71   * <pre>
72   * &lt;context-param>
73   *   &lt;param-name>magnolia.initialization.file&lt;/param-name>
74   *   &lt;param-value>
75   *      WEB-INF/config/${servername}/${webapp}/magnolia.properties,
76   *      WEB-INF/config/${servername}/magnolia.properties,
77   *      WEB-INF/config/${webapp}/magnolia.properties,
78   *      WEB-INF/config/default/magnolia.properties,
79   *      WEB-INF/config/magnolia.properties
80   *   &lt;/param-value>
81   * &lt;/context-param>
82   * </pre>
83   * <p>
84   * The ${servername} variable will be resolved to the full name obtained by using
85   * InetAddress.getLocalHost().getHostName(), which may also contain the server domain, depending on your server
86   * configuration/operating system. You can set the optional context parameter "magnolia.unqualified.server.name" to true
87   * if you prefer using the unqualified name (the server "server.domain.com" will be simply resolved as "server")
88   * </p>
89   *
90   * <pre>
91   * &lt;context-param>
92   *   &lt;param-name>magnolia.unqualified.server.name&lt;/param-name>
93   *   &lt;param-value>true&lt;/param-value>
94   * &lt;/context-param>
95   * </pre>
96   *
97   * The following parameters are needed in the properties file:
98   * <dl>
99   * <dt>magnolia.cache.startdir</dt>
100  * <dd>directory used for cached pages</dd>
101  * <dt>magnolia.upload.tmpdir</dt>
102  * <dd>tmp directory for uploaded files</dd>
103  * <dt>magnolia.exchange.history</dt>
104  * <dd>history directory used for activation</dd>
105  * <dt>magnolia.repositories.config</dt>
106  * <dd>repositories configuration</dd>
107  * <dt>log4j.config</dt>
108  * <dd>Name of a log4j config file. Can be a .properties or .xml file. The value can be:
109  * <ul>
110  * <li>a full path</li>
111  * <li>a path relative to the webapp root</li>
112  * <li>a file name which will be loaded from the classpath</li>
113  * </ul>
114  * </dd>
115  * <dt>magnolia.root.sysproperty</dt>
116  * <dd>Name of a system variable which will be set to the webapp root. You can use this property in log4j configuration
117  * files to handle relative paths, such as <code>${magnolia.root}logs/magnolia-debug.log</code>.
118  * <strong>Important</strong>: if you drop multiple magnolia wars in a container which doesn't isolate system properties
119  * (e.g. tomcat) you will need to change the name of the <code>magnolia.root.sysproperty</code> variable in web.xml and
120  * in log4j configuration files.</dd>
121  * <dt>magnolia.bootstrap.dir</dt>
122  * <dd>Directory containing xml files for initialization of a blank magnolia instance. If no content is found in any of
123  * the repository, they are initialized importing xml files found in this folder. If you don't want to let magnolia
124  * automatically initialize repositories simply remove this parameter.</dd>
125  * </dl>
126  * <h3>Advance use: deployment service</h3>
127  * <p>
128  * Using the <code>${servername}</code> and <code>${webapp}</code> properties you can easily bundle in the same webapp
129  * different set of configurations which are automatically applied dependending on the server name (useful for switching
130  * between development, test and production instances where the repository configuration need to be different) or the
131  * webapp name (useful to bundle both the public and admin log4j/jndi/bootstrap configuration in the same war). By
132  * default the initializer will try to search for the file in different location with different combination of
133  * <code>servername</code> and <code>webapp</code>: the <code>default</code> fallback directory will be used if no other
134  * environment-specific directory has been added.
135  * </p>
136  * @author Fabrizio Giustina
137  */
138 public class MgnlServletContextListener implements ServletContextListener {
139 
140     private static final Logger log = LoggerFactory.getLogger(MgnlServletContextListener.class);
141 
142     /**
143      * Context parameter name.
144      */
145     public static final String MAGNOLIA_INITIALIZATION_FILE = "magnolia.initialization.file"; //$NON-NLS-1$
146 
147     /**
148      * Context parameter name. If set to true in web.xml the server name resolved by magnolia will never contain the
149      * domain (the server "server.domain.com" will be simply resolved as "server").
150      */
151     public static final String MAGNOLIA_UNQUALIFIED_SERVER_NAME = "magnolia.unqualified.server.name"; //$NON-NLS-1$
152 
153     private ConfigLoader loader;
154 
155     public void contextDestroyed(final ServletContextEvent sce) {
156         // avoid disturbing NPEs if the context has never been started (classpath problems, etc)
157         ModuleManager mm = ModuleManager.Factory.getInstance();
158         if (mm != null) {
159             mm.stopModules();
160         }
161 
162         if (loader != null) {
163             MgnlContext.doInSystemContext(new MgnlContext.VoidOp() {
164                 public void doExec() {
165                     loader.unload(sce.getServletContext());
166                 }
167             }, true);
168         }
169 
170         Log4jConfigurer.shutdownLogging();
171     }
172 
173     /**
174      * @see javax.servlet.ServletContextListener#contextInitialized(javax.servlet.ServletContextEvent)
175      */
176     public void contextInitialized(final ServletContextEvent sce) {
177         final ServletContext context = sce.getServletContext();
178 
179         boolean unqualifiedServerName = BooleanUtils.toBoolean(context
180             .getInitParameter(MAGNOLIA_UNQUALIFIED_SERVER_NAME));
181 
182         String servername = initServername(unqualifiedServerName);
183 
184         String rootPath = initRootPath(context);
185 
186         String webapp = initWebappName(rootPath);
187 
188         log.debug("servername is {}, rootPath is {}, webapp is {}", new Object[]{servername, rootPath, webapp}); //$NON-NLS-1$
189 
190         String propertiesFilesString = getPropertiesFilesString(context, servername, webapp);
191 
192         PropertiesInitializer.getInstance().loadAllProperties(propertiesFilesString, rootPath);
193 
194         // expose server name as a system property, so it can be used in log4j configurations
195         // rootPath and webapp are not exposed since there can be different webapps running in the same jvm
196         System.setProperty("server", servername);
197 
198         Log4jConfigurer.initLogging();
199 
200         this.loader = new ConfigLoader(context);
201         startServer(context);
202     }
203 
204     protected void startServer(final ServletContext context) {
205         MgnlContext.doInSystemContext(new MgnlContext.VoidOp() {
206             public void doExec() {
207                 loader.load(context);
208             }
209         }, true);
210     }
211 
212     protected String getPropertiesFilesString(ServletContext context, String servername, String webapp) {
213         String propertiesFilesString = context.getInitParameter(MAGNOLIA_INITIALIZATION_FILE);
214         if (StringUtils.isEmpty(propertiesFilesString)) {
215             log.debug(
216                 "{} value in web.xml is undefined, falling back to default: {}",
217                 MgnlServletContextListener.MAGNOLIA_INITIALIZATION_FILE,
218                 PropertiesInitializer.DEFAULT_INITIALIZATION_PARAMETER);
219             propertiesFilesString = PropertiesInitializer.DEFAULT_INITIALIZATION_PARAMETER;
220         }
221         else {
222             log
223                 .debug(
224                     "{} value in web.xml is :'{}'", MgnlServletContextListener.MAGNOLIA_INITIALIZATION_FILE, propertiesFilesString); //$NON-NLS-1$
225         }
226         return PropertiesInitializer.processPropertyFilesString(context, servername, webapp, propertiesFilesString);
227     }
228 
229     protected String initWebappName(String rootPath) {
230         String webapp = StringUtils.substringAfterLast(rootPath, "/"); //$NON-NLS-1$
231         SystemProperty.setProperty(SystemProperty.MAGNOLIA_WEBAPP, webapp);
232         return webapp;
233     }
234 
235     /**
236      * Initializes the application real root path.
237      * @param context Servlet context.
238      * @return Real path.
239      */
240 
241     protected String initRootPath(final ServletContext context) {
242         String realPath = StringUtils.replace(context.getRealPath(StringUtils.EMPTY), "\\", "/"); //$NON-NLS-1$ //$NON-NLS-2$
243         realPath = StringUtils.removeEnd(realPath, "/");
244         if (realPath == null) {
245             // don't use new java.io.File("x").getParentFile().getAbsolutePath() to find out real directory, could throw
246             // a NPE for unexpanded war
247             throw new RuntimeException(
248                 "Magnolia is not configured properly and therefore unable to start: real path can't be obtained [ctx real path:"
249                     + context.getRealPath(StringUtils.EMPTY)
250                     + "]. Please refer to the Magnolia documentation for installation instructions specific to your environment.");
251         }
252         SystemProperty.setProperty(SystemProperty.MAGNOLIA_APP_ROOTDIR, realPath);
253         return realPath;
254     }
255 
256     protected String initServername(boolean unqualified) {
257         String servername = null;
258 
259         try {
260             servername = StringUtils.lowerCase(InetAddress.getLocalHost().getHostName());
261 
262             if (unqualified && StringUtils.contains(servername, ".")) {
263                 servername = StringUtils.substringBefore(servername, ".");
264             }
265 
266             SystemProperty.setProperty(SystemProperty.MAGNOLIA_SERVERNAME, servername);
267         }
268         catch (UnknownHostException e) {
269             log.error(e.getMessage());
270         }
271         return servername;
272     }
273 
274 }