001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.wicket;
018
019import java.net.URLConnection;
020import java.time.Duration;
021import java.util.Collections;
022import java.util.List;
023import java.util.Map;
024import java.util.ServiceLoader;
025import java.util.Set;
026import java.util.concurrent.ConcurrentHashMap;
027import java.util.function.Supplier;
028import org.apache.wicket.application.ComponentInitializationListenerCollection;
029import org.apache.wicket.application.ComponentInstantiationListenerCollection;
030import org.apache.wicket.application.ComponentOnAfterRenderListenerCollection;
031import org.apache.wicket.application.ComponentOnBeforeRenderListenerCollection;
032import org.apache.wicket.application.ComponentOnConfigureListenerCollection;
033import org.apache.wicket.application.HeaderContributorListenerCollection;
034import org.apache.wicket.application.IComponentInitializationListener;
035import org.apache.wicket.application.IComponentInstantiationListener;
036import org.apache.wicket.application.OnComponentTagListenerCollection;
037import org.apache.wicket.core.request.mapper.IMapperContext;
038import org.apache.wicket.core.util.lang.PropertyResolver;
039import org.apache.wicket.core.util.lang.WicketObjects;
040import org.apache.wicket.core.util.resource.ClassPathResourceFinder;
041import org.apache.wicket.event.IEvent;
042import org.apache.wicket.event.IEventSink;
043import org.apache.wicket.javascript.DefaultJavaScriptCompressor;
044import org.apache.wicket.markup.MarkupFactory;
045import org.apache.wicket.markup.head.HeaderItem;
046import org.apache.wicket.markup.head.IHeaderResponse;
047import org.apache.wicket.markup.head.ResourceAggregator;
048import org.apache.wicket.markup.html.HeaderResponseDecoratorCollection;
049import org.apache.wicket.markup.html.IHeaderContributor;
050import org.apache.wicket.markup.html.IHeaderResponseDecorator;
051import org.apache.wicket.markup.html.image.resource.DefaultButtonImageResourceFactory;
052import org.apache.wicket.markup.parser.filter.EnclosureHandler;
053import org.apache.wicket.markup.parser.filter.InlineEnclosureHandler;
054import org.apache.wicket.markup.parser.filter.RelativePathPrefixHandler;
055import org.apache.wicket.markup.parser.filter.WicketLinkTagHandler;
056import org.apache.wicket.markup.parser.filter.WicketMessageTagHandler;
057import org.apache.wicket.markup.resolver.HtmlHeaderResolver;
058import org.apache.wicket.markup.resolver.WicketContainerResolver;
059import org.apache.wicket.markup.resolver.WicketMessageResolver;
060import org.apache.wicket.page.IPageManager;
061import org.apache.wicket.pageStore.IPageStore;
062import org.apache.wicket.protocol.http.IRequestLogger;
063import org.apache.wicket.protocol.http.RequestLogger;
064import org.apache.wicket.protocol.http.RequestLoggerRequestCycleListener;
065import org.apache.wicket.protocol.http.WebApplication;
066import org.apache.wicket.protocol.http.WebSession;
067import org.apache.wicket.request.IExceptionMapper;
068import org.apache.wicket.request.IRequestHandler;
069import org.apache.wicket.request.IRequestMapper;
070import org.apache.wicket.request.Request;
071import org.apache.wicket.request.Response;
072import org.apache.wicket.request.cycle.IRequestCycleListener;
073import org.apache.wicket.request.cycle.RequestCycle;
074import org.apache.wicket.request.cycle.RequestCycleContext;
075import org.apache.wicket.request.cycle.RequestCycleListenerCollection;
076import org.apache.wicket.request.mapper.ICompoundRequestMapper;
077import org.apache.wicket.request.resource.ResourceReferenceRegistry;
078import org.apache.wicket.response.filter.EmptySrcAttributeCheckFilter;
079import org.apache.wicket.session.DefaultPageFactory;
080import org.apache.wicket.session.ISessionStore;
081import org.apache.wicket.session.ISessionStore.UnboundListener;
082import org.apache.wicket.settings.ApplicationSettings;
083import org.apache.wicket.settings.DebugSettings;
084import org.apache.wicket.settings.ExceptionSettings;
085import org.apache.wicket.settings.FrameworkSettings;
086import org.apache.wicket.settings.JavaScriptLibrarySettings;
087import org.apache.wicket.settings.MarkupSettings;
088import org.apache.wicket.settings.PageSettings;
089import org.apache.wicket.settings.RequestCycleSettings;
090import org.apache.wicket.settings.RequestLoggerSettings;
091import org.apache.wicket.settings.ResourceSettings;
092import org.apache.wicket.settings.SecuritySettings;
093import org.apache.wicket.settings.StoreSettings;
094import org.apache.wicket.util.lang.Args;
095import org.apache.wicket.util.lang.Generics;
096import org.slf4j.Logger;
097import org.slf4j.LoggerFactory;
098
099/**
100 * Base class for all Wicket applications. To create a Wicket application, you generally should
101 * <i>not </i> directly subclass this class. Instead, you will want to subclass some subclass of
102 * Application, like WebApplication, which is appropriate for the protocol and markup type you are
103 * working with.
104 * <p>
105 * Application has the following interesting features / attributes:
106 * <ul>
107 * <li><b>Name </b>- The Application's name, which is the same as its class name.
108 * 
109 * <li><b>Home Page </b>- The Application's home Page class. Subclasses must override getHomePage()
110 * to provide this property value.
111 * 
112 * <li><b>Settings </b>- Application settings are partitioned into sets of related settings using
113 * interfaces in the org.apache.wicket.settings package. These interfaces are returned by the
114 * following methods, which should be used to configure framework settings for your application:
115 * getApplicationSettings(), getDebugSettings(), getExceptionSettings(), getMarkupSettings(),
116 * getPageSettings(), getRequestCycleSettings(), getSecuritySettings and getSessionSettings(). These
117 * settings are configured by default through the constructor or internalInit methods. Default the
118 * application is configured for DEVELOPMENT. You can configure this globally to DEPLOYMENT or
119 * override specific settings by implementing the init() method.
120 * 
121 * <li><b>Shared Resources </b>- Resources added to an Application's SharedResources have
122 * application-wide scope and can be referenced using a logical scope and a name with the
123 * ResourceReference class. ResourceReferences can then be used by multiple components in the same
124 * application without additional overhead (beyond the ResourceReference instance held by each
125 * referee) and will yield a stable URL, permitting efficient browser caching of the resource (even
126 * if the resource is dynamically generated). Resources shared in this manner may also be localized.
127 * See {@link org.apache.wicket.request.resource.ResourceReference} for more details.
128 * 
129 * <li><b>Custom Session Subclasses</b>- In order to install your own {@link Session} subclass you
130 * must override Application{@link #newSession(Request, Response)}. For subclasses of
131 * {@link WebApplication} you will want to subclass {@link WebSession}.
132 * 
133 * </ul>
134 * 
135 * @see org.apache.wicket.protocol.http.WebApplication
136 * @author Jonathan Locke
137 */
138public abstract class Application implements UnboundListener, IEventSink, IMetadataContext<Object, Application>
139{
140        /** Configuration constant for the 2 types */
141        public static final String CONFIGURATION = "configuration";
142
143        /**
144         * Applications keyed on the {@link #getApplicationKey()} so that they can be retrieved even
145         * without being in a request/ being set in the thread local (we need that e.g. for when we are
146         * in a destruction thread).
147         */
148        private static final Map<String, Application> applicationKeyToApplication = Generics.newHashMap(1);
149
150        /** Log. */
151        private static final Logger log = LoggerFactory.getLogger(Application.class);
152
153        /** root mapper */
154        private IRequestMapper rootRequestMapper;
155
156        /** The converter locator instance. */
157        private IConverterLocator converterLocator;
158
159        /** list of initializers. */
160        private final List<IInitializer> initializers = Generics.newArrayList();
161
162        /** Application level meta data. */
163        private final ConcurrentHashMap<MetaDataKey<?>, Object> metaData = new ConcurrentHashMap<>();
164
165        /** Name of application subclass. */
166        private String name;
167
168        /** Request logger instance. */
169        private IRequestLogger requestLogger;
170
171        /** The session facade. */
172        private volatile ISessionStore sessionStore;
173
174        /** page renderer provider */
175        private IPageRendererProvider pageRendererProvider;
176
177        /** request cycle provider */
178        private IRequestCycleProvider requestCycleProvider;
179
180        /** exception mapper provider */
181        private Supplier<IExceptionMapper> exceptionMapperProvider;
182
183        /** session store provider */
184        private Supplier<ISessionStore> sessionStoreProvider;
185
186        /**
187         * The decorator this application uses to decorate any header responses created by Wicket
188         */
189        private HeaderResponseDecoratorCollection headerResponseDecorators =
190                new HeaderResponseDecoratorCollection();
191
192        /**
193         * Checks if the <code>Application</code> threadlocal is set in this thread
194         * 
195         * @return true if {@link Application#get()} can return the instance of application, false
196         *         otherwise
197         */
198        public static boolean exists()
199        {
200                return ThreadContext.getApplication() != null;
201        }
202
203        /**
204         * Get Application for current thread.
205         * 
206         * @return The current thread's Application
207         */
208        public static Application get()
209        {
210                Application application = ThreadContext.getApplication();
211                if (application == null)
212                {
213                        throw new WicketRuntimeException("There is no application attached to current thread " +
214                                Thread.currentThread().getName());
215                }
216                return application;
217        }
218
219        /**
220         * Gets the Application based on the application key of that application. You typically never
221         * have to use this method unless you are working on an integration project.
222         * 
223         * @param applicationKey
224         *            The unique key of the application within a certain context (e.g. a web
225         *            application)
226         * @return The application or <code>null</code> if application has not been found
227         */
228        public static Application get(final String applicationKey)
229        {
230                return applicationKeyToApplication.get(applicationKey);
231        }
232
233        /**
234         * Gets the keys of the currently registered Wicket applications for this web application. You
235         * typically never have to use this method unless you are working on an integration project.
236         * 
237         * @return unmodifiable set with keys that correspond with {@link #getApplicationKey()}. Never
238         *         null, but possibly empty
239         */
240        public static Set<String> getApplicationKeys()
241        {
242                return Collections.unmodifiableSet(applicationKeyToApplication.keySet());
243        }
244
245        /**
246         * Constructor. <strong>Use {@link #init()} for any configuration of your application instead of
247         * overriding the constructor.</strong>
248         */
249        public Application()
250        {
251                // Install default component instantiation listener that uses
252                // authorization strategy to check component instantiations.
253                getComponentInstantiationListeners().add(new IComponentInstantiationListener()
254                {
255                        /**
256                         * @see org.apache.wicket.application.IComponentInstantiationListener#onInstantiation(org.apache.wicket.Component)
257                         */
258                        @Override
259                        public void onInstantiation(final Component component)
260                        {
261                                final Class<? extends Component> cl = component.getClass();
262                                // If component instantiation is not authorized
263                                if (!Session.get().getAuthorizationStrategy().isInstantiationAuthorized(cl))
264                                {
265                                        // then call any unauthorized component instantiation
266                                        // listener
267                                        getSecuritySettings().getUnauthorizedComponentInstantiationListener()
268                                                .onUnauthorizedInstantiation(component);
269                                }
270                        }
271                });
272        }
273
274        /**
275         * Configures application settings to good defaults.
276         */
277        public final void configure()
278        {
279                // As long as this is public api the development and deployment mode
280                // should counter act each other for all properties.
281                switch (getConfigurationType())
282                {
283                        case DEVELOPMENT : {
284                                getResourceSettings().setResourcePollFrequency(Duration.ofSeconds(1));
285                                getResourceSettings().setJavaScriptCompressor(null);
286                                getResourceSettings().setUseMinifiedResources(false);
287                                getMarkupSettings().setStripWicketTags(false);
288                                getExceptionSettings().setUnexpectedExceptionDisplay(
289                                        ExceptionSettings.SHOW_EXCEPTION_PAGE);
290                                getDebugSettings().setComponentUseCheck(true);
291                                getDebugSettings().setAjaxDebugModeEnabled(true);
292                                getDebugSettings().setDevelopmentUtilitiesEnabled(true);
293                                // getDebugSettings().setOutputMarkupContainerClassName(true);
294                                getRequestCycleSettings().addResponseFilter(EmptySrcAttributeCheckFilter.INSTANCE);
295                                break;
296                        }
297                        case DEPLOYMENT : {
298                                getResourceSettings().setResourcePollFrequency(null);
299                                getResourceSettings().setJavaScriptCompressor(new DefaultJavaScriptCompressor());
300                                getMarkupSettings().setStripWicketTags(true);
301                                getExceptionSettings().setUnexpectedExceptionDisplay(
302                                        ExceptionSettings.SHOW_INTERNAL_ERROR_PAGE);
303                                getDebugSettings().setComponentUseCheck(false);
304                                getDebugSettings().setAjaxDebugModeEnabled(false);
305                                getDebugSettings().setDevelopmentUtilitiesEnabled(false);
306                                break;
307                        }
308                }
309        }
310
311        /**
312         * Gets the unique key of this application within a given context (like a web application). NOT
313         * INTENDED FOR FRAMEWORK CLIENTS.
314         * 
315         * @return The unique key of this application
316         */
317        public abstract String getApplicationKey();
318
319        /**
320         * Gets the configuration mode to use for configuring the app, either
321         * {@link RuntimeConfigurationType#DEVELOPMENT} or {@link RuntimeConfigurationType#DEPLOYMENT}.
322         * <p>
323         * The configuration type. Must currently be either DEVELOPMENT or DEPLOYMENT. Currently, if the
324         * configuration type is DEVELOPMENT, resources are polled for changes, component usage is
325         * checked, wicket tags are not stripped from output and a detailed exception page is used. If
326         * the type is DEPLOYMENT, component usage is not checked, wicket tags are stripped from output
327         * and a non-detailed exception page is used to display errors.
328         * <p>
329         * Note that you should not run Wicket in DEVELOPMENT mode on production servers - the various
330         * debugging checks and resource polling is inefficient and may leak resources, particularly on
331         * webapp redeploy.
332         * 
333         * <div style="border-style:solid;">
334         * <p>
335         * To change the deployment mode, add the following to your web.xml, inside your <servlet>
336         * mapping (or <filter> mapping if you're using 1.3.x):
337         * 
338         * <pre>
339         * &lt;init-param&gt;
340         *             &lt;param-name&gt;configuration&lt;/param-name&gt;
341         *             &lt;param-value&gt;deployment&lt;/param-value&gt;
342         * &lt;/init-param&gt;
343         * </pre>
344         * 
345         * <p>
346         * You can alternatively set this as a &lt;context-param&gt; on the whole context.
347         * 
348         * <p>
349         * Another option is to set the "wicket.configuration" system property to either "deployment" or
350         * "development". The value is not case-sensitive.
351         * 
352         * <p>
353         * The system property is checked first, allowing you to add a web.xml param for deployment, and
354         * a command-line override when you want to run in development mode during development.
355         * 
356         * <p>
357         * You may also override Application.getConfigurationType() to provide your own custom switch,
358         * in which case none of the above logic is used. </div>
359         * 
360         * <p>
361         * IMPORTANT NOTE
362         * </p>
363         * THIS METHOD IS CALLED OFTEN FROM MANY DIFFERENT POINTS IN CODE, INCLUDING DURING THE RENDER
364         * PROCESS, THEREFORE THE IMPLEMENTATION SHOULD BE FAST - PREFERRABLY USING A FAST-TO-RETRIEVE
365         * CACHED VALUE
366         * 
367         * @return configuration
368         * @since 1.2.3 (function existed as a property getter)
369         * @since 1.3.0 (abstract, used to configure things)
370         */
371        public abstract RuntimeConfigurationType getConfigurationType();
372
373        /**
374         * Application subclasses must specify a home page class by implementing this abstract method.
375         * 
376         * @return Home page class for this application
377         */
378        public abstract Class<? extends Page> getHomePage();
379
380        /**
381         * @return The converter locator for this application
382         */
383        public final IConverterLocator getConverterLocator()
384        {
385                return converterLocator;
386        }
387
388        /**
389         * Gets metadata for this application using the given key.
390         * 
391         * @param <T>
392         * @param key
393         *            The key for the data
394         * @return The metadata
395         * @see MetaDataKey
396         */
397        @Override
398        @SuppressWarnings("unchecked")
399        public final <T> T getMetaData(final MetaDataKey<T> key)
400        {
401                return (T)metaData.get(key);
402        }
403
404        /**
405         * Gets the name of this application.
406         * 
407         * @return The application name.
408         */
409        public final String getName()
410        {
411                return name;
412        }
413
414        /**
415         * Gets the {@link IRequestLogger}.
416         * 
417         * @return The RequestLogger
418         */
419        public final IRequestLogger getRequestLogger()
420        {
421                if (getRequestLoggerSettings().isRequestLoggerEnabled())
422                {
423                        if (requestLogger == null)
424                        {
425                                requestLogger = newRequestLogger();
426                        }
427                }
428                else
429                {
430                        requestLogger = null;
431                }
432                return requestLogger;
433        }
434
435        /**
436         * Gets the facade object for working getting/ storing session instances.
437         * 
438         * @return The session facade
439         */
440        public final ISessionStore getSessionStore()
441        {
442                if (sessionStore == null)
443                {
444                        synchronized (this)
445                        {
446                                if (sessionStore == null)
447                                {
448                                        sessionStore = sessionStoreProvider.get();
449                                        sessionStore.registerUnboundListener(this);
450                                }
451                        }
452                }
453                return sessionStore;
454        }
455
456        /**
457         * @see org.apache.wicket.session.ISessionStore.UnboundListener#sessionUnbound(java.lang.String)
458         */
459        @Override
460        public void sessionUnbound(final String sessionId)
461        {
462                getSessionListeners().onUnbound(sessionId);
463        }
464
465        /**
466         * THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT CALL.
467         * 
468         * @param target
469         */
470        public void logEventTarget(final IRequestHandler target)
471        {
472        }
473
474        /**
475         * THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT CALL.
476         * 
477         * @param requestTarget
478         */
479        public void logResponseTarget(final IRequestHandler requestTarget)
480        {
481        }
482
483        /**
484         * Creates a new session. Override this method if you want to provide a custom session.
485         * 
486         * @param request
487         *            The request that will create this session.
488         * @param response
489         *            The response to initialize, for example with cookies. This is important to use
490         *            cases involving unit testing because those use cases might want to be able to sign
491         *            a user in automatically when the session is created.
492         * 
493         * @return The session
494         * 
495         * @since 1.3
496         */
497        public abstract Session newSession(Request request, Response response);
498
499        /**
500         * Sets the metadata for this application using the given key. If the metadata object is not of
501         * the correct type for the metadata key, an IllegalArgumentException will be thrown. For
502         * information on creating MetaDataKeys, see {@link MetaDataKey}.
503         * 
504         * @param <T>
505         * @param key
506         *            The singleton key for the metadata
507         * @param object
508         *            The metadata object
509         * @throws IllegalArgumentException
510         * @see MetaDataKey
511         */
512        @Override
513        public final <T> Application setMetaData(final MetaDataKey<T> key, final T object)
514        {
515                metaData.put(key, object);
516                return this;
517        }
518
519        /**
520         * Construct and add initializer from the provided class name.
521         * 
522         * @param className
523         */
524        private void addInitializer(final String className)
525        {
526                IInitializer initializer = WicketObjects.newInstance(className);
527                if (initializer != null)
528                {
529                        initializers.add(initializer);
530                }
531        }
532
533        /**
534         * Iterate initializers list, calling their {@link IInitializer#destroy(Application) destroy}
535         * methods.
536         */
537        private void destroyInitializers()
538        {
539                for (IInitializer initializer : initializers)
540                {
541                        log.info("[{}] destroy: {}", getName(), initializer);
542                        initializer.destroy(this);
543                }
544        }
545
546        /**
547         * Iterate initializers list, calling {@link IInitializer#init(Application)} on any instances
548         * found in it.
549         */
550        private void initInitializers()
551        {
552                for (IInitializer initializer : initializers)
553                {
554                        log.info("[{}] init: {}", getName(), initializer);
555                        initializer.init(this);
556                }
557
558                final ServiceLoader<IInitializer> serviceLoaderInitializers = ServiceLoader.load(IInitializer.class);
559                for (IInitializer serviceLoaderInitializer : serviceLoaderInitializers) {
560                        log.info("[{}] init: {}", getName(), serviceLoaderInitializer);
561                        serviceLoaderInitializer.init(this);
562                        initializers.add(serviceLoaderInitializer);
563                }
564        }
565
566        /**
567         * Called when wicket servlet is destroyed. Overrides do not have to call super.
568         */
569        protected void onDestroy()
570        {
571        }
572
573        /**
574         * Allows for initialization of the application by a subclass. <strong>Use this method for any
575         * application setup instead of the constructor.</strong>
576         */
577        protected void init()
578        {
579        }
580
581        /**
582         * THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT CALL IT.
583         */
584        public void internalDestroy()
585        {
586                applicationListeners.onBeforeDestroyed(this);
587
588                // destroy detach listener
589                final IDetachListener detachListener = getFrameworkSettings().getDetachListener();
590                if (detachListener != null)
591                {
592                        detachListener.onDestroyListener();
593                }
594
595                // Clear caches of Class keys so the classloader can be garbage
596                // collected (WICKET-625)
597                PropertyResolver.destroy(this);
598                MarkupFactory markupFactory = getMarkupSettings().getMarkupFactory();
599
600                if (markupFactory.hasMarkupCache())
601                {
602                        markupFactory.getMarkupCache().shutdown();
603                }
604
605                onDestroy();
606
607                destroyInitializers();
608
609                internalGetPageManager().destroy();
610                getSessionStore().destroy();
611
612                applicationKeyToApplication.remove(getApplicationKey());
613        }
614
615        /**
616         * THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT OVERRIDE OR CALL.
617         * 
618         * Internal initialization.
619         */
620        protected void internalInit()
621        {
622                settingsAccessible = true;
623                PageSettings pageSettings = getPageSettings();
624
625                // Install default component resolvers
626                pageSettings.addComponentResolver(new HtmlHeaderResolver());
627                pageSettings.addComponentResolver(new WicketLinkTagHandler());
628                pageSettings.addComponentResolver(new WicketMessageResolver());
629                pageSettings.addComponentResolver(new RelativePathPrefixHandler());
630                pageSettings.addComponentResolver(new EnclosureHandler());
631                pageSettings.addComponentResolver(new InlineEnclosureHandler());
632                pageSettings.addComponentResolver(new WicketMessageTagHandler());
633                pageSettings.addComponentResolver(new WicketContainerResolver());
634
635                getResourceSettings().getResourceFinders().add(new ClassPathResourceFinder(""));
636
637                // Install button image resource factory
638                getResourceSettings().addResourceFactory("buttonFactory",
639                        new DefaultButtonImageResourceFactory());
640
641                String applicationKey = getApplicationKey();
642                applicationKeyToApplication.put(applicationKey, this);
643
644                converterLocator = newConverterLocator();
645
646                setPageManagerProvider(new DefaultPageManagerProvider(this));
647                resourceReferenceRegistry = newResourceReferenceRegistry();
648                sharedResources = newSharedResources(resourceReferenceRegistry);
649                resourceBundles = newResourceBundles(resourceReferenceRegistry);
650
651                // set up default request mapper
652                setRootRequestMapper(new SystemMapper(this));
653
654                pageFactory = newPageFactory();
655
656                requestCycleProvider = RequestCycle::new;
657                exceptionMapperProvider = DefaultExceptionMapper::new;
658
659                // add a request cycle listener that logs each request for the requestlogger.
660                getRequestCycleListeners().add(new RequestLoggerRequestCycleListener());
661        }
662
663        /**
664         * Returns a supplier of {@link IExceptionMapper} that will be used to
665         * handle exceptions which were not handled by any
666         * {@link IRequestCycleListener#onException(RequestCycle, Exception) request cycle listener}.
667         *
668         * @return the exception mapper supplier
669         * @see IRequestCycleListener#onException(RequestCycle, Exception)
670         */
671        public Supplier<IExceptionMapper> getExceptionMapperProvider()
672        {
673                return exceptionMapperProvider;
674        }
675
676        public void setExceptionMapperProvider(Supplier<IExceptionMapper> exceptionMapperProvider) {
677                this.exceptionMapperProvider = Args.notNull(exceptionMapperProvider, "exceptionMapperProvider");
678        }
679
680        /**
681         * 
682         * @return Session state provider
683         */
684        public final Supplier<ISessionStore> getSessionStoreProvider()
685        {
686                return sessionStoreProvider;
687        }
688
689        /**
690         * 
691         * @param sessionStoreProvider
692         */
693        public final Application setSessionStoreProvider(final Supplier<ISessionStore> sessionStoreProvider)
694        {
695                this.sessionStoreProvider = Args.notNull(sessionStoreProvider, "sessionStoreProvider");
696                this.sessionStore = null;
697                return this;
698        }
699
700        /**
701         * Creates and returns a new instance of {@link IConverterLocator}.
702         * 
703         * @return A new {@link IConverterLocator} instance
704         */
705        protected IConverterLocator newConverterLocator()
706        {
707                return new ConverterLocator();
708        }
709
710        /**
711         * creates a new request logger when requests logging is enabled.
712         * 
713         * @return The new request logger
714         * 
715         */
716        protected IRequestLogger newRequestLogger()
717        {
718                return new RequestLogger();
719        }
720
721        /**
722         * Converts the root mapper to a {@link ICompoundRequestMapper} if necessary and returns the
723         * converted instance.
724         * 
725         * @return compound instance of the root mapper
726         */
727        public final ICompoundRequestMapper getRootRequestMapperAsCompound()
728        {
729                IRequestMapper root = getRootRequestMapper();
730                if (!(root instanceof ICompoundRequestMapper))
731                {
732                        root = new SystemMapper(this).add(root);
733                        setRootRequestMapper(root);
734                }
735                return (ICompoundRequestMapper)root;
736        }
737
738        /**
739         * @return The root request mapper
740         */
741        public final IRequestMapper getRootRequestMapper()
742        {
743                return rootRequestMapper;
744        }
745
746        /**
747         * Sets the root request mapper
748         * 
749         * @param rootRequestMapper
750         */
751        public final Application setRootRequestMapper(final IRequestMapper rootRequestMapper)
752        {
753                this.rootRequestMapper = rootRequestMapper;
754                return this;
755        }
756
757        /**
758         * Initialize the application
759         */
760        public final void initApplication()
761        {
762                if (name == null)
763                {
764                        throw new IllegalStateException("setName must be called before initApplication");
765                }
766                internalInit();
767                initInitializers();
768                init();
769                applicationListeners.onAfterInitialized(this);
770
771                validateInit();
772        }
773
774        /**
775         * Gives the Application object a chance to validate if it has been properly initialized
776         */
777        protected void validateInit()
778        {
779                if (getPageRendererProvider() == null)
780                {
781                        throw new IllegalStateException(
782                                "An instance of IPageRendererProvider has not yet been set on this Application. @see Application#setPageRendererProvider");
783                }
784                if (getSessionStoreProvider() == null)
785                {
786                        throw new IllegalStateException(
787                                "An instance of ISessionStoreProvider has not yet been set on this Application. @see Application#setSessionStoreProvider");
788                }
789                if (getPageManagerProvider() == null)
790                {
791                        throw new IllegalStateException(
792                                "An instance of IPageManagerProvider has not yet been set on this Application. @see Application#setPageManagerProvider");
793                }
794        }
795
796        /**
797         * Sets application name. This method must be called before any other methods are invoked and
798         * can only be called once per application instance.
799         * 
800         * @param name
801         *            unique application name
802         */
803        public final void setName(final String name)
804        {
805                Args.notEmpty(name, "name");
806
807                if (this.name != null)
808                {
809                        throw new IllegalStateException("Application name can only be set once.");
810                }
811
812                if (applicationKeyToApplication.get(name) != null)
813                {
814                        throw new IllegalStateException("Application with name '" + name + "' already exists.'");
815                }
816
817                this.name = name;
818                applicationKeyToApplication.put(name, this);
819        }
820
821        /**
822         * Returns the mime type for given filename.
823         * 
824         * @param fileName
825         * @return mime type
826         */
827        public String getMimeType(final String fileName)
828        {
829                return URLConnection.getFileNameMap().getContentTypeFor(fileName);
830        }
831
832        /** {@inheritDoc} */
833        @Override
834        public void onEvent(final IEvent<?> event)
835        {
836        }
837
838        // /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
839        // /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
840        //
841        // Listeners
842        //
843        // /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
844        // /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
845
846        /** */
847        private final ComponentOnBeforeRenderListenerCollection componentPreOnBeforeRenderListeners = new ComponentOnBeforeRenderListenerCollection();
848
849        /** */
850        private final ComponentOnBeforeRenderListenerCollection componentPostOnBeforeRenderListeners = new ComponentOnBeforeRenderListenerCollection();
851
852        /** */
853        private final ComponentOnAfterRenderListenerCollection componentOnAfterRenderListeners = new ComponentOnAfterRenderListenerCollection();
854
855        /** */
856        private final RequestCycleListenerCollection requestCycleListeners = new RequestCycleListenerCollection();
857
858        private final ApplicationListenerCollection applicationListeners = new ApplicationListenerCollection();
859
860        private final SessionListenerCollection sessionListeners = new SessionListenerCollection();
861
862        /** list of {@link IComponentInstantiationListener}s. */
863        private final ComponentInstantiationListenerCollection componentInstantiationListeners = new ComponentInstantiationListenerCollection();
864
865        /** list of {@link IComponentInitializationListener}s. */
866        private final ComponentInitializationListenerCollection componentInitializationListeners = new ComponentInitializationListenerCollection();
867
868        /** list of {@link org.apache.wicket.application.IComponentOnConfigureListener}s. */
869        private final ComponentOnConfigureListenerCollection componentOnConfigureListeners = new ComponentOnConfigureListenerCollection();
870
871        /** list of {@link IHeaderContributor}s. */
872        private final HeaderContributorListenerCollection headerContributorListeners = new HeaderContributorListenerCollection();
873
874        private final BehaviorInstantiationListenerCollection behaviorInstantiationListeners = new BehaviorInstantiationListenerCollection();
875
876        private final OnComponentTagListenerCollection onComponentTagListeners = new OnComponentTagListenerCollection();
877
878        /**
879         * @return Gets the application's {@link HeaderContributorListenerCollection}
880         */
881        public final HeaderContributorListenerCollection getHeaderContributorListeners()
882        {
883                return headerContributorListeners;
884        }
885
886        /**
887         * @return collection of initializers
888         */
889        public final List<IInitializer> getInitializers()
890        {
891                return Collections.unmodifiableList(initializers);
892        }
893
894        /**
895         * @return collection of application listeners
896         */
897        public final ApplicationListenerCollection getApplicationListeners()
898        {
899                return applicationListeners;
900        }
901
902        /**
903         * @return collection of session listeners
904         */
905        public final SessionListenerCollection getSessionListeners()
906        {
907                return sessionListeners;
908        }
909
910        /**
911         * @return collection of behavior instantiation listeners
912         */
913        public final BehaviorInstantiationListenerCollection getBehaviorInstantiationListeners()
914        {
915                return behaviorInstantiationListeners;
916        }
917
918        /**
919         * @return collection of application's on-component-tag listeners
920         */
921        public final OnComponentTagListenerCollection getOnComponentTagListeners() {
922                return onComponentTagListeners;
923        }
924
925        /**
926         * @return Gets the application's ComponentInstantiationListenerCollection
927         */
928        public final ComponentInstantiationListenerCollection getComponentInstantiationListeners()
929        {
930                return componentInstantiationListeners;
931        }
932
933        /**
934         * @return Gets the application's ComponentInitializationListeners
935         */
936        public final ComponentInitializationListenerCollection getComponentInitializationListeners()
937        {
938                return componentInitializationListeners;
939        }
940
941        /**
942         * @return Gets the application's ComponentOnConfigureListeners
943         */
944        public final ComponentOnConfigureListenerCollection getComponentOnConfigureListeners()
945        {
946                return componentOnConfigureListeners;
947        }
948
949        /**
950         * 
951         * @return ComponentOnBeforeRenderListenerCollection
952         */
953        public final ComponentOnBeforeRenderListenerCollection getComponentPreOnBeforeRenderListeners()
954        {
955                return componentPreOnBeforeRenderListeners;
956        }
957
958        /**
959         * 
960         * @return ComponentOnBeforeRenderListenerCollection
961         */
962        public final ComponentOnBeforeRenderListenerCollection getComponentPostOnBeforeRenderListeners()
963        {
964                return componentPostOnBeforeRenderListeners;
965        }
966
967        /**
968         * @return on after render listeners collection
969         */
970        public final ComponentOnAfterRenderListenerCollection getComponentOnAfterRenderListeners()
971        {
972                return componentOnAfterRenderListeners;
973        }
974
975        /**
976         * @return the unmodifiable request list of {@link IRequestCycleListener}s in this application
977         */
978        public RequestCycleListenerCollection getRequestCycleListeners()
979        {
980                return requestCycleListeners;
981        }
982
983        // /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
984        // /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
985        //
986        // Settings
987        //
988        // /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
989        // /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
990
991        /** Application settings */
992        private ApplicationSettings applicationSettings;
993
994        /** JavaScriptLibrary settings */
995        private JavaScriptLibrarySettings javaScriptLibrarySettings;
996
997        /** Debug Settings */
998        private DebugSettings debugSettings;
999
1000        /** Exception Settings */
1001        private ExceptionSettings exceptionSettings;
1002
1003        /** Framework Settings */
1004        private FrameworkSettings frameworkSettings;
1005
1006        /** The Markup Settings */
1007        private MarkupSettings markupSettings;
1008
1009        /** The Page Settings */
1010        private PageSettings pageSettings;
1011
1012        /** The Request Cycle Settings */
1013        private RequestCycleSettings requestCycleSettings;
1014
1015        /** The Request Logger Settings */
1016        private RequestLoggerSettings requestLoggerSettings;
1017
1018        /** The Resource Settings */
1019        private ResourceSettings resourceSettings;
1020
1021        /** The Security Settings */
1022        private SecuritySettings securitySettings;
1023
1024        /** The settings for {@link IPageStore} and {@link IPageManager} */
1025        private StoreSettings storeSettings;
1026
1027        /** can the settings object be set/used. */
1028        private boolean settingsAccessible;
1029
1030        /**
1031         * @return Application's application-wide settings
1032         * @since 1.2
1033         */
1034        public final ApplicationSettings getApplicationSettings()
1035        {
1036                checkSettingsAvailable();
1037                if (applicationSettings == null)
1038                {
1039                        applicationSettings = new ApplicationSettings();
1040                }
1041                return applicationSettings;
1042        }
1043
1044        /**
1045         * 
1046         * @param applicationSettings
1047         */
1048        public final Application setApplicationSettings(final ApplicationSettings applicationSettings)
1049        {
1050                this.applicationSettings = applicationSettings;
1051                return this;
1052        }
1053
1054        /**
1055         * @return Application's JavaScriptLibrary settings
1056         * @since 6.0
1057         */
1058        public final JavaScriptLibrarySettings getJavaScriptLibrarySettings()
1059        {
1060                checkSettingsAvailable();
1061                if (javaScriptLibrarySettings == null)
1062                {
1063                        javaScriptLibrarySettings = new JavaScriptLibrarySettings();
1064                }
1065                return javaScriptLibrarySettings;
1066        }
1067
1068        /**
1069         * 
1070         * @param javaScriptLibrarySettings
1071         */
1072        public final Application setJavaScriptLibrarySettings(
1073                final JavaScriptLibrarySettings javaScriptLibrarySettings)
1074        {
1075                this.javaScriptLibrarySettings = javaScriptLibrarySettings;
1076                return this;
1077        }
1078
1079        /**
1080         * @return Application's debug related settings
1081         */
1082        public final DebugSettings getDebugSettings()
1083        {
1084                checkSettingsAvailable();
1085                if (debugSettings == null)
1086                {
1087                        debugSettings = new DebugSettings();
1088                }
1089                return debugSettings;
1090        }
1091
1092        /**
1093         * 
1094         * @param debugSettings
1095         */
1096        public final Application setDebugSettings(final DebugSettings debugSettings)
1097        {
1098                this.debugSettings = debugSettings;
1099                return this;
1100        }
1101
1102        /**
1103         * @return Application's exception handling settings
1104         */
1105        public final ExceptionSettings getExceptionSettings()
1106        {
1107                checkSettingsAvailable();
1108                if (exceptionSettings == null)
1109                {
1110                        exceptionSettings = new ExceptionSettings();
1111                }
1112                return exceptionSettings;
1113        }
1114
1115        /**
1116         * 
1117         * @param exceptionSettings
1118         */
1119        public final Application setExceptionSettings(final ExceptionSettings exceptionSettings)
1120        {
1121                this.exceptionSettings = exceptionSettings;
1122                return this;
1123        }
1124
1125        /**
1126         * @return Wicket framework settings
1127         */
1128        public final FrameworkSettings getFrameworkSettings()
1129        {
1130                checkSettingsAvailable();
1131                if (frameworkSettings == null)
1132                {
1133                        frameworkSettings = new FrameworkSettings(this);
1134                }
1135                return frameworkSettings;
1136        }
1137
1138        /**
1139         * 
1140         * @param frameworkSettings
1141         */
1142        public final Application setFrameworkSettings(final FrameworkSettings frameworkSettings)
1143        {
1144                this.frameworkSettings = frameworkSettings;
1145                return this;
1146        }
1147
1148        /**
1149         * @return Application's page related settings
1150         */
1151        public final PageSettings getPageSettings()
1152        {
1153                checkSettingsAvailable();
1154                if (pageSettings == null)
1155                {
1156                        pageSettings = new PageSettings();
1157                }
1158                return pageSettings;
1159        }
1160
1161        /**
1162         * 
1163         * @param pageSettings
1164         */
1165        public final Application setPageSettings(final PageSettings pageSettings)
1166        {
1167                this.pageSettings = pageSettings;
1168                return this;
1169        }
1170
1171        /**
1172         * @return Application's request cycle related settings
1173         */
1174        public RequestCycleSettings getRequestCycleSettings()
1175        {
1176                checkSettingsAvailable();
1177                if (requestCycleSettings == null)
1178                {
1179                        requestCycleSettings = new RequestCycleSettings();
1180                }
1181                return requestCycleSettings;
1182        }
1183
1184        /**
1185         * 
1186         * @param requestCycleSettings
1187         */
1188        public final Application setRequestCycleSettings(final RequestCycleSettings requestCycleSettings)
1189        {
1190                this.requestCycleSettings = requestCycleSettings;
1191                return this;
1192        }
1193
1194        /**
1195         * @return Application's markup related settings
1196         */
1197        public MarkupSettings getMarkupSettings()
1198        {
1199                checkSettingsAvailable();
1200                if (markupSettings == null)
1201                {
1202                        markupSettings = new MarkupSettings();
1203                }
1204                return markupSettings;
1205        }
1206
1207        /**
1208         * 
1209         * @param markupSettings
1210         */
1211        public final Application setMarkupSettings(final MarkupSettings markupSettings)
1212        {
1213                this.markupSettings = markupSettings;
1214                return this;
1215        }
1216
1217        /**
1218         * @return Application's request logger related settings
1219         */
1220        public final RequestLoggerSettings getRequestLoggerSettings()
1221        {
1222                checkSettingsAvailable();
1223                if (requestLoggerSettings == null)
1224                {
1225                        requestLoggerSettings = new RequestLoggerSettings();
1226                }
1227                return requestLoggerSettings;
1228        }
1229
1230        /**
1231         * 
1232         * @param requestLoggerSettings
1233         */
1234        public final Application setRequestLoggerSettings(final RequestLoggerSettings requestLoggerSettings)
1235        {
1236                this.requestLoggerSettings = requestLoggerSettings;
1237                return this;
1238        }
1239
1240        /**
1241         * @return Application's resources related settings
1242         */
1243        public final ResourceSettings getResourceSettings()
1244        {
1245                checkSettingsAvailable();
1246                if (resourceSettings == null)
1247                {
1248                        resourceSettings = new ResourceSettings(this);
1249                }
1250                return resourceSettings;
1251        }
1252
1253        /**
1254         * 
1255         * @param resourceSettings
1256         */
1257        public final Application setResourceSettings(final ResourceSettings resourceSettings)
1258        {
1259                this.resourceSettings = resourceSettings;
1260                return this;
1261        }
1262
1263        /**
1264         * @return Application's security related settings
1265         */
1266        public final SecuritySettings getSecuritySettings()
1267        {
1268                checkSettingsAvailable();
1269                if (securitySettings == null)
1270                {
1271                        securitySettings = new SecuritySettings();
1272                }
1273                return securitySettings;
1274        }
1275
1276        /**
1277         * 
1278         * @param securitySettings
1279         */
1280        public final Application setSecuritySettings(final SecuritySettings securitySettings)
1281        {
1282                this.securitySettings = securitySettings;
1283                return this;
1284        }
1285
1286        /**
1287         * @return Application's stores related settings
1288         */
1289        public final StoreSettings getStoreSettings()
1290        {
1291                checkSettingsAvailable();
1292                if (storeSettings == null)
1293                {
1294                        storeSettings = new StoreSettings(this);
1295                }
1296                return storeSettings;
1297        }
1298
1299        /**
1300         * 
1301         * @param storeSettings
1302         */
1303        public final Application setStoreSettings(final StoreSettings storeSettings)
1304        {
1305                this.storeSettings = storeSettings;
1306                return this;
1307        }
1308
1309        /**
1310         *
1311         */
1312        protected void checkSettingsAvailable()
1313        {
1314                if (!settingsAccessible)
1315                {
1316                        throw new WicketRuntimeException(
1317                                "Use Application.init() method for configuring your application object");
1318                }
1319        }
1320
1321        // /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1322        // /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1323        //
1324        // Page Manager
1325        //
1326        // /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1327        // /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1328
1329        private volatile IPageManager pageManager;
1330        private IPageManagerProvider pageManagerProvider;
1331
1332        /**
1333         * 
1334         * @return PageManagerProvider
1335         */
1336        public final IPageManagerProvider getPageManagerProvider()
1337        {
1338                return pageManagerProvider;
1339        }
1340
1341        /**
1342         * Set the provider of an {@link IPageManager}.
1343         * 
1344         * @param provider
1345         * 
1346         * @see DefaultPageManagerProvider
1347         */
1348        public final Application setPageManagerProvider(final IPageManagerProvider provider)
1349        {
1350                pageManagerProvider = provider;
1351                return this;
1352        }
1353
1354        /**
1355         * Returns an unsynchronized version of page manager
1356         * 
1357         * @return the page manager
1358         */
1359        final IPageManager internalGetPageManager()
1360        {
1361                if (pageManager == null)
1362                {
1363                        synchronized (this)
1364                        {
1365                                if (pageManager == null)
1366                                {
1367                                        pageManager = pageManagerProvider.get();
1368                                }
1369                        }
1370                }
1371                return pageManager;
1372        }
1373
1374        // /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1375        // /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1376        //
1377        // Page Rendering
1378        //
1379        // /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1380        // /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1381
1382        /**
1383         * 
1384         * @return PageRendererProvider
1385         */
1386        public final IPageRendererProvider getPageRendererProvider()
1387        {
1388                return pageRendererProvider;
1389        }
1390
1391        /**
1392         * 
1393         * @param pageRendererProvider
1394         */
1395        public final Application setPageRendererProvider(final IPageRendererProvider pageRendererProvider)
1396        {
1397                Args.notNull(pageRendererProvider, "pageRendererProvider");
1398                this.pageRendererProvider = pageRendererProvider;
1399                return this;
1400        }
1401
1402
1403        // /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1404        // /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1405        //
1406        // Request Handler encoding
1407        //
1408        // /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1409        // /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1410
1411        private ResourceReferenceRegistry resourceReferenceRegistry;
1412
1413        private SharedResources sharedResources;
1414
1415        private ResourceBundles resourceBundles;
1416
1417        private IPageFactory pageFactory;
1418
1419        private IMapperContext encoderContext;
1420
1421        /**
1422         * Override to create custom {@link ResourceReferenceRegistry}.
1423         * 
1424         * @return new {@link ResourceReferenceRegistry} instance.
1425         */
1426        protected ResourceReferenceRegistry newResourceReferenceRegistry()
1427        {
1428                return new ResourceReferenceRegistry();
1429        }
1430
1431        /**
1432         * Returns {@link ResourceReferenceRegistry} for this application.
1433         * 
1434         * @return ResourceReferenceRegistry
1435         */
1436        public final ResourceReferenceRegistry getResourceReferenceRegistry()
1437        {
1438                return resourceReferenceRegistry;
1439        }
1440
1441        /**
1442         * 
1443         * @param registry
1444         * @return SharedResources
1445         */
1446        protected SharedResources newSharedResources(final ResourceReferenceRegistry registry)
1447        {
1448                return new SharedResources(registry);
1449        }
1450
1451        /**
1452         * 
1453         * @return SharedResources
1454         */
1455        public SharedResources getSharedResources()
1456        {
1457                return sharedResources;
1458        }
1459
1460        protected ResourceBundles newResourceBundles(final ResourceReferenceRegistry registry)
1461        {
1462                return new ResourceBundles(registry);
1463        }
1464
1465        /**
1466         * @return The registry for resource bundles
1467         */
1468        public ResourceBundles getResourceBundles()
1469        {
1470                return resourceBundles;
1471        }
1472
1473        /**
1474         * Override to create custom {@link IPageFactory}
1475         * 
1476         * @return new {@link IPageFactory} instance.
1477         */
1478        protected IPageFactory newPageFactory()
1479        {
1480                return new DefaultPageFactory();
1481        }
1482
1483        /**
1484         * Returns {@link IPageFactory} for this application.
1485         * 
1486         * @return page factory
1487         */
1488        public final IPageFactory getPageFactory()
1489        {
1490                return pageFactory;
1491        }
1492
1493        /**
1494         * 
1495         * @return mapper context
1496         */
1497        public final IMapperContext getMapperContext()
1498        {
1499                if (encoderContext == null)
1500                {
1501                        encoderContext = newMapperContext();
1502                }
1503                return encoderContext;
1504        }
1505
1506        /**
1507         * Factory method for {@link IMapperContext} implementations. {@link DefaultMapperContext} may
1508         * be a good starting point for custom implementations.
1509         * 
1510         * @return new instance of mapper context to be used in the application
1511         */
1512        protected IMapperContext newMapperContext()
1513        {
1514                return new DefaultMapperContext(this);
1515        }
1516
1517        /**
1518         * 
1519         * @param requestCycle
1520         * @return Session
1521         */
1522        public Session fetchCreateAndSetSession(final RequestCycle requestCycle)
1523        {
1524                Args.notNull(requestCycle, "requestCycle");
1525
1526                Session session = getSessionStore().lookup(requestCycle.getRequest());
1527                if (session == null)
1528                {
1529                        session = newSession(requestCycle.getRequest(), requestCycle.getResponse());
1530                        ThreadContext.setSession(session);
1531                        internalGetPageManager().clear();
1532                        sessionListeners.onCreated(session);
1533                }
1534                else
1535                {
1536                        ThreadContext.setSession(session);
1537                }
1538                return session;
1539        }
1540
1541        /**
1542         * 
1543         * @return RequestCycleProvider
1544         */
1545        public final IRequestCycleProvider getRequestCycleProvider()
1546        {
1547                return requestCycleProvider;
1548        }
1549
1550        /**
1551         * 
1552         * @param requestCycleProvider
1553         */
1554        public final Application setRequestCycleProvider(final IRequestCycleProvider requestCycleProvider)
1555        {
1556                this.requestCycleProvider = requestCycleProvider;
1557                return this;
1558        }
1559
1560        /**
1561         * 
1562         * @param request
1563         * @param response
1564         * @return request cycle
1565         */
1566        public final RequestCycle createRequestCycle(final Request request, final Response response)
1567        {
1568                RequestCycleContext context = new RequestCycleContext(request, response,
1569                        getRootRequestMapper(), getExceptionMapperProvider().get());
1570
1571                RequestCycle requestCycle = getRequestCycleProvider().apply(context);
1572                requestCycle.getListeners().add(requestCycleListeners);
1573                requestCycle.getListeners().add(new IRequestCycleListener()
1574                {
1575                        @Override
1576                        public void onEndRequest(RequestCycle cycle)
1577                        {
1578                                internalGetPageManager().end();
1579                        }
1580                        
1581                        @Override
1582                        public void onDetach(final RequestCycle requestCycle)
1583                        {
1584                                internalGetPageManager().detach();
1585
1586                                IRequestLogger requestLogger = getRequestLogger();
1587                                if (requestLogger != null)
1588                                {
1589                                        requestLogger.requestTime((System.currentTimeMillis() - requestCycle.getStartTime()));
1590                                }
1591                        }
1592                });
1593                return requestCycle;
1594        }
1595
1596        /**
1597         * Sets an {@link IHeaderResponseDecorator} that you want your application to use to decorate
1598         * header responses.
1599         * <p>
1600         * Calling this method replaces the default decorator, which utilizes a
1601         * {@link ResourceAggregator}: The given implementation should make sure, that it too wraps
1602         * responses in a {@link ResourceAggregator}, otherwise no dependencies for {@link HeaderItem}s
1603         * will be resolved.
1604         * 
1605         * @param headerResponseDecorator
1606         *            your custom decorator, must not be null
1607         * @deprecated use {@code add(...)} on {@link #getHeaderResponseDecorators()}. This method
1608         *             removes the {@link ResourceAggregator}, which is needed to resolve resource
1609         *             dependencies.
1610         */
1611        @Deprecated
1612        public final Application
1613                        setHeaderResponseDecorator(final IHeaderResponseDecorator headerResponseDecorator)
1614        {
1615                headerResponseDecorators.replaceAll(headerResponseDecorator);
1616                return this;
1617        }
1618
1619        /**
1620         * Returns the {@link HeaderResponseDecoratorCollection} used by this application. On this
1621         * collection you can add additional decorators, which will be nested in the order added.
1622         * 
1623         * @return The {@link HeaderResponseDecoratorCollection} used by this application.
1624         */
1625        public HeaderResponseDecoratorCollection getHeaderResponseDecorators()
1626        {
1627                return headerResponseDecorators;
1628        }
1629        
1630        /**
1631         * INTERNAL METHOD - You shouldn't need to call this. This is called every time Wicket creates
1632         * an IHeaderResponse. It gives you the ability to incrementally add features to an
1633         * IHeaderResponse implementation by wrapping it in another implementation.
1634         * 
1635         * To decorate an IHeaderResponse in your application, set the {@link IHeaderResponseDecorator}
1636         * on the application.
1637         * 
1638         * @see IHeaderResponseDecorator
1639         * @param response
1640         *            the response Wicket created
1641         * @return the response Wicket should use in IHeaderContributor traversal
1642         */
1643        public final IHeaderResponse decorateHeaderResponse(final IHeaderResponse response)
1644        {
1645                return headerResponseDecorators.decorate(response);
1646        }
1647
1648        /**
1649         * 
1650         * @return true, of app is in Development mode
1651         */
1652        public final boolean usesDevelopmentConfig()
1653        {
1654                return RuntimeConfigurationType.DEVELOPMENT.equals(getConfigurationType());
1655        }
1656
1657        /**
1658         * 
1659         * @return true, of app is in Deployment mode
1660         */
1661        public final boolean usesDeploymentConfig()
1662        {
1663                return RuntimeConfigurationType.DEPLOYMENT.equals(getConfigurationType());
1664        }
1665}