NIHVIVO-1261 Add the new ConfigurationProperties classes, and add the setup to web.xml
This commit is contained in:
parent
3134054f7b
commit
b7510fa355
6 changed files with 533 additions and 0 deletions
|
@ -0,0 +1,145 @@
|
|||
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||
|
||||
package edu.cornell.mannlib.vitro.webapp.config;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.ServletConfig;
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletContextEvent;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpSession;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
/**
|
||||
* Provides an mechanism for modules to read the configuration properties that
|
||||
* are attached to the servlet context.
|
||||
*
|
||||
* The customary behavior is for ConfigurationPropertiesSetup to create a
|
||||
* ConfigurationPropertiesImpl, which will parse the deploy.properties file for
|
||||
* these properties.
|
||||
*/
|
||||
public abstract class ConfigurationProperties {
|
||||
private static final Log log = LogFactory
|
||||
.getLog(ConfigurationProperties.class);
|
||||
|
||||
/** The bean is attached to the session by this name. */
|
||||
private static final String ATTRIBUTE_NAME = ConfigurationProperties.class
|
||||
.getName();
|
||||
|
||||
/** If they ask for a bean before one has been set, they get this. */
|
||||
private static final ConfigurationProperties DUMMY_PROPERTIES = new DummyConfigurationProperties();
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// static methods
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
public static ConfigurationProperties getBean(ServletRequest request) {
|
||||
if (request == null) {
|
||||
throw new NullPointerException("request may not be null.");
|
||||
}
|
||||
if (!(request instanceof HttpServletRequest)) {
|
||||
throw new IllegalArgumentException(
|
||||
"request must be an HttpServletRequest");
|
||||
}
|
||||
HttpServletRequest httpRequest = (HttpServletRequest) request;
|
||||
return getBean(httpRequest.getSession());
|
||||
}
|
||||
|
||||
public static ConfigurationProperties getBean(HttpSession session) {
|
||||
if (session == null) {
|
||||
throw new NullPointerException("session may not be null.");
|
||||
}
|
||||
return getBean(session.getServletContext());
|
||||
}
|
||||
|
||||
public static ConfigurationProperties getBean(HttpServlet servlet) {
|
||||
if (servlet == null) {
|
||||
throw new NullPointerException("servlet may not be null.");
|
||||
}
|
||||
return getBean(servlet.getServletContext());
|
||||
}
|
||||
|
||||
public static ConfigurationProperties getBean(ServletContextEvent sce) {
|
||||
if (sce == null) {
|
||||
throw new NullPointerException("sce may not be null.");
|
||||
}
|
||||
return getBean(sce.getServletContext());
|
||||
}
|
||||
|
||||
public static ConfigurationProperties getBean(ServletConfig servletConfig) {
|
||||
if (servletConfig == null) {
|
||||
throw new NullPointerException("servletConfig may not be null.");
|
||||
}
|
||||
return getBean(servletConfig.getServletContext());
|
||||
}
|
||||
|
||||
public static ConfigurationProperties getBean(ServletContext context) {
|
||||
if (context == null) {
|
||||
throw new NullPointerException("context may not be null.");
|
||||
}
|
||||
|
||||
Object o = context.getAttribute(ATTRIBUTE_NAME);
|
||||
if (o == null) {
|
||||
log.error("ConfigurationProperties bean has not been set.");
|
||||
return DUMMY_PROPERTIES;
|
||||
} else if (!(o instanceof ConfigurationProperties)) {
|
||||
log.error("Error: ConfigurationProperties was set to an "
|
||||
+ "invalid object: " + o);
|
||||
return DUMMY_PROPERTIES;
|
||||
}
|
||||
|
||||
return (ConfigurationProperties) o;
|
||||
}
|
||||
|
||||
/**
|
||||
* Protected access, so the Stub class can call it for unit tests.
|
||||
* Otherwise, this should only be called by ConfigurationPropertiesSetup.
|
||||
*/
|
||||
protected static void setBean(ServletContext context,
|
||||
ConfigurationProperties bean) {
|
||||
if (context == null) {
|
||||
throw new NullPointerException("context may not be null.");
|
||||
}
|
||||
if (bean == null) {
|
||||
throw new NullPointerException("bean may not be null.");
|
||||
}
|
||||
context.setAttribute(ATTRIBUTE_NAME, bean);
|
||||
log.info(bean);
|
||||
}
|
||||
|
||||
/** Package access, so unit tests can call it. */
|
||||
static void removeBean(ServletContext context) {
|
||||
if (context == null) {
|
||||
throw new NullPointerException("context may not be null.");
|
||||
}
|
||||
context.removeAttribute(ATTRIBUTE_NAME);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// The interface
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Get the value of the property, or <code>null</code> if the property has
|
||||
* not been assigned a value.
|
||||
*/
|
||||
public abstract String getProperty(String key);
|
||||
|
||||
/**
|
||||
* Get the value of the property, or use the default value if the property
|
||||
* has not been assigned a value.
|
||||
*/
|
||||
public abstract String getProperty(String key, String defaultValue);
|
||||
|
||||
/**
|
||||
* Get a copy of the map of the configuration properties and their settings.
|
||||
* Because this is a copy, it cannot be used to modify the settings.
|
||||
*/
|
||||
public abstract Map<String, String> getPropertyMap();
|
||||
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||
|
||||
package edu.cornell.mannlib.vitro.webapp.config;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
/**
|
||||
* The basic implementation of ConfigurationProperties. It loads the
|
||||
* configuration properties from a properties file and stores them in a map.
|
||||
*
|
||||
* Leading and trailing white space are trimmed from the property values.
|
||||
*
|
||||
* Once the properties have been parsed and stored, they are immutable.
|
||||
*/
|
||||
public class ConfigurationPropertiesImpl extends ConfigurationProperties {
|
||||
private static final Log log = LogFactory
|
||||
.getLog(ConfigurationPropertiesImpl.class);
|
||||
|
||||
private final Map<String, String> propertyMap;
|
||||
|
||||
public ConfigurationPropertiesImpl(InputStream stream) {
|
||||
Properties props = loadFromPropertiesFile(stream);
|
||||
Map<String, String> map = copyPropertiesToMap(props);
|
||||
trimWhiteSpaceFromValues(map);
|
||||
this.propertyMap = Collections.unmodifiableMap(map);
|
||||
|
||||
log.debug("Configuration properties are: " + map);
|
||||
}
|
||||
|
||||
private Properties loadFromPropertiesFile(InputStream stream) {
|
||||
Properties props = new Properties();
|
||||
try {
|
||||
props.load(stream);
|
||||
} catch (IOException e) {
|
||||
throw new IllegalStateException(
|
||||
"Failed to parse the configuration properties file.", e);
|
||||
}
|
||||
return props;
|
||||
}
|
||||
|
||||
private Map<String, String> copyPropertiesToMap(Properties props) {
|
||||
Map<String, String> map = new HashMap<String, String>();
|
||||
for (Enumeration<?> keys = props.keys(); keys.hasMoreElements();) {
|
||||
String key = (String) keys.nextElement();
|
||||
String value = props.getProperty(key);
|
||||
map.put(key, value);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
private void trimWhiteSpaceFromValues(Map<String, String> map) {
|
||||
for (String key : map.keySet()) {
|
||||
map.put(key, map.get(key).trim());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProperty(String key) {
|
||||
return propertyMap.get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProperty(String key, String defaultValue) {
|
||||
if (propertyMap.containsKey(key)) {
|
||||
return propertyMap.get(key);
|
||||
} else {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getPropertyMap() {
|
||||
return new HashMap<String, String>(propertyMap);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ConfigurationPropertiesImpl[propertyMap=" + propertyMap + "]";
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,186 @@
|
|||
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||
|
||||
package edu.cornell.mannlib.vitro.webapp.config;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import javax.naming.Context;
|
||||
import javax.naming.InitialContext;
|
||||
import javax.naming.NamingException;
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletContextEvent;
|
||||
import javax.servlet.ServletContextListener;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import edu.cornell.mannlib.vitro.webapp.servlet.setup.AbortStartup;
|
||||
|
||||
/**
|
||||
* Reads the configuration properties from a file and stores them in the servlet
|
||||
* context.
|
||||
*
|
||||
* This must be invoked before any listener that requires configuration
|
||||
* properties.
|
||||
*
|
||||
* The path to the file can be specified by an Environment name in the Context,
|
||||
* like this:
|
||||
*
|
||||
* <pre>
|
||||
*
|
||||
* <Context override="true">
|
||||
* <Environment name="path.configuration"
|
||||
* value="/wherever/the/file/lives/deploy.properties"
|
||||
* type="java.lang.String"
|
||||
* override="false" />
|
||||
* </Context>
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
* We look in this environment variable to find the path to the properties file.
|
||||
* If there is no such environment variable, the default path is used.
|
||||
*
|
||||
* Once the path has been determined, we will use it to look for a resource in
|
||||
* the classpath. So if the path is "deploy.properties", it might be found in
|
||||
* "tomcat/webapps/vivo/WEB-INF/classes/deploy.properties". Of course, it might
|
||||
* also be found in any other portion of the classpath as well.
|
||||
*
|
||||
* If we can't find the resource in the classpath, we will use it to look for an
|
||||
* external file. So, one might reasonably set this value to something like
|
||||
* "/usr/local/vitro/stuff/my.deploy.properties".
|
||||
*
|
||||
* If neither a resource nor an external file can be found, we throw an
|
||||
* exception and set AbortStartup.
|
||||
*/
|
||||
public class ConfigurationPropertiesSetup implements ServletContextListener {
|
||||
private static final Log log = LogFactory
|
||||
.getLog(ConfigurationPropertiesSetup.class);
|
||||
|
||||
/**
|
||||
* The JNDI naming context where Tomcat stores environment attributes.
|
||||
*/
|
||||
static final String JNDI_BASE = "java:comp/env";
|
||||
|
||||
/**
|
||||
* The name of the JNDI environment mapping for the path to the
|
||||
* configuration file (or resource).
|
||||
*/
|
||||
static final String PATH_CONFIGURATION = "path.configuration";
|
||||
|
||||
/**
|
||||
* If we don't find the path to the config properties from a JNDI mapping,
|
||||
* use this. Not final, so we can jigger it for unit tests.
|
||||
*/
|
||||
private static String DEFAULT_CONFIG_PATH = "deploy.properties";
|
||||
|
||||
@Override
|
||||
public void contextInitialized(ServletContextEvent sce) {
|
||||
if (AbortStartup.isStartupAborted(sce.getServletContext())) {
|
||||
return;
|
||||
}
|
||||
|
||||
ServletContext ctx = sce.getServletContext();
|
||||
|
||||
try {
|
||||
InputStream stream = null;
|
||||
try {
|
||||
stream = locatePropertiesFile();
|
||||
ConfigurationProperties.setBean(ctx,
|
||||
new ConfigurationPropertiesImpl(stream));
|
||||
} finally {
|
||||
if (stream != null) {
|
||||
try {
|
||||
stream.close();
|
||||
} catch (IOException e) {
|
||||
log.error(e, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error(e, e);
|
||||
AbortStartup.abortStartup(ctx);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private InputStream locatePropertiesFile() {
|
||||
String path = determinePathToProperties();
|
||||
log.debug("Configuration properties path is '" + path + "'");
|
||||
|
||||
if (resourceExists(path)) {
|
||||
log.debug("Found configuration properties as a resource.");
|
||||
return getResourceStream(path);
|
||||
}
|
||||
|
||||
if (externalFileExists(path)) {
|
||||
log.debug("Found configuration properties as an external file.");
|
||||
return getExternalFileStream(path);
|
||||
}
|
||||
|
||||
throw new IllegalStateException("Can't find the properties file at '"
|
||||
+ path + "'");
|
||||
}
|
||||
|
||||
/**
|
||||
* If we can't find it with JNDI, use the default.
|
||||
*/
|
||||
private String determinePathToProperties() {
|
||||
try {
|
||||
Context envCtx = (Context) new InitialContext().lookup(JNDI_BASE);
|
||||
if (envCtx == null) {
|
||||
log.debug("JNDI Lookup on '" + JNDI_BASE + "' failed.");
|
||||
return DEFAULT_CONFIG_PATH;
|
||||
}
|
||||
|
||||
String configPath = (String) envCtx.lookup(PATH_CONFIGURATION);
|
||||
if (configPath == null) {
|
||||
log.debug("JNDI Lookup on '" + PATH_CONFIGURATION + "' failed.");
|
||||
return DEFAULT_CONFIG_PATH;
|
||||
}
|
||||
|
||||
log.debug("deploy.property as specified by JNDI: " + configPath);
|
||||
return configPath;
|
||||
} catch (NamingException e) {
|
||||
log.warn("JNDI lookup failed. "
|
||||
+ "Using default path for config properties.", e);
|
||||
return DEFAULT_CONFIG_PATH;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean resourceExists(String path) {
|
||||
return getResourceStream(path) != null;
|
||||
}
|
||||
|
||||
private InputStream getResourceStream(String path) {
|
||||
return getClass().getClassLoader().getResourceAsStream(path);
|
||||
}
|
||||
|
||||
private boolean externalFileExists(String path) {
|
||||
File file = new File(path);
|
||||
return file.isFile();
|
||||
}
|
||||
|
||||
private InputStream getExternalFileStream(String path) {
|
||||
InputStream stream = null;
|
||||
File file = new File(path);
|
||||
if (file.isFile()) {
|
||||
try {
|
||||
stream = new FileInputStream(file);
|
||||
} catch (FileNotFoundException e) {
|
||||
// testing file.isFile() should have prevented this
|
||||
log.error(e, e);
|
||||
}
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void contextDestroyed(ServletContextEvent sce) {
|
||||
ConfigurationProperties.removeBean(sce.getServletContext());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||
|
||||
package edu.cornell.mannlib.vitro.webapp.config;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
/**
|
||||
* If somebody asks for ConfigurationProperties before it has been initialized,
|
||||
* they get this. It doesn't stop them from proceeding, it just yields no
|
||||
* properties while logging warning messages for each request.
|
||||
*/
|
||||
class DummyConfigurationProperties extends ConfigurationProperties {
|
||||
private static final Log log = LogFactory
|
||||
.getLog(DummyConfigurationProperties.class);
|
||||
|
||||
@Override
|
||||
public String getProperty(String key) {
|
||||
log.warn("ConfigurationProperties has not been initialized: getProperty(\""
|
||||
+ key + "\")");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProperty(String key, String defaultValue) {
|
||||
log.warn("ConfigurationProperties has not been initialized: getProperty(\""
|
||||
+ key + "\", \"" + defaultValue + "\")");
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getPropertyMap() {
|
||||
log.warn("ConfigurationProperties has not been initialized: "
|
||||
+ "getPropertyMap()");
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue