/* * 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.io.UnsupportedEncodingException; import java.nio.charset.Charset; import java.util.Collection; import java.util.LinkedList; import java.util.Locale; import java.util.function.Function; import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.apache.wicket.Application; import org.apache.wicket.Page; import org.apache.wicket.RuntimeConfigurationType; import org.apache.wicket.Session; import org.apache.wicket.WicketRuntimeException; import org.apache.wicket.ajax.AjaxRequestHandler; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.ajax.AjaxRequestTargetListenerCollection; import org.apache.wicket.core.request.mapper.MountedMapper; import org.apache.wicket.core.request.mapper.PackageMapper; import org.apache.wicket.core.request.mapper.ResourceMapper; import org.apache.wicket.core.util.file.WebApplicationPath; import org.apache.wicket.core.util.resource.ClassPathResourceFinder; import org.apache.wicket.markup.MarkupType; import org.apache.wicket.markup.head.CssHeaderItem; import org.apache.wicket.markup.head.JavaScriptHeaderItem; import org.apache.wicket.markup.html.WebPage; import org.apache.wicket.markup.html.form.AutoLabelResolver; import org.apache.wicket.markup.html.form.AutoLabelTextResolver; 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.AbstractRequestWrapperFactory; import org.apache.wicket.protocol.http.servlet.FilterFactoryManager; import org.apache.wicket.protocol.http.servlet.ServletWebRequest; import org.apache.wicket.protocol.http.servlet.ServletWebResponse; import org.apache.wicket.request.IRequestHandler; import org.apache.wicket.request.IRequestMapper; import org.apache.wicket.request.Request; import org.apache.wicket.request.Response; import org.apache.wicket.request.Url; import org.apache.wicket.request.cycle.RequestCycle; import org.apache.wicket.request.handler.render.WebPageRenderer; import org.apache.wicket.request.http.WebRequest; import org.apache.wicket.request.http.WebResponse; import org.apache.wicket.request.mapper.ICompoundRequestMapper; import org.apache.wicket.request.mapper.IRequestMapperDelegate; import org.apache.wicket.request.resource.CssResourceReference; import org.apache.wicket.request.resource.JavaScriptResourceReference; import org.apache.wicket.request.resource.ResourceReference; import org.apache.wicket.resource.bundles.ReplacementResourceBundleReference; import org.apache.wicket.session.HttpSessionStore; import org.apache.wicket.util.crypt.CharEncoding; import org.apache.wicket.util.file.FileCleaner; import org.apache.wicket.util.file.IFileCleaner; import org.apache.wicket.util.file.Path; import org.apache.wicket.util.lang.Args; import org.apache.wicket.util.lang.PackageName; import org.apache.wicket.util.string.Strings; import org.apache.wicket.util.time.Duration; import org.apache.wicket.util.watch.IModificationWatcher; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * 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.ApplicationSettings * @see org.apache.wicket.settings.DebugSettings * @see org.apache.wicket.settings.ExceptionSettings * @see org.apache.wicket.settings.MarkupSettings * @see org.apache.wicket.settings.PageSettings * @see org.apache.wicket.settings.RequestCycleSettings * @see org.apache.wicket.settings.ResourceSettings * @see org.apache.wicket.settings.SecuritySettings * @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 { /** Log. */ private static final Logger log = LoggerFactory.getLogger(WebApplication.class); public static final String META_INF_RESOURCES = "META-INF/resources"; private ServletContext servletContext; private final AjaxRequestTargetListenerCollection ajaxRequestTargetListeners; private Function<Page, AjaxRequestTarget> ajaxRequestTargetProvider; private FilterFactoryManager filterFactoryManager; /** * Cached value of the parsed (from system properties or Servlet init/context parameter) * <code>wicket.configuration</code> setting. No need to re-read it because it wont change at * runtime. */ private RuntimeConfigurationType configurationType; /** * Covariant override for easy getting the current {@link WebApplication} without having to cast * it. */ public static WebApplication get() { Application application = Application.get(); if (application instanceof WebApplication == false) { throw new WicketRuntimeException( "The application attached to the current thread is not a " + WebApplication.class.getSimpleName()); } return (WebApplication)application; } /** * the prefix for storing variables in the actual session (typically {@link HttpSession} for * this application instance. */ private String sessionAttributePrefix; /** 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() { ajaxRequestTargetListeners = new AjaxRequestTargetListenerCollection(); } /** * @see org.apache.wicket.Application#getApplicationKey() */ @Override public final String getApplicationKey() { return getName(); } /** * Gets an init parameter of the filter, or null if the parameter does not exist. * * @param key * the key to search for * @return the value of the filter init parameter */ public String getInitParameter(String key) { if (wicketFilter != null) { return wicketFilter.getFilterConfig().getInitParameter(key); } throw new IllegalStateException("init parameter '" + key + "' 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"); } /** * Sets servlet context this application runs after. This is uaully done from a filter or a * servlet responsible for managing this application object, such as {@link WicketFilter} * * @param servletContext */ public void setServletContext(ServletContext servletContext) { this.servletContext = servletContext; } /** * 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 ServletContext getServletContext() { if (servletContext == null) { 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"); } return servletContext; } /** * Gets the prefix for storing variables in the actual session (typically {@link HttpSession} * for this application instance. * * @param request * the request * @param filterName * If null, than it defaults to the WicketFilter filter name. However according to * the ServletSpec the Filter is not guaranteed to be initialized e.g. when * WicketSessionFilter gets initialized. Thus, though you (and WicketSessionFilter) * can provide a filter name, you must make sure it is the same as WicketFilter will * provide once initialized. * * @return the prefix for storing variables in the actual session */ public String getSessionAttributePrefix(final WebRequest request, String filterName) { if (sessionAttributePrefix == null) { if (filterName == null) { // According to the ServletSpec, the filter might not yet been initialized filterName = getWicketFilter().getFilterConfig().getFilterName(); } String namespace = getMapperContext().getNamespace(); sessionAttributePrefix = namespace + ':' + filterName + ':'; } // 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.request.IRequestHandler) */ @Override public void logEventTarget(IRequestHandler target) { super.logEventTarget(target); IRequestLogger rl = getRequestLogger(); if (rl != null) { rl.logEventTarget(target); } } /** * @see org.apache.wicket.Application#logResponseTarget(org.apache.wicket.request.IRequestHandler) */ @Override public void logResponseTarget(IRequestHandler target) { super.logResponseTarget(target); IRequestLogger rl = getRequestLogger(); if (rl != null) { rl.logResponseTarget(target); } } /** * Mounts an encoder at the given path. * * @param mapper * the encoder that will be used for this mount */ public void mount(final IRequestMapper mapper) { Args.notNull(mapper, "mapper"); getRootRequestMapperAsCompound().add(mapper); } /** * Mounts a page class to the given path. * * @param <T> * type of page * * @param path * the path to mount the page class on * @param pageClass * the page class to be mounted */ public <T extends Page> MountedMapper mountPage(final String path, final Class<T> pageClass) { MountedMapper mapper = new MountedMapper(path, pageClass); mount(mapper); return mapper; } /** * Mounts a shared resource to the given path. * * @param path * the path to mount the resource reference on * @param reference * resource reference to be mounted */ public ResourceMapper mountResource(final String path, final ResourceReference reference) { if (reference.canBeRegistered()) { getResourceReferenceRegistry().registerResourceReference(reference); } ResourceMapper mapper = new ResourceMapper(path, reference); mount(mapper); return mapper; } /** * Mounts mounts all bookmarkable pages in a the pageClass's package to the given path. * * @param <P> * type of page * * @param path * the path to mount the page class on * @param pageClass * the page class to be mounted */ public <P extends Page> PackageMapper mountPackage(final String path, final Class<P> pageClass) { PackageMapper packageMapper = new PackageMapper(path, PackageName.forClass(pageClass)); mount(packageMapper); return packageMapper; } /** * Unregisters all {@link IRequestMapper}s which would match on a this path. * <p> * Useful in OSGi environments where a bundle may want to update the mount point. * </p> * * @param path * the path to unmount */ public void unmount(String path) { Args.notNull(path, "path"); if (path.charAt(0) == '/') { path = path.substring(1); } IRequestMapper mapper = getRootRequestMapper(); while (mapper instanceof IRequestMapperDelegate) { mapper = ((IRequestMapperDelegate) mapper).getDelegateMapper(); } /* * Only attempt to unmount if root request mapper is either a compound, or wraps a compound to avoid leaving the * application with no mappers installed. */ if (mapper instanceof ICompoundRequestMapper) { final Url url = Url.parse(path); Request request = new Request() { @Override public Url getUrl() { return url; } @Override public Url getClientUrl() { return url; } @Override public Locale getLocale() { return null; } @Override public Charset getCharset() { return null; } @Override public Object getContainerRequest() { return null; } }; unmountFromCompound((ICompoundRequestMapper) mapper, request); } } /** * Descends the tree of {@link ICompoundRequestMapper}s and {@link IRequestMapperDelegate}s to find the correct descendant * to remove. * * @param parent * The {@link ICompoundRequestMapper} from which to unmount the matching mapper. * @param request * The request used to find the mapper to remove. */ private void unmountFromCompound(ICompoundRequestMapper parent, Request request) { Collection<IRequestMapper> toRemove = new LinkedList<>(); for (IRequestMapper mapper : parent) { if (mapper.mapRequest(request) != null) { IRequestMapper actualMapper = mapper; while (actualMapper instanceof IRequestMapperDelegate) { actualMapper = ((IRequestMapperDelegate) actualMapper).getDelegateMapper(); } if (actualMapper instanceof ICompoundRequestMapper) { unmountFromCompound((ICompoundRequestMapper) actualMapper, request); } else { toRemove.add(mapper); } } } for (IRequestMapper mapper : toRemove) { parent.remove(mapper); } } /** * Registers a replacement resource for the given javascript resource. This replacement can be * another {@link JavaScriptResourceReference} for a packaged resource, but it can also be an * {@link org.apache.wicket.request.resource.UrlResourceReference} to replace the resource by a * resource hosted on a CDN. Registering a replacement will cause the resource to replaced by * the given resource throughout the application: if {@code base} is added, {@code replacement} * will be added instead. * * @param base * The resource to replace * @param replacement * The replacement */ public final void addResourceReplacement(JavaScriptResourceReference base, ResourceReference replacement) { ReplacementResourceBundleReference bundle = new ReplacementResourceBundleReference(replacement); bundle.addProvidedResources(JavaScriptHeaderItem.forReference(base)); getResourceBundles().addBundle(JavaScriptHeaderItem.forReference(bundle)); } /** * Registers a replacement resource for the given CSS resource. This replacement can be another * {@link CssResourceReference} for a packaged resource, but it can also be an * {@link org.apache.wicket.request.resource.UrlResourceReference} to replace the resource by a * resource hosted on a CDN. Registering a replacement will cause the resource to replaced by * the given resource throughout the application: if {@code base} is added, {@code replacement} * will be added instead. * * @param base * The resource to replace * @param replacement * The replacement */ public final void addResourceReplacement(CssResourceReference base, ResourceReference replacement) { ReplacementResourceBundleReference bundle = new ReplacementResourceBundleReference(replacement); bundle.addProvidedResources(CssHeaderItem.forReference(base)); getResourceBundles().addBundle(CssHeaderItem.forReference(bundle)); } /** * Create a new WebRequest. Subclasses of WebRequest could e.g. decode and obfuscate URL which * has been encoded by an appropriate WebResponse. * * @param servletRequest * the current HTTP Servlet request * @param filterPath * the filter mapping read from web.xml * @return a WebRequest object */ public WebRequest newWebRequest(HttpServletRequest servletRequest, final String filterPath) { return new ServletWebRequest(servletRequest, filterPath); } /** * Pre- and post- configures the {@link WebRequest} created by user override-able * {@link #newWebRequest(HttpServletRequest, String)} * * @param servletRequest * the current HTTP Servlet request * @param filterPath * the filter mapping read from web.xml * @return a WebRequest object */ WebRequest createWebRequest(HttpServletRequest servletRequest, final String filterPath) { if (hasFilterFactoryManager()) { for (AbstractRequestWrapperFactory factory : getFilterFactoryManager()) { servletRequest = factory.getWrapper(servletRequest); } } WebRequest webRequest = newWebRequest(servletRequest, filterPath); if (servletRequest.getCharacterEncoding() == null) { try { if (webRequest.isAjax()) { // WICKET-3908, WICKET-1816: Forms submitted with Ajax are always UTF-8 encoded servletRequest.setCharacterEncoding(CharEncoding.UTF_8); } else { String requestEncoding = getRequestCycleSettings().getResponseRequestEncoding(); servletRequest.setCharacterEncoding(requestEncoding); } } catch (UnsupportedEncodingException e) { throw new WicketRuntimeException(e); } } return webRequest; } /** * Creates 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 webRequest * the {@link WebRequest} that will handle the current HTTP Servlet request * @param httpServletResponse * the current HTTP Servlet response * @return a WebResponse object */ protected WebResponse newWebResponse(final WebRequest webRequest, final HttpServletResponse httpServletResponse) { return new ServletWebResponse((ServletWebRequest)webRequest, httpServletResponse); } /** * Pre- and post- configures the {@link WebResponse} returned from * {@link #newWebResponse(WebRequest, HttpServletResponse)} * * @param webRequest * the {@link WebRequest} that will handle the current HTTP Servlet request * @param httpServletResponse * the current HTTP Servlet response * @return the configured WebResponse object */ WebResponse createWebResponse(final WebRequest webRequest, final HttpServletResponse httpServletResponse) { WebResponse webResponse = newWebResponse(webRequest, httpServletResponse); boolean shouldBufferResponse = getRequestCycleSettings().getBufferResponse(); return shouldBufferResponse ? new HeaderBufferingWebResponse(webResponse) : webResponse; } /** * @see org.apache.wicket.Application#newSession(org.apache.wicket.request.Request, * org.apache.wicket.request.Response) */ @Override public Session newSession(Request request, Response response) { return new WebSession(request); } /** * @see org.apache.wicket.Application#sessionUnbound(java.lang.String) */ @Override public void sessionUnbound(final String sessionId) { super.sessionUnbound(sessionId); IRequestLogger logger = getRequestLogger(); if (logger != null) { logger.sessionDestroyed(sessionId); } } /** * 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) { Args.notNull(wicketFilter, "wicketFilter"); this.wicketFilter = wicketFilter; servletContext = wicketFilter.getFilterConfig().getServletContext(); } /** * Initialize; if you need the wicket servlet/filter 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/filter is * set. <strong>Use this method for any application setup instead of the constructor.</strong> */ @Override protected void init() { super.init(); } /** * THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT CALL IT. */ @Override public void internalDestroy() { // destroy the resource watcher IModificationWatcher resourceWatcher = getResourceSettings().getResourceWatcher(false); if (resourceWatcher != null) { resourceWatcher.destroy(); } IFileCleaner fileCleaner = getResourceSettings().getFileCleaner(); if (fileCleaner != null) { fileCleaner.destroy(); } super.internalDestroy(); } /** * THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT CALL IT. * * Internal initialization. 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 parameter * <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. */ @Override protected void internalInit() { super.internalInit(); getResourceSettings().getResourceFinders().add( new WebApplicationPath(getServletContext(), "")); getResourceSettings().getResourceFinders().add( new ClassPathResourceFinder(META_INF_RESOURCES)); // 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()); getPageSettings().addComponentResolver(new AutoLabelResolver()); getPageSettings().addComponentResolver(new AutoLabelTextResolver()); getResourceSettings().setFileCleaner(new FileCleaner()); if (getConfigurationType() == RuntimeConfigurationType.DEVELOPMENT) { // Add optional sourceFolder for resources. String resourceFolder = getInitParameter("sourceFolder"); if (resourceFolder != null) { getResourceSettings().getResourceFinders().add(new Path(resourceFolder)); } } setPageRendererProvider(WebPageRenderer::new); setSessionStoreProvider(HttpSessionStore::new); setAjaxRequestTargetProvider(AjaxRequestHandler::new); getAjaxRequestTargetListeners().add(new AjaxEnclosureListener()); // Configure the app. configure(); } /** * set runtime configuration type * <p/> * this is a write-once property: once configured it can not be changed later on. * * @param configurationType */ public Application setConfigurationType(RuntimeConfigurationType configurationType) { if (this.configurationType != null) { throw new IllegalStateException( "Configuration type is write-once. You can not change it. " + "" + "Current value='" + configurationType + "'"); } this.configurationType = Args.notNull(configurationType, "configurationType"); return this; } /** * {@inheritDoc} */ @Override public RuntimeConfigurationType getConfigurationType() { if (configurationType == null) { String result = null; try { result = System.getProperty("wicket." + Application.CONFIGURATION); } catch (SecurityException e) { log.warn("SecurityManager doesn't allow to read the configuration type from " + "the system properties. The configuration type will be read from the web.xml."); } // If no system parameter check filter/servlet <init-param> and <context-param> if (result == null) { result = getInitParameter("wicket." + Application.CONFIGURATION); } if (result == null) { result = getServletContext().getInitParameter("wicket." + Application.CONFIGURATION); } // If no system parameter check filter/servlet specific <init-param> if (result == null) { result = getInitParameter(Application.CONFIGURATION); } // If no system parameter and no <init-param>, then check // <context-param> if (result == null) { result = getServletContext().getInitParameter(Application.CONFIGURATION); } // Return result if we have found it, else fall back to DEVELOPMENT mode // as the default. if (result != null) { try { configurationType = RuntimeConfigurationType.valueOf(result.toUpperCase()); } catch (IllegalArgumentException e) { // Ignore : fall back to DEVELOPMENT mode // log.warn("Unknown runtime configuration type '" + result + // "', falling back to DEVELOPMENT mode."); throw new IllegalArgumentException("Invalid configuration type: '" + result + "'. Must be \"development\" or \"deployment\"."); } } } if (configurationType == null) { configurationType = RuntimeConfigurationType.DEVELOPMENT; } return configurationType; } /** * The rules if and when to insert an xml decl in the response are a bit tricky. Hence, we allow * the user to replace the default implementation per page and per application. * <p> * Default implementation: the page mime type must be "application/xhtml+xml" and request * HTTP_ACCEPT header must include "application/xhtml+xml" to automatically include the xml * decl. Please see <a href= * "https://developer.mozilla.org/en/Writing_JavaScript_for_XHTML#Finally:_Content_Negotiation" * >Writing JavaScript for XHTML</a> for details. * <p> * Please note that xml decls in Wicket's markup are only used for reading the markup. The * markup's xml decl will always be removed and never be used to configure the response. * * @param page * The page currently being rendered * @param insert * If false, than the rules are applied. If true, it'll always be written. In order * to never insert it, than subclass renderXmlDecl() with an empty implementation. */ public void renderXmlDecl(final WebPage page, boolean insert) { if (insert || MarkupType.XML_MIME.equalsIgnoreCase(page.getMarkupType().getMimeType())) { final RequestCycle cycle = RequestCycle.get(); if (insert == false) { WebRequest request = (WebRequest)cycle.getRequest(); String accept = request.getHeader("Accept"); insert = ((accept == null) || (accept.indexOf(MarkupType.XML_MIME) != -1)); } if (insert) { WebResponse response = (WebResponse)cycle.getResponse(); response.write("<?xml version='1.0'"); String encoding = getRequestCycleSettings().getResponseRequestEncoding(); if (Strings.isEmpty(encoding) == false) { response.write(" encoding='"); response.write(encoding); response.write("'"); } response.write(" ?>"); } } } /** * Creates a new ajax request target used to control ajax responses * * @param page * page on which ajax response is made * @return non-null ajax request target instance */ public final AjaxRequestTarget newAjaxRequestTarget(final Page page) { AjaxRequestTarget target = getAjaxRequestTargetProvider().apply(page); for (AjaxRequestTarget.IListener listener : ajaxRequestTargetListeners) { target.addListener(listener); } return target; } /** * Log that this application is started. */ final void logStarted() { if (log.isInfoEnabled()) { String version = getFrameworkSettings().getVersion(); StringBuilder b = new StringBuilder(); 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()); } if (usesDevelopmentConfig()) { outputDevelopmentModeWarning(); } } /** * This method prints a warning to stderr that we are starting in development mode. * <p> * If you really need to test Wicket in development mode on a staging server somewhere and are * annoying the sysadmin for it with stderr messages, you can override this to make it do * something else. */ protected void outputDevelopmentModeWarning() { System.err.print("********************************************************************\n" + "*** WARNING: Wicket is running in DEVELOPMENT mode. ***\n" + "*** ^^^^^^^^^^^ ***\n" + "*** Do NOT deploy to your live server(s) without changing this. ***\n" + "*** See Application#getConfigurationType() for more information. ***\n" + "********************************************************************\n"); } /* * Can contain at most 1000 responses and each entry can live at most one minute. For now there * is no need to configure these parameters externally. */ private final StoredResponsesMap storedResponses = new StoredResponsesMap(1000, Duration.seconds(60)); /** * * @param sessionId * @param url * @return true if has buffered response */ public boolean hasBufferedResponse(String sessionId, Url url) { String key = sessionId + url.toString(); return storedResponses.containsKey(key); } /** * Retrieves a stored buffered response for a given sessionId and url. * * @param url * The url used as a key * @return the stored buffered response. {@code null} if there is no stored response for the given url * @see org.apache.wicket.settings.RequestCycleSettings.RenderStrategy#REDIRECT_TO_BUFFER */ public BufferedWebResponse getAndRemoveBufferedResponse(String sessionId, Url url) { String key = sessionId + url.toString(); return storedResponses.remove(key); } /** * Store the buffered response at application level to use it at a later time. * * @param sessionId * @param url * @param response */ public void storeBufferedResponse(String sessionId, Url url, BufferedWebResponse response) { if (Strings.isEmpty(sessionId)) { log.error("storeBufferedResponse needs a valid session id to store the response, but a null one was found. " + "Please report the problem to dev team and try to reproduce it in a quickstart project."); return; } String key = sessionId + url.toString(); storedResponses.put(key, response); } @Override public String getMimeType(String fileName) { String mimeType = getServletContext().getMimeType(fileName); return mimeType != null ? mimeType : super.getMimeType(fileName); } /** * Returns the provider for {@link org.apache.wicket.ajax.AjaxRequestTarget} objects. * * @return the provider for {@link org.apache.wicket.ajax.AjaxRequestTarget} objects. */ public Function<Page, AjaxRequestTarget> getAjaxRequestTargetProvider() { return ajaxRequestTargetProvider; } /** * Sets the provider for {@link org.apache.wicket.ajax.AjaxRequestTarget} objects. * * @param ajaxRequestTargetProvider * the new provider */ public Application setAjaxRequestTargetProvider( Function<Page, AjaxRequestTarget> ajaxRequestTargetProvider) { this.ajaxRequestTargetProvider = ajaxRequestTargetProvider; return this; } /** * Returns the registered {@link org.apache.wicket.ajax.AjaxRequestTarget.IListener} objects. * * @return the registered {@link org.apache.wicket.ajax.AjaxRequestTarget.IListener} objects. */ public AjaxRequestTargetListenerCollection getAjaxRequestTargetListeners() { return ajaxRequestTargetListeners; } /** * @return True if at least one filter factory has been added. */ public final boolean hasFilterFactoryManager() { return filterFactoryManager != null; } /** * @return The filter factory manager */ public final FilterFactoryManager getFilterFactoryManager() { if (filterFactoryManager == null) { filterFactoryManager = new FilterFactoryManager(); } return filterFactoryManager; } /** * If true, auto label css classes such as {@code error} and {@code required} will be updated * after form component processing during an ajax request. This allows auto labels to correctly * reflect the state of the form component even if they are not part of the ajax markup update. * * TODO in wicket-7 this should move into a settings object. cannot move in 6.x because it * requires a change to a setting interface. * * @return {@code true} iff enabled */ public boolean getUpdateAutoLabelsOnAjaxRequests() { return true; } }