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 * <context-param> 73 * <param-name>magnolia.initialization.file</param-name> 74 * <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 * </param-value> 81 * </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 * <context-param> 92 * <param-name>magnolia.unqualified.server.name</param-name> 93 * <param-value>true</param-value> 94 * </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 }