001package votorola.a.web.gwt; // Copyright 2010-2012, Michael Allan. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Votorola Software"), to deal in the Votorola Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicence, and/or sell copies of the Votorola Software, and to permit persons to whom the Votorola Software is furnished to do so, subject to the following conditions: The preceding copyright notice and this permission notice shall be included in all copies or substantial portions of the Votorola Software. THE VOTOROLA SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE VOTOROLA SOFTWARE OR THE USE OR OTHER DEALINGS IN THE VOTOROLA SOFTWARE. 002 003import com.google.gwt.core.client.*; 004import com.google.gwt.event.shared.GwtEvent; 005import com.google.gwt.event.shared.HasHandlers; 006import com.google.gwt.jsonp.client.JsonpRequestBuilder; 007import votorola.g.lang.*; 008import votorola.g.web.gwt.*; 009import votorola.g.web.gwt.event.*; 010 011 012/** A GWT web application under Votorola. State changes are expected as late as the 013 * "finally" phase. Related events are subsequently dispatched in the "deferred" phase 014 * where they appear atomic regardless of the number of state variables involved. 015 */ 016public final class App implements HasHandlers 017{ 018 019 020 /** Does nothing itself but the call forces static initialization of this class. 021 */ 022 static void forceInitClass() {} 023 024 025 026 /** The single instance of App. 027 */ 028 public static App i() { return instance; } 029 030 031 private static App instance; 032 033 { // constructed by AMod 034 if( instance != null ) throw new IllegalStateException(); 035 036 instance = App.this; 037 } 038 039 040 041 // ------------------------------------------------------------------------------------ 042 043 044 /** The URL of the vote-server's context on the static mirror, or null if the context 045 * is not statically mirrored. The location is specified without a trailing slash 046 * (/). 047 * 048 * @see #staticContextLocation() 049 * @see #setMirroredContextLocation(String) 050 */ 051 public static @GWTConfigCallback String getMirroredContextLocation() { return mirroredContextLocation; } 052 053 054 private static String mirroredContextLocation; 055 056 057 private static native void exposeMirroredContextLocation() 058 /*-{ 059 $wnd.a_web_gwt_App_getMirroredContextLocation = $entry( 060 @votorola.a.web.gwt.App::getMirroredContextLocation() ); 061 $wnd.a_web_gwt_App_setMirroredContextLocation = $entry( 062 @votorola.a.web.gwt.App::setMirroredContextLocation(Ljava/lang/String;) ); 063 }-*/; 064 065 static 066 { 067 assert AMod.isForcedInit(): "forced init " + App.class.getName(); 068 exposeMirroredContextLocation(); 069 } 070 071 072 /** Sets the URL of the vote-server's context on the static mirror. Call it from 073 * the global configuration function {@linkplain AMod voGWTConfig.a} in this 074 * manner:<pre 075 * 076 *> a_web_gwt_App_setMirroredContextLocation( 'http://HOST-NAME/PATH' ); 077 * // default is null, meaning no static mirror</pre> 078 * 079 * <p>The default value is appropriate for an out-of-the-box test deployment in a 080 * servlet container such as Tomcat or Jetty. For an online vote-server it is 081 * often more flexible and efficient to serve the static web pages 082 * (like <a href='../../../../../a/web/context/' target='_top'>CONTEXT</a>/xf/default.html) 083 * and resource files (JavaScript, 084 * CSS, and so forth) from a static web server such as Apache httpd. Here is an 085 * example of that:</p><ul><li> 086 * 087 * <p>GWT configuration: 088 * <a href='http://reluk.ca/y/vw/w/publicConfig/gwt.js' target='_top'>http://reluk.ca/y/vw/w/publicConfig/gwt.js</a></p> 089 * 090 * <p>Note the call to <code>a_web_gwt_App_setMirroredContextLocation</code> 091 * (this method).</p> 092 * 093 * </li><li> 094 * 095 * <p>Apache httpd configuration: 096 * <a href='http://reluk.ca/system/host/obsidian/etc/apache2/7_domain/reluk.ca/public/5_in_project_votorola.conf' target='_top'>http://reluk.ca/system/host/obsidian/etc/apache2/7_domain/reluk.ca/public/5_in_project_votorola.conf</a></p> 097 * 098 * <p>Note that the Alias directives serve both the public configuration 099 * (~/votorola/web/publicConfig) and code 100 * (~/votorola/code/votorola/a/web/context) under request URLs that share a 101 * common basepath (/y/vw). Choose any basepath.</p><pre 102 * 103 *> Alias /y/vw/w/publicConfig /home/v/votorola/web/publicConfig 104 * Alias /y/vw /home/v/votorola/code/votorola/a/web/context 105 * # -------------------- ----------------------------------------- 106 * # Formal request URL Actual directory served</pre> 107 * 108 * <p>You may wish to configure <a href='http://code.google.com/webtoolkit/doc/latest/DevGuideCompilingAndDebugging.html' target='_top'>perfect caching</a>. 109 * See for example the various 110 * <code>Expires</code> directives under:</p><pre 111 * 112 *> <Directory "/home/v/votorola/code/votorola/a/web/context"></pre> 113 * 114 * </li><li> 115 * 116 * <p>Wicket configuration: 117 * <a href='http://reluk.ca/system/host/obsidian/home/v/votorola/web/vowicket.js' target='_top' >http://reluk.ca/system/host/obsidian/home/v/votorola/web/vowicket.js</a></p> 118 * 119 * <p>Note the call to wicCC.{@linkplain 120 * votorola.a.web.wic.VOWicket.ConstructionContext#setMirroredContextLocation(String) 121 * setMirroredContextLocation}().</p> 122 * 123 * </li><li> 124 * 125 * <p>Result as served statically, for example: 126 * <a href='http://reluk.ca/y/vw/xf/' target='_top' >http://reluk.ca/y/vw/xf/</a></p></li></ul> 127 * 128 * @throws IllegalArgumentException if the location ends with a slash '/' 129 * character. 130 * @see #getMirroredContextLocation() 131 */ 132 public static @GWTConfigCallback void setMirroredContextLocation( final String s ) 133 { 134 if( s.endsWith( "/" )) throw new IllegalArgumentException( "location ends with '/'" ); 135 136 mirroredContextLocation = s; 137 } 138 139 140 141 /** The URL of the vote-server's context on the servlet container (Tomcat), without a 142 * trailing slash (/). 143 * 144 * @see #staticContextLocation() 145 * @see #setServletContextLocation(String) 146 */ 147 public static @GWTConfigCallback String getServletContextLocation() { return servletContextLocation; } 148 149 150 private static String servletContextLocation; 151 152 153 private static native void exposeServletContextLocation() 154 /*-{ 155 $wnd.a_web_gwt_App_getServletContextLocation = $entry( 156 @votorola.a.web.gwt.App::getServletContextLocation() ); 157 $wnd.a_web_gwt_App_setServletContextLocation = $entry( 158 @votorola.a.web.gwt.App::setServletContextLocation(Ljava/lang/String;) ); 159 }-*/; 160 161 static 162 { 163 assert AMod.isForcedInit(): "forced init " + App.class.getName(); 164 exposeServletContextLocation(); 165 } 166 167 168 /** Sets the URL of the vote-server's context on the servlet container (Tomcat). 169 * Call it from the global configuration function {@linkplain AMod 170 * voGWTConfig.a} in this manner:<pre 171 * 172 *> a_web_gwt_App_setServletContextLocation( 173 * 'http://http://HOST-NAME:8080/VOTE-SERVER-NAME' ); 174 * // there is no dependable default, you should set a value</pre> 175 * 176 * @throws IllegalArgumentException if the location ends with a slash '/' 177 * character. 178 * @see #getServletContextLocation() 179 */ 180 public static @GWTConfigCallback void setServletContextLocation( final String s ) 181 { 182 if( s.endsWith( "/" )) throw new IllegalArgumentException( "location ends with '/'" ); 183 184 servletContextLocation = s; 185 } 186 187 188 189 /** The name of the authenticated user if known, otherwise null. The value is bound via 190 * the {@linkplain GWTX#bus() event bus} to property name <tt>username</tt>. 191 * 192 * @see votorola.a.voter.IDPair#username() 193 * @see #setUsername(String) 194 */ 195 public static @GWTConfigCallback String getUsername() { return username; } 196 197 198 private static String username; 199 200 201 private static native void exposeUsername() 202 /*-{ 203 $wnd.a_web_gwt_App_getUsername = $entry( @votorola.a.web.gwt.App::getUsername() ); 204 $wnd.a_web_gwt_App_setUsername = $entry( 205 @votorola.a.web.gwt.App::setUsername(Ljava/lang/String;) ); 206 }-*/; 207 208 static 209 { 210 assert AMod.isForcedInit(): "forced init " + App.class.getName(); 211 exposeUsername(); 212 } 213 214 215 /** Sets the name of the authenticated user. This configuration method is for 216 * developers. Call it from the global configuration function {@linkplain 217 * AMod voGWTConfig.a} like this for example:<pre 218 * 219 *> a_web_gwt_App_setUsername( 'Joe-GmailCom' ); // otherwise null</pre> 220 * 221 * @see #getUsername() 222 */ 223 public static @GWTConfigCallback void setUsername( String newUsername ) 224 { 225 if( newUsername == null ) newUsername = username; 226 if( ObjectX.nullEquals( username, newUsername )) return; 227 228 username = newUsername; 229 if( instance != null ) instance.gun.schedule( new PropertyChange( "username" )); 230 } 231 232 233 234 /** A common JSONP request builder with a default configuration. Do not change the 235 * configuration. 236 */ 237 public JsonpRequestBuilder jsonp() { return jsonp; } 238 239 240 private final JsonpRequestBuilder jsonp = new JsonpRequestBuilder(); 241 242 243 244 /** A common JSONP request builder configured for WAP requests. Do not change the 245 * configuration. 246 * 247 * @see votorola.a.web.wap.WAP 248 */ 249 public JsonpRequestBuilder jsonpWAP() { return jsonpWAP; } 250 251 252 private final JsonpRequestBuilder jsonpWAP = new JsonpRequestBuilder(); 253 254 { 255 jsonpWAP.setCallbackParam( "wCallback" ); 256 } 257 258 259 260 /** The localized messages for package {@linkplain votorola.a a}. 261 */ 262 public votorola.a.locale.gwt.A mesA() { return mesA; } 263 264 265 private final votorola.a.locale.gwt.A mesA = 266 (votorola.a.locale.gwt.A)GWT.create( 267 votorola.a.locale.gwt.A.class ); 268 269 270 271 /** The localized messages for package {@linkplain votorola.s s}. 272 */ 273 public votorola.a.locale.gwt.S mesS() { return mesS; } 274 275 276 private final votorola.a.locale.gwt.S mesS = 277 (votorola.a.locale.gwt.S)GWT.create( 278 votorola.a.locale.gwt.S.class ); 279 280 281 282 /** The pollwiki associated with the vote-server. 283 */ 284 public PollwikiG pollwiki() { return pollwiki; } 285 286 287 private final PollwikiG pollwiki = new PollwikiG(); 288 289 290 291 /** Returns the preferred location for fetching static files. This is on the mirror 292 * if there is a mirror, otherwise the servlet container. 293 * 294 * @see #getMirroredContextLocation() 295 * @see #getServletContextLocation() 296 */ 297 public String staticContextLocation() 298 { 299 return mirroredContextLocation == null? servletContextLocation: mirroredContextLocation; 300 } 301 302 303 304 // - H a s - H a n d l e r s ---------------------------------------------------------- 305 306 307 public void fireEvent( final GwtEvent<?> e ) 308 { 309 GWTX.i().bus().fireEventFromSource( e, App.this ); 310 } 311 312 313 314//// P r i v a t e /////////////////////////////////////////////////////////////////////// 315 316 317 private final DelayedEventGun gun = new DelayedEventGun( /*source*/App.this, 318 CoalescingSchedulerS.DEFERRED ); 319 320 321}