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