blob: af2632da6549badfa521d6b5e9980089c6bfed40 [file] [log] [blame]
/*
* 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;
import java.net.URLConnection;
import java.time.Duration;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.function.Supplier;
import org.apache.wicket.application.ComponentInitializationListenerCollection;
import org.apache.wicket.application.ComponentInstantiationListenerCollection;
import org.apache.wicket.application.ComponentOnAfterRenderListenerCollection;
import org.apache.wicket.application.ComponentOnBeforeRenderListenerCollection;
import org.apache.wicket.application.ComponentOnConfigureListenerCollection;
import org.apache.wicket.application.HeaderContributorListenerCollection;
import org.apache.wicket.application.IComponentInitializationListener;
import org.apache.wicket.application.IComponentInstantiationListener;
import org.apache.wicket.application.OnComponentTagListenerCollection;
import org.apache.wicket.core.request.mapper.IMapperContext;
import org.apache.wicket.core.util.lang.PropertyResolver;
import org.apache.wicket.core.util.lang.WicketObjects;
import org.apache.wicket.core.util.resource.ClassPathResourceFinder;
import org.apache.wicket.event.IEvent;
import org.apache.wicket.event.IEventSink;
import org.apache.wicket.javascript.DefaultJavaScriptCompressor;
import org.apache.wicket.markup.MarkupFactory;
import org.apache.wicket.markup.head.HeaderItem;
import org.apache.wicket.markup.head.IHeaderResponse;
import org.apache.wicket.markup.head.ResourceAggregator;
import org.apache.wicket.markup.html.HeaderResponseDecoratorCollection;
import org.apache.wicket.markup.html.IHeaderContributor;
import org.apache.wicket.markup.html.IHeaderResponseDecorator;
import org.apache.wicket.markup.html.image.resource.DefaultButtonImageResourceFactory;
import org.apache.wicket.markup.parser.filter.EnclosureHandler;
import org.apache.wicket.markup.parser.filter.InlineEnclosureHandler;
import org.apache.wicket.markup.parser.filter.RelativePathPrefixHandler;
import org.apache.wicket.markup.parser.filter.WicketLinkTagHandler;
import org.apache.wicket.markup.parser.filter.WicketMessageTagHandler;
import org.apache.wicket.markup.resolver.HtmlHeaderResolver;
import org.apache.wicket.markup.resolver.WicketContainerResolver;
import org.apache.wicket.markup.resolver.WicketMessageResolver;
import org.apache.wicket.page.IPageManager;
import org.apache.wicket.pageStore.IPageStore;
import org.apache.wicket.protocol.http.IRequestLogger;
import org.apache.wicket.protocol.http.RequestLogger;
import org.apache.wicket.protocol.http.RequestLoggerRequestCycleListener;
import org.apache.wicket.protocol.http.WebApplication;
import org.apache.wicket.protocol.http.WebSession;
import org.apache.wicket.request.IExceptionMapper;
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.cycle.IRequestCycleListener;
import org.apache.wicket.request.cycle.RequestCycle;
import org.apache.wicket.request.cycle.RequestCycleContext;
import org.apache.wicket.request.cycle.RequestCycleListenerCollection;
import org.apache.wicket.request.mapper.ICompoundRequestMapper;
import org.apache.wicket.request.resource.ResourceReferenceRegistry;
import org.apache.wicket.response.filter.EmptySrcAttributeCheckFilter;
import org.apache.wicket.session.DefaultPageFactory;
import org.apache.wicket.session.ISessionStore;
import org.apache.wicket.session.ISessionStore.UnboundListener;
import org.apache.wicket.settings.ApplicationSettings;
import org.apache.wicket.settings.DebugSettings;
import org.apache.wicket.settings.ExceptionSettings;
import org.apache.wicket.settings.FrameworkSettings;
import org.apache.wicket.settings.JavaScriptLibrarySettings;
import org.apache.wicket.settings.MarkupSettings;
import org.apache.wicket.settings.PageSettings;
import org.apache.wicket.settings.RequestCycleSettings;
import org.apache.wicket.settings.RequestLoggerSettings;
import org.apache.wicket.settings.ResourceSettings;
import org.apache.wicket.settings.SecuritySettings;
import org.apache.wicket.settings.StoreSettings;
import org.apache.wicket.util.lang.Args;
import org.apache.wicket.util.lang.Generics;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Base class for all Wicket applications. To create a Wicket application, you generally should
* <i>not </i> directly subclass this class. Instead, you will want to subclass some subclass of
* Application, like WebApplication, which is appropriate for the protocol and markup type you are
* working with.
* <p>
* Application has the following interesting features / attributes:
* <ul>
* <li><b>Name </b>- The Application's name, which is the same as its class name.
*
* <li><b>Home Page </b>- The Application's home Page class. Subclasses must override getHomePage()
* to provide this property value.
*
* <li><b>Settings </b>- Application settings are partitioned into sets of related settings using
* interfaces in the org.apache.wicket.settings package. These interfaces are returned by the
* following methods, which should be used to configure framework settings for your application:
* getApplicationSettings(), getDebugSettings(), getExceptionSettings(), getMarkupSettings(),
* getPageSettings(), getRequestCycleSettings(), getSecuritySettings and getSessionSettings(). These
* settings are configured by default through the constructor or internalInit methods. Default the
* application is configured for DEVELOPMENT. You can configure this globally to DEPLOYMENT or
* override specific settings by implementing the init() method.
*
* <li><b>Shared Resources </b>- Resources added to an Application's SharedResources have
* application-wide scope and can be referenced using a logical scope and a name with the
* ResourceReference class. ResourceReferences can then be used by multiple components in the same
* application without additional overhead (beyond the ResourceReference instance held by each
* referee) and will yield a stable URL, permitting efficient browser caching of the resource (even
* if the resource is dynamically generated). Resources shared in this manner may also be localized.
* See {@link org.apache.wicket.request.resource.ResourceReference} for more details.
*
* <li><b>Custom Session Subclasses</b>- In order to install your own {@link Session} subclass you
* must override Application{@link #newSession(Request, Response)}. For subclasses of
* {@link WebApplication} you will want to subclass {@link WebSession}.
*
* </ul>
*
* @see org.apache.wicket.protocol.http.WebApplication
* @author Jonathan Locke
*/
public abstract class Application implements UnboundListener, IEventSink, IMetadataContext<Object, Application>
{
/** Configuration constant for the 2 types */
public static final String CONFIGURATION = "configuration";
/**
* Applications keyed on the {@link #getApplicationKey()} so that they can be retrieved even
* without being in a request/ being set in the thread local (we need that e.g. for when we are
* in a destruction thread).
*/
private static final Map<String, Application> applicationKeyToApplication = Generics.newHashMap(1);
/** Log. */
private static final Logger log = LoggerFactory.getLogger(Application.class);
/** root mapper */
private IRequestMapper rootRequestMapper;
/** The converter locator instance. */
private IConverterLocator converterLocator;
/** list of initializers. */
private final List<IInitializer> initializers = Generics.newArrayList();
/** Application level meta data. */
private MetaDataEntry<?>[] metaData;
/** Name of application subclass. */
private String name;
/** Request logger instance. */
private IRequestLogger requestLogger;
/** The session facade. */
private volatile ISessionStore sessionStore;
/** page renderer provider */
private IPageRendererProvider pageRendererProvider;
/** request cycle provider */
private IRequestCycleProvider requestCycleProvider;
/** exception mapper provider */
private Supplier<IExceptionMapper> exceptionMapperProvider;
/** session store provider */
private Supplier<ISessionStore> sessionStoreProvider;
/**
* The decorator this application uses to decorate any header responses created by Wicket
*/
private HeaderResponseDecoratorCollection headerResponseDecorators =
new HeaderResponseDecoratorCollection();
/**
* Checks if the <code>Application</code> threadlocal is set in this thread
*
* @return true if {@link Application#get()} can return the instance of application, false
* otherwise
*/
public static boolean exists()
{
return ThreadContext.getApplication() != null;
}
/**
* Get Application for current thread.
*
* @return The current thread's Application
*/
public static Application get()
{
Application application = ThreadContext.getApplication();
if (application == null)
{
throw new WicketRuntimeException("There is no application attached to current thread " +
Thread.currentThread().getName());
}
return application;
}
/**
* Gets the Application based on the application key of that application. You typically never
* have to use this method unless you are working on an integration project.
*
* @param applicationKey
* The unique key of the application within a certain context (e.g. a web
* application)
* @return The application or <code>null</code> if application has not been found
*/
public static Application get(final String applicationKey)
{
return applicationKeyToApplication.get(applicationKey);
}
/**
* Gets the keys of the currently registered Wicket applications for this web application. You
* typically never have to use this method unless you are working on an integration project.
*
* @return unmodifiable set with keys that correspond with {@link #getApplicationKey()}. Never
* null, but possibly empty
*/
public static Set<String> getApplicationKeys()
{
return Collections.unmodifiableSet(applicationKeyToApplication.keySet());
}
/**
* Constructor. <strong>Use {@link #init()} for any configuration of your application instead of
* overriding the constructor.</strong>
*/
public Application()
{
// Install default component instantiation listener that uses
// authorization strategy to check component instantiations.
getComponentInstantiationListeners().add(new IComponentInstantiationListener()
{
/**
* @see org.apache.wicket.application.IComponentInstantiationListener#onInstantiation(org.apache.wicket.Component)
*/
@Override
public void onInstantiation(final Component component)
{
final Class<? extends Component> cl = component.getClass();
// If component instantiation is not authorized
if (!Session.get().getAuthorizationStrategy().isInstantiationAuthorized(cl))
{
// then call any unauthorized component instantiation
// listener
getSecuritySettings().getUnauthorizedComponentInstantiationListener()
.onUnauthorizedInstantiation(component);
}
}
});
}
/**
* Configures application settings to good defaults.
*/
public final void configure()
{
// As long as this is public api the development and deployment mode
// should counter act each other for all properties.
switch (getConfigurationType())
{
case DEVELOPMENT : {
getResourceSettings().setResourcePollFrequency(Duration.ofSeconds(1));
getResourceSettings().setJavaScriptCompressor(null);
getResourceSettings().setUseMinifiedResources(false);
getMarkupSettings().setStripWicketTags(false);
getExceptionSettings().setUnexpectedExceptionDisplay(
ExceptionSettings.SHOW_EXCEPTION_PAGE);
getDebugSettings().setComponentUseCheck(true);
getDebugSettings().setAjaxDebugModeEnabled(true);
getDebugSettings().setDevelopmentUtilitiesEnabled(true);
// getDebugSettings().setOutputMarkupContainerClassName(true);
getRequestCycleSettings().addResponseFilter(EmptySrcAttributeCheckFilter.INSTANCE);
break;
}
case DEPLOYMENT : {
getResourceSettings().setResourcePollFrequency(null);
getResourceSettings().setJavaScriptCompressor(new DefaultJavaScriptCompressor());
getMarkupSettings().setStripWicketTags(true);
getExceptionSettings().setUnexpectedExceptionDisplay(
ExceptionSettings.SHOW_INTERNAL_ERROR_PAGE);
getDebugSettings().setComponentUseCheck(false);
getDebugSettings().setAjaxDebugModeEnabled(false);
getDebugSettings().setDevelopmentUtilitiesEnabled(false);
break;
}
}
}
/**
* Gets the unique key of this application within a given context (like a web application). NOT
* INTENDED FOR FRAMEWORK CLIENTS.
*
* @return The unique key of this application
*/
public abstract String getApplicationKey();
/**
* Gets the configuration mode to use for configuring the app, either
* {@link RuntimeConfigurationType#DEVELOPMENT} or {@link RuntimeConfigurationType#DEPLOYMENT}.
* <p>
* The configuration type. Must currently be either DEVELOPMENT or DEPLOYMENT. Currently, if the
* configuration type is DEVELOPMENT, resources are polled for changes, component usage is
* checked, wicket tags are not stripped from output and a detailed exception page is used. If
* the type is DEPLOYMENT, component usage is not checked, wicket tags are stripped from output
* and a non-detailed exception page is used to display errors.
* <p>
* Note that you should not run Wicket in DEVELOPMENT mode on production servers - the various
* debugging checks and resource polling is inefficient and may leak resources, particularly on
* webapp redeploy.
*
* <div style="border-style:solid;">
* <p>
* To change the deployment mode, add the following to your web.xml, inside your <servlet>
* mapping (or <filter> mapping if you're using 1.3.x):
*
* <pre>
* &lt;init-param&gt;
* &lt;param-name&gt;configuration&lt;/param-name&gt;
* &lt;param-value&gt;deployment&lt;/param-value&gt;
* &lt;/init-param&gt;
* </pre>
*
* <p>
* You can alternatively set this as a &lt;context-param&gt; on the whole context.
*
* <p>
* Another option is to set the "wicket.configuration" system property to either "deployment" or
* "development". The value is not case-sensitive.
*
* <p>
* The system property is checked first, allowing you to add a web.xml param for deployment, and
* a command-line override when you want to run in development mode during development.
*
* <p>
* You may also override Application.getConfigurationType() to provide your own custom switch,
* in which case none of the above logic is used. </div>
*
* <p>
* IMPORTANT NOTE
* </p>
* THIS METHOD IS CALLED OFTEN FROM MANY DIFFERENT POINTS IN CODE, INCLUDING DURING THE RENDER
* PROCESS, THEREFORE THE IMPLEMENTATION SHOULD BE FAST - PREFERRABLY USING A FAST-TO-RETRIEVE
* CACHED VALUE
*
* @return configuration
* @since 1.2.3 (function existed as a property getter)
* @since 1.3.0 (abstract, used to configure things)
*/
public abstract RuntimeConfigurationType getConfigurationType();
/**
* Application subclasses must specify a home page class by implementing this abstract method.
*
* @return Home page class for this application
*/
public abstract Class<? extends Page> getHomePage();
/**
* @return The converter locator for this application
*/
public final IConverterLocator getConverterLocator()
{
return converterLocator;
}
/**
* Gets metadata for this application using the given key.
*
* @param <T>
* @param key
* The key for the data
* @return The metadata
* @see MetaDataKey
*/
@Override
public final synchronized <T> T getMetaData(final MetaDataKey<T> key)
{
return key.get(metaData);
}
/**
* Gets the name of this application.
*
* @return The application name.
*/
public final String getName()
{
return name;
}
/**
* Gets the {@link IRequestLogger}.
*
* @return The RequestLogger
*/
public final IRequestLogger getRequestLogger()
{
if (getRequestLoggerSettings().isRequestLoggerEnabled())
{
if (requestLogger == null)
{
requestLogger = newRequestLogger();
}
}
else
{
requestLogger = null;
}
return requestLogger;
}
/**
* Gets the facade object for working getting/ storing session instances.
*
* @return The session facade
*/
public final ISessionStore getSessionStore()
{
if (sessionStore == null)
{
synchronized (this)
{
if (sessionStore == null)
{
sessionStore = sessionStoreProvider.get();
sessionStore.registerUnboundListener(this);
}
}
}
return sessionStore;
}
/**
* @see org.apache.wicket.session.ISessionStore.UnboundListener#sessionUnbound(java.lang.String)
*/
@Override
public void sessionUnbound(final String sessionId)
{
getSessionListeners().onUnbound(sessionId);
}
/**
* THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT CALL.
*
* @param target
*/
public void logEventTarget(final IRequestHandler target)
{
}
/**
* THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT CALL.
*
* @param requestTarget
*/
public void logResponseTarget(final IRequestHandler requestTarget)
{
}
/**
* Creates a new session. Override this method if you want to provide a custom session.
*
* @param request
* The request that will create this session.
* @param response
* The response to initialize, for example with cookies. This is important to use
* cases involving unit testing because those use cases might want to be able to sign
* a user in automatically when the session is created.
*
* @return The session
*
* @since 1.3
*/
public abstract Session newSession(Request request, Response response);
/**
* Sets the metadata for this application using the given key. If the metadata object is not of
* the correct type for the metadata key, an IllegalArgumentException will be thrown. For
* information on creating MetaDataKeys, see {@link MetaDataKey}.
*
* @param <T>
* @param key
* The singleton key for the metadata
* @param object
* The metadata object
* @throws IllegalArgumentException
* @see MetaDataKey
*/
@Override
public synchronized final <T> Application setMetaData(final MetaDataKey<T> key, final T object)
{
metaData = key.set(metaData, object);
return this;
}
/**
* Construct and add initializer from the provided class name.
*
* @param className
*/
private void addInitializer(final String className)
{
IInitializer initializer = WicketObjects.newInstance(className);
if (initializer != null)
{
initializers.add(initializer);
}
}
/**
* Iterate initializers list, calling their {@link IInitializer#destroy(Application) destroy}
* methods.
*/
private void destroyInitializers()
{
for (IInitializer initializer : initializers)
{
log.info("[{}] destroy: {}", getName(), initializer);
initializer.destroy(this);
}
}
/**
* Iterate initializers list, calling {@link IInitializer#init(Application)} on any instances
* found in it.
*/
private void initInitializers()
{
for (IInitializer initializer : initializers)
{
log.info("[{}] init: {}", getName(), initializer);
initializer.init(this);
}
final ServiceLoader<IInitializer> serviceLoaderInitializers = ServiceLoader.load(IInitializer.class);
for (IInitializer serviceLoaderInitializer : serviceLoaderInitializers) {
log.info("[{}] init: {}", getName(), serviceLoaderInitializer);
serviceLoaderInitializer.init(this);
initializers.add(serviceLoaderInitializer);
}
}
/**
* Called when wicket servlet is destroyed. Overrides do not have to call super.
*/
protected void onDestroy()
{
}
/**
* Allows for initialization of the application by a subclass. <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.
*/
public void internalDestroy()
{
applicationListeners.onBeforeDestroyed(this);
// destroy detach listener
final IDetachListener detachListener = getFrameworkSettings().getDetachListener();
if (detachListener != null)
{
detachListener.onDestroyListener();
}
// Clear caches of Class keys so the classloader can be garbage
// collected (WICKET-625)
PropertyResolver.destroy(this);
MarkupFactory markupFactory = getMarkupSettings().getMarkupFactory();
if (markupFactory.hasMarkupCache())
{
markupFactory.getMarkupCache().shutdown();
}
onDestroy();
destroyInitializers();
internalGetPageManager().destroy();
getSessionStore().destroy();
applicationKeyToApplication.remove(getApplicationKey());
}
/**
* THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT OVERRIDE OR CALL.
*
* Internal initialization.
*/
protected void internalInit()
{
settingsAccessible = true;
PageSettings pageSettings = getPageSettings();
// Install default component resolvers
pageSettings.addComponentResolver(new HtmlHeaderResolver());
pageSettings.addComponentResolver(new WicketLinkTagHandler());
pageSettings.addComponentResolver(new WicketMessageResolver());
pageSettings.addComponentResolver(new RelativePathPrefixHandler());
pageSettings.addComponentResolver(new EnclosureHandler());
pageSettings.addComponentResolver(new InlineEnclosureHandler());
pageSettings.addComponentResolver(new WicketMessageTagHandler());
pageSettings.addComponentResolver(new WicketContainerResolver());
getResourceSettings().getResourceFinders().add(new ClassPathResourceFinder(""));
// Install button image resource factory
getResourceSettings().addResourceFactory("buttonFactory",
new DefaultButtonImageResourceFactory());
String applicationKey = getApplicationKey();
applicationKeyToApplication.put(applicationKey, this);
converterLocator = newConverterLocator();
setPageManagerProvider(new DefaultPageManagerProvider(this));
resourceReferenceRegistry = newResourceReferenceRegistry();
sharedResources = newSharedResources(resourceReferenceRegistry);
resourceBundles = newResourceBundles(resourceReferenceRegistry);
// set up default request mapper
setRootRequestMapper(new SystemMapper(this));
pageFactory = newPageFactory();
requestCycleProvider = (context) -> new RequestCycle(context);
exceptionMapperProvider = () -> new DefaultExceptionMapper();
// add a request cycle listener that logs each request for the requestlogger.
getRequestCycleListeners().add(new RequestLoggerRequestCycleListener());
}
/**
* Returns a supplier of {@link IExceptionMapper} that will be used to
* handle exceptions which were not handled by any
* {@link IRequestCycleListener#onException(RequestCycle, Exception) request cycle listener}.
*
* @return the exception mapper supplier
* @see IRequestCycleListener#onException(RequestCycle, Exception)
*/
public Supplier<IExceptionMapper> getExceptionMapperProvider()
{
return exceptionMapperProvider;
}
/**
*
* @return Session state provider
*/
public final Supplier<ISessionStore> getSessionStoreProvider()
{
return sessionStoreProvider;
}
/**
*
* @param sessionStoreProvider
*/
public final Application setSessionStoreProvider(final Supplier<ISessionStore> sessionStoreProvider)
{
this.sessionStoreProvider = Args.notNull(sessionStoreProvider, "sessionStoreProvider");
this.sessionStore = null;
return this;
}
/**
* Creates and returns a new instance of {@link IConverterLocator}.
*
* @return A new {@link IConverterLocator} instance
*/
protected IConverterLocator newConverterLocator()
{
return new ConverterLocator();
}
/**
* creates a new request logger when requests logging is enabled.
*
* @return The new request logger
*
*/
protected IRequestLogger newRequestLogger()
{
return new RequestLogger();
}
/**
* Converts the root mapper to a {@link ICompoundRequestMapper} if necessary and returns the
* converted instance.
*
* @return compound instance of the root mapper
*/
public final ICompoundRequestMapper getRootRequestMapperAsCompound()
{
IRequestMapper root = getRootRequestMapper();
if (!(root instanceof ICompoundRequestMapper))
{
root = new SystemMapper(this).add(root);
setRootRequestMapper(root);
}
return (ICompoundRequestMapper)root;
}
/**
* @return The root request mapper
*/
public final IRequestMapper getRootRequestMapper()
{
return rootRequestMapper;
}
/**
* Sets the root request mapper
*
* @param rootRequestMapper
*/
public final Application setRootRequestMapper(final IRequestMapper rootRequestMapper)
{
this.rootRequestMapper = rootRequestMapper;
return this;
}
/**
* Initialize the application
*/
public final void initApplication()
{
if (name == null)
{
throw new IllegalStateException("setName must be called before initApplication");
}
internalInit();
initInitializers();
init();
applicationListeners.onAfterInitialized(this);
validateInit();
}
/**
* Gives the Application object a chance to validate if it has been properly initialized
*/
protected void validateInit()
{
if (getPageRendererProvider() == null)
{
throw new IllegalStateException(
"An instance of IPageRendererProvider has not yet been set on this Application. @see Application#setPageRendererProvider");
}
if (getSessionStoreProvider() == null)
{
throw new IllegalStateException(
"An instance of ISessionStoreProvider has not yet been set on this Application. @see Application#setSessionStoreProvider");
}
if (getPageManagerProvider() == null)
{
throw new IllegalStateException(
"An instance of IPageManagerProvider has not yet been set on this Application. @see Application#setPageManagerProvider");
}
}
/**
* Sets application name. This method must be called before any other methods are invoked and
* can only be called once per application instance.
*
* @param name
* unique application name
*/
public final void setName(final String name)
{
Args.notEmpty(name, "name");
if (this.name != null)
{
throw new IllegalStateException("Application name can only be set once.");
}
if (applicationKeyToApplication.get(name) != null)
{
throw new IllegalStateException("Application with name '" + name + "' already exists.'");
}
this.name = name;
applicationKeyToApplication.put(name, this);
}
/**
* Returns the mime type for given filename.
*
* @param fileName
* @return mime type
*/
public String getMimeType(final String fileName)
{
return URLConnection.getFileNameMap().getContentTypeFor(fileName);
}
/** {@inheritDoc} */
@Override
public void onEvent(final IEvent<?> event)
{
}
// /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Listeners
//
// /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/** */
private final ComponentOnBeforeRenderListenerCollection componentPreOnBeforeRenderListeners = new ComponentOnBeforeRenderListenerCollection();
/** */
private final ComponentOnBeforeRenderListenerCollection componentPostOnBeforeRenderListeners = new ComponentOnBeforeRenderListenerCollection();
/** */
private final ComponentOnAfterRenderListenerCollection componentOnAfterRenderListeners = new ComponentOnAfterRenderListenerCollection();
/** */
private final RequestCycleListenerCollection requestCycleListeners = new RequestCycleListenerCollection();
private final ApplicationListenerCollection applicationListeners = new ApplicationListenerCollection();
private final SessionListenerCollection sessionListeners = new SessionListenerCollection();
/** list of {@link IComponentInstantiationListener}s. */
private final ComponentInstantiationListenerCollection componentInstantiationListeners = new ComponentInstantiationListenerCollection();
/** list of {@link IComponentInitializationListener}s. */
private final ComponentInitializationListenerCollection componentInitializationListeners = new ComponentInitializationListenerCollection();
/** list of {@link org.apache.wicket.application.IComponentOnConfigureListener}s. */
private final ComponentOnConfigureListenerCollection componentOnConfigureListeners = new ComponentOnConfigureListenerCollection();
/** list of {@link IHeaderContributor}s. */
private final HeaderContributorListenerCollection headerContributorListeners = new HeaderContributorListenerCollection();
private final BehaviorInstantiationListenerCollection behaviorInstantiationListeners = new BehaviorInstantiationListenerCollection();
private final OnComponentTagListenerCollection onComponentTagListeners = new OnComponentTagListenerCollection();
/**
* @return Gets the application's {@link HeaderContributorListenerCollection}
*/
public final HeaderContributorListenerCollection getHeaderContributorListeners()
{
return headerContributorListeners;
}
/**
* @return collection of initializers
*/
public final List<IInitializer> getInitializers()
{
return Collections.unmodifiableList(initializers);
}
/**
* @return collection of application listeners
*/
public final ApplicationListenerCollection getApplicationListeners()
{
return applicationListeners;
}
/**
* @return collection of session listeners
*/
public final SessionListenerCollection getSessionListeners()
{
return sessionListeners;
}
/**
* @return collection of behavior instantiation listeners
*/
public final BehaviorInstantiationListenerCollection getBehaviorInstantiationListeners()
{
return behaviorInstantiationListeners;
}
/**
* @return collection of application's on-component-tag listeners
*/
public final OnComponentTagListenerCollection getOnComponentTagListeners() {
return onComponentTagListeners;
}
/**
* @return Gets the application's ComponentInstantiationListenerCollection
*/
public final ComponentInstantiationListenerCollection getComponentInstantiationListeners()
{
return componentInstantiationListeners;
}
/**
* @return Gets the application's ComponentInitializationListeners
*/
public final ComponentInitializationListenerCollection getComponentInitializationListeners()
{
return componentInitializationListeners;
}
/**
* @return Gets the application's ComponentOnConfigureListeners
*/
public final ComponentOnConfigureListenerCollection getComponentOnConfigureListeners()
{
return componentOnConfigureListeners;
}
/**
*
* @return ComponentOnBeforeRenderListenerCollection
*/
public final ComponentOnBeforeRenderListenerCollection getComponentPreOnBeforeRenderListeners()
{
return componentPreOnBeforeRenderListeners;
}
/**
*
* @return ComponentOnBeforeRenderListenerCollection
*/
public final ComponentOnBeforeRenderListenerCollection getComponentPostOnBeforeRenderListeners()
{
return componentPostOnBeforeRenderListeners;
}
/**
* @return on after render listeners collection
*/
public final ComponentOnAfterRenderListenerCollection getComponentOnAfterRenderListeners()
{
return componentOnAfterRenderListeners;
}
/**
* @return the unmodifiable request list of {@link IRequestCycleListener}s in this application
*/
public RequestCycleListenerCollection getRequestCycleListeners()
{
return requestCycleListeners;
}
// /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Settings
//
// /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/** Application settings */
private ApplicationSettings applicationSettings;
/** JavaScriptLibrary settings */
private JavaScriptLibrarySettings javaScriptLibrarySettings;
/** Debug Settings */
private DebugSettings debugSettings;
/** Exception Settings */
private ExceptionSettings exceptionSettings;
/** Framework Settings */
private FrameworkSettings frameworkSettings;
/** The Markup Settings */
private MarkupSettings markupSettings;
/** The Page Settings */
private PageSettings pageSettings;
/** The Request Cycle Settings */
private RequestCycleSettings requestCycleSettings;
/** The Request Logger Settings */
private RequestLoggerSettings requestLoggerSettings;
/** The Resource Settings */
private ResourceSettings resourceSettings;
/** The Security Settings */
private SecuritySettings securitySettings;
/** The settings for {@link IPageStore} and {@link IPageManager} */
private StoreSettings storeSettings;
/** can the settings object be set/used. */
private boolean settingsAccessible;
/**
* @return Application's application-wide settings
* @since 1.2
*/
public final ApplicationSettings getApplicationSettings()
{
checkSettingsAvailable();
if (applicationSettings == null)
{
applicationSettings = new ApplicationSettings();
}
return applicationSettings;
}
/**
*
* @param applicationSettings
*/
public final Application setApplicationSettings(final ApplicationSettings applicationSettings)
{
this.applicationSettings = applicationSettings;
return this;
}
/**
* @return Application's JavaScriptLibrary settings
* @since 6.0
*/
public final JavaScriptLibrarySettings getJavaScriptLibrarySettings()
{
checkSettingsAvailable();
if (javaScriptLibrarySettings == null)
{
javaScriptLibrarySettings = new JavaScriptLibrarySettings();
}
return javaScriptLibrarySettings;
}
/**
*
* @param javaScriptLibrarySettings
*/
public final Application setJavaScriptLibrarySettings(
final JavaScriptLibrarySettings javaScriptLibrarySettings)
{
this.javaScriptLibrarySettings = javaScriptLibrarySettings;
return this;
}
/**
* @return Application's debug related settings
*/
public final DebugSettings getDebugSettings()
{
checkSettingsAvailable();
if (debugSettings == null)
{
debugSettings = new DebugSettings();
}
return debugSettings;
}
/**
*
* @param debugSettings
*/
public final Application setDebugSettings(final DebugSettings debugSettings)
{
this.debugSettings = debugSettings;
return this;
}
/**
* @return Application's exception handling settings
*/
public final ExceptionSettings getExceptionSettings()
{
checkSettingsAvailable();
if (exceptionSettings == null)
{
exceptionSettings = new ExceptionSettings();
}
return exceptionSettings;
}
/**
*
* @param exceptionSettings
*/
public final Application setExceptionSettings(final ExceptionSettings exceptionSettings)
{
this.exceptionSettings = exceptionSettings;
return this;
}
/**
* @return Wicket framework settings
*/
public final FrameworkSettings getFrameworkSettings()
{
checkSettingsAvailable();
if (frameworkSettings == null)
{
frameworkSettings = new FrameworkSettings(this);
}
return frameworkSettings;
}
/**
*
* @param frameworkSettings
*/
public final Application setFrameworkSettings(final FrameworkSettings frameworkSettings)
{
this.frameworkSettings = frameworkSettings;
return this;
}
/**
* @return Application's page related settings
*/
public final PageSettings getPageSettings()
{
checkSettingsAvailable();
if (pageSettings == null)
{
pageSettings = new PageSettings();
}
return pageSettings;
}
/**
*
* @param pageSettings
*/
public final Application setPageSettings(final PageSettings pageSettings)
{
this.pageSettings = pageSettings;
return this;
}
/**
* @return Application's request cycle related settings
*/
public final RequestCycleSettings getRequestCycleSettings()
{
checkSettingsAvailable();
if (requestCycleSettings == null)
{
requestCycleSettings = new RequestCycleSettings();
}
return requestCycleSettings;
}
/**
*
* @param requestCycleSettings
*/
public final Application setRequestCycleSettings(final RequestCycleSettings requestCycleSettings)
{
this.requestCycleSettings = requestCycleSettings;
return this;
}
/**
* @return Application's markup related settings
*/
public MarkupSettings getMarkupSettings()
{
checkSettingsAvailable();
if (markupSettings == null)
{
markupSettings = new MarkupSettings();
}
return markupSettings;
}
/**
*
* @param markupSettings
*/
public final Application setMarkupSettings(final MarkupSettings markupSettings)
{
this.markupSettings = markupSettings;
return this;
}
/**
* @return Application's request logger related settings
*/
public final RequestLoggerSettings getRequestLoggerSettings()
{
checkSettingsAvailable();
if (requestLoggerSettings == null)
{
requestLoggerSettings = new RequestLoggerSettings();
}
return requestLoggerSettings;
}
/**
*
* @param requestLoggerSettings
*/
public final Application setRequestLoggerSettings(final RequestLoggerSettings requestLoggerSettings)
{
this.requestLoggerSettings = requestLoggerSettings;
return this;
}
/**
* @return Application's resources related settings
*/
public final ResourceSettings getResourceSettings()
{
checkSettingsAvailable();
if (resourceSettings == null)
{
resourceSettings = new ResourceSettings(this);
}
return resourceSettings;
}
/**
*
* @param resourceSettings
*/
public final Application setResourceSettings(final ResourceSettings resourceSettings)
{
this.resourceSettings = resourceSettings;
return this;
}
/**
* @return Application's security related settings
*/
public final SecuritySettings getSecuritySettings()
{
checkSettingsAvailable();
if (securitySettings == null)
{
securitySettings = new SecuritySettings();
}
return securitySettings;
}
/**
*
* @param securitySettings
*/
public final Application setSecuritySettings(final SecuritySettings securitySettings)
{
this.securitySettings = securitySettings;
return this;
}
/**
* @return Application's stores related settings
*/
public final StoreSettings getStoreSettings()
{
checkSettingsAvailable();
if (storeSettings == null)
{
storeSettings = new StoreSettings(this);
}
return storeSettings;
}
/**
*
* @param storeSettings
*/
public final Application setStoreSettings(final StoreSettings storeSettings)
{
this.storeSettings = storeSettings;
return this;
}
/**
*
*/
private void checkSettingsAvailable()
{
if (!settingsAccessible)
{
throw new WicketRuntimeException(
"Use Application.init() method for configuring your application object");
}
}
// /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Page Manager
//
// /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
private volatile IPageManager pageManager;
private IPageManagerProvider pageManagerProvider;
/**
*
* @return PageManagerProvider
*/
public final IPageManagerProvider getPageManagerProvider()
{
return pageManagerProvider;
}
/**
* Set the provider of an {@link IPageManager}.
*
* @param provider
*
* @see DefaultPageManagerProvider
*/
public final Application setPageManagerProvider(final IPageManagerProvider provider)
{
pageManagerProvider = provider;
return this;
}
/**
* Returns an unsynchronized version of page manager
*
* @return the page manager
*/
final IPageManager internalGetPageManager()
{
if (pageManager == null)
{
synchronized (this)
{
if (pageManager == null)
{
pageManager = pageManagerProvider.get();
}
}
}
return pageManager;
}
// /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Page Rendering
//
// /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
*
* @return PageRendererProvider
*/
public final IPageRendererProvider getPageRendererProvider()
{
return pageRendererProvider;
}
/**
*
* @param pageRendererProvider
*/
public final Application setPageRendererProvider(final IPageRendererProvider pageRendererProvider)
{
Args.notNull(pageRendererProvider, "pageRendererProvider");
this.pageRendererProvider = pageRendererProvider;
return this;
}
// /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Request Handler encoding
//
// /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
private ResourceReferenceRegistry resourceReferenceRegistry;
private SharedResources sharedResources;
private ResourceBundles resourceBundles;
private IPageFactory pageFactory;
private IMapperContext encoderContext;
/**
* Override to create custom {@link ResourceReferenceRegistry}.
*
* @return new {@link ResourceReferenceRegistry} instance.
*/
protected ResourceReferenceRegistry newResourceReferenceRegistry()
{
return new ResourceReferenceRegistry();
}
/**
* Returns {@link ResourceReferenceRegistry} for this application.
*
* @return ResourceReferenceRegistry
*/
public final ResourceReferenceRegistry getResourceReferenceRegistry()
{
return resourceReferenceRegistry;
}
/**
*
* @param registry
* @return SharedResources
*/
protected SharedResources newSharedResources(final ResourceReferenceRegistry registry)
{
return new SharedResources(registry);
}
/**
*
* @return SharedResources
*/
public SharedResources getSharedResources()
{
return sharedResources;
}
protected ResourceBundles newResourceBundles(final ResourceReferenceRegistry registry)
{
return new ResourceBundles(registry);
}
/**
* @return The registry for resource bundles
*/
public ResourceBundles getResourceBundles()
{
return resourceBundles;
}
/**
* Override to create custom {@link IPageFactory}
*
* @return new {@link IPageFactory} instance.
*/
protected IPageFactory newPageFactory()
{
return new DefaultPageFactory();
}
/**
* Returns {@link IPageFactory} for this application.
*
* @return page factory
*/
public final IPageFactory getPageFactory()
{
return pageFactory;
}
/**
*
* @return mapper context
*/
public final IMapperContext getMapperContext()
{
if (encoderContext == null)
{
encoderContext = newMapperContext();
}
return encoderContext;
}
/**
* Factory method for {@link IMapperContext} implementations. {@link DefaultMapperContext} may
* be a good starting point for custom implementations.
*
* @return new instance of mapper context to be used in the application
*/
protected IMapperContext newMapperContext()
{
return new DefaultMapperContext(this);
}
/**
*
* @param requestCycle
* @return Session
*/
public Session fetchCreateAndSetSession(final RequestCycle requestCycle)
{
Args.notNull(requestCycle, "requestCycle");
Session session = getSessionStore().lookup(requestCycle.getRequest());
if (session == null)
{
session = newSession(requestCycle.getRequest(), requestCycle.getResponse());
ThreadContext.setSession(session);
internalGetPageManager().clear();
sessionListeners.onCreated(session);
}
else
{
ThreadContext.setSession(session);
}
return session;
}
/**
*
* @return RequestCycleProvider
*/
public final IRequestCycleProvider getRequestCycleProvider()
{
return requestCycleProvider;
}
/**
*
* @param requestCycleProvider
*/
public final Application setRequestCycleProvider(final IRequestCycleProvider requestCycleProvider)
{
this.requestCycleProvider = requestCycleProvider;
return this;
}
/**
*
* @param request
* @param response
* @return request cycle
*/
public final RequestCycle createRequestCycle(final Request request, final Response response)
{
RequestCycleContext context = new RequestCycleContext(request, response,
getRootRequestMapper(), getExceptionMapperProvider().get());
RequestCycle requestCycle = getRequestCycleProvider().apply(context);
requestCycle.getListeners().add(requestCycleListeners);
requestCycle.getListeners().add(new IRequestCycleListener()
{
@Override
public void onDetach(final RequestCycle requestCycle)
{
internalGetPageManager().detach();
IRequestLogger requestLogger = getRequestLogger();
if (requestLogger != null)
{
requestLogger.requestTime((System.currentTimeMillis() - requestCycle.getStartTime()));
}
}
});
return requestCycle;
}
/**
* Sets an {@link IHeaderResponseDecorator} that you want your application to use to decorate
* header responses.
* <p>
* Calling this method replaces the default decorator, which utilizes a
* {@link ResourceAggregator}: The given implementation should make sure, that it too wraps
* responses in a {@link ResourceAggregator}, otherwise no dependencies for {@link HeaderItem}s
* will be resolved.
*
* @param headerResponseDecorator
* your custom decorator, must not be null
* @deprecated use {@code add(...)} on {@link #getHeaderResponseDecorators()}. This method
* removes the {@link ResourceAggregator}, which is needed to resolve resource
* dependencies.
*/
@Deprecated
public final Application
setHeaderResponseDecorator(final IHeaderResponseDecorator headerResponseDecorator)
{
headerResponseDecorators.replaceAll(headerResponseDecorator);
return this;
}
/**
* Returns the {@link HeaderResponseDecoratorCollection} used by this application. On this
* collection you can add additional decorators, which will be nested in the order added.
*
* @return The {@link HeaderResponseDecoratorCollection} used by this application.
*/
public HeaderResponseDecoratorCollection getHeaderResponseDecorators()
{
return headerResponseDecorators;
}
/**
* INTERNAL METHOD - You shouldn't need to call this. This is called every time Wicket creates
* an IHeaderResponse. It gives you the ability to incrementally add features to an
* IHeaderResponse implementation by wrapping it in another implementation.
*
* To decorate an IHeaderResponse in your application, set the {@link IHeaderResponseDecorator}
* on the application.
*
* @see IHeaderResponseDecorator
* @param response
* the response Wicket created
* @return the response Wicket should use in IHeaderContributor traversal
*/
public final IHeaderResponse decorateHeaderResponse(final IHeaderResponse response)
{
return headerResponseDecorators.decorate(response);
}
/**
*
* @return true, of app is in Development mode
*/
public final boolean usesDevelopmentConfig()
{
return RuntimeConfigurationType.DEVELOPMENT.equals(getConfigurationType());
}
/**
*
* @return true, of app is in Deployment mode
*/
public final boolean usesDeploymentConfig()
{
return RuntimeConfigurationType.DEPLOYMENT.equals(getConfigurationType());
}
}