Java tutorial
/* $HeadURL:: $ * $Id$ * * Copyright (c) 2007-2010 by Public Library of Science * http://plos.org * http://ambraproject.org * * 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.ambraproject.web; import org.apache.commons.configuration.Configuration; import org.ambraproject.configuration.ConfigurationStore; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import java.io.File; import java.net.MalformedURLException; import java.util.Collection; import java.util.StringTokenizer; /** * The virtual journal context. * * Various values relating to the virtual journal context and the current Request. * Designed to be put into the Request as an attribute for easy access by the web application. * In particular, allows templates full consistent access to the same information that is available * to the Java Action. * * Note that templates do not have full access to the Request, so a Request * attribute is used to bridge between the two. * * Application usage: * <pre> * VirtualJournalContext requestContent = ServletRequest.getAttribute(PUB_VIRTUALJOURNAL_CONTEXT); * String requestJournal = requestContext.getJournal(); * </pre> */ public class VirtualJournalContext { /** ServletRequest attribute for the virtual journal context. */ public static final String PUB_VIRTUALJOURNAL_CONTEXT = "ambra.virtualjournal.context"; private final String journal; private final String mappingPrefix; private final String defaultMappingPrefix; private final String requestScheme; private final int requestPort; private final String requestServerName; private final String requestContext; private final String baseUrl; private final String baseHostUrl; private final Collection<String> virtualJournals; /** * Construct an immutable VirtualJournalContext. */ public VirtualJournalContext(final String journal, final String defaultJournal, final String requestScheme, final int requestPort, final String requestServerName, final String requestContext, final Collection<String> virtualJournals) { this.journal = journal; this.requestScheme = requestScheme; this.requestPort = requestPort; this.requestServerName = requestServerName; this.requestContext = requestContext; this.mappingPrefix = "journals/" + journal; this.defaultMappingPrefix = "journals/" + defaultJournal; StringBuilder urlBaseValue = new StringBuilder(); // assume that we're dealing with http or https schemes for now // TODO: understand how scheme can be null in request? for now, guard against null if (requestScheme != null) { urlBaseValue.append(requestScheme).append("://").append(requestServerName); //assume that we don't want to put the default ports numbers on the URL if (("http".equals(requestScheme.toLowerCase()) && requestPort != 80) || ("https".equals(requestScheme.toLowerCase()) && requestPort != 443)) { urlBaseValue.append(":").append(requestPort); } } this.baseHostUrl = urlBaseValue.toString(); urlBaseValue.append(requestContext); this.baseUrl = urlBaseValue.toString(); this.virtualJournals = virtualJournals; } private static String[] createMapping(String virtualContextPath, String virtualServletPath, String virtualPathInfo) { return new String[] { virtualContextPath, virtualServletPath, virtualPathInfo, virtualContextPath + virtualServletPath + (virtualPathInfo != null ? virtualPathInfo : "") }; } /** * Virtualize URI values. * * If URI is already prefixed with the mappingPrefix, it is already virtualized.<br/> * If URI is prefixed with the virtual journal name, replace with mappingPrefix to * virtualize.<br/> * Else, prefix with mappingPrefix to virtualize. * * @return String[] of mapped {contextPath, servletPath, pathInfo, requestUri} */ public String[] virtualizeUri(final String contextPath, final String servletPath, final String pathInfo) { // in tests below, be flexible with servletPath and pathInfo, not all containers adhere tightly // to Servlet SRV.4.4. e.g. prefix could be in servletPath or pathInfo. // does URI already contain mappingPrefix? if (servletPath != null && servletPath.startsWith(mappingPrefix) || pathInfo != null && pathInfo.startsWith(mappingPrefix)) { return createMapping(contextPath, servletPath, pathInfo); } // does URI contain journal name? if (servletPath != null && servletPath.startsWith("/" + journal)) { return createMapping(contextPath, mappingPrefix + servletPath.substring(journal.length() + 1), pathInfo); } if (pathInfo != null && pathInfo.startsWith("/" + journal)) { return createMapping(contextPath, servletPath, mappingPrefix + pathInfo.substring(journal.length() + 1)); } // need to add mappingPrefix to URI if (servletPath != null && servletPath.length() > 0) { return createMapping(contextPath, mappingPrefix + servletPath, pathInfo); } else { return createMapping(contextPath, servletPath, mappingPrefix + pathInfo); } } /** * Virtualize URI values to default journal. * * If URI is already prefixed with the mappingPrefix, replace with default journal mappingPrefix.<br/> * If URI is prefixed with the virtual journal name, replace with default journal mappingPrefix.<br/> * Else, prefix with default journal mappingPrefix to virtualize. * * @return String[] of mapped {contextPath, servletPath, pathInfo, requestUri} */ public String[] siteDefaultUri(final String contextPath, final String servletPath, final String pathInfo) { // in tests below, be flexible with servletPath and pathInfo, not all containers adhere tightly // to Servlet SRV.4.4. e.g. prefix could be in servletPath or pathInfo. // does URI already contain defaultMappingPrefix? if (servletPath != null && servletPath.startsWith(defaultMappingPrefix) || pathInfo != null && pathInfo.startsWith(defaultMappingPrefix)) { return createMapping(contextPath, servletPath, pathInfo); } // does URI contain mappingPrefix? if (servletPath != null && servletPath.startsWith(mappingPrefix)) { return createMapping(contextPath, servletPath.replaceFirst(mappingPrefix, defaultMappingPrefix), pathInfo); } if (pathInfo != null && pathInfo.startsWith(mappingPrefix)) { return createMapping(contextPath, servletPath, pathInfo.replaceFirst(mappingPrefix, defaultMappingPrefix)); } // does URI contain journal name? if (servletPath != null && servletPath.startsWith("/" + journal)) { return createMapping(contextPath, defaultMappingPrefix + servletPath.substring(journal.length() + 1), pathInfo); } if (pathInfo != null && pathInfo.startsWith("/" + journal)) { return createMapping(contextPath, servletPath, defaultMappingPrefix + pathInfo.substring(journal.length() + 1)); } // need to add defaultMappingPrefix to URI if (servletPath != null && servletPath.length() > 0) { return createMapping(contextPath, defaultMappingPrefix + servletPath, pathInfo); } else { return createMapping(contextPath, servletPath, defaultMappingPrefix + pathInfo); } } /** * Default URI values. * * If URI is already prefixed with the mappingPrefix, remove it.<br/> * If URI is prefixed with the virtual journal name, remove it.<br/> * Else, already default values. * * @return String[] of defaulted {contextPath, servletPath, pathInfo, requestUri} */ public String[] defaultUri(final String contextPath, final String servletPath, final String pathInfo) { /* * in tests below, be flexible with servletPath and pathInfo, not all containers adhere tightly * to Servlet SRV.4.4. e.g. prefix could be in servletPath or pathInfo. */ // does URI already contain mappingPrefix? if (servletPath != null && servletPath.startsWith(mappingPrefix)) { return createMapping(contextPath, servletPath.substring(mappingPrefix.length()), pathInfo); } if (pathInfo != null && pathInfo.startsWith(mappingPrefix)) { return createMapping(contextPath, servletPath, pathInfo.substring(mappingPrefix.length())); } // does URI contain journal name? if (servletPath != null && servletPath.startsWith("/" + journal)) { return createMapping(contextPath, servletPath.substring(journal.length() + 1), pathInfo); } if (pathInfo != null && pathInfo.startsWith("/" + journal)) { return createMapping(contextPath, servletPath, pathInfo.substring(journal.length() + 1)); } // already defaulted return createMapping(contextPath, servletPath, pathInfo); } /** * For a given request, returns a request with paths mapped to the appropriate journal. * * @param request the original request, with non-journal-relative paths * @param configuration configuration object * @param servletContext servlet context for the given request * @return a request with paths mapped appropriately. May be the original request passed in, if no changes were * needed. * @throws ServletException */ public HttpServletRequest mapRequest(HttpServletRequest request, Configuration configuration, ServletContext servletContext) throws ServletException { String journal = getJournal(); if (journal == null) return request; String cp = request.getContextPath(); String sp = request.getServletPath(); String pi = request.getPathInfo(); // Find resource in journal String[] mapped = getMappedPaths(virtualizeUri(cp, sp, pi), configuration, servletContext); // Find resource in default journal if (mapped == null) mapped = getMappedPaths(siteDefaultUri(cp, sp, pi), configuration, servletContext); // Find resource in app defaults if (mapped == null) mapped = getMappedPaths(defaultUri(cp, sp, pi), configuration, servletContext); if ((mapped != null) && mapped[3].equals(request.getRequestURI())) return request; if (mapped == null) return request; else return wrapRequest(request, mapped); } private String[] getMappedPaths(String[] paths, Configuration configuration, ServletContext servletContext) throws ServletException { // strip contextPath ("/ambra-webapp") from resource path String resource = paths[3].substring(paths[0].length()); if (resource.startsWith("journals") || resource.startsWith("/journals")) { String path = getJournalResourcePath(resource, configuration); String fullPath = path.startsWith("/") ? path : "/" + path; if (resourceExistsInPath(fullPath)) { return new String[] { paths[0], "", fullPath, paths[0] + fullPath }; } } else if (resourceExistsInServletContext(resource, servletContext)) { return paths; } return null; } /** * Path /journal/journal-name/resource changes to /journal/journal-name/webapp/resource * @param resource Resource path * @return Absolute resource path */ private String getJournalResourcePath(String resource, Configuration configuration) { String templatePath = configuration.getString(ConfigurationStore.JOURNAL_TEMPLATE_DIR, null); StringTokenizer tokenizer = new StringTokenizer(resource, "/"); StringBuilder stringBuilder = new StringBuilder(); boolean addWebapp = false; while (tokenizer.hasMoreTokens()) { String token = tokenizer.nextToken(); if (token.equals("journals") && stringBuilder.length() == 0) { addWebapp = true; } stringBuilder.append('/').append(token); if (addWebapp && !token.equals("journals")) { stringBuilder.append("/webapp"); addWebapp = false; } } return templatePath + stringBuilder.toString(); } /** * Search in servlet context - struts directory * @param resource path to resource * @return true if resource exists in servlet context * @throws ServletException Servlet exception */ private boolean resourceExistsInServletContext(String resource, ServletContext servletContext) throws ServletException { try { return servletContext.getResource(resource) != null; } catch (MalformedURLException mre) { throw new ServletException("Invalid resource path: " + resource, mre); } } /** * Search in file system * @param resource path to resource * @return true if resource exists in servlet context * @throws ServletException Servlet exception */ private boolean resourceExistsInPath(String resource) throws ServletException { File file = new File(resource); return file.isFile() && file.canRead(); } /** * Wrap an HttpServletRequest with arbitrary URI values. * * @param request the request to wrap * @param paths the paths to substitute * * @return the wrapped request instance * * @throws IllegalArgumentException DOCUMENT ME! */ public static HttpServletRequest wrapRequest(final HttpServletRequest request, final String[] paths) { if ((paths == null) || (paths.length != 4)) throw new IllegalArgumentException("Invalid path list"); return new HttpServletRequestWrapper(request) { public String getRequestURI() { return paths[3]; } public String getContextPath() { return paths[0]; } public String getServletPath() { return paths[1]; } public String getPathInfo() { return paths[2]; } }; } /** * Get the virtual journal name. * * @return Journal name, may be <code>null</code>. */ public String getJournal() { return journal; } /** * Get the virtual journal Request scheme. * * @return Request scheme. */ public String getRequestScheme() { return requestScheme; } /** * Get the virtual journal Request port. * * @return Request port. */ public int getRequestPort() { return requestPort; } /** * Get the virtual journal Request server name. * * @return Request server name. */ public String getRequestServerName() { return requestServerName; } /** * Get the virtual journal Request context. * * @return Request context. */ public String getRequestContext() { return requestContext; } /** * Get the base url of the request which consists of the scheme, server name, server port, and * context. * * @return string representing the base request URL */ public String getBaseUrl() { return baseUrl; } /** * Get the base host url of the request which consists of the scheme, server name, and server port * * @return string representing the base host URL */ public String getBaseHostUrl() { return baseHostUrl; } /** * Get the virtual journals that are available. * * @return Available virtual journals. */ public Collection<String> getVirtualJournals() { return virtualJournals; } public String toString() { String result = String.format( "VirtualJournalContext: journal=%s, defaultMappingPrefix=%s, requestScheme=%s, " + "requestPort=%s, requestServerName=%s, requestContext=%s, virtualJournals=", journal, defaultMappingPrefix, requestScheme, requestPort, requestServerName, requestContext); for (String s : virtualJournals) { result += s + ","; } return result; } }