Project

General

Profile

Feature #496 ยป WApplication.java

Thim Anneessens, 09/16/2010 10:10 PM

 
/*
* Copyright (C) 2009 Emweb bvba, Leuven, Belgium.
*
* See the LICENSE file for terms of use.
*/
package eu.webtoolkit.jwt;

import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import eu.webtoolkit.jwt.utils.MathUtils;

/**
* Represents an application instance for a single session
* <p>
*
* Each user session of your application has a corresponding WApplication
* instance. You need to create a new instance and return it as the result of
* {@link WtServlet#createApplication(WEnvironment)}. The instance is the main
* entry point to session information, and holds a reference to the
* {@link WApplication#getRoot() getRoot()} of the widget tree.
* <p>
* The recipe for a JWt web application, which allocates new
* {@link WApplication} instances for every user visiting the application is
* thus:
* <p>
* <blockquote>
*
* <pre>
* public class HelloServlet extends WtServlet {
* public HelloServlet() {
* super();
* }
*
* public WApplication createApplication(WEnvironment env) {
* // In practice, you will specialize WApplication and simply
* // return a new instance.
* WApplication app = new WApplication(env);
* app.getRoot().addWidget(new WText(&quot;Hello world.&quot;));
* return app;
* }
* }
* </pre>
*
* </blockquote>
* <p>
* Throughout the session, the instance is available through the static method
* {@link WApplication#getInstance() getInstance()}, which uses thread-specific
* storage to keep track of the current session. The application may be quited
* either using the method {@link WApplication#quit() quit()}, or because of a
* timeout after the user has closed the window, but not because the user does
* not interact: keep-alive messages in the background will keep the session
* around as long as the user has the page opened.
* <p>
* The WApplication object provides access to session-wide settings, including:
* <p>
* <ul>
* <li>circumstancial information through {@link WApplication#getEnvironment()
* getEnvironment()}, which gives details about the user, start-up arguments,
* and user agent capabilities.</li>
* <li>the application title with
* {@link WApplication#setTitle(CharSequence title) setTitle()}.</li>
* <li>inline and external style sheets using
* {@link WApplication#getStyleSheet() getStyleSheet()} and
* {@link WApplication#useStyleSheet(String uri) useStyleSheet()}.</li>
* <li>inline and external JavaScript using
* {@link WApplication#doJavaScript(String javascript, boolean afterLoaded)
* doJavaScript()} and {@link WApplication#require(String uri, String symbol)
* require()}.</li>
* <li>the top-level widget in {@link WApplication#getRoot() getRoot()},
* representing the entire browser window, or multiple top-level widgets using
* {@link WApplication#bindWidget(WWidget widget, String domId) bindWidget()}
* when deployed in WidgetSet mode to manage a number of widgets within a 3rd
* party page.</li>
* <li>definition of cookies using
* {@link WApplication#setCookie(String name, String value, int maxAge, String domain, String path)
* setCookie()} to persist information across sessions, which may be read using
* {@link WEnvironment#getCookie(String cookieNname) WEnvironment#getCookie()}
* in a future session.</li>
* <li>management of the internal path (that enables browser history and
* bookmarks) using
* {@link WApplication#setInternalPath(String path, boolean emitChange)
* setInternalPath()} and related methods.</li>
* <li>support for server-initiated updates with
* {@link WApplication#enableUpdates(boolean enabled) enableUpdates()}</li>
* </ul>
* <p>
* <ul>
* <li>localization information and message resources bundles, with
* {@link WApplication#setLocale(Locale locale) setLocale()} and
* {@link WApplication#setLocalizedStrings(WLocalizedStrings translator)
* setLocalizedStrings()}</li>
* </ul>
*/
public class WApplication extends WObject {
/**
* Enumeration that indicates the method for dynamic (AJAX-alike) updates.
* <p>
*
* @see WApplication#setAjaxMethod(WApplication.AjaxMethod method)
*/
public enum AjaxMethod {
/**
* Using the XMLHttpRequest object (real AJAX).
*/
XMLHttpRequest,
/**
* Using dynamic script tags (for cross-domain AJAX).
*/
DynamicScriptTag;

/**
* Returns the numerical representation of this enum.
*/
public int getValue() {
return ordinal();
}
}

/**
* Creates a new application instance.
* <p>
* The <code>environment</code> provides information on the initial request,
* user agent, and deployment-related information.
*/
public WApplication(WEnvironment env) {
super();
this.requestTooLarge_ = new Signal1<Integer>();
this.session_ = env.session_;
this.title_ = new WString();
this.titleChanged_ = false;
this.styleSheet_ = new WCssStyleSheet();
this.localizedStrings_ = null;
this.locale_ = new Locale("");
this.oldInternalPath_ = "";
this.newInternalPath_ = "";
this.internalPathChanged_ = new Signal1<String>(this);
this.serverPush_ = 0;
this.shouldTriggerUpdate_ = false;
this.javaScriptClass_ = "Wt";
this.dialogCover_ = null;
this.quited_ = false;
this.onePixelGifUrl_ = "";
this.rshLoaded_ = false;
this.exposedOnly_ = null;
this.loadingIndicator_ = null;
this.connected_ = true;
this.htmlClass_ = "";
this.bodyClass_ = "";
this.bodyHtmlClassChanged_ = false;
this.enableAjax_ = false;
this.scriptLibraries_ = new ArrayList<WApplication.ScriptLibrary>();
this.scriptLibrariesAdded_ = 0;
this.theme_ = "default";
this.styleSheets_ = new ArrayList<WApplication.StyleSheet>();
this.styleSheetsAdded_ = 0;
this.metaHeaders_ = new ArrayList<WApplication.MetaHeader>();
this.exposedSignals_ = new HashMap<String, WeakReference<AbstractEventSignal>>();
this.exposedResources_ = new HashMap<String, WResource>();
this.encodedObjects_ = new HashMap<String, WObject>();
this.exposeSignals_ = true;
this.afterLoadJavaScript_ = "";
this.beforeLoadJavaScript_ = "";
this.newBeforeLoadJavaScript_ = "";
this.autoJavaScript_ = "";
this.javaScriptLoaded_ = new HashSet<String>();
this.autoJavaScriptChanged_ = false;
this.soundManager_ = null;
this.session_.setApplication(this);
this.locale_ = this.getEnvironment().getLocale();
this.newInternalPath_ = this.getEnvironment().getInternalPath();
this.internalPathIsChanged_ = false;
this.setLocalizedStrings((WLocalizedStrings) null);
this.domRoot_ = new WContainerWidget();
this.domRoot_.setStyleClass("Wt-domRoot");
this.domRoot_.load();
if (this.session_.getType() == EntryPointType.Application) {
this.domRoot_.resize(WLength.Auto, new WLength(100,
WLength.Unit.Percentage));
}
this.timerRoot_ = new WContainerWidget(this.domRoot_);
this.timerRoot_.setId("Wt-timers");
this.timerRoot_.resize(WLength.Auto, new WLength(0));
this.timerRoot_.setPositionScheme(PositionScheme.Absolute);
if (this.session_.getType() == EntryPointType.Application) {
this.ajaxMethod_ = WApplication.AjaxMethod.XMLHttpRequest;
this.domRoot2_ = null;
this.widgetRoot_ = new WContainerWidget(this.domRoot_);
;
this.widgetRoot_.resize(new WLength(100, WLength.Unit.Percentage),
new WLength(100, WLength.Unit.Percentage));
} else {
this.ajaxMethod_ = WApplication.AjaxMethod.DynamicScriptTag;
this.domRoot2_ = new WContainerWidget();
this.domRoot2_.load();
this.widgetRoot_ = null;
}
this.styleSheet_.addRule("table",
"border-collapse: collapse; border: 0px");
this.styleSheet_.addRule("div, td, img",
"margin: 0px; padding: 0px; border: 0px");
this.styleSheet_
.addRule("td", "vertical-align: top; text-align: left;");
this.styleSheet_.addRule("button", "white-space: nowrap");
this.styleSheet_.addRule("video", "display: block");
if (this.getEnvironment().getContentType() == WEnvironment.ContentType.XHTML1) {
this.styleSheet_.addRule("button", "display: inline");
}
if (this.getEnvironment().agentIsGecko()) {
this.styleSheet_.addRule("html", "overflow: auto;");
}
this.styleSheet_.addRule("iframe.Wt-resource",
"width: 0px; height: 0px; border: 0px;");
if (this.getEnvironment().agentIsIE()) {
this.styleSheet_
.addRule(
"iframe.Wt-shim",
"position: absolute; top: -1px; left: -1px; z-index: -1;opacity: 0; filter: alpha(opacity=0);border: none; margin: 0; padding: 0;");
}
this.styleSheet_
.addRule(
".Wt-wrap",
"border: 0px;text-align: left;margin: 0px;padding: 0px;font-size: inherit; pointer: hand; cursor: pointer; cursor: hand;background: transparent;text-decoration: none;color: inherit;");
this.styleSheet_.addRule(".Wt-invalid", "background-color: #f79a9a;");
this.styleSheet_.addRule("span.Wt-disabled", "color: gray;");
this.styleSheet_.addRule("fieldset.Wt-disabled legend", "color: gray;");
this.styleSheet_
.addRule(".unselectable",
"-moz-user-select:-moz-none;-khtml-user-select: none;user-select: none;");
this.styleSheet_
.addRule(".selectable",
"-moz-user-select: text;-khtml-user-select: normal;user-select: text;");
this.styleSheet_
.addRule(".Wt-sbspacer",
"float: right; width: 16px; height: 1px;border: 0px; display: none;");
this.styleSheet_.addRule(".Wt-domRoot", "position: relative;");
this.styleSheet_
.addRule(
"body.Wt-layout",
""
+ "height: 100%; width: 100%;margin: 0px; padding: 0px; border: none;"
+ (this.getEnvironment().hasJavaScript() ? "overflow:hidden"
: ""));
this.styleSheet_
.addRule(
"html.Wt-layout",
""
+ "height: 100%; width: 100%;margin: 0px; padding: 0px; border: none;"
+ (this.getEnvironment().hasJavaScript() ? "overflow:hidden"
: ""));
if (this.getEnvironment().agentIsOpera()) {
if (this.getEnvironment().getUserAgent().indexOf("Mac OS X") != -1) {
this.styleSheet_.addRule("img.Wt-indeterminate",
"margin: 4px 1px -3px 2px;");
} else {
this.styleSheet_.addRule("img.Wt-indeterminate",
"margin: 4px 2px -3px 0px;");
}
} else {
if (this.getEnvironment().getUserAgent().indexOf("Mac OS X") != -1) {
this.styleSheet_.addRule("img.Wt-indeterminate",
"margin: 4px 3px 0px 4px;");
} else {
this.styleSheet_.addRule("img.Wt-indeterminate",
"margin: 3px 3px 0px 4px;");
}
}
this.showLoadingIndicator_ = new EventSignal("showload", this);
this.hideLoadingIndicator_ = new EventSignal("hideload", this);
this.setLoadingIndicator(new WDefaultLoadingIndicator());
}

/**
* Returns the current application instance.
* <p>
* This method uses thread-specific storage to fetch the current session.
*/
public static WApplication getInstance() {
WebSession session = WebSession.getInstance();
return session != null ? session.getApp() : null;
}

/**
* Returns the environment information.
* <p>
* This method returns the environment object that was used when
* constructing the application. The environment provides information on the
* initial request, user agent, and deployment-related information.
* <p>
*
* @see WApplication#getUrl()
* @see WApplication#getSessionId()
*/
public WEnvironment getEnvironment() {
return this.session_.getEnv();
}

/**
* Returns the root container.
* <p>
* This is the top-level widget container of the application, and
* corresponds to entire browser window. The user interface of your
* application is represented by the content of this container.
* <p>
* The {@link WApplication#getRoot() getRoot()} widget is only defined when
* the application manages the entire window. When deployed as a
* {@link EntryPointType#WidgetSet WidgetSet} application, there is no
* root() container, and <code>null</code> is returned. Instead, use
* {@link WApplication#bindWidget(WWidget widget, String domId)
* bindWidget()} to bind one or more root widgets to existing HTML
* &lt;div&gt; (or other) elements on the page.
*/
public WContainerWidget getRoot() {
return this.widgetRoot_;
}

/**
* Returns a reference to the inline style sheet.
* <p>
* Widgets may allow configuration of their look and feel through style
* classes. These may be defined in this inline stylesheet, or in external
* style sheets.
* <p>
* It is usually preferable to use external stylesheets (and consider more
* accessible). Still, the internal stylesheet has as benefit that style
* rules may be dynamically updated, and it is easier to manage
* logistically.
* <p>
*
* @see WApplication#useStyleSheet(String uri)
* @see WWidget#setStyleClass(String styleClass)
*/
public WCssStyleSheet getStyleSheet() {
return this.styleSheet_;
}

/**
* Adds an external style sheet.
* <p>
* Widgets may allow configuration of their look and feel through style
* classes. These may be defined in an inline stylesheet, or in external
* style sheets.
* <p>
* The <code>url</code> indicates a relative or absolute URL to the
* stylesheet.
* <p>
* External stylesheets are inserted after the internal style sheet, and can
* therefore override default styles set by widgets in the internal style
* sheet.
* <p>
*
* @see WApplication#getStyleSheet()
* @see WWidget#setStyleClass(String styleClass)
*/
public void useStyleSheet(String uri) {
this.styleSheets_.add(new WApplication.StyleSheet(uri, ""));
++this.styleSheetsAdded_;
}

/**
* Adds an external style sheet, constrained with conditions.
* <p>
* If not empty, <code>condition</code> is a string that is used to apply
* the stylesheet to specific versions of IE. Only a limited subset of the
* IE conditional comments syntax is supported (since these are in fact
* interpreted server-side instead of client-side). Examples are:
* <p>
* <ul>
* <li>&quot;IE gte 6&quot;: only for IE version 6 or later.</li>
* <li>&quot;!IE gte 6&quot;: only for IE versions prior to IE6.</li>
* <li>&quot;IE lte 7&quot;: only for IE versions prior to IE7.</li>
* </ul>
* <p>
* The <code>media</code> indicates the CSS media to which this stylesheet
* applies. This may be a comma separated list of media. The default value
* is &quot;all&quot; indicating all media.
* <p>
* The <code>url</code> indicates a relative or absolute URL to the
* stylesheet.
* <p>
*
* @see WApplication#useStyleSheet(String uri)
*/
public void useStyleSheet(String uri, String condition, String media) {
boolean display = true;
if (condition.length() != 0) {
display = false;
if (this.getEnvironment().agentIsIE()) {
int thisVersion = 4;
switch (this.getEnvironment().getAgent()) {
case IEMobile:
thisVersion = 5;
break;
case IE6:
thisVersion = 6;
break;
default:
thisVersion = 7;
}
final int lte = 0;
final int lt = 1;
final int eq = 2;
final int gt = 3;
final int gte = 4;
int cond = eq;
boolean invert = false;
String r = condition;
while (r.length() != 0) {
if (r.length() >= 3 && r.substring(0, 0 + 3).equals("IE ")) {
r = r.substring(3);
} else {
if (r.charAt(0) == '!') {
r = r.substring(1);
invert = !invert;
} else {
if (r.length() >= 4
&& r.substring(0, 0 + 4).equals("lte ")) {
r = r.substring(4);
cond = lte;
} else {
if (r.length() >= 3
&& r.substring(0, 0 + 3).equals("lt ")) {
r = r.substring(3);
cond = lt;
} else {
if (r.length() >= 3
&& r.substring(0, 0 + 3).equals(
"gt ")) {
r = r.substring(3);
cond = gt;
} else {
if (r.length() >= 4
&& r.substring(0, 0 + 4)
.equals("gte ")) {
r = r.substring(4);
cond = gte;
} else {
try {
int version = Integer
.parseInt(r);
switch (cond) {
case eq:
display = thisVersion == version;
break;
case lte:
display = thisVersion <= version;
break;
case lt:
display = thisVersion < version;
break;
case gte:
display = thisVersion >= version;
break;
case gt:
display = thisVersion > version;
break;
}
if (invert) {
display = !display;
}
} catch (RuntimeException e) {
this
.log("error")
.append(
"Could not parse condition: '")
.append(condition)
.append("'");
}
r = "";
}
}
}
}
}
}
}
}
}
if (display) {
this.styleSheets_.add(new WApplication.StyleSheet(uri, media));
++this.styleSheetsAdded_;
}
}

/**
* Adds an external style sheet, constrained with conditions.
* <p>
* Calls {@link #useStyleSheet(String uri, String condition, String media)
* useStyleSheet(uri, condition, "all")}
*/
public final void useStyleSheet(String uri, String condition) {
useStyleSheet(uri, condition, "all");
}

/**
* Sets the theme.
* <p>
* The theme provides the look and feel of several built-in widgets, using
* CSS style rules. Rules for each theme are defined in the
* <code>resources/themes/</code><i>theme</i><code>/</code> folder.
* <p>
* The default theme is &quot;default&quot;. When setting &quot;&quot;, the
* external style sheets related to the theme are not loaded.
*/
public void setCssTheme(String theme) {
this.theme_ = theme;
}

/**
* Returns the theme.
* <p>
*
* @see WApplication#setCssTheme(String theme)
*/
public String getCssTheme() {
return this.theme_;
}

/**
* Sets a style class to the entire page &lt;body&gt;.
* <p>
*
* @see WApplication#setHtmlClass(String styleClass)
*/
public void setBodyClass(String styleClass) {
this.bodyClass_ = styleClass;
this.bodyHtmlClassChanged_ = true;
}

/**
* Returns the style class set for the entire page &lt;body&gt;.
* <p>
*
* @see WApplication#setBodyClass(String styleClass)
*/
public String getBodyClass() {
return this.bodyClass_;
}

/**
* Sets a style class to the entire page &lt;html&gt;.
* <p>
*
* @see WApplication#setBodyClass(String styleClass)
*/
public void setHtmlClass(String styleClass) {
this.htmlClass_ = styleClass;
this.bodyHtmlClassChanged_ = true;
}

/**
* Returns the style class set for the entire page &lt;html&gt;.
* <p>
*
* @see WApplication#setHtmlClass(String styleClass)
*/
public String getHtmlClass() {
return this.htmlClass_;
}

/**
* Sets the window title.
* <p>
* Sets the browser window title to <code>title</code>.
* <p>
* The default title is &quot;&quot;.
* <p>
*
* @see WApplication#getTitle()
*/
public void setTitle(CharSequence title) {
if (this.getSession().getRenderer().isPreLearning()
|| !this.title_.equals(title)) {
this.title_ = WString.toWString(title);
this.titleChanged_ = true;
}
}

/**
* Returns the window title.
* <p>
*
* @see WApplication#setTitle(CharSequence title)
*/
public WString getTitle() {
return this.title_;
}

/**
* Returns the resource object that provides localized strings.
* <p>
* This returns the object previously set using
* {@link WApplication#setLocalizedStrings(WLocalizedStrings translator)
* setLocalizedStrings()}.
* <p>
* {@link WString#tr(String key) WString#tr()} is used to create localized
* strings, whose localized translation is looked up through this object,
* using a key.
* <p>
*
* @see WString#tr(String key)
*/
public WLocalizedStrings getLocalizedStrings() {
return this.localizedStrings_.getItems().get(0);
}

/**
* Sets the resource object that provides localized strings.
* <p>
* The <code>translator</code> resolves localized strings within the current
* application locale.
* <p>
*
* @see WApplication#getLocalizedStrings()
* @see WString#tr(String key)
*/
public void setLocalizedStrings(WLocalizedStrings translator) {
;
this.localizedStrings_ = new WCombinedLocalizedStrings();
if (translator != null) {
this.localizedStrings_.add(translator);
}
WStdLocalizedStrings defaultMessages = new WStdLocalizedStrings();
defaultMessages.useBuiltin(WtServlet.WtMessages_xml);
this.localizedStrings_.add(defaultMessages);
}

/**
* Changes the locale.
* <p>
* The locale is used by the localized strings resource to resolve localized
* strings.
* <p>
* By passing an empty <code>locale</code>, the default locale is chosen.
* <p>
* When the locale is changed, {@link WApplication#refresh() refresh()} is
* called, which will resolve the strings of the current user-interface in
* the new locale.
* <p>
* The default locale is copied from the environment (
* {@link WEnvironment#getLocale() WEnvironment#getLocale()}), and this is
* the locale that was configured by the user in his browser preferences,
* and passed using an HTTP request header.
* <p>
*
* @see WApplication#getLocalizedStrings()
* @see WString#tr(String key)
*/
public void setLocale(Locale locale) {
this.locale_ = locale;
this.refresh();
}

/**
* Returns the current locale.
* <p>
*/
public Locale getLocale() {
return this.locale_;
}

/**
* Refreshes the application.
* <p>
* This lets the application to refresh its data, including strings from
* message-resource bundles. This done by propagating
* {@link WWidget#refresh() WWidget#refresh()} through the widget hierarchy.
* <p>
* This method is also called when the user hits the refresh (or reload)
* button, if this can be caught within the current session.
* <p>
* The reload button may only be caught when cookies for session tracking
* are configured in the servlet container.
* <p>
*
* @see WWidget#refresh()
*/
public void refresh() {
if (this.localizedStrings_ != null) {
this.localizedStrings_.refresh();
}
if (this.domRoot2_ != null) {
this.domRoot2_.refresh();
} else {
this.widgetRoot_.refresh();
}
if (this.title_.refresh()) {
this.titleChanged_ = true;
}
}

/**
* Binds a top-level widget for a WidgetSet deployment.
* <p>
* This method binds a <code>widget</code> to an existing element with DOM
* id <code>domId</code> on the page. The element type should correspond
* with the widget type (e.g. it should be a &lt;div&gt; for a
* {@link WContainerWidget}, or a &lt;table&gt; for a {@link WTable}).
* <p>
*
* @see WApplication#getRoot()
* @see EntryPointType#WidgetSet
*/
public void bindWidget(WWidget widget, String domId) {
if (this.session_.getType() != EntryPointType.WidgetSet) {
throw new WtException(
"WApplication::bind() can be used only in WidgetSet mode.");
}
widget.setId(domId);
this.domRoot2_.addWidget(widget);
}

/**
* Returns a URL for the current session.
* <p>
* Returns the (relative) URL for this application session (including the
* session ID if necessary). The URL includes the full application path, and
* is expanded by the browser into a full URL.
* <p>
* For example, for an application deployed at <blockquote>
*
* <pre>
* http://www.mydomain.com/stuff/app.wt
* </pre>
*
* </blockquote> this method would return
* <code>&quot;/stuff/app.wt?wtd=AbCdEf&quot;</code>, when using URL
* rewriting for session-tracking or
* <code>&quot;/stuff/app.wt?a=a&quot;</code> when using cookies for
* session-tracking
* <p>
* . As in each case, a query is appended at the end of the URL, additional
* query parameters can be appended in the form of
* <code>&quot;&amp;param1=value&amp;param2=value&quot;</code>.
* <p>
* To obtain a URL that is suitable for bookmarking the current application
* state, to be used across sessions, use
* {@link WApplication#getBookmarkUrl() getBookmarkUrl()} instead.
* <p>
*
* @see WApplication#redirect(String url)
* @see WEnvironment#getHostName()
* @see WEnvironment#getUrlScheme()
* @see WApplication#getBookmarkUrl()
*/
public String getUrl() {
return this.fixRelativeUrl(this.session_.getApplicationUrl());
}

/**
* Returns a bookmarkable URL for the current internal path.
* <p>
* Is equivalent to
* <code>bookmarkUrl({@link WApplication#getInternalPath() getInternalPath()})</code>
* , see {@link WApplication#getBookmarkUrl(String internalPath)
* getBookmarkUrl()}.
* <p>
* To obtain a URL that is refers to the current session of the application,
* use {@link WApplication#getUrl() getUrl()} instead.
* <p>
*
* @see WApplication#getUrl()
* @see WApplication#getBookmarkUrl(String internalPath)
*/
public String getBookmarkUrl() {
return this.getBookmarkUrl(this.newInternalPath_);
}

/**
* Returns a bookmarkable URL for a given internal path.
* <p>
* Returns the (relative) URL for this application that includes the
* internal path <code>internalPath</code>, usable across sessions. The URL
* is relative and expanded into a full URL by the browser.
* <p>
* For example, for an application with current URL: <blockquote>
*
* <pre>
* http://www.mydomain.com/stuff/app.wt#/project/internal/
* </pre>
*
* </blockquote> when called with <code>&quot;/project/external&quot;</code>
* , this method would return:
* <ul>
* <li><code>&quot;app.wt/project/external/&quot;</code> when JavaScript is
* available, or the agent is a web spider, or</li>
* <li><code>&quot;app.wt/project/external/?wtd=AbCdEf&quot;</code> when no
* JavaScript is available and URL rewriting is used for session-tracking</li>
* </ul>
* <p>
* When the application is deployed at a folder (ending with &apos;/&apos;),
* this style of URLs is not possible, and URLs are of the form:
* <ul>
* <li><code>&quot;?_=/project/external/&quot;</code> when JavaScript is
* available, or the agent is a web spider, or</li>
* <li><code>&quot;?_=/project/external/&amp;wtd=AbCdEf&quot;</code> when no
* JavaScript is available and URL rewriting is used for session-tracking.</li>
* </ul>
* <p>
* You can use {@link WApplication#getBookmarkUrl() getBookmarkUrl()} as the
* destination for a {@link WAnchor}, and listen to a click event is
* attached to a slot that switches to the internal path
* <code>internalPath</code> (see
* {@link WAnchor#setRefInternalPath(String path)
* WAnchor#setRefInternalPath()}). In this way, an anchor can be used to
* switch between internal paths within an application regardless of the
* situation (browser with or without Ajax support, or a web spider bot),
* but still generates suitable URLs across sessions, which can be used for
* bookmarking, opening in a new window/tab, or indexing.
* <p>
* To obtain a URL that refers to the current session of the application,
* use {@link WApplication#getUrl() getUrl()} instead.
* <p>
*
* @see WApplication#getUrl()
* @see WApplication#getBookmarkUrl()
*/
public String getBookmarkUrl(String internalPath) {
if (!this.getEnvironment().hasJavaScript()) {
if (this.getEnvironment().agentIsSpiderBot()) {
return this.session_.getBookmarkUrl(internalPath);
} else {
return this.session_.getMostRelativeUrl(internalPath);
}
} else {
return this.session_.getBookmarkUrl(internalPath);
}
}

/**
* Change the internal path.
* <p>
* A JWt application may manage multiple virtual paths. The virtual path is
* appended to the application URL. Depending on the situation, the path is
* directly appended to the application URL or it is appended using a name
* anchor (#).
* <p>
* For example, for an application deployed at: <blockquote>
*
* <pre>
* http://www.mydomain.com/stuff/app.wt
* </pre>
*
* </blockquote> for which an <code>internalPath</code>
* <code>&quot;/project/z3cbc/details/&quot;</code> is set, the two forms
* for the application URL are:
* <ul>
* <li>
* in an AJAX session: <blockquote>
*
* <pre>
* http://www.mydomain.com/stuff/app.wt#/project/z3cbc/details/
* </pre>
*
* </blockquote></li>
* <li>
* in a plain HTML session: <blockquote>
*
* <pre>
* http://www.mydomain.com/stuff/app.wt/project/z3cbc/details/
* </pre>
*
* </blockquote> This has as major consequence that from the browser stand
* point, the application now serves many different URLs. As a consequence,
* relative URLs will break. Still, you can specify relative URLs within
* your application (in for example {@link WAnchor#setRef(String ref)
* WAnchor#setRef()} or {@link WImage#setImageRef(String ref)
* WImage#setImageRef()}) since JWt will transform them to absolute URLs
* when needed. But, this in turn may break deployments behind reverse
* proxies when the context paths differ. For the same reason, you will need
* to use absolute URLs in any XHTML or CSS you write manually. <br>
* This type of URLs are only used when the your application is deployed at
* a location that does not end with a &apos;/&apos;. Otherwise, JWt will
* generate URLS like: <blockquote>
*
* <pre>
* http://www.mydomain.com/stuff/?_=/project/z3cbc/details/
* </pre>
*
* </blockquote></li>
* </ul>
* <p>
* When the internal path is changed, an entry is added to the browser
* history. When the user navigates back and forward through this history
* (using the browser back/forward buttons), an
* {@link WApplication#internalPathChanged() internalPathChanged()} event is
* emitted. You should listen to this signal to switch the application to
* the corresponding state. When <code>emitChange</code> is
* <code>true</code>, this signal is also emitted by setting the path.
* <p>
* A url that includes the internal path may be obtained using
* {@link WApplication#getBookmarkUrl() getBookmarkUrl()}.
* <p>
* The <code>internalPath</code> must start with a &apos;/&apos;. In this
* way, you can still use normal anchors in your HTML. Internal path changes
* initiated in the browser to paths that do not start with a &apos;/&apos;
* are ignored.
* <p>
*
* @see WApplication#getBookmarkUrl()
* @see WApplication#getInternalPath()
* @see WApplication#internalPathChanged()
*/
public void setInternalPath(String path, boolean emitChange) {
this.isLoadRsh();
if (!this.internalPathIsChanged_) {
this.oldInternalPath_ = this.newInternalPath_;
}
if (!this.session_.getRenderer().isPreLearning() && emitChange) {
this.changeInternalPath(path);
} else {
this.newInternalPath_ = path;
}
this.internalPathIsChanged_ = true;
}

/**
* Change the internal path.
* <p>
* Calls {@link #setInternalPath(String path, boolean emitChange)
* setInternalPath(path, false)}
*/
public final void setInternalPath(String path) {
setInternalPath(path, false);
}

/**
* Returns the current internal path.
* <p>
* When the application is just created, this is equal to
* {@link WEnvironment#getInternalPath() WEnvironment#getInternalPath()}.
* <p>
*
* @see WApplication#setInternalPath(String path, boolean emitChange)
* @see WApplication#getInternalPathNextPart(String path)
* @see WApplication#internalPathMatches(String path)
*/
public String getInternalPath() {
return this.newInternalPath_;
}

/**
* Returns a part of the current internal path.
* <p>
* This is a convenience method which returns the next <code>folder</code>
* in the internal path, after the given <code>path</code>.
* <p>
* For example, when the current internal path is
* <code>&quot;/project/z3cbc/details&quot;</code>, this method returns
* <code>&quot;details&quot;</code> when called with
* <code>&quot;/project/z3cbc/&quot;</code> as <code>path</code> argument.
* <p>
* The <code>path</code> must start with a &apos;/&apos;, and
* {@link WApplication#internalPathMatches(String path)
* internalPathMatches()} should evaluate to <code>true</code> for the given
* <code>path</code>. If not, an empty string is returned and an error
* message is logged.
* <p>
*
* @see WApplication#getInternalPath()
* @see WApplication#internalPathChanged()
*/
public String getInternalPathNextPart(String path) {
String current = StringUtils.terminate(this.newInternalPath_, '/');
if (!pathMatches(current, path)) {
this.log("warn").append("WApplication::internalPath(): path '")
.append(path).append("' not within current path '").append(
this.newInternalPath_).append("'");
return "";
}
int startPos = path.length();
int t = current.indexOf('/', startPos);
String result = "";
if (t == -1) {
result = current.substring(startPos);
} else {
result = current.substring(startPos, startPos + t - startPos);
}
return result;
}

/**
* Checks if the internal path matches a given path.
* <p>
* Returns whether the current {@link WApplication#getInternalPath()
* getInternalPath()} starts with <code>path</code> (or is equal to
* <code>path</code>). You will typically use this method within a slot
* conneted to the {@link WApplication#internalPathChanged()
* internalPathChanged()} signal, to check that an internal path change
* affects the widget. It may also be useful before changing
* <code>path</code> using
* {@link WApplication#setInternalPath(String path, boolean emitChange)
* setInternalPath()} if you do not intend to remove sub paths when the
* current internal path already matches <code>path</code>.
* <p>
* The <code>path</code> must start with a &apos;/&apos;.
* <p>
*
* @see WApplication#setInternalPath(String path, boolean emitChange)
* @see WApplication#getInternalPath()
*/
public boolean internalPathMatches(String path) {
if (this.session_.getRenderer().isPreLearning()) {
return false;
} else {
return pathMatches(StringUtils
.terminate(this.newInternalPath_, '/'), path);
}
}

/**
* Signal which indicates that the user changes the internal path.
* <p>
* This signal indicates a change to the internal path, which is usually
* triggered by the user using the browser back/forward buttons.
* <p>
* The argument contains the new internal path.
* <p>
*
* @see WApplication#setInternalPath(String path, boolean emitChange)
*/
public Signal1<String> internalPathChanged() {
return this.internalPathChanged_;
}

/**
* Redirects the application to another location.
* <p>
* The client will be redirected to a new location identified by
* <code>url</code>. Use this in conjunction with
* {@link WApplication#quit() quit()} if you want to the application to be
* terminated as well.
* <p>
* Calling redirect() does not imply quit() since it may be useful to switch
* between a non-secure and secure (SSL) transport connection.
*/
public void redirect(String url) {
this.session_.redirect(url);
}

/**
* Returns the URL at which the resources are deployed.
*/
public static String getResourcesUrl() {
String path = WApplication.getInstance().session_.getController()
.getConfiguration().getProperty(WApplication.RESOURCES_URL);
if (path == "/wt-resources/") {
String result = WApplication.getInstance().getEnvironment()
.getDeploymentPath();
if (result.length() != 0
&& result.charAt(result.length() - 1) == '/') {
return result + path.substring(1);
} else {
return result + path;
}
} else {
return path;
}
}

/**
* Returns the unique identifier for the current session.
* <p>
* The session id is a string that uniquely identifies the current session.
* Note that the actual contents has no particular meaning and client
* applications should in no way try to interpret its value.
*/
public String getSessionId() {
return this.session_.getSessionId();
}

WebSession getSession() {
return this.session_;
}

/**
* Enables server-initiated updates.
* <p>
* By default, updates to the user interface are possible only at startup,
* during any event (in a slot), or at regular time points using
* {@link WTimer}. This is the normal JWt event loop.
* <p>
* In some cases, one may want to modify the user interface from a second
* thread, outside the event loop. While this may be worked around by the
* {@link WTimer}, in some cases, there are bandwidth and processing
* overheads associated which may be unnecessary, and which create a
* trade-off with time resolution of the updates.
* <p>
* When <code>enabled</code> is <code>true</code>, this enables &quot;server
* push&quot; (what is called &apos;comet&apos; in AJAX terminology).
* Widgets may then be modified, created or deleted outside of the event
* loop (e.g. in response to execution of another thread), and these changes
* are propagated by calling {@link WApplication#triggerUpdate()
* triggerUpdate()}.
* <p>
* Note that you need to grab the application&apos;s update lock to avoid
* concurrency problems, whenever you modify the application&apos;s state
* from another thread.
* <p>
* An example of how to modify the widget tree outside the event loop and
* propagate changes is:
* <p>
* <blockquote>
*
* <pre>
* // You need to have a reference to the application whose state
* // you are about to manipulate.
* WApplication app = ...;
*
* // Grab the application lock
* WApplication.UpdateLock lock = app.getUpdateLock();
*
* try {
* // We now have exclusive access to the application:
* // we can safely modify the widget tree for example.
* app.getRoot().addWidget(new WText(&quot;Something happened!&quot;));
*
* // Push the changes to the browser
* app.triggerUpdate();
* } finally {
* lock.release();
* }
* </pre>
*
* </blockquote>
* <p>
* <p>
* <i><b>Note: </b>This works only if JavaScript is available on the
* client.</i>
* </p>
*
* @see WApplication#triggerUpdate()
*/
public void enableUpdates(boolean enabled) {
if (enabled) {
++this.serverPush_;
} else {
--this.serverPush_;
}
if (enabled && this.serverPush_ == 1 || !enabled
&& this.serverPush_ == 0) {
this.doJavaScript(this.javaScriptClass_ + "._p_.setServerPush("
+ (enabled ? "true" : "false") + ");");
}
}

/**
* Enables server-initiated updates.
* <p>
* Calls {@link #enableUpdates(boolean enabled) enableUpdates(true)}
*/
public final void enableUpdates() {
enableUpdates(true);
}

/**
* Returns whether server-initiated updates are enabled.
* <p>
*
* @see WApplication#enableUpdates(boolean enabled)
*/
public boolean isUpdatesEnabled() {
return this.serverPush_ > 0;
}

/**
* Propagates server-initiated updates.
* <p>
* Propagate changes made to the user interface outside of the main event
* loop. This is only possible after a call to
* {@link WApplication#enableUpdates(boolean enabled) enableUpdates()}, and
* must be done while holding the {@link UpdateLock}.
* <p>
*
* @see WApplication#enableUpdates(boolean enabled)
* @see WApplication#getUpdateLock()
*/
public void triggerUpdate() {
if (!this.shouldTriggerUpdate_) {
return;
}
if (this.serverPush_ > 0) {
this.session_.pushUpdates();
} else {
throw new WtException(
"WApplication::triggerUpdate() called but server-triggered updates not enabled using WApplication::enableUpdates()");
}
}

/**
* A synchronisation lock for manipulating and updating the application and
* its widgets outside of the event loop
* <p>
*
* You need to get this lock only when you want to manipulate widgets
* outside of the event loop. Inside the event loop, this lock is already
* held by the library itself.
* <p>
*
* @see WApplication#getUpdateLock()
*/
public static class UpdateLock {
/**
* Releases the scope dependent lock.
*/
public void release() {
System.err.append("Releasing update lock").append('\n');
if (WApplication.getInstance().shouldTriggerUpdate_) {
System.err.append("Releasing handler").append('\n');
WApplication.getInstance().shouldTriggerUpdate_ = false;
WebSession.Handler.getInstance().release();
}
}

private UpdateLock(WApplication app) {
System.err.append("Grabbing update lock").append('\n');
WebSession.Handler handler = WebSession.Handler.getInstance();
if (!(handler != null) || !handler.isHaveLock()
|| handler.getSession() != app.session_) {
System.err.append(
"Creating new handler for app: app.sessionId()")
.append('\n');
new WebSession.Handler(app.getSession(), true);
app.shouldTriggerUpdate_ = true;
}
}
}

/**
* Grabs and returns the lock for manipulating widgets outside the event
* loop.
* <p>
* You need to keep this lock in scope while manipulating widgets outside of
* the event loop. In normal cases, inside the JWt event loop, you do not
* need to care about it.
* <p>
*
* @see WApplication#enableUpdates(boolean enabled)
* @see WApplication#triggerUpdate()
*/
public WApplication.UpdateLock getUpdateLock() {
return new WApplication.UpdateLock(this);
}

/**
* Attach an auxiliary thread to this application.
* <p>
* In a multi-threaded environment, {@link WApplication#getInstance()
* getInstance()} uses thread-local data to retrieve the application object
* that corresponds to the session currently being handled by the thread.
* This is set automatically by the library whenever an event is delivered
* to the application, or when you use the
* {@link WApplication#getUpdateLock() getUpdateLock()} to modify the
* application from an auxiliary thread outside the normal event loop.
* <p>
* When you want to manipulate the widget tree inside the main event loop,
* but from within an auxiliary thread, then you cannot use the
* {@link WApplication#getUpdateLock() getUpdateLock()} since this will
* create an immediate dead lock. Instead, you may attach the auxiliary
* thread to the application, by calling this method from the auxiliary
* thread, and in this way you can modify the application from within that
* thread without needing the update lock.
*/
public void attachThread() {
WebSession.Handler.attachThreadToSession(this.session_);
}

/**
* Executes some JavaScript code.
* <p>
* This method may be used to call some custom <code>javaScript</code> code
* as part of an event response.
* <p>
* This function does not wait until the JavaScript is run, but returns
* immediately. The JavaScript will be run after the normal event handling,
* unless <code>afterLoaded</code> is set to <code>false</code>.
* <p>
*
* @see WApplication#addAutoJavaScript(String javascript)
* @see WApplication#declareJavaScriptFunction(String name, String function)
*/
public void doJavaScript(String javascript, boolean afterLoaded) {
if (afterLoaded) {
this.afterLoadJavaScript_ += javascript;
this.afterLoadJavaScript_ += '\n';
} else {
this.beforeLoadJavaScript_ += javascript;
this.beforeLoadJavaScript_ += '\n';
this.newBeforeLoadJavaScript_ += javascript;
this.newBeforeLoadJavaScript_ += '\n';
}
}

/**
* Executes some JavaScript code.
* <p>
* Calls {@link #doJavaScript(String javascript, boolean afterLoaded)
* doJavaScript(javascript, true)}
*/
public final void doJavaScript(String javascript) {
doJavaScript(javascript, true);
}

/**
* Adds JavaScript statements that should be run continuously.
* <p>
* This is an internal method.
* <p>
* It is used by for example layout managers to adjust the layout whenever
* the DOM tree is manipulated.
* <p>
*
* @see WApplication#doJavaScript(String javascript, boolean afterLoaded)
*/
public void addAutoJavaScript(String javascript) {
this.autoJavaScript_ += javascript;
this.autoJavaScriptChanged_ = true;
}

/**
* Declares an application-wide JavaScript function.
* <p>
* This is an internal method.
*/
public void declareJavaScriptFunction(String name, String function) {
this.doJavaScript(this.javaScriptClass_ + '.' + name + '=' + function
+ ';', false);
}

/**
* Loads a JavaScript library.
* <p>
* Loads a JavaScript library located at the URL <code>url</code>. JWt keeps
* track of libraries (with the same URL) that already have been loaded, and
* will load a library only once. In addition, you may provide a
* <code>symbol</code> which if already defined will also indicate that the
* library was already loaded (possibly outside of JWt when in WidgetSet
* mode).
* <p>
* This method returns <code>true</code> only when the library is loaded for
* the first time.
* <p>
* JavaScript libraries may be loaded at any point in time. Any JavaScript
* code is deferred until the library is loaded, except for JavaScript that
* was defined to load before, passing <code>false</code> as second
* parameter to
* {@link WApplication#doJavaScript(String javascript, boolean afterLoaded)
* doJavaScript()}.
*/
public boolean require(String uri, String symbol) {
WApplication.ScriptLibrary sl = new WApplication.ScriptLibrary(uri,
symbol);
if (this.scriptLibraries_.indexOf(sl) == -1) {
sl.beforeLoadJS = this.newBeforeLoadJavaScript_;
this.newBeforeLoadJavaScript_ = "";
this.scriptLibraries_.add(sl);
++this.scriptLibrariesAdded_;
return true;
} else {
return false;
}
}

/**
* Loads a JavaScript library.
* <p>
* Returns {@link #require(String uri, String symbol) require(uri, "")}
*/
public final boolean require(String uri) {
return require(uri, "");
}

/**
* Returns the name of the application JavaScript class.
* <p>
* This JavaScript class encapsulates all JavaScript methods specific to
* this application instance. The method is foreseen to allow multiple
* applications to run simultaneously on the same page in Wt::WidgtSet mode,
* without interfering.
*/
public String getJavaScriptClass() {
return this.javaScriptClass_;
}

/**
* Processes UI events.
* <p>
* You may call this method during a long operation to:
* <ul>
* <li>propagate widget changes to the client.</li>
* <li>process UI events.</li>
* </ul>
* <p>
* This method starts a recursive event loop, blocking the current thread,
* and resumes when all pending user interface events have been processed.
* <p>
* Because a thread is blocked, this may affect your application
* scalability.
*/
public void processEvents() {
this.doJavaScript("setTimeout(\"" + this.javaScriptClass_
+ "._p_.update(null,'none',null,false);\",0);");
this.session_.doRecursiveEventLoop();
}

/**
* Reads a configuration property.
* <p>
* Tries to read a configured value for the property <code>name</code>. If
* no value was configured, the default <code>value</code> is returned.
*/
public static String readConfigurationProperty(String name, String value) {
String property = WApplication.getInstance().session_.getController()
.getConfiguration().getProperty(name);
if (property != null) {
return property;
} else {
return value;
}
}

/**
* Sets the Ajax communication method.
* <p>
* You may change the communication method only from within the application
* constructor.
* <p>
* The default method depends on your application deployment type.
* <p>
* For plain applications, {@link WApplication.AjaxMethod#XMLHttpRequest
* XMLHttpRequest} is used, while for WidgetSet applications,
* {@link WApplication.AjaxMethod#DynamicScriptTag DynamicScriptTag} is
* used. The latter is less efficient, but has the benefit to allow serving
* the application from a different server than the page that hosts the
* embedded widgets.
*/
public void setAjaxMethod(WApplication.AjaxMethod method) {
this.ajaxMethod_ = method;
}

/**
* This method returns the absolute base url of the application.
* <p>
* <strong>For instance:</strong>
* Url: www.domain.com:8080/myApplicationWebRoot/something/...</br>
* Absolute base Url: www.domain.com:8080/myApplicationWebRoot/
* </p>
*/
public String getAbsoluteBaseUrl(){
return getSession().getAbsoluteBaseUrl();
}
/**
* This method allows to set a property in the configuration
*/
public void setConfigurationProperty(String name, String value){
getSession().getController().getConfiguration().getProperties().put(name, value);
}
/**
* Returns the Ajax communication method.
* <p>
*
* @see WApplication#setAjaxMethod(WApplication.AjaxMethod method)
*/
public WApplication.AjaxMethod getAjaxMethod() {
return this.ajaxMethod_;
}

WContainerWidget getDomRoot() {
return this.domRoot_;
}

WContainerWidget getDomRoot2() {
return this.domRoot2_;
}

String encodeObject(WObject object) {
String result = "w" + object.getUniqueId();
this.encodedObjects_.put(result, object);
return result;
}

WObject decodeObject(String objectId) {
WObject i = this.encodedObjects_.get(objectId);
if (i != null) {
return i;
} else {
return null;
}
}

String fixRelativeUrl(String url) {
if (url.indexOf("://") != -1) {
return url;
}
if (url.length() > 0 && url.charAt(0) == '#') {
return url;
}
if (this.ajaxMethod_ == WApplication.AjaxMethod.XMLHttpRequest) {
if (!this.getEnvironment().hasJavaScript()
&& WebSession.Handler.getInstance().getRequest()
.getPathInfo().length() != 0) {
if (url.length() != 0 && url.charAt(0) == '/') {
return url;
} else {
return this.session_.getBaseUrl() + url;
}
} else {
return url;
}
} else {
if (url.length() != 0) {
if (url.charAt(0) != '/') {
return this.session_.getAbsoluteBaseUrl() + url;
} else {
return this.getEnvironment().getUrlScheme() + "://"
+ this.getEnvironment().getHostName() + url;
}
} else {
return url;
}
}
}

/**
* Initializes the application, post-construction.
* <p>
* This method is invoked by the JWt library after construction of a new
* application. You may reimplement this method to do additional
* initialization that is not possible from the constructor (e.g. which uses
* virtual methods).
*/
public void initialize() {
}

/**
* Changes the threshold for two-phase rendering.
* <p>
* This changes the threshold for the <code>size</code> of a JavaScript
* response (in bytes) to render invisible changes in one go. If the
* bandwidth for rendering the invisible changes exceed the threshold, they
* will be fetched in a second communication, after the visible changes have
* been rendered.
* <p>
* The value is a trade-off: setting it smaller will always use two-phase
* rendering, increasing the total render time but reducing the latency for
* the visible changes. Setting it too large will increase the latency to
* render the visible changes, since first also all invisible changes need
* to be computed and received in the browser.
*/
public void setTwoPhaseRenderingThreshold(int bytes) {
this.session_.getRenderer().setTwoPhaseThreshold(bytes);
}

/**
* Sets a new cookie.
* <p>
* Use cookies to transfer information across different sessions (e.g. a
* user name). In a subsequent session you will be able to read this cookie
* using {@link WEnvironment#getCookie(String cookieNname)
* WEnvironment#getCookie()}. You cannot use a cookie to store information
* in the current session.
* <p>
* The name must be a valid cookie name (of type &apos;token&apos;: no
* special characters or separators, see RFC2616 page 16). The value may be
* anything. Specify the maximum age (in seconds) after which the client
* must discard the cookie. To delete a cookie, use a value of
* &apos;0&apos;.
* <p>
* By default the cookie only applies to the current path on the current
* domain. To set a proper value for domain, see also RFC2109.
* <p>
*
* @see WEnvironment#supportsCookies()
* @see WEnvironment#getCookie(String cookieNname)
*/
public void setCookie(String name, String value, int maxAge, String domain,
String path) {
this.session_.getRenderer()
.setCookie(name, value, maxAge, domain, path);
}

/**
* Sets a new cookie.
* <p>
* Calls
* {@link #setCookie(String name, String value, int maxAge, String domain, String path)
* setCookie(name, value, maxAge, "", "")}
*/
public final void setCookie(String name, String value, int maxAge) {
setCookie(name, value, maxAge, "", "");
}

/**
* Sets a new cookie.
* <p>
* Calls
* {@link #setCookie(String name, String value, int maxAge, String domain, String path)
* setCookie(name, value, maxAge, domain, "")}
*/
public final void setCookie(String name, String value, int maxAge,
String domain) {
setCookie(name, value, maxAge, domain, "");
}

/**
* Adds an HTML meta header.
* <p>
* A meta header can only be added in the following situations:
* <p>
* <ul>
* <li>when a plain HTML session is used (including when the user agent is a
* bot), you can add meta headers at any time.</li>
* </ul>
* <p>
* <ul>
* <li>or, when DOCREF<a class="el"
* href="overview.html#progressive_bootstrap">progressive bootstrap</a> is
* used, you can set meta headers for any type of session, from within the
* application constructor (which corresponds to the initial request).</li>
* </ul>
* <p>
* <ul>
* <li>but never for a {@link EntryPointType#WidgetSet} mode application
* since then the application is hosted within a foreign HTML page.</li>
* </ul>
* <p>
* These situations coincide with {@link WEnvironment#hasAjax()
* WEnvironment#hasAjax()} returning <code>false</code> (see
* {@link WApplication#getEnvironment() getEnvironment()}).
*/
public void addMetaHeader(String name, CharSequence content, String lang) {
if (this.getEnvironment().hasJavaScript()) {
this.log("warn").append(
"WApplication::addMetaHeader() with no effect");
}
this.metaHeaders_.add(new WApplication.MetaHeader(name, content, lang));
}

/**
* Adds an HTML meta header.
* <p>
* Calls {@link #addMetaHeader(String name, CharSequence content, String lang)
* addMetaHeader(name, content, "")}
*/
public final void addMetaHeader(String name, CharSequence content) {
addMetaHeader(name, content, "");
}

/**
* Adds an entry to the application log.
* <p>
* Starts a new log entry of the given <code>type</code> in the JWt
* application log file. This method returns a stream-like object to which
* the message may be streamed.
*/
public WLogEntry log(String type) {
return this.session_.log(type);
}

/**
* Sets the loading indicator.
* <p>
* The loading indicator is shown to indicate that a response from the
* server is pending or JavaScript is being evaluated.
* <p>
* The default loading indicator is a {@link WDefaultLoadingIndicator}.
* <p>
* When setting a new loading indicator, the previous one is deleted.
*/
public void setLoadingIndicator(WLoadingIndicator indicator) {
;
this.loadingIndicator_ = indicator;
if (this.loadingIndicator_ != null) {
this.loadingIndicatorWidget_ = indicator.getWidget();
this.domRoot_.addWidget(this.loadingIndicatorWidget_);
JSlot showLoadJS = new JSlot();
showLoadJS.setJavaScript("function(o,e) {Wt3_1_5.inline('"
+ this.loadingIndicatorWidget_.getId() + "');}");
this.showLoadingIndicator_.addListener(showLoadJS);
JSlot hideLoadJS = new JSlot();
hideLoadJS.setJavaScript("function(o,e) {Wt3_1_5.hide('"
+ this.loadingIndicatorWidget_.getId() + "');}");
this.hideLoadingIndicator_.addListener(hideLoadJS);
this.loadingIndicatorWidget_.hide();
}
}

/**
* Returns the loading indicator.
* <p>
*
* @see WApplication#setLoadingIndicator(WLoadingIndicator indicator)
*/
public WLoadingIndicator getLoadingIndicator() {
return this.loadingIndicator_;
}

String getOnePixelGifUrl() {
if (this.onePixelGifUrl_.length() == 0) {
WMemoryResource w = new WMemoryResource("image/gif", this);
w.setData(gifData);
this.onePixelGifUrl_ = w.getUrl();
}
return this.onePixelGifUrl_;
}

String getDocType() {
return this.session_.getDocType();
}

/**
* Quits the application.
* <p>
* The method returns immediately, but has as effect that the application
* will be terminated after the current event is completed.
* <p>
* The current widget tree (including any modifications still pending and
* applied during the current event handling) will still be rendered, after
* which the application is terminated.
* <p>
* You might want to make sure no more events can be received from the user,
* by not having anything clickable, for example by displaying only text.
* Even better is to {@link WApplication#redirect(String url) redirect()}
* the user to another, static, page in conjunction with quit().
* <p>
*
* @see WApplication#redirect(String url)
*/
public void quit() {
this.quited_ = true;
}

/**
* Returns whether the application is quited.
* <p>
*
* @see WApplication#quit()
*/
public boolean isQuited() {
return this.quited_;
}

/**
* Returns the current maximum size of a request to the application.
* <p>
*
* @see WApplication#requestTooLarge()
*/
public int getMaximumRequestSize() {
return this.session_.getController().getConfiguration()
.getMaxRequestSize() * 1024;
}

/**
* Signal which indicates that too a large request was received.
* <p>
* The integer parameter is the request size that was received in bytes.
*/
public Signal1<Integer> requestTooLarge() {
return this.requestTooLarge_;
}

void redirectToSession(String newSessionId) {
Configuration conf = this.session_.getController().getConfiguration();
String redirectUrl = this.getBookmarkUrl();
if (conf.getSessionTracking() == Configuration.SessionTracking.CookiesURL
&& this.getEnvironment().supportsCookies()) {
String cookieName = this.getEnvironment().getDeploymentPath();
this.setCookie(cookieName, newSessionId, -1);
} else {
redirectUrl += "?wtd=" + newSessionId;
}
this.redirect(redirectUrl);
}

boolean isConnected() {
return this.connected_;
}

/**
* Event signal emitted when a keyboard key is pushed down.
* <p>
* The application receives key events when no widget currently has focus.
* Otherwise, key events are handled by the widget in focus, and its
* ancestors.
* <p>
*
* @see WInteractWidget#keyWentDown()
*/
public EventSignal1<WKeyEvent> globalKeyWentDown() {
return this.domRoot_.keyWentDown();
}

/**
* Event signal emitted when a &quot;character&quot; was entered.
* <p>
* The application receives key events when no widget currently has focus.
* Otherwise, key events are handled by the widget in focus, and its
* ancestors.
* <p>
*
* @see WInteractWidget#keyPressed()
*/
public EventSignal1<WKeyEvent> globalKeyPressed() {
return this.domRoot_.keyPressed();
}

/**
* Event signal emitted when a keyboard key is released.
* <p>
* The application receives key events when no widget currently has focus.
* Otherwise, key events are handled by the widget in focus, and its
* ancestors.
* <p>
*
* @see WInteractWidget#keyWentUp()
*/
public EventSignal1<WKeyEvent> globalKeyWentUp() {
return this.domRoot_.keyWentUp();
}

/**
* Event signal emitted when enter was pressed.
* <p>
* The application receives key events when no widget currently has focus.
* Otherwise, key events are handled by the widget in focus, and its
* ancestors.
* <p>
*
* @see WInteractWidget#enterPressed()
*/
public EventSignal globalEnterPressed() {
return this.domRoot_.enterPressed();
}

/**
* Event signal emitted when escape was pressed.
* <p>
* The application receives key events when no widget currently has focus.
* Otherwise, key events are handled by the widget in focus, and its
* ancestors.
* <p>
*
* @see WInteractWidget#escapePressed()
*/
public EventSignal globalEscapePressed() {
return this.domRoot_.escapePressed();
}

boolean isDebug() {
return this.session_.isDebug();
}

boolean isJavaScriptLoaded(String jsFile) {
return this.javaScriptLoaded_.contains(jsFile) != false;
}

void setJavaScriptLoaded(String jsFile) {
this.javaScriptLoaded_.add(jsFile);
}

/**
* Notifies an event to the application.
* <p>
* This method is called by the event loop for propagating an event to the
* application. It provides a single point of entry for events to the
* application, besides the application constructor.
* <p>
* You may want to reimplement this method for two reasons:
* <p>
* <ul>
* <li>for having a single point for exception handling: while you may want
* to catch recoverable exceptions in a more appropriate place, general
* (usually fatal) exceptions may be caught here. You will in probably also
* want to catch the same exceptions in the application constructor in the
* same way.</li>
* <li>you want to manage resource usage during requests. For example, at
* the end of request handling, you want to return a database session back
* to the pool. Since notify() is also used for rendering right after the
* application is created, this will also clean up resources after
* application construction.</li>
* </ul>
* <p>
* In either case, you will need to call the base class implementation of
* notify(), as otherwise no events will be delivered to your application.
* <p>
* The following shows a generic template for reimplementhing this method
* for both managing request resources and generic exception handling.
* <p>
* <blockquote>
*
* <pre>
* void notify(WEvent event) {
* // Grab resources for during request handling
* try {
* super.notify(event);
* } catch (MyException exception) {
* // handle this exception in a central place
* }
* // Free resources used during request handling
* }
* </pre>
*
* </blockquote>
* <p>
* Note that any uncaught exception throw during event handling terminates
* the session.
*/
protected void notify(WEvent e) throws IOException {
this.session_.notify(e);
}

/**
* Returns whether a widget is exposed in the interface.
* <p>
* The default implementation simply returns <code>true</code>, unless a
* modal dialog is active, in which case it returns <code>true</code> only
* for widgets that are inside the dialog.
* <p>
* You may want to reimplement this method if you wish to disallow events
* from certain widgets even when they are inserted in the widget hierachy.
*/
protected boolean isExposed(WWidget w) {
if (w != this.domRoot_ && this.exposedOnly_ != null) {
for (WWidget p = w; p != null; p = p.getParent()) {
if (p == this.exposedOnly_ || p == this.timerRoot_) {
return true;
}
}
return false;
} else {
WWidget p = w.getAdam();
return p == this.domRoot_ || p == this.domRoot2_;
}
}

/**
* Progresses to an Ajax-enabled user interface.
* <p>
* This method is called when the progressive bootstrap method is used, and
* support for AJAX has been detected. The default behavior will propagate
* the {@link WWidget#enableAjax() WWidget#enableAjax()} method through the
* widget hierarchy.
* <p>
* You may want to reimplement this method if you want to make changes to
* the user-interface when AJAX is enabled. You should always call the base
* implementation.
* <p>
*
* @see WWidget#enableAjax()
*/
protected void enableAjax() {
this.enableAjax_ = true;
this.session_.getRenderer().beforeLoadJS_
.append(this.newBeforeLoadJavaScript_);
this.newBeforeLoadJavaScript_ = "";
this.session_.getRenderer().beforeLoadJS_
.append(this.afterLoadJavaScript_);
this.afterLoadJavaScript_ = "";
this.domRoot_.enableAjax();
if (this.domRoot2_ != null) {
this.domRoot2_.enableAjax();
}
}

private Signal1<Integer> requestTooLarge_;

static class ScriptLibrary {
public ScriptLibrary(String anUri, String aSymbol) {
this.uri = anUri;
this.symbol = aSymbol;
this.beforeLoadJS = "";
}

public String uri;
public String symbol;
public String beforeLoadJS;

public boolean equals(WApplication.ScriptLibrary other) {
return this.uri.equals(other.uri);
}
}

static class MetaHeader {
public MetaHeader(String aName, CharSequence aContent, String aLang) {
this.name = aName;
this.lang = aLang;
this.content = WString.toWString(aContent);
}

public String name;
public String lang;
public WString content;
}

private WebSession session_;
private WString title_;
boolean titleChanged_;
private WContainerWidget widgetRoot_;
WContainerWidget domRoot_;
WContainerWidget domRoot2_;
private WContainerWidget timerRoot_;
private WCssStyleSheet styleSheet_;
WCombinedLocalizedStrings localizedStrings_;
private Locale locale_;
String oldInternalPath_;
String newInternalPath_;
Signal1<String> internalPathChanged_;
boolean internalPathIsChanged_;
private int serverPush_;
private boolean shouldTriggerUpdate_;
private String javaScriptClass_;
private WApplication.AjaxMethod ajaxMethod_;
private WContainerWidget dialogCover_;
private boolean quited_;
private String onePixelGifUrl_;
private boolean rshLoaded_;
private WWidget exposedOnly_;
private WLoadingIndicator loadingIndicator_;
WWidget loadingIndicatorWidget_;
private boolean connected_;
String htmlClass_;
String bodyClass_;
boolean bodyHtmlClassChanged_;
boolean enableAjax_;
List<WApplication.ScriptLibrary> scriptLibraries_;
int scriptLibrariesAdded_;

static class StyleSheet {
public String uri;
public String media;

public StyleSheet(String anUri, String aMedia) {
this.uri = anUri;
this.media = aMedia;
}
}

private String theme_;
List<WApplication.StyleSheet> styleSheets_;
int styleSheetsAdded_;
List<WApplication.MetaHeader> metaHeaders_;
private Map<String, WeakReference<AbstractEventSignal>> exposedSignals_;
private Map<String, WResource> exposedResources_;
private Map<String, WObject> encodedObjects_;
private boolean exposeSignals_;
private String afterLoadJavaScript_;
String beforeLoadJavaScript_;
String newBeforeLoadJavaScript_;
String autoJavaScript_;
private Set<String> javaScriptLoaded_;
boolean autoJavaScriptChanged_;
EventSignal showLoadingIndicator_;
EventSignal hideLoadingIndicator_;

WContainerWidget getTimerRoot() {
return this.timerRoot_;
}

WContainerWidget getDialogCover(boolean create) {
if (this.dialogCover_ == null && create && this.timerRoot_ != null) {
this.dialogCover_ = new WContainerWidget(this.domRoot_);
this.dialogCover_.setStyleClass("Wt-dialogcover");
this.dialogCover_.hide();
}
return this.dialogCover_;
}

final WContainerWidget getDialogCover() {
return getDialogCover(true);
}

WEnvironment getEnv() {
return this.session_.getEnv();
}

void addExposedSignal(AbstractEventSignal signal) {
String s = signal.encodeCmd();
this.exposedSignals_.put(s, new WeakReference<AbstractEventSignal>(
signal));
}

void removeExposedSignal(AbstractEventSignal signal) {
String s = signal.encodeCmd();
if (this.exposedSignals_.remove(s) != null) {
} else {
System.err.append(
" WApplication::removeExposedSignal of non-exposed ")
.append(s).append("??").append('\n');
}
}

AbstractEventSignal decodeExposedSignal(String signalName) {
WeakReference<AbstractEventSignal> i = this.exposedSignals_
.get(signalName);
if (i != null) {
AbstractEventSignal esb = i.get();
if (!(esb != null)) {
return null;
}
WWidget w = ((i.get().getSender()) instanceof WWidget ? (WWidget) (i
.get().getSender())
: null);
if (!(w != null) || this.isExposed(w)
|| signalName.endsWith(".resized")) {
return i.get();
} else {
return null;
}
} else {
return null;
}
}

AbstractEventSignal decodeExposedSignal(String objectId, String name) {
String signalName = (objectId.equals("app") ? this.getId() : objectId)
+ '.' + name;
return this.decodeExposedSignal(signalName);
}

Map<String, WeakReference<AbstractEventSignal>> exposedSignals() {
return this.exposedSignals_;
}

private String resourceMapKey(WResource resource) {
return resource.getInternalPath().length() == 0 ? resource.getId()
: "/path/" + resource.getInternalPath();
}

String addExposedResource(WResource resource, String internalPath) {
this.exposedResources_.put(this.resourceMapKey(resource), resource);
String fn = resource.getSuggestedFileName();
if (fn.length() != 0 && fn.charAt(0) != '/') {
fn = '/' + fn;
}
if (resource.getInternalPath().length() == 0) {
return this.session_.getMostRelativeUrl(fn)
+ "&request=resource&resource="
+ DomElement.urlEncodeS(resource.getId()) + "&rand="
+ String.valueOf(MathUtils.randomInt());
} else {
fn = resource.getInternalPath() + fn;
if (this.session_.getApplicationName().length() != 0
&& fn.charAt(0) != '/') {
fn = '/' + fn;
}
return this.session_.getMostRelativeUrl(fn);
}
}

void removeExposedResource(WResource resource) {
this.exposedResources_.remove(this.resourceMapKey(resource));
}

WResource decodeExposedResource(String resourceKey) {
WResource i = this.exposedResources_.get(resourceKey);
if (i != null) {
return i;
} else {
int j = resourceKey.lastIndexOf('/');
if (j != -1 && j > 1) {
return this.decodeExposedResource(resourceKey.substring(0,
0 + j));
} else {
return null;
}
}
}

private boolean isLoadRsh() {
if (!this.rshLoaded_) {
this.rshLoaded_ = true;
if (this.session_.getApplicationName().length() == 0) {
this
.log("warn")
.append(
"Deploy-path ends with '/', using /?_= for internal paths");
}
return true;
} else {
return false;
}
}

void changeInternalPath(String aPath) {
String path = aPath;
if (path.length() == 0 || path.charAt(0) == '/') {
if (!path.equals(this.newInternalPath_)) {
String v = "";
this.newInternalPath_ = path;
this.internalPathChanged().trigger(this.newInternalPath_);
}
}
}

String getAfterLoadJavaScript() {
String result = this.afterLoadJavaScript_;
this.afterLoadJavaScript_ = "";
return result;
}

String getBeforeLoadJavaScript() {
this.newBeforeLoadJavaScript_ = "";
return this.beforeLoadJavaScript_;
}

String getNewBeforeLoadJavaScript() {
String result = this.newBeforeLoadJavaScript_;
this.newBeforeLoadJavaScript_ = "";
return result;
}

void setExposeSignals(boolean how) {
this.exposeSignals_ = how;
}

boolean isExposeSignals() {
return this.exposeSignals_;
}

void constrainExposed(WWidget w) {
this.exposedOnly_ = w;
}
WWidget getExposeConstraint() {
return this.exposedOnly_;
}

private static boolean pathMatches(String path, String query) {
if (query.length() <= path.length()
&& path.substring(0, 0 + query.length()).equals(query)) {
return true;
} else {
return false;
}
}

SoundManager getSoundManager() {
if (!(this.soundManager_ != null)) {
this.soundManager_ = new SoundManager(this);
}
return this.soundManager_;
}

private SoundManager soundManager_;
private static char[] gifData = { 0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x01,
0x00, 0x01, 0x00, 0x80, 0x00, 0x00, 0xdb, 0xdf, 0xef, 0x00, 0x00,
0x00, 0x21, 0xf9, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00,
0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x02, 0x02, 0x44,
0x01, 0x00, 0x3b };
static String RESOURCES_URL = "resourcesURL";
}
    (1-1/1)