| /* |
| * 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.wicket.protocol.http; |
| |
| import java.util.HashMap; |
| import java.util.Map; |
| |
| import javax.servlet.ServletContext; |
| import javax.servlet.http.HttpServletRequest; |
| import javax.servlet.http.HttpServletResponse; |
| import javax.servlet.http.HttpSession; |
| |
| import org.apache.commons.logging.Log; |
| import org.apache.commons.logging.LogFactory; |
| import org.apache.wicket.Application; |
| import org.apache.wicket.IRequestCycleFactory; |
| import org.apache.wicket.IRequestTarget; |
| import org.apache.wicket.ISessionFactory; |
| import org.apache.wicket.Request; |
| import org.apache.wicket.RequestCycle; |
| import org.apache.wicket.Response; |
| import org.apache.wicket.Session; |
| import org.apache.wicket.WicketRuntimeException; |
| import org.apache.wicket.markup.html.pages.AccessDeniedPage; |
| import org.apache.wicket.markup.html.pages.InternalErrorPage; |
| import org.apache.wicket.markup.html.pages.PageExpiredErrorPage; |
| import org.apache.wicket.markup.resolver.AutoLinkResolver; |
| import org.apache.wicket.protocol.http.servlet.ServletWebRequest; |
| import org.apache.wicket.request.IRequestCycleProcessor; |
| import org.apache.wicket.request.target.coding.BookmarkablePageRequestTargetUrlCodingStrategy; |
| import org.apache.wicket.request.target.coding.IRequestTargetUrlCodingStrategy; |
| import org.apache.wicket.request.target.coding.PackageRequestTargetUrlCodingStrategy; |
| import org.apache.wicket.request.target.coding.SharedResourceRequestTargetUrlCodingStrategy; |
| import org.apache.wicket.session.ISessionStore; |
| import org.apache.wicket.util.collections.MostRecentlyUsedMap; |
| import org.apache.wicket.util.file.WebApplicationPath; |
| import org.apache.wicket.util.lang.PackageName; |
| import org.apache.wicket.util.watch.ModificationWatcher; |
| |
| |
| /** |
| * A web application is a subclass of Application which associates with an |
| * instance of WicketServlet to serve pages over the HTTP protocol. This class |
| * is intended to be subclassed by framework clients to define a web |
| * application. |
| * <p> |
| * Application settings are given defaults by the WebApplication() constructor |
| * and internalInit method, such as error page classes appropriate for HTML. |
| * WebApplication subclasses can override these values and/or modify other |
| * application settings by overriding the init() method and then by calling |
| * getXXXSettings() to retrieve an interface to a mutable Settings object. Do |
| * not do this in the constructor itself because the defaults will then override |
| * your settings. |
| * <p> |
| * If you want to use a filter specific configuration, e.g. using init |
| * parameters from the {@link javax.servlet.FilterConfig} object, you should |
| * override the init() method. For example: |
| * |
| * <pre> |
| * public void init() |
| * { |
| * String webXMLParameter = getInitParameter("myWebXMLParameter"); |
| * URL schedulersConfig = getServletContext().getResource("/WEB-INF/schedulers.xml"); |
| * ... |
| * </pre> |
| * |
| * @see WicketFilter |
| * @see org.apache.wicket.settings.IApplicationSettings |
| * @see org.apache.wicket.settings.IDebugSettings |
| * @see org.apache.wicket.settings.IExceptionSettings |
| * @see org.apache.wicket.settings.IMarkupSettings |
| * @see org.apache.wicket.settings.IPageSettings |
| * @see org.apache.wicket.settings.IRequestCycleSettings |
| * @see org.apache.wicket.settings.IResourceSettings |
| * @see org.apache.wicket.settings.ISecuritySettings |
| * @see org.apache.wicket.settings.ISessionSettings |
| * @see javax.servlet.Filter |
| * @see javax.servlet.FilterConfig |
| * @see javax.servlet.ServletContext |
| * |
| * @author Jonathan Locke |
| * @author Chris Turner |
| * @author Johan Compagner |
| * @author Eelco Hillenius |
| * @author Juergen Donnerstag |
| */ |
| public abstract class WebApplication extends Application implements ISessionFactory |
| { |
| /** Log. */ |
| private static final Log log = LogFactory.getLog(WebApplication.class); |
| |
| /** |
| * The cached application key. Will be set in |
| * {@link #setWicketServlet(WicketServlet)} based on the servlet context. |
| */ |
| private String applicationKey; |
| |
| /** |
| * Map of buffered responses that are in progress per session. Buffered |
| * responses are temporarily stored |
| */ |
| private final Map bufferedResponses = new HashMap(); |
| |
| /** the default request cycle processor implementation. */ |
| private IRequestCycleProcessor requestCycleProcessor; |
| |
| /** |
| * the prefix for storing variables in the actual session (typically |
| * {@link HttpSession} for this application instance. |
| */ |
| private String sessionAttributePrefix; |
| |
| /** Session factory for this web application */ |
| private ISessionFactory sessionFactory = this; |
| |
| /** The WicketFilter that this application is attached to */ |
| private WicketFilter wicketFilter; |
| |
| /** |
| * Constructor. <strong>Use {@link #init()} for any configuration of your |
| * application instead of overriding the constructor.</strong> |
| */ |
| public WebApplication() |
| { |
| } |
| |
| /** |
| * @see org.apache.wicket.Application#getApplicationKey() |
| */ |
| public final String getApplicationKey() |
| { |
| if (applicationKey == null) |
| { |
| throw new IllegalStateException("the application key does not seem to" |
| + " be set properly or this method is called before WicketServlet is" |
| + " set, which leads to the wrong behavior"); |
| } |
| return applicationKey; |
| } |
| |
| /** |
| * Gets an init parameter from the filter's context. |
| * |
| * @param key |
| * the key to search for |
| * @return the value of the filter init parameter |
| */ |
| public final String getInitParameter(String key) |
| { |
| if (wicketFilter != null) |
| { |
| return wicketFilter.getFilterConfig().getInitParameter(key); |
| } |
| throw new IllegalStateException("servletContext is not set yet. Any code in your" |
| + " Application object that uses the wicketServlet/Filter instance should be put" |
| + " in the init() method instead of your constructor"); |
| } |
| |
| /** |
| * Gets the default request cycle processor (with lazy initialization). This |
| * is the {@link IRequestCycleProcessor} that will be used by |
| * {@link RequestCycle}s when custom implementations of the request cycle |
| * do not provide their own customized versions. |
| * |
| * @return the default request cycle processor |
| */ |
| public final IRequestCycleProcessor getRequestCycleProcessor() |
| { |
| if (requestCycleProcessor == null) |
| { |
| requestCycleProcessor = newRequestCycleProcessor(); |
| } |
| return requestCycleProcessor; |
| } |
| |
| /** |
| * Gets the servlet context for this application. Use this to get references |
| * to absolute paths, global web.xml parameters (<context-param>), etc. |
| * |
| * @return The servlet context for this application |
| */ |
| public final ServletContext getServletContext() |
| { |
| if (wicketFilter != null) |
| { |
| return wicketFilter.getFilterConfig().getServletContext(); |
| } |
| throw new IllegalStateException("servletContext is not set yet. Any code in your" |
| + " Application object that uses the wicket filter instance should be put" |
| + " in the init() method instead of your constructor"); |
| } |
| |
| /** |
| * Gets the prefix for storing variables in the actual session (typically |
| * {@link HttpSession} for this application instance. |
| * |
| * @param request |
| * the request |
| * |
| * @return the prefix for storing variables in the actual session |
| */ |
| public final String getSessionAttributePrefix(final WebRequest request) |
| { |
| if (sessionAttributePrefix == null) |
| { |
| String servletPath = request.getServletPath(); |
| if (servletPath == null) |
| { |
| throw new WicketRuntimeException("unable to retrieve servlet path"); |
| } |
| sessionAttributePrefix = "wicket:" + servletPath + ":"; |
| } |
| // Namespacing for session attributes is provided by |
| // adding the servlet path |
| return sessionAttributePrefix; |
| } |
| |
| /** |
| * @return The Wicket filter for this application |
| */ |
| public final WicketFilter getWicketFilter() |
| { |
| return wicketFilter; |
| } |
| |
| /** |
| * @see org.apache.wicket.Application#logEventTarget(org.apache.wicket.IRequestTarget) |
| */ |
| public void logEventTarget(IRequestTarget target) |
| { |
| super.logEventTarget(target); |
| IRequestLogger rl = getRequestLogger(); |
| if (rl != null) |
| { |
| rl.logEventTarget(target); |
| } |
| } |
| |
| /** |
| * @see org.apache.wicket.Application#logResponseTarget(org.apache.wicket.IRequestTarget) |
| */ |
| public void logResponseTarget(IRequestTarget target) |
| { |
| super.logResponseTarget(target); |
| IRequestLogger rl = getRequestLogger(); |
| if (rl != null) |
| { |
| rl.logResponseTarget(target); |
| } |
| } |
| |
| /** |
| * Mounts an encoder at the given path. |
| * |
| * @param encoder |
| * the encoder that will be used for this mount |
| */ |
| public final void mount(IRequestTargetUrlCodingStrategy encoder) |
| { |
| if (encoder == null) |
| { |
| throw new IllegalArgumentException("Encoder must be not null"); |
| } |
| |
| getRequestCycleProcessor().getRequestCodingStrategy().mount(encoder); |
| } |
| |
| /** |
| * Mounts all bookmarkable pages at the given path. |
| * |
| * @param path |
| * the path to mount the bookmarkable page class on |
| * @param packageName |
| * the name of the package for which all bookmarkable pages or |
| * sharedresources should be mounted |
| */ |
| public final void mount(final String path, final PackageName packageName) |
| { |
| if (packageName == null) |
| { |
| throw new IllegalArgumentException("PackageName cannot be null"); |
| } |
| mount(new PackageRequestTargetUrlCodingStrategy(path, packageName)); |
| } |
| |
| /** |
| * Mounts a bookmarkable page class to the given path. |
| * |
| * @param path |
| * the path to mount the bookmarkable page class on |
| * @param bookmarkablePageClass |
| * the bookmarkable page class to mount |
| */ |
| public final void mountBookmarkablePage(final String path, final Class bookmarkablePageClass) |
| { |
| mount(new BookmarkablePageRequestTargetUrlCodingStrategy(path, bookmarkablePageClass, null)); |
| } |
| |
| /** |
| * Mounts a bookmarkable page class to the given pagemap and path. |
| * |
| * @param path |
| * the path to mount the bookmarkable page class on |
| * @param pageMapName |
| * name of the pagemap this mount is for |
| * @param bookmarkablePageClass |
| * the bookmarkable page class to mount |
| */ |
| public final void mountBookmarkablePage(final String path, final String pageMapName, |
| final Class bookmarkablePageClass) |
| { |
| mount(new BookmarkablePageRequestTargetUrlCodingStrategy(path, bookmarkablePageClass, |
| pageMapName)); |
| } |
| |
| /** |
| * Mounts a shared resource class to the given path. |
| * |
| * @param path |
| * the path to mount the bookmarkable page class on |
| * @param resourceKey |
| * the shared key of the resource being mounted |
| */ |
| public final void mountSharedResource(final String path, final String resourceKey) |
| { |
| mount(new SharedResourceRequestTargetUrlCodingStrategy(path, resourceKey)); |
| } |
| |
| /** |
| * Create new Wicket Session object. Note, this method is not called if you |
| * registered your own ISessionFactory with the Application. |
| * |
| * @return The created session |
| * @deprecated see {@link WebApplication#newSession(Request, Response)}. |
| */ |
| // FIXME remove this method after 1.3.0 |
| public final Session newSession() |
| { |
| throw new UnsupportedOperationException("this method is replaced by Application#newSession"); |
| } |
| |
| /** |
| * Create new Wicket Session object. Note, this method is not called if you |
| * registered your own ISessionFactory with the Application. |
| * |
| * @param request |
| * @return The created session |
| * @deprecated {@link WebApplication#newSession(Request, Response)}. |
| */ |
| // FIXME remove this method after 1.3.0 |
| public final Session newSession(Request request) |
| { |
| throw new UnsupportedOperationException("this method is replaced by Application#newSession"); |
| } |
| |
| /** |
| * @see org.apache.wicket.ISessionFactory#newSession(org.apache.wicket.Request, |
| * org.apache.wicket.Response) |
| */ |
| public Session newSession(Request request, Response response) |
| { |
| return new WebSession(WebApplication.this, request); |
| } |
| |
| /** |
| * @param sessionId |
| * The session id that was destroyed |
| */ |
| public void sessionDestroyed(String sessionId) |
| { |
| bufferedResponses.remove(sessionId); |
| |
| IRequestLogger logger = getRequestLogger(); |
| if (logger != null) |
| { |
| logger.sessionDestroyed(sessionId); |
| } |
| } |
| |
| /** |
| * @param sessionFactory |
| * The session factory to use |
| */ |
| public final void setSessionFactory(final ISessionFactory sessionFactory) |
| { |
| this.sessionFactory = sessionFactory; |
| } |
| |
| /** |
| * THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT CALL IT. |
| * |
| * @param wicketFilter |
| * The wicket filter instance for this application |
| */ |
| public final void setWicketFilter(final WicketFilter wicketFilter) |
| { |
| this.wicketFilter = wicketFilter; |
| this.applicationKey = wicketFilter.getFilterConfig().getFilterName(); |
| } |
| |
| /** |
| * Unmounts whatever encoder is mounted at a given path. |
| * |
| * @param path |
| * the path of the encoder to unmount |
| */ |
| public final void unmount(String path) |
| { |
| getRequestCycleProcessor().getRequestCodingStrategy().unmount(path); |
| } |
| |
| /** |
| * @return |
| * @deprecated Replaced by {@link #getRequestCycleFactory()} |
| */ |
| protected final IRequestCycleFactory getDefaultRequestCycleFactory() |
| { |
| throw new UnsupportedOperationException("obsolete method. see getRequestCycleFactory"); |
| } |
| |
| /** |
| * Create a request cycle factory which is used by default by WebSession. |
| * You may provide your own default factory by subclassing WebApplication |
| * and overriding this method or your may subclass WebSession to create a |
| * session specific request cycle factory. |
| * |
| * @see WebSession#getRequestCycleFactory() |
| * @see IRequestCycleFactory |
| * |
| * @return Request cycle factory |
| */ |
| protected IRequestCycleFactory getRequestCycleFactory() |
| { |
| return new IRequestCycleFactory() |
| { |
| private static final long serialVersionUID = 1L; |
| |
| public RequestCycle newRequestCycle(final Application application, |
| final Request request, final Response response) |
| { |
| // Respond to request |
| return new WebRequestCycle((WebApplication)application, (WebRequest)request, |
| (WebResponse)response); |
| } |
| }; |
| } |
| |
| /** |
| * @see org.apache.wicket.Application#getSessionFactory() |
| */ |
| protected ISessionFactory getSessionFactory() |
| { |
| return this.sessionFactory; |
| } |
| |
| /** |
| * Initialize; if you need the wicket servlet for initialization, e.g. |
| * because you want to read an initParameter from web.xml or you want to |
| * read a resource from the servlet's context path, you can override this |
| * method and provide custom initialization. This method is called right |
| * after this application class is constructed, and the wicket servlet is |
| * set. <strong>Use this method for any application setup instead of the |
| * constructor.</strong> |
| */ |
| protected void init() |
| { |
| } |
| |
| /** |
| * THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT CALL IT. |
| */ |
| protected void internalDestroy() |
| { |
| // destroy the resource watcher |
| ModificationWatcher resourceWatcher = getResourceSettings().getResourceWatcher(false); |
| if (resourceWatcher != null) |
| { |
| resourceWatcher.destroy(); |
| } |
| super.internalDestroy(); |
| bufferedResponses.clear(); |
| getSessionStore().destroy(); |
| } |
| |
| /** |
| * THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT CALL IT. |
| * |
| * Internal intialization. First determine the deployment mode. First check |
| * the system property -Dwicket.configuration. If it does not exist check |
| * the servlet init parameter ( |
| * <code><init-param><param-name>configuration</param-name></code>). |
| * If not found check the servlet context init paramert |
| * <code><context-param><param-name6gt;configuration</param-name></code>). |
| * If the parameter is "development" (which is default), settings |
| * appropriate for development are set. If it's "deployment" , deployment |
| * settings are used. If development is specified and a "sourceFolder" init |
| * parameter is also set, then resources in that folder will be polled for |
| * changes. |
| */ |
| protected void internalInit() |
| { |
| super.internalInit(); |
| |
| // Set default error pages for HTML markup |
| getApplicationSettings().setPageExpiredErrorPage(PageExpiredErrorPage.class); |
| getApplicationSettings().setInternalErrorPage(InternalErrorPage.class); |
| getApplicationSettings().setAccessDeniedPage(AccessDeniedPage.class); |
| |
| // Add resolver for automatically resolving HTML links |
| getPageSettings().addComponentResolver(new AutoLinkResolver()); |
| |
| // Set resource finder to web app path |
| getResourceSettings().setResourceFinder(new WebApplicationPath(getServletContext())); |
| |
| String contextPath = getInitParameter(Application.CONTEXTPATH); |
| if (contextPath != null) |
| { |
| getApplicationSettings().setContextPath(contextPath); |
| } |
| |
| // Check if system property -Dwicket.configuration exists |
| String configuration = null; |
| try |
| { |
| configuration = System.getProperty("wicket." + Application.CONFIGURATION); |
| } |
| catch (SecurityException e) |
| { |
| // ignore; it is not allowed to read system properties |
| } |
| |
| // If no system parameter check servlet specific <init-param> |
| if (configuration == null) |
| { |
| configuration = getInitParameter(Application.CONFIGURATION); |
| } |
| // If no system parameter and no <init-param>, then check |
| // <context-param> |
| if (configuration == null) |
| { |
| configuration = getServletContext().getInitParameter(Application.CONFIGURATION); |
| } |
| |
| // Development mode is the default if no settings have been found |
| if (configuration != null) |
| { |
| configure(configuration, getInitParameter("sourceFolder")); |
| } |
| else |
| { |
| configure(Application.DEVELOPMENT, getInitParameter("sourceFolder")); |
| } |
| } |
| |
| /** |
| * Gets a new request cycle processor for web requests. May be replaced by |
| * subclasses which whishes to uses there own implementation of |
| * IRequestCycleProcessor. |
| * |
| * NOTE this can't be moved to application as portlets use two different |
| * request cycle processors, and hence have two different methods for them, |
| * depending on the kind of request. |
| * |
| * @return IRequestCycleProcessor |
| */ |
| protected IRequestCycleProcessor newRequestCycleProcessor() |
| { |
| return new WebRequestCycleProcessor(); |
| } |
| |
| /** |
| * @see org.apache.wicket.Application#newSessionStore() |
| */ |
| protected ISessionStore newSessionStore() |
| { |
| return new SecondLevelCacheSessionStore(this, new FilePageStore()); |
| } |
| |
| /** |
| * Create a new WebRequest. Subclasses of WebRequest could e.g. decode and |
| * obfuscated URL which has been encoded by an appropriate WebResponse. |
| * |
| * @param servletRequest |
| * @return a WebRequest object |
| */ |
| protected WebRequest newWebRequest(final HttpServletRequest servletRequest) |
| { |
| return new ServletWebRequest(servletRequest); |
| } |
| |
| /** |
| * Create a WebResponse. Subclasses of WebRequest could e.g. encode wicket's |
| * default URL and hide the details from the user. A appropriate WebRequest |
| * must be implemented and configured to decode the encoded URL. |
| * |
| * @param servletResponse |
| * @return a WebResponse object |
| */ |
| protected WebResponse newWebResponse(final HttpServletResponse servletResponse) |
| { |
| return (getRequestCycleSettings().getBufferResponse() ? new BufferedWebResponse( |
| servletResponse) : new WebResponse(servletResponse)); |
| } |
| |
| /* |
| * Set the application key value |
| */ |
| protected final void setApplicationKey(String applicationKey) |
| { |
| this.applicationKey = applicationKey; |
| } |
| |
| /** |
| * Add a buffered response to the redirect buffer. |
| * |
| * @param sessionId |
| * the session id |
| * @param bufferId |
| * the id that should be used for storing the buffer |
| * @param renderedResponse |
| * the response to buffer |
| */ |
| final void addBufferedResponse(String sessionId, String bufferId, |
| BufferedHttpServletResponse renderedResponse) |
| { |
| Map responsesPerSession = (Map)bufferedResponses.get(sessionId); |
| if (responsesPerSession == null) |
| { |
| responsesPerSession = new MostRecentlyUsedMap(4); |
| bufferedResponses.put(sessionId, responsesPerSession); |
| } |
| responsesPerSession.put(bufferId, renderedResponse); |
| } |
| |
| /** |
| * Log that this application is started. |
| */ |
| final void logStarted() |
| { |
| String version = getFrameworkSettings().getVersion(); |
| StringBuffer b = new StringBuffer(); |
| b.append("[").append(getName()).append("] Started Wicket "); |
| if (!"n/a".equals(version)) |
| { |
| b.append("version ").append(version).append(" "); |
| } |
| b.append("in ").append(getConfigurationType()).append(" mode"); |
| log.info(b.toString()); |
| } |
| |
| /** |
| * THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT CALL IT. |
| * |
| * Creates a new RequestCycle for the given request and response using the |
| * application's request cycle factory. |
| * |
| * @param request |
| * The request |
| * @param response |
| * The response |
| * @return The new request cycle. |
| */ |
| final RequestCycle newRequestCycle(final Request request, final Response response) |
| { |
| return getRequestCycleFactory().newRequestCycle(this, request, response); |
| } |
| |
| // TODO remove after deprecation release |
| |
| /** |
| * Returns the redirect map where the buffered render pages are stored in |
| * and removes it immediately. |
| * |
| * @param sessionId |
| * the session id |
| * |
| * @param bufferId |
| * the id of the buffer as passed in as a request parameter |
| * @return the buffered response or null if not found (when this request is |
| * on a different box than the original request came in |
| */ |
| final BufferedHttpServletResponse popBufferedResponse(String sessionId, String bufferId) |
| { |
| Map responsesPerSession = (Map)bufferedResponses.get(sessionId); |
| if (responsesPerSession != null) |
| { |
| BufferedHttpServletResponse buffered = (BufferedHttpServletResponse)responsesPerSession |
| .remove(bufferId); |
| if (responsesPerSession.size() == 0) |
| { |
| bufferedResponses.remove(sessionId); |
| } |
| return buffered; |
| } |
| return null; |
| } |
| } |