package org.apache.velocity.tools.view;
/*
* 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.
*/
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.IOException;
import java.lang.reflect.Constructor;
import javax.servlet.FilterConfig;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.apache.velocity.tools.ClassUtils;
import org.apache.velocity.tools.Toolbox;
import org.apache.velocity.tools.config.FactoryConfiguration;
import org.apache.velocity.tools.config.FileFactoryConfiguration;
import org.apache.velocity.tools.config.PropertiesFactoryConfiguration;
import org.apache.velocity.tools.config.XmlFactoryConfiguration;
/**
* <p>A set of utility methods for supporting and using
* VelocityTools in the servlet environment.</p>
*
* @version $Id: ServletUtils.java 471244 2006-11-04 18:34:38Z henning $
*/
public class ServletUtils
{
public static final String VELOCITY_VIEW_KEY =
VelocityView.class.getName();
public static final String SHARED_CONFIG_PARAM =
"org.apache.velocity.tools.shared.config";
public static final String ALT_VELOCITY_VIEW_KEY =
"org.apache.velocity.tools.view.class";
/**
* Key used to access a live {@link FactoryConfiguration} previously
* placed in the ServletContext attributes.
*/
public static final String CONFIGURATION_KEY =
"org.apache.velocity.tools";
public static final ServletUtils INSTANCE = new ServletUtils();
protected ServletUtils() {}
public ServletUtils getInstance()
{
return INSTANCE;
}
/**
* Retrieves the path for the specified request regardless of
* whether this is a direct request or an include by the
* RequestDispatcher.
*/
public static String getPath(HttpServletRequest request)
{
// If we get here from RequestDispatcher.include(), getServletPath()
// will return the original (wrong) URI requested. The following special
// attribute holds the correct path. See section 8.3 of the Servlet
// 2.3 specification.
String path = (String)request.getAttribute("javax.servlet.include.servlet_path");
// also take into account the PathInfo stated on SRV.4.4 Request Path Elements
String info = (String)request.getAttribute("javax.servlet.include.path_info");
if (path == null)
{
path = request.getServletPath();
info = request.getPathInfo();
}
if (info != null)
{
path += info;
}
return path;
}
/**
* Returns the shared {@link VelocityView} for the specified
* {@link ServletConfig}'s context. If one has not yet been created, it
* will create, store it for future access, and then return it.
*/
public static VelocityView getVelocityView(ServletConfig config)
{
return getVelocityView(new JeeServletConfig(config));
}
/**
* Returns the shared {@link VelocityView} for the specified
* {@link FilterConfig}'s context. If one has not yet been created, it
* will create, store it for future access, and then return it.
*/
public static VelocityView getVelocityView(FilterConfig config)
{
return getVelocityView(new JeeFilterConfig(config));
}
/**
* Returns the shared {@link VelocityView} for the specified
* {@link JeeConfig}'s context. If one has not yet been created, it
* will create, store it for future access, and then return it.
*/
public static VelocityView getVelocityView(JeeConfig config)
{
// check for an init-param telling this servlet/filter NOT
// to share its VelocityView with others. by default, we
// play nice and share the VelocityView with the other kids.
String shared = config.findInitParameter(SHARED_CONFIG_PARAM);
if (shared != null && shared.equals("false"))
{
// just create a new, non-shared VelocityView
return createView(config);
}
ServletContext application = config.getServletContext();
// check for an already initialized VelocityView to use
VelocityView view = getVelocityView(application, false);
if (view == null)
{
// only create a new one if we don't already have one
view = createView(config);
// and store it in the application attributes, so other
// servlets, filters, or tags can use it
application.setAttribute(VELOCITY_VIEW_KEY, view);
}
return view;
}
private static VelocityView createView(JeeConfig config)
{
String cls = config.findInitParameter(ALT_VELOCITY_VIEW_KEY);
if (cls == null)
{
return new VelocityView(config);
}
try
{
return createView(ClassUtils.getClass(cls), config);
}
catch (ClassNotFoundException cnfe)
{
throw new IllegalArgumentException("Could not find class "+cls, cnfe);
}
}
private static VelocityView createView(Class klass, JeeConfig config)
{
if (!VelocityView.class.isAssignableFrom(klass))
{
throw new IllegalArgumentException(klass+" must extend "+VelocityView.class);
}
try
{
Constructor ctor = klass.getConstructor(JeeConfig.class);
return (VelocityView)ctor.newInstance(config);
}
catch (NoSuchMethodException nsme)
{
throw new IllegalArgumentException(klass+" must have a constructor that takes "+JeeConfig.class, nsme);
}
catch (Exception e)
{
throw new RuntimeException("Could not instantiate "+klass+" with "+config, e);
}
}
/**
* Returns the shared {@link VelocityView} for the specified
* {@link ServletContext}. If one has not yet been created,
* it will create one, store it for future access, and then return it.
*/
public static VelocityView getVelocityView(ServletContext application)
{
return getVelocityView(new JeeContextConfig(application));
}
/**
* Returns the shared {@link VelocityView} for the specified
* {@link ServletContext}. If one has not yet been created and
* the second parameter is <code>true</code>, then it will
* create one, store it for future access, and return it.
*/
public static VelocityView getVelocityView(ServletContext application,
boolean createIfMissing) {
VelocityView view =
(VelocityView)application.getAttribute(VELOCITY_VIEW_KEY);
if (view == null && createIfMissing)
{
return getVelocityView(application);
}
return view;
}
public static Object findTool(String key, ServletContext application)
{
return findTool(key, VelocityView.DEFAULT_TOOLBOX_KEY, application);
}
public static Object findTool(String key, String toolboxKey,
ServletContext application)
{
Toolbox toolbox = (Toolbox)application.getAttribute(toolboxKey);
if (toolbox != null)
{
return toolbox.get(key);
}
return null;
}
public static Object findTool(String key, HttpServletRequest request)
{
return findTool(key, request, null);
}
public static Object findTool(String key, String toolboxKey,
HttpServletRequest request)
{
return findTool(key, toolboxKey, request, null);
}
public static Object findTool(String key, HttpServletRequest request,
ServletContext application)
{
return findTool(key, VelocityView.DEFAULT_TOOLBOX_KEY,
request, application);
}
public static Object findTool(String key, String toolboxKey,
HttpServletRequest request,
ServletContext application)
{
String path = getPath(request);
Toolbox toolbox = (Toolbox)request.getAttribute(toolboxKey);
if (toolbox != null)
{
Object tool = toolbox.get(key, path);
if (tool != null)
{
return tool;
}
}
HttpSession session = request.getSession(false);
if (session != null)
{
toolbox = (Toolbox)session.getAttribute(toolboxKey);
if (toolbox != null)
{
Object tool = toolbox.get(key, path);
if (tool != null)
{
return tool;
}
}
if (application == null)
{
application = session.getServletContext();
}
}
if (application != null)
{
toolbox = (Toolbox)application.getAttribute(toolboxKey);
if (toolbox != null)
{
return toolbox.get(key, path);
}
}
return null;
}
public static InputStream getInputStream(String path, ServletContext application)
{
// first, search the classpath
InputStream inputStream = ClassUtils.getResourceAsStream(path, ServletUtils.class);
if (inputStream == null)
{
// then, try the servlet context
inputStream = application.getResourceAsStream(path);
if (inputStream == null)
{
// then, try the file system directly
File file = new File(path);
if (file.exists())
{
try
{
inputStream = new FileInputStream(file);
}
catch (FileNotFoundException fnfe)
{
// we should not be able to get here
// since we already checked whether the file exists
throw new IllegalStateException(fnfe);
}
}
}
}
return inputStream;
}
public static FactoryConfiguration getConfiguration(ServletContext application)
{
Object obj = application.getAttribute(CONFIGURATION_KEY);
if (obj instanceof FactoryConfiguration)
{
FactoryConfiguration injected = (FactoryConfiguration)obj;
// make note of where we found this
String source = injected.getSource();
String addnote = " from ServletContext.getAttribute("+CONFIGURATION_KEY+")";
if (!source.endsWith(addnote))
{
injected.setSource(source+addnote);
}
return injected;
}
return null;
}
public static FactoryConfiguration getConfiguration(String path,
ServletContext application)
{
return getConfiguration(path, application, path.endsWith("toolbox.xml"));
}
public static FactoryConfiguration getConfiguration(String path,
ServletContext application,
boolean deprecationSupportMode)
{
// first make sure we can even get such a file
InputStream inputStream = getInputStream(path, application);
if (inputStream == null)
{
return null;
}
// then make sure it's a file type we recognize
FileFactoryConfiguration config = null;
String source = "ServletUtils.getConfiguration("+path+",ServletContext[,depMode="+deprecationSupportMode+"])";
if (path.endsWith(".xml"))
{
config = new XmlFactoryConfiguration(deprecationSupportMode, source);
}
else if (path.endsWith(".properties"))
{
config = new PropertiesFactoryConfiguration(source);
}
else
{
String msg = "Unknown configuration file type: " + path +
"\nOnly .xml and .properties configuration files are supported at this time.";
throw new UnsupportedOperationException(msg);
}
// now, try to read the file
try
{
config.read(inputStream);
}
catch (IOException ioe)
{
throw new RuntimeException("Failed to load configuration at: "+path, ioe);
}
finally
{
try
{
if (inputStream != null)
{
inputStream.close();
}
}
catch (IOException ioe)
{
throw new RuntimeException("Failed to close input stream for "+path, ioe);
}
}
return config;
}
/**
* Returns a mutex (lock object) unique to the specified session
* and stored under the specified key to allow for reliable
* synchronization on the session.
*/
public static Object getMutex(HttpSession session, String key, Object caller)
{
// yes, this uses double-checked locking, but it is safe here
// since partial initialization of the lock is not an issue
Object lock = session.getAttribute(key);
if (lock == null)
{
// one thread per caller at a time
synchronized(caller)
{
// in case another thread already came thru
lock = session.getAttribute(key);
if (lock == null)
{
// use a small, serializable object
// that is unlikely to be unfortunately optimized
lock = new SessionMutex();
session.setAttribute(key, lock);
}
}
}
return lock;
}
private static class SessionMutex implements java.io.Serializable
{
}
}