Java tutorial
/* * 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.click.servlet; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; import java.net.URI; import java.net.URL; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import javax.servlet.RequestDispatcher; import javax.servlet.Servlet; import javax.servlet.ServletContext; import javax.servlet.ServletException; import org.apache.click.util.ClickUtils; import org.apache.click.util.HtmlStringBuffer; import org.apache.commons.io.FileUtils; import org.apache.commons.lang.StringUtils; /** * Mock implementation of {@link javax.servlet.ServletContext}. * <p/> * This implementation supports all of the standard context methods except for * request dispatching which just indicates what is being dispatched to, rather * than performing an actual dispatch. * <p/> * The context can be configured with a path parameter that should point to an * directory location that represents the place where the contents of * the WAR bundle are located. The path can either be an absolute or relative * path. If the path is not found, the classpath will be checked for such a * directory. Setting this value allows all of the resource location * functionality to work as in a fully functioning web application. If this * value is not set then not resource location functionality will work and * instead null will always be returned. * <p/> * This class was adapted from <a href="http://wicket.apache.org">Apache Wicket</a>. */ public class MockServletContext implements ServletContext { // -------------------------------------------------------- Constants /** * The servlet context default context path, <em>"/mock"</em>. */ public static final String DEFAULT_CONTEXT_PATH = "/mock"; // -------------------------------------------------------- Private variables /** Map of attributes. */ private final Map<String, Object> attributes = new HashMap<String, Object>(); /** Map of initialization parameters. */ private final Map<String, String> initParameters = new HashMap<String, String>(); /** Map of mime types. */ private final Map<String, String> mimeTypes = new HashMap<String, String>(); /** The context temporary path. */ private String tempPath; /** The web application path. */ private String webappPath; /** * The web application root file. The File is created from the * {@link #webappPath} value. */ private File webappRoot; /** The servlet context name, <em>"mock"</em>. */ private String servletContextName = "mock"; /** * The context path, by default its value is set to * {@link #DEFAULT_CONTEXT_PATH}. */ private String contextPath = DEFAULT_CONTEXT_PATH; /** * Default constructor for this mock object. * <p/> * The servlet context name is set to 'mock'. * The web content root and temporary work direcotry are set to null. */ public MockServletContext() { this(DEFAULT_CONTEXT_PATH, null, null); } /** * Create the mock object. As part of the creation, the context sets the * root directory where web application content is stored. This must be an * ABSOLUTE directory relative to where the tests are being executed. * <p/> * For example: <code>System.getProperty("user.dir") + "/src/webapp"</code> * <p/> * In addition to setting the web root directory, this constructor also sets * up a temporary work directory for things like file uploads. * <p/> * <b>Note</b> this temporary work directory is set as the value of the * ServletContext attribute 'javax.servlet.context.tempdir'. * <p/> * The temporary work directory defaults to * System.getProperty("java.io.tmpdir"). * * @param contextPath the servlet context path * @param webappPath The path to the root of the web application */ public MockServletContext(final String contextPath, final String webappPath) { this(contextPath, webappPath, System.getProperty("java.io.tmpdir")); } /** * Create the mock object. As part of the creation, the context sets the * root directory where web application content is stored. This must be an * ABSOLUTE directory relative to where the tests are being executed. * <p/> * For example: <code>System.getProperty("user.dir") + "/src/webapp"</code> * <p/> * In addition to setting the web root directory, this constructor also sets * up a temporary work directory for things like file uploads. * <p/> * <b>Note</b> this temporary work directory is set as the value of the * ServletContext attribute 'javax.servlet.context.tempdir'. * * @param contextPath the servlet context path * @param webappPath the path to the root of the web application * @param tempPath the temporary work directory */ public MockServletContext(final String contextPath, final String webappPath, final String tempPath) { setContextPath(contextPath); //Setup temp path, before webapp path, since setWebappPath() will //default tempPath to java.io.tmpdir if tempPath does not have a value //yet this.setTempPath(tempPath); this.setWebappPath(webappPath); mimeTypes.put("html", "text/html"); mimeTypes.put("htm", "text/html"); mimeTypes.put("css", "text/css"); mimeTypes.put("xml", "text/xml"); mimeTypes.put("js", "text/plain"); mimeTypes.put("gif", "image/gif"); mimeTypes.put("jpg", "image/jpeg"); mimeTypes.put("png", "image/png"); } /** * Creates the web application root File {@link #getWebappRoot()}. * * @throws IllegalStateException if the {@link #getWebappPath()} cannot * be found */ public void createWebappRoot() { webappRoot = null; if (StringUtils.isBlank(getWebappPath())) { return; } webappRoot = new File(getWebappPath()); if (webappRoot.exists() && webappRoot.isDirectory()) { //If the webappRoot is a legal directory, we can return System.out.println(" WEB root directory defined at -> " + webappRoot.getAbsolutePath()); return; } //Build up a string of locations that were checked String checkedPaths = webappRoot.getAbsolutePath(); //If webappRoot is not a legal directory, look for the webappRoot on //the classpath URL url = null; try { url = getResource(getWebappPath()); if (url == null) { webappRoot = null; } else { URI uri = new URI(url.toString()); webappRoot = new File(uri); if (webappRoot.exists() && webappRoot.isDirectory()) { //If the webappRoot is a legal directory on the classpath //we can return System.out.println(" WEB root directory defined at -> " + webappRoot.getAbsolutePath()); return; } } if (webappRoot == null) { checkedPaths += ". Also note that the path '" + getWebappPath() + "' was checked but not found on the classpath"; } else { checkedPaths += ", " + webappRoot.getAbsolutePath(); } } catch (Exception ex) { String msg = "error occurred while checking for existence of the web" + " application root directory at : " + url; throw new RuntimeException(msg, ex); } //At this stage it seems that the path cannot be mapped to a //valid directory, so throw an exception so user can provide the //correct path throw new IllegalStateException("ERROR: The " + "directory cannot be found: " + getWebappPath() + ". " + "The following absolute locations were checked for the path: " + checkedPaths + "."); } /** * Creates a temporary directory as specified by {@link #getTempPath()}. * * @throws IllegalStateException if the {@link #getTempPath()} is not valid */ public void createTempDir() { attributes.put("javax.servlet.context.tempdir", null); if (StringUtils.isNotBlank(getTempPath())) { final File tempDirectory = new File(getTempPath()); if (!tempDirectory.exists()) { tempDirectory.mkdirs(); } if (tempDirectory.exists() && tempDirectory.isDirectory()) { deleteDirectoryOnShutdown(tempDirectory); attributes.put("javax.servlet.context.tempdir", tempDirectory); System.out.println(" WEB temp directory defined at -> " + tempDirectory.getAbsolutePath()); } else { throw new IllegalStateException("ERROR: The " + "directory cannot be found: " + getTempPath() + ". " + "The following absolute locations were checked for the " + "path: " + tempDirectory.getAbsolutePath()); } } } /** * Set the servlet context name to the specified value. * * @param servletContextName the servlet context name */ public void setServletContextName(String servletContextName) { this.servletContextName = servletContextName; } /** * Return the temporary path where files are stored during test runs. * * @return the temporary path where files are stored during test runs */ public String getTempPath() { return tempPath; } /** * Set the temporary path where files are stored during test runs. * * @param tempPath the temporary path where files are stored during test * runs */ public void setTempPath(String tempPath) { if (StringUtils.isBlank(tempPath)) { return; } //If the specified temp path is the same as the temp dir, add a mock //folder to stop possible locking on Windows OS. if (System.getProperty("java.io.tmpdir").equals(tempPath)) { if (!tempPath.endsWith("/")) { tempPath += "/"; } tempPath = tempPath + "click-temp"; } this.tempPath = tempPath; //Create the temporary context directory createTempDir(); } /** * Return the web application path where resources like javascript, css * and images can be picked up. * * @return the web application path */ public String getWebappPath() { return webappPath; } /** * Return the web application root File where resources like javascript, css * and images can be picked up. * * @return the web application root File */ public File getWebappRoot() { return webappRoot; } /** * Sets the mock web application path to the specified webappPath. * <p/> * <b>Note:</b> this method will also set the web application's temporary * directory to the value {@link #getTempPath()}. If {@link #getTempPath()} * is not set, this method will default tempPath to: * <tt>System.getProperty("java.io.tmpdir")</tt>. * * @param webappPath set the context web application path */ public void setWebappPath(String webappPath) { this.webappPath = webappPath; if (StringUtils.isBlank(webappPath)) { return; } //Create the context root createWebappRoot(); if (getTempPath() == null) { setTempPath(System.getProperty("java.io.tmpdir")); } } /** * Add an init parameter. * * @param name The parameter name * @param value The parameter value */ public void addInitParameter(final String name, final String value) { initParameters.put(name, value); } /** * Add the map of init parameters. * * @param initParameters A map of init parameters */ public void addInitParameters(final Map<String, String> initParameters) { if (initParameters == null) { return; } initParameters.putAll(initParameters); } // Configuration methods /** * Add a new recognized mime type. * * @param fileExtension The file extension (e.g. "jpg") * @param mimeType The mime type (e.g. "image/jpeg") */ public void addMimeType(final String fileExtension, final String mimeType) { mimeTypes.put(fileExtension, mimeType); } /** * Get an attribute with the given name. * * @param name The attribute name * @return The value, or null */ public Object getAttribute(final String name) { return attributes.get(name); } /** * Get all of the attribute names. * * @return The attribute names */ public Enumeration<String> getAttributeNames() { return Collections.enumeration(attributes.keySet()); } // -------------------------------------------------------- ServletContext interface methods /** * Get the context for the given URL path. * * @param name The url path * @return Always returns this */ public ServletContext getContext(String name) { return this; } /** * Return the servlet context path. * * @return the servletContext path */ public String getContextPath() { return contextPath; } /** * Sets the servlet context path. * * @param contextPath the servlet context path */ public void setContextPath(String contextPath) { this.contextPath = contextPath; } /** * Get the init parameter with the given name. * * @param name The name * @return The parameter, or null if no such parameter */ public String getInitParameter(final String name) { return initParameters.get(name); } /** * Get the name of all of the init parameters. * * @return The init parameter names */ public Enumeration<String> getInitParameterNames() { return Collections.enumeration(initParameters.keySet()); } /** * Return the major version of the Servlet spec that this package supports, * defaults to 2. * * @return the major version of the Servlet spec that this package supports, * defaults to 2. */ public int getMajorVersion() { return 2; } /** * Get the mime type for the given file. Uses a hardcoded map of mime * types set at initialization time. If the mime type was not explicitly * set, this method will fallback to * {@link org.apache.click.util.ClickUtils#getMimeType(String)}. * * @param name The name to get the mime type for * * @return The mime type */ public String getMimeType(final String name) { int index = name.lastIndexOf('.'); if (index == -1 || index == (name.length() - 1)) { return null; } String type = name.substring(index + 1); if (mimeTypes.containsKey(type)) { return mimeTypes.get(type); } else { return ClickUtils.getMimeType(type); } } /** * Return the minor version of the Servlet spec that this package supports, * defaults to 3. * * @return the minor version of the Servlet spec that this package supports, * defaults to 3. */ public int getMinorVersion() { return 3; } /** * Get the real file path of the given resource name. * * @param name The name * @return The real path or null */ public String getRealPath(String name) { if (webappRoot == null) { return null; } if (name.startsWith("/")) { name = name.substring(1); } File f = new File(webappRoot, name); if (!f.exists()) { return null; } else { return f.getPath(); } } /** * Returns a RequestDispatcher for the specified path. The dispatcher * will not dispatch to the resource. It only records the specified path * so that one can test if the correct path was dispatched to. * * @param path a String specifying the pathname to the resource * @return a dispatcher for the specified path */ public RequestDispatcher getRequestDispatcher(String path) { return new MockRequestDispatcher(path); } /** * Returns a RequestDispatcher for the specified name. The dispatcher * will not dispatch to the resource. It only records the specified name * so that one can test if the correct name was dispatched to. * * @param name a String specifying the name of a servlet to wrap * @return a dispatcher for the specified name */ public RequestDispatcher getNamedDispatcher(final String name) { return getRequestDispatcher(name); } /** * Get the URL for a particular resource that is relative to the web app * root directory. * * @param name The name of the resource to get * @return The resource, or null if resource not found * @throws MalformedURLException If the URL is invalid */ public URL getResource(String name) throws MalformedURLException { if (webappRoot == null) { name = removeLeadingSlash(name); return Thread.currentThread().getContextClassLoader().getResource(name); } File f = new File(webappRoot, name); if (!f.exists()) { name = removeLeadingSlash(name); return Thread.currentThread().getContextClassLoader().getResource(name); } else { return f.toURI().toURL(); } } /** * Get an input stream for a particular resource that is relative to the * web app root directory or the current classpath. If the webappRoot is * not set, this method will try and load the resource from the classpath. * * @param name The name of the resource to get * @return The input stream for the resource, or null if resource is not * found */ public InputStream getResourceAsStream(String name) { if (webappRoot == null) { name = removeLeadingSlash(name); return Thread.currentThread().getContextClassLoader().getResourceAsStream(name); } File f = new File(webappRoot, name); if (!f.exists()) { name = removeLeadingSlash(name); return Thread.currentThread().getContextClassLoader().getResourceAsStream(name); } else { try { return new FileInputStream(f); } catch (FileNotFoundException e) { e.printStackTrace(); return null; } } } /** * Get the resource paths starting from the web app root directory and then * relative to the given name. * * @param name The starting name * @return The set of resource paths at this location * @throws IllegalArgumentException if the specified name does not start * with a "/" character */ public Set<String> getResourcePaths(String name) { if (!name.startsWith("/")) { throw new IllegalArgumentException("Path " + name + " does not start with a \"/\" character"); } if (webappRoot == null) { return new HashSet<String>(); } name = name.substring(1); if (name.endsWith("/")) { name = name.substring(0, name.length() - 1); } String[] elements = null; if (name.trim().length() == 0) { elements = new String[0]; } else { elements = name.split("/"); } //Find the most specific matching path File current = webappRoot; for (int i = 0; i < elements.length; i++) { File[] files = current.listFiles(); boolean match = false; for (int f = 0; f < files.length; f++) { if (files[f].getName().equals(elements[i]) && files[f].isDirectory()) { current = files[f]; match = true; break; } } if (!match) { return null; } } //List of resources in the matching path File[] files = current.listFiles(); Set<String> result = new HashSet<String>(); int stripLength = webappRoot.getPath().length(); for (int f = 0; f < files.length; f++) { String s = files[f].getPath().substring(stripLength).replace('\\', '/'); if (files[f].isDirectory()) { s = s + "/"; } result.add(s); } return result; } /** * Get the server info. * * @return The server info */ public String getServerInfo() { return "Click Mock Environment"; } /** * NOT USED - Servlet Spec requires that this always returns null. * * @param name Not used * * @return null * * @throws ServletException Not used */ public Servlet getServlet(String name) throws ServletException { return null; } /** * Return the name of the servlet context. * * @return The name */ public String getServletContextName() { return servletContextName; } /** * NOT USED - Servlet spec requires that this always returns null. * * @return null */ public Enumeration<String> getServletNames() { return null; } /** * NOT USED - Servlet spec requires that this always returns null. * * @return null */ public Enumeration<?> getServlets() { return null; } /** * Log the exception to System.err and the message to System.out. * * @param e The exception to log * @param msg The message to log */ public void log(Exception e, String msg) { log(msg, e); } /** * Log the message to System.out. * * @param msg The message to log */ public void log(String msg) { System.out.println(msg); } /** * Log the cause to System.err and the message to System.out. * * @param msg The message to log * @param cause The cause exception */ public void log(String msg, Throwable cause) { log(msg); cause.printStackTrace(); } /** * Remove an attribute with the given name. * * @param name The name */ public void removeAttribute(final String name) { attributes.remove(name); } /** * Set an attribute. * * @param name The name of the attribute * @param o The value */ public void setAttribute(final String name, final Object o) { attributes.put(name, o); } /** * Delete the specified directory and any subdirectories. * * @param directory to delete * @return true if the directory was successfully deleted, false otherwise */ static synchronized boolean deleteDirectory(final File directory) { if (directory == null) { return true; } if (!directory.exists() || !directory.isDirectory()) { return true; } Throwable shutdownException = null; try { System.out.println("Deleting temporary directory '" + directory.getAbsolutePath() + "'"); FileUtils.deleteDirectory(directory); } catch (IOException e) { shutdownException = e; } finally { if (directory.exists()) { System.err.println("======================================================================="); HtmlStringBuffer buffer = new HtmlStringBuffer(); buffer.append(" WARNING: could not delete all files "); buffer.append("in the temporary directory: '"); buffer.append(directory.getAbsolutePath()); buffer.append("'.\n"); buffer.append(" This can occur (especially on a Windows OS) when some of the files in the "); buffer.append("directory are locked by another process. "); buffer.append("You can delete this directory manually."); System.err.println(buffer.toString()); System.err.println("=======================================================================\n"); if (shutdownException != null) { shutdownException.printStackTrace(System.err); } return false; } } return true; } /** * Deletes the specified directory when the JVM is shutdown. * * @param directory the directory to delete */ private void deleteDirectoryOnShutdown(final File directory) { if (directory == null) { return; } Runtime.getRuntime().addShutdownHook(new Thread() { @Override public void run() { //delete the temporary directory and all subdirectories deleteDirectory(directory); } }); } /** * Remove the leading slash '/' from the specified name. * * @param name the name from which to remove the leading slash '/' * @return the name with the leading slash removed */ private String removeLeadingSlash(String name) { if (name.startsWith("/")) { name = name.substring(1); } return name; } }