blob: 685d28875df91ffd965ea2d0ad696619cd66d151 [file] [log] [blame]
/*
* 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.
*/
package org.apache.cocoon.spring.configurator.impl;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Properties;
import org.apache.cocoon.configuration.MutableSettings;
import org.apache.cocoon.configuration.Settings;
import org.apache.cocoon.configuration.SettingsDefaults;
import org.apache.cocoon.spring.configurator.ResourceUtils;
/**
* This is a bean factory post processor which handles all the settings stuff for Cocoon. It reads
* in all properties files and replaces references to them in the spring configuration files. In
* addition this bean acts as a factory bean providing the settings object.
*
* <p>
* The settings object is created by reading several property files and merging of the values. If
* there is more than one definition for a property, the last one wins. The property files are read
* in the following order:
* <ol>
* <li>If {@link #readFromClasspath} is true: classpath*:/META-INF/cocoon/properties/*.properties
* Default values for the core and each block - the files are read in alphabetical order. Actually
* the files are read in two chunks, the first one containing all property files from jar files, and
* the second one containing all property files from WEB-INF/classes.
* <li>If {@link #readFromClasspath} is true:
* classpath*:/META-INF/cocoon/properties/[RUNNING_MODE]/*.properties Default values for the core
* and each block for a specific running mode - the files are read in alphabetical order. Actually
* the files are read in two chunks, the first one containing all property files from jar files, and
* the second one containing all property files from WEB-INF/classes.
* <li>If {@link #readFromGlobalLocation} is true: /WEB-INF/cocoon/properties/*.properties Default
* values for the core and each block - the files are read in alphabetical order. Actually the files
* are read in two chunks, the first one containing all property files from jar files, and the
* second one containing all property files from WEB-INF/classes.
* <li>If {@link #readFromGlobalLocation} is true:
* /WEB-INF/cocoon/properties/[RUNNING_MODE]/*.properties Default values for the core and each block
* for a specific running mode - the files are read in alphabetical order. Actually the files are
* read in two chunks, the first one containing all property files from jar files, and the second
* one containing all property files from WEB-INF/classes.
* <li>Working directory from servlet context (if not already set)
* <li>Optional property file which is stored under ".cocoon/settings.properties" in the user
* directory.
* <li>Additional property file specified by the "org.apache.cocoon.settings" property. If the
* property defines a directory, all property files from this directory are read in alphabetical
* order and all files from a sub directory with the name of the current running mode are read in
* alphabetical order as well.
* <li>Property provider (if configured in the bean factory)
* <li>Add properties from configured directories {@link #directories}.
* <li>Add additional properties configured at {@link #additionalProperties}
* <li>System properties
* </ol>
*
* This means that system properties (provided on startup of the web application) override all
* others etc.
*
* @since 1.0
* @version $Id$
*/
public class SettingsBeanFactoryPostProcessor extends AbstractSettingsBeanFactoryPostProcessor {
/**
* The running mode for the web application.
*/
protected String runningMode = SettingsDefaults.DEFAULT_RUNNING_MODE;
/**
* Should we read the properties from the classpath?
*
* @see Constants#CLASSPATH_PROPERTIES_LOCATION
*/
protected boolean readFromClasspath = true;
/**
* Should we read the properties from the global location?
*
* @see Constants#GLOBAL_PROPERTIES_LOCATION
*/
protected boolean readFromGlobalLocation = true;
/**
* Set the running mode.
*
* @param mode The new running mode.
*/
public void setRunningMode(String mode) {
this.runningMode = mode;
}
/**
* Set if we read property configurations from the classpath.
*
* @param readFromClasspath
*/
public void setReadFromClasspath(boolean readFromClasspath) {
this.readFromClasspath = readFromClasspath;
}
/**
* Set if we read property configurations from the global location.
*/
public void setReadFromGlobalLocation(boolean readFromGlobalLocation) {
this.readFromGlobalLocation = readFromGlobalLocation;
}
/**
* Initialize this processor. Setup the settings object.
*
* @throws Exception
*/
@Override
public void init() throws Exception {
// get the running mode
final String mode = this.getRunningMode();
RunningModeHelper.checkRunningMode(mode);
// print out version information
final Properties pomProps = ResourceUtils.getPOMProperties("org.apache.cocoon", "cocoon-spring-configurator");
final String version;
if (pomProps != null) {
version = pomProps.getProperty("version");
} else {
version = null;
}
// give a startup message
final String msg = "Apache Cocoon Spring Configurator " + (version != null ? "v" + version + " " : "")
+ "is running in mode '" + mode + "'.";
if (this.servletContext != null) {
this.servletContext.log(msg);
} else {
this.logger.info(msg);
}
// first we dump the system properties
this.dumpSystemProperties();
// now create the settings object
super.init();
// finally pre load classes
this.forceLoad();
}
/**
* @see org.apache.cocoon.spring.configurator.impl.AbstractSettingsBeanFactoryPostProcessor#getRunningMode()
*/
@Override
protected String getRunningMode() {
return RunningModeHelper.determineRunningMode(this.runningMode);
}
@Override
protected void preInit(final MutableSettings s, final Properties properties) {
final String mode = this.getRunningMode();
if (this.readFromClasspath) {
// now read all properties from classpath directory
ResourceUtils.readProperties(Constants.CLASSPATH_PROPERTIES_LOCATION, properties, this.getResourceLoader(),
this.resourceFilter, this.logger);
// read all properties from the mode dependent directory
ResourceUtils.readProperties(Constants.CLASSPATH_PROPERTIES_LOCATION + "/" + mode, properties, this
.getResourceLoader(), this.resourceFilter, this.logger);
}
if (this.readFromGlobalLocation) {
// now read all properties from the properties directory
ResourceUtils.readProperties(Constants.GLOBAL_PROPERTIES_LOCATION, properties, this.getResourceLoader(),
this.resourceFilter, this.logger);
// read all properties from the mode dependent directory
ResourceUtils.readProperties(Constants.GLOBAL_PROPERTIES_LOCATION + "/" + mode, properties, this
.getResourceLoader(), this.resourceFilter, this.logger);
}
// set default work directory value
if (s.getWorkDirectory() == null) {
File workDir;
// fill from the servlet context
if (this.servletContext != null) {
workDir = (File) this.servletContext.getAttribute("javax.servlet.context.tempdir");
} else {
workDir = new File("cocoon-files");
}
s.setWorkDirectory(workDir.getAbsolutePath());
}
// set default cache directory value
if (s.getCacheDirectory() == null) {
s.setCacheDirectory(new File(s.getWorkDirectory(), "cache-dir").getAbsolutePath());
}
// read additional properties file
// first try in home directory
final String homeDir = this.getSystemProperty("user.home");
if (homeDir != null) {
final String fileName = homeDir + File.separator + ".cocoon" + File.separator + "settings.properties";
final File testFile = new File(fileName);
if (testFile.exists()) {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Reading user settings from '" + fileName + "'");
}
try {
final FileInputStream fis = new FileInputStream(fileName);
try {
properties.load(fis);
} finally {
try {
fis.close();
} catch (IOException ioe) {
this.logger.warn("Failed to close FileInputStream:", ioe);
}
}
} catch (IOException ignore) {
this.logger.info("Unable to read '" + fileName + "' - continuing with initialization.", ignore);
}
}
}
// check for additionally specified custom file:
// 1. bean attribute
// 2. servletContext init parameter
// 3. system property
String additionalPropertyFile = s.getProperty(Settings.PROPERTY_USER_SETTINGS, this
.getServletContextInitParameter(Settings.PROPERTY_USER_SETTINGS, this
.getSystemProperty(Settings.PROPERTY_USER_SETTINGS)));
if (additionalPropertyFile != null) {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Reading user settings from '" + additionalPropertyFile + "'");
}
final File additionalFile = new File(additionalPropertyFile);
if (additionalFile.exists()) {
if (additionalFile.isDirectory()) {
// read from directory
ResourceUtils.readProperties(additionalFile.getAbsolutePath(), properties,
this.getResourceLoader(), this.resourceFilter, this.logger);
// read all properties from the mode dependent directory
ResourceUtils.readProperties(additionalFile.getAbsolutePath() + File.separatorChar + mode,
properties, this.getResourceLoader(), this.resourceFilter, this.logger);
} else {
// read the file
try {
final FileInputStream fis = new FileInputStream(additionalFile);
properties.load(fis);
fis.close();
} catch (IOException ignore) {
this.logger.info("Unable to read '" + additionalPropertyFile
+ "' - continuing with initialization.", ignore);
}
}
} else {
this.logger.info("Additional settings file '" + additionalPropertyFile
+ "' does not exist - continuing with initialization.");
}
}
}
protected String getServletContextInitParameter(String key, String defaultValue) {
if (this.servletContext == null) {
return defaultValue;
}
String servletContextSettings = this.servletContext.getInitParameter(key);
if (servletContextSettings == null || "".equals(servletContextSettings)) {
return defaultValue;
}
return servletContextSettings;
}
@Override
protected MutableSettings createSettings() {
final MutableSettings s = super.createSettings();
// first init the work-directory for the logger.
// this is required if we are running inside a war file!
File workDir = new File(s.getWorkDirectory());
workDir.mkdirs();
if (this.logger.isDebugEnabled()) {
this.logger.debug("Using work-directory " + workDir);
}
File cacheDir = new File(s.getCacheDirectory());
cacheDir.mkdirs();
if (this.logger.isDebugEnabled()) {
this.logger.debug("Using cache-directory " + cacheDir);
}
return s;
}
/**
* Dump System Properties.
*/
protected void dumpSystemProperties() {
if (this.logger.isDebugEnabled()) {
try {
Enumeration e = System.getProperties().propertyNames();
this.logger.debug("===== System Properties Start =====");
while (e.hasMoreElements()) {
String key = (String) e.nextElement();
this.logger.debug(key + "=" + System.getProperty(key));
}
this.logger.debug("===== System Properties End =====");
} catch (SecurityException se) {
// Ignore Exceptions.
}
}
}
/**
* Handle the <code>load-class</code> settings. This overcomes limits in many classpath issues.
* One of the more notorious ones is a bug in WebSphere that does not load the URL handler for
* the <code>classloader://</code> protocol. In order to overcome that bug, set
* <code>org.apache.cocoon.classloader.load.classes.XY</code> property to the
* <code>com.ibm.servlet.classloader.Handler</code> value.
*
* <p>
* If you need to load more than one class, then add several properties, all starting with
* <cod>org.apache.cocoon.classloader.load.classes.</code> followed by a self defined
* identifier.
* </p>
*/
protected void forceLoad() {
final Iterator i = this.settings.getLoadClasses().iterator();
while (i.hasNext()) {
final String fqcn = (String) i.next();
try {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Loading class: " + fqcn);
}
Thread.currentThread().getContextClassLoader().loadClass(fqcn).newInstance();
} catch (Exception e) {
if (this.logger.isWarnEnabled()) {
this.logger.warn("Could not load class: " + fqcn + ". Continuing initialization.", e);
}
// Do not throw an exception, because it is not a fatal error.
}
}
}
}