/*
* Copyright 2000-2004 The Apache Software Foundation.
*
* Licensed 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.jetspeed.services.resources;
// Java Core Classes
import java.io.File;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
// Turbine stuff.
import org.apache.commons.configuration.Configuration;
import org.apache.turbine.services.resources.ResourceService;
import org.apache.turbine.services.resources.TurbineResourceService;
import org.apache.turbine.services.TurbineServices;
import org.apache.turbine.services.InitializationException;
// Jetspeed classes
import org.apache.jetspeed.services.logging.JetspeedLogFactoryService;
import org.apache.jetspeed.services.logging.JetspeedLogger;
/**
* <p>This implementation of the <code>resourcesService</code> relies
* on an external properties file for storing the configuration keys
* and values</p>
*
* <P>In order to be compatible with legacy applications, this implementation
* kept a static method for initializing the service, so it's still possible
* to write the following code:
* <p><code>
* TurbineResourceService.setPropertiesName("d:/conf/Turbine.properties");
* Vector myVar = TurbineResources.getVector("myvar");
* </code></p>
*
* <p>This implementation allows the use of several pre-defined variables within
* the configuration file. The variables are identified by the following
* sequence of tokens: ${<varname>}. Varname is always folded to lowercase.</p>
* <P>The predefined variables are:
* <ul>
* <li>webapp.dir: base directory for the web application
* <li>jvm.dir: JVM startup directory
* </ul>
* </p>
* <p>The init parameters of the servlet are also imported as default variables.
* They may override the previously defined default variables
* </p>
*
* @author <a href="mailto:raphael@apache.org">Rapha�l Luta</a>
* @version $Id: VariableResourcesService.java,v 1.15 2004/02/23 03:29:53 jford Exp $
*/
public class VariableResourcesService extends TurbineResourceService
{
/**
* Static initialization of the logger for this class
*/
private static final JetspeedLogger logger = JetspeedLogFactoryService.getLogger(VariableResourcesService.class.getName());
public static final String WEBAPP_DIR="webapp.dir";
public static final String WEB_DIR="web.dir";
public static final String JVM_DIR="jvm.dir";
public static final String START_TOKEN="${";
public static final String END_TOKEN="}";
/** The container for the generic resources. */
private Hashtable variables = null;
/** The container for the generic resources. */
private Hashtable strings = null;
private Hashtable vectors = null;
private Hashtable arrays = null;
/**
* Late init. Don't return control until early init says we're done.
*/
public void init( )
{
while( !getInit() ) {
try {
Thread.sleep(500);
} catch (InterruptedException ie ) {
logger.info("VariableResources service: Waiting for init()..." );
}
}
}
/**
* This method is called when the Service is initialized
*
* @param config a ServletConfig object
*/
public synchronized void init(ServletConfig config) throws InitializationException
{
if (getInit()) return;
String props = config.getInitParameter(TurbineServices.PROPERTIES_PATH_KEY);
variables = new Hashtable();
strings = new Hashtable();
vectors = new Hashtable();
arrays = new Hashtable();
initVariables(config);
super.init(config);
}
/**
* Initializer method that sets up the generic resources.
*
* @param confs A Configurations object.
*/
private void initVariables(ServletConfig config)
{
ServletContext ctxt = config.getServletContext();
String path = ctxt.getRealPath("/");
// define web app dir
if (path != null) {
variables.put(WEBAPP_DIR, normalizePath(path) );
}
// FIXME. the following code blocks on Tomcat
// when loaded on startup
/*
path = ctxt.getContext("/").getRealPath("/");
if (path != null ) {
variables.put(WEB_DIR, normalizePath(path) );
}
*/
// define JVM app dir
try {
path = new File(".").getCanonicalPath();
if (path != null) {
variables.put(JVM_DIR, normalizePath(path) );
}
} catch (IOException e) {
//very unlikely that the JVM can't
//resolve its path
//But logging it anyway...
logger.error( "Exception define JVM app dir", e );
}
// load servlet init parameters as variables, they may override
// the previously defined variables. All param names are folded
// to lower case
Enumeration en = config.getInitParameterNames();
while( en.hasMoreElements() ) {
String paramName = (String)en.nextElement();
String paramValue = config.getInitParameter(paramName);
variables.put(paramName.toLowerCase(),paramValue);
}
}
private static String normalizePath(String path) {
// change all separators to forward
// slashes
if (File.separatorChar != '/') {
path = path.replace(File.separatorChar,'/');
}
// remove any trailing slash
if (path.endsWith("/")) {
path = path.substring(0,path.length()-1);
}
return path;
}
protected void setVariables(Hashtable vars)
{
synchronized (this)
{
this.variables = vars;
this.strings = new Hashtable();
this.vectors = new Hashtable();
this.arrays = new Hashtable();
}
}
protected String substituteString( String base ) {
if (base == null) return null;
int begin = -1;
int end = -1;
int prec = 0-END_TOKEN.length();
String var = null;
StringBuffer result = new StringBuffer();
// FIXME: we should probably allow the escaping of the start token
while ( ((begin=base.indexOf(START_TOKEN,prec+END_TOKEN.length()))>-1)
&& ((end=base.indexOf(END_TOKEN,begin))>-1) ) {
result.append(base.substring(prec+END_TOKEN.length(),begin));
var = base.substring(begin+START_TOKEN.length(),end);
if (variables.get(var)!=null) {
result.append(variables.get(var));
}
prec=end;
}
result.append(base.substring(prec+END_TOKEN.length(),base.length()));
return result.toString();
}
/**
* The purpose of this method is to get the configuration resource
* with the given name as a string.
*
* @param name The resource name.
* @return The value of the resource as a string.
*/
public String getString(String name)
{
String std = (String)strings.get(name);
if (std == null) {
std = substituteString(super.getString(name));
if (std != null) strings.put(name,std);
}
return std;
}
/**
* The purpose of this method is to get the configuration resource
* with the given name as a string, or a default value.
*
* @param name The resource name.
* @param def The default value of the resource.
* @return The value of the resource as a string.
*/
public String getString(String name,
String def)
{
String std = getString(name);
if (std == null)
std = substituteString(def);
return std;
}
/**
* The purpose of this method is to get the configuration resource
* with the given name as a string array.
*
* @param name The resource name.
* @return The value of the resource as a string array.
*/
public String[] getStringArray(String name)
{
String[] std = (String[])arrays.get(name);
if (std==null) {
std = super.getStringArray(name);
if (std != null) {
for(int i=0;i<std.length;i++) {
std[i]=substituteString(std[i]);
}
arrays.put(name,std);
}
}
return std;
}
/**
* The purpose of this method is to get the configuration resource
* with the given name as a vector.
*
* @param name The resource name.
* @return The value of the resource as a vector.
*/
public Vector getVector(String name)
{
Vector std = (Vector)vectors.get(name);
if (std==null) {
std = super.getVector(name);
if (std != null) {
Vector newstd = new Vector();
Enumeration en = std.elements();
while (en.hasMoreElements()) {
newstd.addElement(substituteString((String)en.nextElement()));
}
std = newstd;
vectors.put(name,std);
}
}
return std;
}
/**
* The purpose of this method is to get the configuration resource
* with the given name as a vector, or a default value.
*
* @param name The resource name.
* @param def The default value of the resource.
* @return The value of the resource as a vector.
*/
public Vector getVector(String name,
Vector def)
{
Vector std = getVector(name);
if ( std == null) {
if (def != null) {
std = new Vector();
Enumeration en = def.elements();
while (en.hasMoreElements()) {
std.addElement(substituteString((String)en.nextElement()));
}
}
}
return std;
}
/**
* The purpose of this method is to extract a subset of configuraton
* resources sharing a common name prefix. The prefix is stripped
* from the names of the resulting resources.
*
* @param prefix the common name prefix
* @return A ResourceService providing the subset of configuration.
*/
public ResourceService getResources(String prefix)
{
Configuration config = getConfiguration().subset(prefix);
if (config == null)
{
return null;
}
VariableResourcesService res = new VariableResourcesService();
try
{
res.init(config);
}
catch (Exception e)
{
logger.error( "Unable to init resources for " + prefix, e );
}
res.setVariables(this.variables);
return (ResourceService)res;
}
}