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         *>  &lt;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}