0001: /*
0002: * Licensed to the Apache Software Foundation (ASF) under one or more
0003: * contributor license agreements. See the NOTICE file distributed with
0004: * this work for additional information regarding copyright ownership.
0005: * The ASF licenses this file to You under the Apache License, Version 2.0
0006: * (the "License"); you may not use this file except in compliance with
0007: * the License. You may obtain a copy of the License at
0008: *
0009: * http://www.apache.org/licenses/LICENSE-2.0
0010: *
0011: * Unless required by applicable law or agreed to in writing, software
0012: * distributed under the License is distributed on an "AS IS" BASIS,
0013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0014: * See the License for the specific language governing permissions and
0015: * limitations under the License.
0016: */
0017: package org.apache.wicket;
0018:
0019: import java.io.IOException;
0020: import java.io.InputStream;
0021: import java.io.Serializable;
0022: import java.net.URL;
0023: import java.util.ArrayList;
0024: import java.util.Collections;
0025: import java.util.Enumeration;
0026: import java.util.HashMap;
0027: import java.util.Iterator;
0028: import java.util.List;
0029: import java.util.Map;
0030: import java.util.Properties;
0031: import java.util.Set;
0032:
0033: import org.apache.wicket.application.IComponentInstantiationListener;
0034: import org.apache.wicket.application.IComponentOnAfterRenderListener;
0035: import org.apache.wicket.application.IComponentOnBeforeRenderListener;
0036: import org.apache.wicket.markup.IMarkupCache;
0037: import org.apache.wicket.markup.html.IHeaderContributor;
0038: import org.apache.wicket.markup.html.IHeaderResponse;
0039: import org.apache.wicket.markup.html.image.resource.DefaultButtonImageResourceFactory;
0040: import org.apache.wicket.markup.parser.filter.RelativePathPrefixHandler;
0041: import org.apache.wicket.markup.parser.filter.WicketMessageTagHandler;
0042: import org.apache.wicket.markup.resolver.AutoComponentResolver;
0043: import org.apache.wicket.markup.resolver.BodyContainerResolver;
0044: import org.apache.wicket.markup.resolver.BorderBodyResolver;
0045: import org.apache.wicket.markup.resolver.EnclosureResolver;
0046: import org.apache.wicket.markup.resolver.FragmentResolver;
0047: import org.apache.wicket.markup.resolver.HtmlHeaderResolver;
0048: import org.apache.wicket.markup.resolver.MarkupInheritanceResolver;
0049: import org.apache.wicket.markup.resolver.ParentResolver;
0050: import org.apache.wicket.markup.resolver.WicketContainerResolver;
0051: import org.apache.wicket.markup.resolver.WicketLinkResolver;
0052: import org.apache.wicket.markup.resolver.WicketMessageResolver;
0053: import org.apache.wicket.protocol.http.IRequestLogger;
0054: import org.apache.wicket.protocol.http.RequestLogger;
0055: import org.apache.wicket.session.ISessionStore;
0056: import org.apache.wicket.settings.IApplicationSettings;
0057: import org.apache.wicket.settings.IDebugSettings;
0058: import org.apache.wicket.settings.IExceptionSettings;
0059: import org.apache.wicket.settings.IFrameworkSettings;
0060: import org.apache.wicket.settings.IMarkupSettings;
0061: import org.apache.wicket.settings.IPageSettings;
0062: import org.apache.wicket.settings.IRequestCycleSettings;
0063: import org.apache.wicket.settings.IRequestLoggerSettings;
0064: import org.apache.wicket.settings.IResourceSettings;
0065: import org.apache.wicket.settings.ISecuritySettings;
0066: import org.apache.wicket.settings.ISessionSettings;
0067: import org.apache.wicket.settings.Settings;
0068: import org.apache.wicket.util.convert.ConverterLocator;
0069: import org.apache.wicket.util.lang.Classes;
0070: import org.apache.wicket.util.lang.Objects;
0071: import org.apache.wicket.util.lang.PropertyResolver;
0072: import org.apache.wicket.util.time.Duration;
0073: import org.slf4j.Logger;
0074: import org.slf4j.LoggerFactory;
0075:
0076: /**
0077: * Base class for all Wicket applications. To create a Wicket application, you
0078: * generally should <i>not </i> directly subclass this class. Instead, you will
0079: * want to subclass some subclass of Application, like WebApplication, which is
0080: * appropriate for the protocol and markup type you are working with.
0081: * <p>
0082: * Application has the following interesting features / attributes:
0083: * <ul>
0084: * <li><b>Name </b>- The Application's name, which is the same as its class
0085: * name.
0086: *
0087: * <li><b>Home Page </b>- The Application's home Page class. Subclasses must
0088: * override getHomePage() to provide this property value.
0089: *
0090: * <li><b>Settings </b>- Application settings are partitioned into sets of
0091: * related settings using interfaces in the org.apache.wicket.settings package.
0092: * These interfaces are returned by the following methods, which should be used
0093: * to configure framework settings for your application:
0094: * getApplicationSettings(), getDebugSettings(), getExceptionSettings(),
0095: * getMarkupSettings(), getPageSettings(), getRequestCycleSettings(),
0096: * getSecuritySettings and getSessionSettings(). These settings are configured
0097: * by default through the constructor or internalInit methods. Default the
0098: * application is configured for DEVELOPMENT. You can configure this globally to
0099: * DEPLOYMENT or override specific settings by implementing the init() method.
0100: *
0101: * <li><b>Shared Resources </b>- Resources added to an Application's
0102: * SharedResources have application-wide scope and can be referenced using a
0103: * logical scope and a name with the ResourceReference class. ResourceReferences
0104: * can then be used by multiple components in the same application without
0105: * additional overhead (beyond the ResourceReference instance held by each
0106: * referee) and will yield a stable URL, permitting efficient browser caching of
0107: * the resource (even if the resource is dynamically generated). Resources
0108: * shared in this manner may also be localized. See
0109: * {@link org.apache.wicket.ResourceReference} for more details.
0110: *
0111: * <li><b>Session Factory </b>- The Application subclass WebApplication
0112: * supplies an implementation of getSessionFactory() which returns an
0113: * implementation of ISessionFactory that creates WebSession Session objects
0114: * appropriate for web applications. You can (and probably will want to)
0115: * override getSessionFactory() to provide your own session factory that creates
0116: * Session instances of your own application-specific subclass of WebSession.
0117: *
0118: * </ul>
0119: *
0120: * @see org.apache.wicket.protocol.http.WebApplication
0121: * @author Jonathan Locke
0122: */
0123: public abstract class Application {
0124: /** Configuration constant for the 2 types */
0125: public static final String CONFIGURATION = "configuration";
0126:
0127: /**
0128: * Configuration type constant for getting the context path out of the
0129: * web.xml
0130: */
0131: public static final String CONTEXTPATH = "contextpath";
0132:
0133: /** Configuration type constant for deployment */
0134: public static final String DEPLOYMENT = "deployment";
0135:
0136: /** Configuration type constant for development */
0137: public static final String DEVELOPMENT = "development";
0138:
0139: /**
0140: * Applications keyed on the {@link #getApplicationKey()} so that they can
0141: * be retrieved even without being in a request/ being set in the thread
0142: * local (we need that e.g. for when we are in a destruction thread).
0143: */
0144: private static final Map applicationKeyToApplication = new HashMap(
0145: 1);
0146:
0147: /** Thread local holder of the application object. */
0148: private static final ThreadLocal current = new ThreadLocal();
0149:
0150: /** Log. */
0151: private static final Logger log = LoggerFactory
0152: .getLogger(Application.class);
0153:
0154: /**
0155: * Checks if the <code>Application</code> threadlocal is set in this
0156: * thread
0157: *
0158: * @return true if {@link Application#get()} can return the instance of
0159: * application, false otherwise
0160: */
0161: public static boolean exists() {
0162: return current.get() != null;
0163: }
0164:
0165: /**
0166: * Get Application for current thread.
0167: *
0168: * @return The current thread's Application
0169: */
0170: public static Application get() {
0171: final Application application = (Application) current.get();
0172: if (application == null) {
0173: throw new WicketRuntimeException(
0174: "There is no application attached to current thread "
0175: + Thread.currentThread().getName());
0176: }
0177: return application;
0178: }
0179:
0180: /**
0181: * Gets the Application based on the application key of that application.
0182: * You typically never have to use this method unless you are working on an
0183: * integration project.
0184: *
0185: * @param applicationKey
0186: * The unique key of the application within a certain context
0187: * (e.g. a web application)
0188: * @return The application
0189: * @throws IllegalArgumentException
0190: * When no application was found with the provided key
0191: */
0192: public static Application get(String applicationKey) {
0193: Application application = (Application) applicationKeyToApplication
0194: .get(applicationKey);
0195: return application;
0196: }
0197:
0198: /**
0199: * Gets the keys of the currently registered Wicket applications for this
0200: * web application. You typically never have to use this method unless you
0201: * are working on an integration project.
0202: *
0203: * @return unmodifiable set with keys that correspond with
0204: * {@link #getApplicationKey()}. Never null, but possibly empty
0205: */
0206: public static Set/* <String> */getApplicationKeys() {
0207: return Collections.unmodifiableSet(applicationKeyToApplication
0208: .keySet());
0209: }
0210:
0211: /**
0212: * THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT USE IT.
0213: *
0214: * @param application
0215: * The current application or null for this thread
0216: */
0217: public static void set(final Application application) {
0218: if (application == null) {
0219: throw new IllegalArgumentException(
0220: "Argument application can not be null");
0221: }
0222: current.set(application);
0223: }
0224:
0225: /**
0226: * THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT USE IT.
0227: */
0228: public static void unset() {
0229: current.set(null);
0230: }
0231:
0232: /** list of {@link IComponentInstantiationListener}s. */
0233: private IComponentInstantiationListener[] componentInstantiationListeners = new IComponentInstantiationListener[0];
0234:
0235: /** The converter locator instance. */
0236: private IConverterLocator converterLocator;
0237:
0238: /** list of initializers. */
0239: private final List initializers = new ArrayList();
0240:
0241: /** Application level meta data. */
0242: private MetaDataEntry[] metaData;
0243:
0244: /** Name of application subclass. */
0245: private final String name;
0246:
0247: /** Request logger instance. */
0248: private IRequestLogger requestLogger;
0249:
0250: /** The session facade. */
0251: private ISessionStore sessionStore;
0252:
0253: /** Settings for this application. */
0254: private Settings settings;
0255:
0256: /** can the settings object be set/used. */
0257: private boolean settingsAccessible;
0258:
0259: /** Shared resources for this application */
0260: private final SharedResources sharedResources;
0261:
0262: /**
0263: * Constructor. <strong>Use {@link #init()} for any configuration of your
0264: * application instead of overriding the constructor.</strong>
0265: */
0266: public Application() {
0267: // Create name from subclass
0268: this .name = Classes.simpleName(getClass());
0269:
0270: // Create shared resources repository
0271: this .sharedResources = new SharedResources(this );
0272:
0273: // Install default component instantiation listener that uses
0274: // authorization strategy to check component instantiations.
0275: addComponentInstantiationListener(new IComponentInstantiationListener() {
0276: /**
0277: * @see org.apache.wicket.application.IComponentInstantiationListener#onInstantiation(org.apache.wicket.Component)
0278: */
0279: public void onInstantiation(final Component component) {
0280: // If component instantiation is not authorized
0281: if (!Session
0282: .get()
0283: .getAuthorizationStrategy()
0284: .isInstantiationAuthorized(component.getClass())) {
0285: // then call any unauthorized component instantiation
0286: // listener
0287: getSecuritySettings()
0288: .getUnauthorizedComponentInstantiationListener()
0289: .onUnauthorizedInstantiation(component);
0290: }
0291: }
0292: });
0293: }
0294:
0295: /**
0296: * Adds a component instantiation listener. This method should typicaly only
0297: * be called during application startup; it is not thread safe.
0298: * <p>
0299: * Note: wicket does not guarantee the execution order of added listeners
0300: *
0301: * @param listener
0302: * the listener to add
0303: */
0304: public final void addComponentInstantiationListener(
0305: final IComponentInstantiationListener listener) {
0306: if (listener == null) {
0307: throw new IllegalArgumentException(
0308: "argument listener may not be null");
0309: }
0310:
0311: // if an instance of this listener is already present ignore this call
0312: for (int i = 0; i < componentInstantiationListeners.length; i++) {
0313: if (listener == componentInstantiationListeners[i]) {
0314: return;
0315: }
0316: }
0317:
0318: final IComponentInstantiationListener[] newListeners = new IComponentInstantiationListener[componentInstantiationListeners.length + 1];
0319: System
0320: .arraycopy(componentInstantiationListeners, 0,
0321: newListeners, 0,
0322: componentInstantiationListeners.length);
0323: newListeners[componentInstantiationListeners.length] = listener;
0324: componentInstantiationListeners = newListeners;
0325: }
0326:
0327: /**
0328: * Configures application settings to good defaults.
0329: */
0330: public final void configure() {
0331: final String configurationType = getConfigurationType();
0332:
0333: // As long as this is public api the development and deployment mode
0334: // should counter act each other for all properties.
0335: if (DEVELOPMENT.equalsIgnoreCase(configurationType)) {
0336: getResourceSettings().setResourcePollFrequency(
0337: Duration.ONE_SECOND);
0338: getDebugSettings().setComponentUseCheck(true);
0339: getMarkupSettings().setStripWicketTags(false);
0340: getExceptionSettings().setUnexpectedExceptionDisplay(
0341: IExceptionSettings.SHOW_EXCEPTION_PAGE);
0342: getDebugSettings().setAjaxDebugModeEnabled(true);
0343: getResourceSettings()
0344: .setStripJavascriptCommentsAndWhitespace(false);
0345: } else if (DEPLOYMENT.equalsIgnoreCase(configurationType)) {
0346: getResourceSettings().setResourcePollFrequency(null);
0347: getDebugSettings().setComponentUseCheck(false);
0348: getMarkupSettings().setStripWicketTags(true);
0349: getExceptionSettings().setUnexpectedExceptionDisplay(
0350: IExceptionSettings.SHOW_INTERNAL_ERROR_PAGE);
0351: getDebugSettings().setAjaxDebugModeEnabled(false);
0352: getResourceSettings()
0353: .setStripJavascriptCommentsAndWhitespace(true);
0354: } else {
0355: throw new IllegalArgumentException(
0356: "Invalid configuration type: '"
0357: + configurationType
0358: + "'. Must be \"development\" or \"deployment\".");
0359: }
0360: }
0361:
0362: /**
0363: * Gets the unique key of this application within a given context (like a
0364: * web application). NOT INTENDED FOR FRAMEWORK CLIENTS.
0365: *
0366: * @return The unique key of this application
0367: */
0368: public abstract String getApplicationKey();
0369:
0370: /**
0371: * @return Application's application-wide settings
0372: * @see IApplicationSettings
0373: * @since 1.2
0374: */
0375: public IApplicationSettings getApplicationSettings() {
0376: return getSettings();
0377: }
0378:
0379: /**
0380: * Gets the configuration mode to use for configuring the app, either
0381: * {@link #DEVELOPMENT} or {@link #DEPLOYMENT}.
0382: * <p>
0383: * The configuration type. Must currently be either DEVELOPMENT or
0384: * DEPLOYMENT. Currently, if the configuration type is DEVELOPMENT,
0385: * resources are polled for changes, component usage is checked, wicket tags
0386: * are not stripped from ouput and a detailed exception page is used. If the
0387: * type is DEPLOYMENT, component usage is not checked, wicket tags are
0388: * stripped from output and a non-detailed exception page is used to display
0389: * errors.
0390: * <p>
0391: * Note that you should not run Wicket in DEVELOPMENT mode on production
0392: * servers - the various debugging checks and resource polling is
0393: * inefficient and may leak resources, particularly on webapp redeploy.
0394: *
0395: * @return configuration
0396: * @since 1.2.3 (function existed as a property getter)
0397: * @since 1.3.0 (abstract, used to configure things)
0398: */
0399: public abstract String getConfigurationType();
0400:
0401: /**
0402: * @return The converter locator for this application
0403: */
0404: public final IConverterLocator getConverterLocator() {
0405: return converterLocator;
0406: }
0407:
0408: /**
0409: * @return Application's debug related settings
0410: * @see IDebugSettings
0411: * @since 1.2
0412: */
0413: public IDebugSettings getDebugSettings() {
0414: return getSettings();
0415: }
0416:
0417: /**
0418: * @return Application's exception handling settings
0419: * @see IExceptionSettings
0420: * @since 1.2
0421: */
0422: public IExceptionSettings getExceptionSettings() {
0423: return getSettings();
0424: }
0425:
0426: /**
0427: * @return Wicket framework settings
0428: * @see IFrameworkSettings
0429: * @since 1.2
0430: */
0431: public IFrameworkSettings getFrameworkSettings() {
0432: return getSettings();
0433: }
0434:
0435: /**
0436: * Application subclasses must specify a home page class by implementing
0437: * this abstract method.
0438: *
0439: * @return Home page class for this application
0440: */
0441: public abstract Class getHomePage();
0442:
0443: /**
0444: * THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT USE IT.
0445: *
0446: * @return The markup cache associated with the application
0447: * @deprecated please use {@link IMarkupSettings#getMarkupCache()} instead
0448: */
0449: public final IMarkupCache getMarkupCache() {
0450: return getMarkupSettings().getMarkupCache();
0451: }
0452:
0453: /**
0454: * @return Application's markup related settings
0455: * @see IMarkupSettings
0456: * @since 1.2
0457: */
0458: public IMarkupSettings getMarkupSettings() {
0459: return getSettings();
0460: }
0461:
0462: /**
0463: * Gets metadata for this application using the given key.
0464: *
0465: * @param key
0466: * The key for the data
0467: * @return The metadata
0468: * @see MetaDataKey
0469: */
0470: public final Serializable getMetaData(final MetaDataKey key) {
0471: return key.get(metaData);
0472: }
0473:
0474: /**
0475: * Gets the name of this application.
0476: *
0477: * @return The application name.
0478: */
0479: public final String getName() {
0480: return name;
0481: }
0482:
0483: /**
0484: * @return Application's page related settings
0485: * @see IPageSettings
0486: * @since 1.2
0487: */
0488: public IPageSettings getPageSettings() {
0489: return getSettings();
0490: }
0491:
0492: /**
0493: * @return Application's request cycle related settings
0494: * @see IDebugSettings
0495: * @since 1.2
0496: */
0497: public IRequestCycleSettings getRequestCycleSettings() {
0498: return getSettings();
0499: }
0500:
0501: /**
0502: * Gets the {@link RequestLogger}.
0503: *
0504: * @return The RequestLogger
0505: */
0506: public final IRequestLogger getRequestLogger() {
0507: if (getRequestLoggerSettings().isRequestLoggerEnabled()) {
0508: if (requestLogger == null) {
0509: requestLogger = newRequestLogger();
0510: }
0511: } else {
0512: requestLogger = null;
0513: }
0514: return requestLogger;
0515: }
0516:
0517: /**
0518: * @return Application's resources related settings
0519: * @see IResourceSettings
0520: * @since 1.3
0521: */
0522: public IRequestLoggerSettings getRequestLoggerSettings() {
0523: return getSettings();
0524: }
0525:
0526: /**
0527: * @return Application's resources related settings
0528: * @see IResourceSettings
0529: * @since 1.2
0530: */
0531: public IResourceSettings getResourceSettings() {
0532: return getSettings();
0533: }
0534:
0535: /**
0536: * @return Application's security related settings
0537: * @see ISecuritySettings
0538: * @since 1.2
0539: */
0540: public ISecuritySettings getSecuritySettings() {
0541: return getSettings();
0542: }
0543:
0544: /**
0545: * @return Application's session related settings
0546: * @see ISessionSettings
0547: * @since 1.2
0548: */
0549: public ISessionSettings getSessionSettings() {
0550: return getSettings();
0551: }
0552:
0553: /**
0554: * Gets the facade object for working getting/ storing session instances.
0555: *
0556: * @return The session facade
0557: */
0558: public final ISessionStore getSessionStore() {
0559: return sessionStore;
0560: }
0561:
0562: /**
0563: * Gets the shared resources.
0564: *
0565: * @return The SharedResources for this application.
0566: */
0567: public final SharedResources getSharedResources() {
0568: return sharedResources;
0569: }
0570:
0571: /**
0572: * THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT CALL.
0573: *
0574: * Initializes wicket components.
0575: */
0576: public final void initializeComponents() {
0577: // Load any wicket properties files we can find
0578: try {
0579: // Load properties files used by all libraries
0580: final Enumeration resources = Thread.currentThread()
0581: .getContextClassLoader().getResources(
0582: "wicket.properties");
0583: while (resources.hasMoreElements()) {
0584: InputStream in = null;
0585: try {
0586: final URL url = (URL) resources.nextElement();
0587: final Properties properties = new Properties();
0588: in = url.openStream();
0589: properties.load(in);
0590: load(properties);
0591: } finally {
0592: if (in != null) {
0593: in.close();
0594: }
0595: }
0596: }
0597: } catch (IOException e) {
0598: throw new WicketRuntimeException(
0599: "Unable to load initializers file", e);
0600: }
0601:
0602: // now call any initializers we read
0603: callInitializers();
0604: }
0605:
0606: /**
0607: * THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT CALL.
0608: *
0609: * @param target
0610: */
0611: public void logEventTarget(IRequestTarget target) {
0612: }
0613:
0614: /**
0615: * THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT CALL.
0616: *
0617: * @param requestTarget
0618: */
0619: public void logResponseTarget(IRequestTarget requestTarget) {
0620: }
0621:
0622: /**
0623: * Creates a new RequestCycle object. Override this method if you want to
0624: * provide a custom request cycle.
0625: *
0626: * @param application
0627: * The application
0628: * @param request
0629: * The request
0630: * @param response
0631: * The response
0632: * @return The request cycle
0633: *
0634: * @since 1.3
0635: */
0636: public abstract RequestCycle newRequestCycle(final Request request,
0637: final Response response);
0638:
0639: /**
0640: * FOR DEPRECATION ONLY.
0641: *
0642: * @param application
0643: * @param request
0644: * @param response
0645: * @return nothing
0646: * @throws UnsupportedOperationException
0647: * always
0648: * @deprecated interface {@link IRequestCycleFactory} will be removed in the
0649: * next release. applications wishing to provide custom request
0650: * cycles should override method
0651: * {@link #newRequestCycle(Request, Response)}
0652: */
0653: public final RequestCycle newRequestCycle(Application application,
0654: Request request, Response response) {
0655: throw new UnsupportedOperationException();
0656: }
0657:
0658: /**
0659: * Creates a new session. Override this method if you want to provide a
0660: * custom session.
0661: *
0662: * @param request
0663: * The request that will create this session.
0664: * @param response
0665: * The response to initialize, for example with cookies. This is
0666: * important to use cases involving unit testing because those
0667: * use cases might want to be able to sign a user in
0668: * automatically when the session is created.
0669: *
0670: * @return The session
0671: *
0672: * @since 1.3
0673: */
0674: public abstract Session newSession(Request request,
0675: Response response);
0676:
0677: /**
0678: * Removes a component instantiation listener. This method should typicaly
0679: * only be called during application startup; it is not thread safe.
0680: *
0681: * @param listener
0682: * the listener to remove
0683: */
0684: public final void removeComponentInstantiationListener(
0685: final IComponentInstantiationListener listener) {
0686: final IComponentInstantiationListener[] listeners = componentInstantiationListeners;
0687: final int len = listeners.length;
0688:
0689: if (listener != null && len > 0) {
0690: int pos = 0;
0691:
0692: for (pos = 0; pos < len; pos++) {
0693: if (listener == listeners[pos]) {
0694: break;
0695: }
0696: }
0697:
0698: if (pos < len) {
0699: listeners[pos] = listeners[len - 1];
0700: final IComponentInstantiationListener[] newListeners = new IComponentInstantiationListener[len - 1];
0701: System.arraycopy(listeners, 0, newListeners, 0,
0702: newListeners.length);
0703:
0704: componentInstantiationListeners = newListeners;
0705: }
0706: }
0707: }
0708:
0709: /**
0710: * Sets the metadata for this application using the given key. If the
0711: * metadata object is not of the correct type for the metadata key, an
0712: * IllegalArgumentException will be thrown. For information on creating
0713: * MetaDataKeys, see {@link MetaDataKey}.
0714: *
0715: * @param key
0716: * The singleton key for the metadata
0717: * @param object
0718: * The metadata object
0719: * @throws IllegalArgumentException
0720: * @see MetaDataKey
0721: */
0722: public final synchronized void setMetaData(final MetaDataKey key,
0723: final Serializable object) {
0724: metaData = key.set(metaData, object);
0725: }
0726:
0727: /**
0728: * Construct and add initializer from the provided class name.
0729: *
0730: * @param className
0731: */
0732: private final void addInitializer(String className) {
0733: IInitializer initializer = (IInitializer) Objects
0734: .newInstance(className);
0735: if (initializer != null) {
0736: initializers.add(initializer);
0737: }
0738: }
0739:
0740: /**
0741: * @param properties
0742: * Properties map with names of any library destroyers in it
0743: */
0744: private final void callDestroyers() {
0745: for (Iterator i = initializers.iterator(); i.hasNext();) {
0746: IInitializer initializer = (IInitializer) i.next();
0747: if (initializer instanceof IDestroyer) {
0748: log.info("[" + getName() + "] destroy: " + initializer);
0749: ((IDestroyer) initializer).destroy(this );
0750: }
0751: }
0752: }
0753:
0754: /**
0755: * @param properties
0756: * Properties map with names of any library destroyers in it
0757: */
0758: private final void callInitializers() {
0759: for (Iterator i = initializers.iterator(); i.hasNext();) {
0760: IInitializer initializer = (IInitializer) i.next();
0761: log.info("[" + getName() + "] init: " + initializer);
0762: initializer.init(this );
0763: }
0764: }
0765:
0766: /**
0767: * This method is still here for backwards compatibility with 1.1 source
0768: * code. The getXXXSettings() methods are now preferred. This method will be
0769: * removed post 1.2 version.
0770: *
0771: * @return Application settings
0772: *
0773: * @see Application#getApplicationSettings()
0774: * @see Application#getDebugSettings()
0775: * @see Application#getExceptionSettings()
0776: * @see Application#getMarkupSettings()
0777: * @see Application#getPageSettings()
0778: * @see Application#getRequestCycleSettings()
0779: * @see Application#getResourceSettings()
0780: * @see Application#getSecuritySettings()
0781: * @see Application#getSessionSettings()
0782: */
0783: private Settings getSettings() {
0784: if (!settingsAccessible) {
0785: throw new WicketRuntimeException(
0786: "Use Application.init() method for configuring your application object");
0787: }
0788:
0789: if (settings == null) {
0790: settings = new Settings(this );
0791: }
0792: return settings;
0793: }
0794:
0795: /**
0796: * @param properties
0797: * Properties map with names of any library initializers in it
0798: */
0799: private final void load(final Properties properties) {
0800: addInitializer(properties.getProperty("initializer"));
0801: addInitializer(properties.getProperty(getName()
0802: + "-initializer"));
0803: }
0804:
0805: /**
0806: * Called when wicket servlet is destroyed. Overrides do not have to call
0807: * super.
0808: *
0809: * @deprecated use {@link #onDestroy()} instead
0810: */
0811: // TODO remove after deprecation release
0812: protected final void destroy() {
0813: }
0814:
0815: /**
0816: * Called when wicket servlet is destroyed. Overrides do not have to call
0817: * super.
0818: */
0819: protected void onDestroy() {
0820: }
0821:
0822: /**
0823: * @return Request cycle factory for this kind of session.
0824: * @deprecated replaced by {@link #newRequestCycle(Request, Response)}
0825: */
0826: // TODO remove after deprecation release
0827: protected final Object getRequestCycleFactory() {
0828: throw new UnsupportedOperationException();
0829: }
0830:
0831: /**
0832: * Gets the factory for creating session instances.
0833: *
0834: * @return Factory for creating session instances
0835: * @deprecated replaced by {@link #newSession(Request, Response)}
0836: */
0837: // TODO remove after deprecation release
0838: protected final Object getSessionFactory() {
0839: throw new UnsupportedOperationException();
0840: }
0841:
0842: /**
0843: * Allows for initialization of the application by a subclass. <strong>Use
0844: * this method for any application setup instead of the constructor.</strong>
0845: */
0846: protected void init() {
0847: }
0848:
0849: /**
0850: * THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT CALL IT.
0851: */
0852: protected void internalDestroy() {
0853: // Clear caches of Class keys so the classloader can be garbage
0854: // collected (WICKET-625)
0855: PropertyResolver.destroy(this );
0856: getMarkupSettings().getMarkupCache().shutdown();
0857:
0858: onDestroy();
0859: callDestroyers();
0860: applicationKeyToApplication.remove(getApplicationKey());
0861: Session.unset();
0862: RequestCycle.set(null);
0863: }
0864:
0865: /**
0866: * THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT OVERRIDE OR
0867: * CALL.
0868: *
0869: * Internal initialization.
0870: */
0871: protected void internalInit() {
0872: settingsAccessible = true;
0873: IPageSettings pageSettings = getPageSettings();
0874:
0875: // Install default component resolvers
0876: pageSettings.addComponentResolver(new ParentResolver());
0877: pageSettings.addComponentResolver(new AutoComponentResolver());
0878: pageSettings
0879: .addComponentResolver(new MarkupInheritanceResolver());
0880: pageSettings.addComponentResolver(new HtmlHeaderResolver());
0881: pageSettings.addComponentResolver(new WicketLinkResolver());
0882: pageSettings.addComponentResolver(new WicketMessageResolver());
0883: pageSettings
0884: .addComponentResolver(new WicketMessageTagHandler());
0885: pageSettings.addComponentResolver(new FragmentResolver());
0886: pageSettings
0887: .addComponentResolver(new RelativePathPrefixHandler());
0888: pageSettings.addComponentResolver(new EnclosureResolver());
0889: pageSettings
0890: .addComponentResolver(new WicketContainerResolver());
0891: pageSettings.addComponentResolver(new BodyContainerResolver());
0892: pageSettings.addComponentResolver(new BorderBodyResolver());
0893:
0894: // Install button image resource factory
0895: getResourceSettings().addResourceFactory("buttonFactory",
0896: new DefaultButtonImageResourceFactory());
0897:
0898: String applicationKey = getApplicationKey();
0899: applicationKeyToApplication.put(applicationKey, this );
0900:
0901: sessionStore = newSessionStore();
0902: converterLocator = newConverterLocator();
0903: }
0904:
0905: /**
0906: * Creates and returns a new instance of {@link IConverterLocator}.
0907: *
0908: * @return A new {@link IConverterLocator} instance
0909: */
0910: protected IConverterLocator newConverterLocator() {
0911: return new ConverterLocator();
0912: }
0913:
0914: /**
0915: * creates a new request logger when requests logging is enabled.
0916: *
0917: * @return The new request logger
0918: *
0919: */
0920: protected IRequestLogger newRequestLogger() {
0921: return new RequestLogger();
0922: }
0923:
0924: /**
0925: * Creates a new session facade. Is called once per application, and is
0926: * typically not something clients reimplement.
0927: *
0928: * @return The session facade
0929: */
0930: protected abstract ISessionStore newSessionStore();
0931:
0932: /**
0933: * Notifies the registered component instantiation listeners of the
0934: * construction of the provided component
0935: *
0936: * @param component
0937: * the component that is being instantiated
0938: */
0939: final void notifyComponentInstantiationListeners(
0940: final Component component) {
0941: final int len = componentInstantiationListeners.length;
0942: for (int i = 0; i < len; i++) {
0943: componentInstantiationListeners[i]
0944: .onInstantiation(component);
0945: }
0946: }
0947:
0948: private List componentOnBeforeRenderListeners = null;
0949:
0950: /**
0951: * Adds an {@link IComponentOnBeforeRenderListener}. This method should
0952: * typicaly only be called during application startup; it is not thread
0953: * safe.
0954: *
0955: * @param listener
0956: */
0957: final public void addComponentOnBeforeRenderListener(
0958: IComponentOnBeforeRenderListener listener) {
0959: if (componentOnBeforeRenderListeners == null) {
0960: componentOnBeforeRenderListeners = new ArrayList();
0961: }
0962:
0963: if (componentOnBeforeRenderListeners.contains(listener) == false) {
0964: componentOnBeforeRenderListeners.add(listener);
0965: }
0966: }
0967:
0968: /**
0969: * Removes an {@link IComponentOnBeforeRenderListener}.
0970: *
0971: * @param listener
0972: */
0973: final public void removeComponentOnBeforeRenderListener(
0974: IComponentOnBeforeRenderListener listener) {
0975: if (componentOnBeforeRenderListeners != null) {
0976: componentOnBeforeRenderListeners.remove(listener);
0977: if (componentOnBeforeRenderListeners.isEmpty()) {
0978: componentOnBeforeRenderListeners = null;
0979: }
0980: }
0981: }
0982:
0983: /**
0984: * Notifies the {@link IComponentOnBeforeRenderListener}s.
0985: *
0986: * @param component
0987: */
0988: final void notifyComponentOnBeforeRenderListeners(
0989: Component component) {
0990: if (componentOnBeforeRenderListeners != null) {
0991: for (Iterator i = componentOnBeforeRenderListeners
0992: .iterator(); i.hasNext();) {
0993: IComponentOnBeforeRenderListener listener = (IComponentOnBeforeRenderListener) i
0994: .next();
0995: listener.onBeforeRender(component);
0996: }
0997: }
0998: }
0999:
1000: private List componentOnAfterRenderListeners = null;
1001:
1002: /**
1003: * Adds an {@link IComponentOnAfterRenderListener}. This method should
1004: * typicaly only be called during application startup; it is not thread
1005: * safe.
1006: *
1007: * @param listener
1008: */
1009: final public void addComponentOnAfterRenderListener(
1010: IComponentOnAfterRenderListener listener) {
1011: if (componentOnAfterRenderListeners == null) {
1012: componentOnAfterRenderListeners = new ArrayList();
1013: }
1014:
1015: if (componentOnAfterRenderListeners.contains(listener) == false) {
1016: componentOnAfterRenderListeners.add(listener);
1017: }
1018: }
1019:
1020: /**
1021: * Removes an {@link IComponentOnAfterRenderListener}.
1022: *
1023: * @param listener
1024: */
1025: final public void removeComponentOnAfterRenderListener(
1026: IComponentOnAfterRenderListener listener) {
1027: if (componentOnAfterRenderListeners != null) {
1028: componentOnAfterRenderListeners.remove(listener);
1029: if (componentOnAfterRenderListeners.isEmpty()) {
1030: componentOnAfterRenderListeners = null;
1031: }
1032: }
1033: }
1034:
1035: /**
1036: * Notifies the {@link IComponentOnAfterRenderListener}s.
1037: *
1038: * @param component
1039: */
1040: final void notifyComponentOnAfterRenderListeners(Component component) {
1041: if (componentOnAfterRenderListeners != null) {
1042: for (Iterator i = componentOnAfterRenderListeners
1043: .iterator(); i.hasNext();) {
1044: IComponentOnAfterRenderListener listener = (IComponentOnAfterRenderListener) i
1045: .next();
1046: listener.onAfterRender(component);
1047: }
1048: }
1049: }
1050:
1051: private List renderHeadListeners = null;
1052:
1053: /**
1054: * Adds a listener that will be invoked for every header response
1055: * @param listener
1056: */
1057: public final void addRenderHeadListener(IHeaderContributor listener) {
1058: if (renderHeadListeners == null) {
1059: renderHeadListeners = new ArrayList();
1060: }
1061: renderHeadListeners.add(listener);
1062: }
1063:
1064: /**
1065: *
1066: * @param listener
1067: */
1068: public void removeRenderHeadListener(IHeaderContributor listener) {
1069: if (renderHeadListeners != null) {
1070: renderHeadListeners.remove(listener);
1071: if (renderHeadListeners.isEmpty()) {
1072: renderHeadListeners = null;
1073: }
1074: }
1075: }
1076:
1077: /**
1078: * INTERNAL
1079: * @param response
1080: */
1081: public void notifyRenderHeadListener(IHeaderResponse response) {
1082: if (renderHeadListeners != null) {
1083: for (Iterator i = renderHeadListeners.iterator(); i
1084: .hasNext();) {
1085: IHeaderContributor listener = (IHeaderContributor) i
1086: .next();
1087: listener.renderHead(response);
1088: }
1089: }
1090: }
1091: }
|