<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:blogger='http://schemas.google.com/blogger/2008' xmlns:georss='http://www.georss.org/georss' xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-3248335039039953287</id><updated>2024-08-28T07:48:27.571-05:00</updated><category term="gae"/><category term="gwt"/><category term="dependency_injection"/><category term="guice"/><category term="datastore"/><category term="rpc"/><category term="best_practices"/><category term="mvp"/><category term="servlet"/><category term="tutorial"/><category term="dao"/><category term="gin"/><category term="objectify"/><category term="security"/><category term="tools"/><category term="bookmarks"/><category term="code_splitting"/><category term="csrf"/><category term="cygwin"/><category term="debugging"/><category term="eclipse"/><category term="git"/><category term="goals"/><category term="gwt-dispatch"/><category term="gwt-presenter"/><category term="max_ross"/><category term="mvp4g"/><category term="refactor"/><category term="scaling"/><category term="schemas"/><category term="session"/><category term="xsrf"/><title type='text'>Learning Google App Engine and GWT Best Practices</title><subtitle type='html'>Learning about Google App Engine and Google Web Toolkit by building an open source volunteer information manager, while chronicling both good and bad ideas, brain farts, dead ends, screw ups, and successes.</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://gaegwtbestpractices.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3248335039039953287/posts/default?redirect=false'/><link rel='alternate' type='text/html' href='http://gaegwtbestpractices.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>John Schutz</name><uri>http://www.blogger.com/profile/12385766703556945544</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiRkpC7-LIVJCQcGKsOoSDO2UZitOmX-n7VCMnXq4HqTQKmeonrbB6vpvwBWw_hKCrK3wFhKstFOC9cObvuJjzpB27YS6SRMGMs9_RT8_GmNKc8KRDMAAqFQaDwZxPhMXo/s220/2009-10-26.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>11</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-3248335039039953287.post-7834401570091718469</id><published>2010-09-23T22:02:00.001-05:00</published><updated>2010-09-23T22:03:47.869-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="dependency_injection"/><category scheme="http://www.blogger.com/atom/ns#" term="gae"/><category scheme="http://www.blogger.com/atom/ns#" term="guice"/><category scheme="http://www.blogger.com/atom/ns#" term="security"/><category scheme="http://www.blogger.com/atom/ns#" term="servlet"/><title type='text'>Dumbassing things up</title><content type='html'>In a previous entry, &lt;a href=&quot;http://gaegwtbestpractices.blogspot.com/2010/09/some-management-servlets.html&quot;&gt;Some management servlets&lt;/a&gt;, in the section &lt;b&gt;You do know that anyone in the world can delete your datastore, right?&lt;/b&gt;, I goofed.&lt;br /&gt;
&lt;br /&gt;
In my defense, the first section of that entry &lt;b&gt;WAS&lt;/b&gt; entitled &lt;b&gt;I have no idea what I&#39;m talking about&lt;/b&gt;.  But it turns out my ignorance knows few bounds.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;web.xml, access control, and Guice&lt;/h3&gt;&lt;br /&gt;
I stated that in moving my BuildDB servlet under the control of Guice, I lost the ability to use a &lt;tt&gt;security-constraint&lt;/tt&gt; in the &lt;tt&gt;web.xml&lt;/tt&gt; file to stop the hoi polloi from accessing our servlet.  Not so.&lt;br /&gt;
&lt;br /&gt;
Simply have Guice respond to a URL pattern (such as &lt;tt&gt;&quot;/*&quot;&lt;/tt&gt;) that includes the &lt;tt&gt;security-constraint&lt;/tt&gt; constrained URL below it, say &lt;tt&gt;/admin/*&lt;/tt&gt;.  All of your servlets now are Guicified, and the ones you want protected are protected.&lt;br /&gt;
&lt;br /&gt;
Yeah, shoulda figured that out the first time.&lt;br /&gt;
&lt;br /&gt;
&lt;tt&gt;war/WEB-INF/web.xml&lt;/tt&gt;&lt;br /&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;&amp;lt;web-app&amp;gt;
 &amp;lt;!-- Servlets --&amp;gt;
 &amp;lt;security-constraint&amp;gt;
  &amp;lt;web-resource-collection&amp;gt;
   &amp;lt;url-pattern&amp;gt;/admin/*&amp;lt;/url-pattern&amp;gt;
  &amp;lt;/web-resource-collection&amp;gt;
  &amp;lt;auth-constraint&amp;gt;
   &amp;lt;role-name&amp;gt;admin&amp;lt;/role-name&amp;gt;
  &amp;lt;/auth-constraint&amp;gt;
 &amp;lt;/security-constraint&amp;gt;

 &amp;lt;filter&amp;gt;
  &amp;lt;filter-name&amp;gt;guiceFilter&amp;lt;/filter-name&amp;gt;
  &amp;lt;filter-class&amp;gt;com.google.inject.servlet.GuiceFilter&amp;lt;/filter-class&amp;gt;
 &amp;lt;/filter&amp;gt;

 &amp;lt;filter-mapping&amp;gt;
  &amp;lt;filter-name&amp;gt;guiceFilter&amp;lt;/filter-name&amp;gt;
  &amp;lt;url-pattern&amp;gt;/*&amp;lt;/url-pattern&amp;gt;
 &amp;lt;/filter-mapping&amp;gt;

 &amp;lt;listener&amp;gt;
  &amp;lt;listener-class&amp;gt;com.lisedex.voluntickler.server.guice.VolunticklerServletContextListener&amp;lt;/listener-class&amp;gt;
 &amp;lt;/listener&amp;gt;

 &amp;lt;!-- Default page to serve --&amp;gt;
 &amp;lt;welcome-file-list&amp;gt;
  &amp;lt;welcome-file&amp;gt;index.html&amp;lt;/welcome-file&amp;gt;
 &amp;lt;/welcome-file-list&amp;gt;
&amp;lt;/web-app&amp;gt;
&lt;/pre&gt;&lt;br /&gt;
The observant among you will notice that our package is now &lt;tt&gt;com.lisedex.voluntickler&lt;/tt&gt; instead of &lt;tt&gt;com.lisedex.volinfoman&lt;/tt&gt;.  It&#39;s a sexy name change, I know, and I realize that you&#39;re a bit jealous, but the domains are already registered.  Sorry.</content><link rel='replies' type='application/atom+xml' href='http://gaegwtbestpractices.blogspot.com/feeds/7834401570091718469/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://gaegwtbestpractices.blogspot.com/2010/09/dumbassing-things-up.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3248335039039953287/posts/default/7834401570091718469'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3248335039039953287/posts/default/7834401570091718469'/><link rel='alternate' type='text/html' href='http://gaegwtbestpractices.blogspot.com/2010/09/dumbassing-things-up.html' title='Dumbassing things up'/><author><name>John Schutz</name><uri>http://www.blogger.com/profile/12385766703556945544</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiRkpC7-LIVJCQcGKsOoSDO2UZitOmX-n7VCMnXq4HqTQKmeonrbB6vpvwBWw_hKCrK3wFhKstFOC9cObvuJjzpB27YS6SRMGMs9_RT8_GmNKc8KRDMAAqFQaDwZxPhMXo/s220/2009-10-26.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3248335039039953287.post-5416267311374335049</id><published>2010-09-04T23:42:00.000-05:00</published><updated>2010-09-04T23:42:55.110-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="best_practices"/><category scheme="http://www.blogger.com/atom/ns#" term="csrf"/><category scheme="http://www.blogger.com/atom/ns#" term="gwt"/><category scheme="http://www.blogger.com/atom/ns#" term="rpc"/><category scheme="http://www.blogger.com/atom/ns#" term="security"/><category scheme="http://www.blogger.com/atom/ns#" term="session"/><category scheme="http://www.blogger.com/atom/ns#" term="xsrf"/><title type='text'>Authenticating in GWT</title><content type='html'>Now that we have some basic ground work done for creating accounts and logging in, we can integrate this into our GWT application.  We always need to assume that the client is hostile, that the JavaScript code has been changed, or someone is calling our RPC methods directly.  None of our authentication checking should be done in the GWT client, except for stuff that decides which pages should be shown.  Any method that then provides data to these pages must still do a proper permissions and authentication check before returning anything.&lt;br /&gt;
&lt;br /&gt;
The code is &lt;a href=&quot;http://github.com/tofupup/volinfoman&quot;&gt;available on github&lt;/a&gt;, and the &lt;a href=&quot;http://github.com/tofupup/volinfoman/tree/add-gwt-authentication&quot;&gt;tag for these revisions is add-gwt-authentication&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;No more client passwords&lt;/h3&gt;&lt;br /&gt;
Since we&#39;re no longer using GWT to enter the user&#39;s login information, we no longer need any of the methods we had that passed entered passwords through RPC, namely &lt;tt&gt;checkUserPassword()&lt;/tt&gt; and &lt;tt&gt;authenticatedUser()&lt;/tt&gt;.  The password can be sent once through &lt;tt&gt;HTTPS&lt;/tt&gt; on our login form, and a session cookie is set on the client.  That cookie maps to a session stored in both the App Engine datastore, as well as &lt;tt&gt;memcache&lt;/tt&gt;.  We can use the session under App Engine to store information about the session, such as whether the user is authenticated, and no client program can change the value without going through an RPC call.&lt;br /&gt;
&lt;br /&gt;
So our &lt;tt&gt;UserService&lt;/tt&gt; &lt;tt&gt;RemoteService&lt;/tt&gt; is stripped down to an &lt;tt&gt;isAuthenticated()&lt;/tt&gt; method.&lt;br /&gt;
&lt;br /&gt;
&lt;tt&gt;src/com/lisedex/volinfoman/server/UserServiceImpl.java&lt;/tt&gt;&lt;br /&gt;
&lt;pre class=&quot;brush: java&quot;&gt;        @Override
        public boolean isAuthenticated() {
                return SessionHandler.isAuthenticated(getSession());
        }

        private HttpSession getSession() {
                return this.getThreadLocalRequest().getSession();
        }
&lt;/pre&gt;&lt;br /&gt;
When we call the &lt;tt&gt;isAuthenticated()&lt;/tt&gt; method, if our session cookie is set, it will be passed to the server in the &lt;tt&gt;Cookie&lt;/tt&gt; header.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;Oops.  I just made a security hole.&lt;/h3&gt;&lt;br /&gt;
Writing this rattled something loose in the back of my brain, which is apparently the &lt;a href=&quot;http://code.google.com/p/google-web-toolkit-incubator/wiki/LoginSecurityFAQ&quot;&gt;Login Security FAQ for GWT&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
The server should not rely on the session ID that comes in the &lt;tt&gt;Cookie&lt;/tt&gt; header, as doing so opens up the &lt;a href=&quot;http://en.wikipedia.org/wiki/Cross-site_request_forgery&quot;&gt;cross-site request forgery (XSRF or CSRF) vulnerability&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
In short, if the server relies on the &lt;tt&gt;Cookie&lt;/tt&gt; header for the session, and the user&#39;s session has not expired, then another site could have a link to something like &lt;tt&gt;http://www.vulnerablesite.com/user?deleteMe&lt;/tt&gt;, which could be even hidden as an &lt;tt&gt;&amp;lt;img src&amp;gt;&lt;/tt&gt; tag.  The user&#39;s session cookie would automatically be sent with the &lt;tt&gt;deleteMe&lt;/tt&gt; request, and the vulnerable server would go ahead and process the request.  If we require the session to be submitted in the RPC payload, the link would have to be something like &lt;tt&gt;http://www.vulnerablesite.com/user?deleteMe?sessionId=787asdfasdf&lt;/tt&gt;.  Since the evil site has no way to access the user&#39;s session cookie, it can&#39;t generate a usable link.  Hole closed.&lt;br /&gt;
&lt;br /&gt;
So let&#39;s rewind, patch the hole, and do this again.  The new code is available under the &lt;a href=&quot;http://github.com/tofupup/volinfoman/tree/fix-csrf-hole&quot;&gt;fix-csrf-hole tag on github&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;Fixing the CSRF vulnerability&lt;/h3&gt;&lt;br /&gt;
In looking for a pretty solution to this issue, I came across references to CSRF being addressed in GWT 2.1.  The current RpcRequestBuilder in 2.0.4 adds a custom header, &lt;tt&gt;X-GWT-Permutation&lt;/tt&gt;, and it looks like the code in 2.1 at least checks if this header is set.  But it doesn&#39;t do anything beyond an existence test, on the idea that CSRF attacks can&#39;t add custom HTTP headers.  However, apparently by using Adobe Flash, &lt;a href=&quot;http://www.securiteam.com/securityreviews/5KP0M1FJ5E.html&quot;&gt;you can add custom headers&lt;/a&gt;.  It would appear that this simple test is not enough.  But since 2.1 is not yet out, I&#39;m not sure if this is the extent of what will be checked.  Assuming that Google will probably handle it correctly in the end, I decided not to spend a ton of time architecting an elegant solution.&lt;br /&gt;
&lt;br /&gt;
&lt;tt&gt;src/com/lisedex/volinfoman/client/data/UserService.java&lt;/tt&gt;&lt;br /&gt;
&lt;pre class=&quot;brush: java&quot;&gt;       boolean isAuthenticated(final String sessionId);
&lt;/pre&gt;&lt;br /&gt;
All of my &lt;tt&gt;UserService&lt;/tt&gt; RPC methods will also pass a copy of the &lt;tt&gt;JSESSIONID&lt;/tt&gt; cookie in its parameters.  The servlet will then compare the passed copy with the header cookie, and if they match, we assume its a valid RPC call.  There&#39;s still a possibility that someone could sniff the cookie, but since this software is not exactly financial software, I&#39;m pretty comfortable with the level of security this adds.  More elaborate security could be added by &lt;a href=&quot;http://en.wikipedia.org/wiki/Cryptographic_nonce&quot;&gt;using a nonce&lt;/a&gt;, or maybe a hash across the session and the command name.&lt;br /&gt;
&lt;br /&gt;
&lt;tt&gt;src/com/lisedex/volinfoman/server/UserServiceImpl.java&lt;/tt&gt;&lt;br /&gt;
&lt;pre class=&quot;brush: java&quot;&gt;        public boolean isAuthenticated(final String sessionId) {
                if (validateSessionId(sessionId)) {
                        return SessionHandler.isAuthenticated(getSession());
                } else {
                        LOG.severe(&amp;quot;Possible CSRF.  JSESSIONID cookie does &amp;quot; +
                           &amp;quot;not match included session id&amp;quot;);
                        return false;
                }
        }

        private HttpSession getSession() {
                return this.getThreadLocalRequest().getSession();
        }

        private boolean validateSessionId(String sessionId) {
                String headerSessionId = (String) getSession().getId();
                if (sessionId != null &amp;amp;&amp;amp; headerSessionId != null
                                &amp;amp;&amp;amp; headerSessionId.equals(sessionId)) {
                        return true;
                }
                return false;
        }
&lt;/pre&gt;&lt;br /&gt;
In each RPC method, we call &lt;tt&gt;validateSessionId&lt;/tt&gt;, which gets the session id from the &lt;tt&gt;HttpServletRequest&lt;/tt&gt;, and compares it with the passed session id.  Assuming they match, the RPC method will continue.&lt;br /&gt;
&lt;br /&gt;
&lt;tt&gt;src/com/lisedex/volinfoman/server/SessionHandler.java&lt;/tt&gt;&lt;br /&gt;
&lt;pre class=&quot;brush: java&quot;&gt;        public static final String AUTHENTICATED = &amp;quot;authenticated&amp;quot;;

        public static void setAuthenticated(HttpSession session,
                        boolean authenticated) {
                session.setAttribute(AUTHENTICATED, authenticated);
        }

        public static boolean isAuthenticated(HttpSession session) {
                if (session == null)
                        return false;

                Boolean auth = (Boolean) session.getAttribute(AUTHENTICATED);
                if (auth == null) {
                        return false;
                } else {
                        return auth.booleanValue();
                }
        }
&lt;/pre&gt;&lt;br /&gt;
In the case of &lt;tt&gt;isAuthenticated&lt;/tt&gt;, we call &lt;tt&gt;SessionHandler.isAuthenticated&lt;/tt&gt;, a static method that operates on the &lt;tt&gt;HttpServletRequest&lt;/tt&gt; session, and just gets the value of the &lt;tt&gt;authenticated&lt;/tt&gt; attribute stored in the session table in the datastore.&lt;br /&gt;
&lt;br /&gt;
In our &lt;tt&gt;Login&lt;/tt&gt; servlet, we call the &lt;tt&gt;setAuthenticated()&lt;/tt&gt; method to mark the session as authenticated if the login is successful, and before redirecting the user to the GWT application.&lt;br /&gt;
&lt;br /&gt;
The new home page, &lt;tt&gt;MainPageView&lt;/tt&gt;, is just a &lt;tt&gt;SimplePanel&lt;/tt&gt;, which should allow us to do a very small application load before checking whether the session is authenticated.  If it is, we can put a new widget which contains the actual layout in the panel.&lt;br /&gt;
&lt;br /&gt;
&lt;tt&gt;src/com/lisedex/volinfoman/client/MainPagePresenter.java&lt;/tt&gt;&lt;br /&gt;
&lt;pre class=&quot;brush: java&quot;&gt;    private static final String HOME_PAGE_URL = &amp;quot;/index.html&amp;quot;;

    private UserServiceAsync userService = null;

    @Override
    public void bind() {
        view.setContent(new HTMLPanel(LOADING_MESSAGE));
        userService.isAuthenticated(
                Cookies.getCookie(Volinfoman.SESSION_COOKIE),
                    new AsyncCallback&amp;lt;Boolean&amp;gt;() {
                        @Override
                        public void onSuccess(Boolean result) {
                                if (result.booleanValue() == true) {
                                        view.setContent(new HTMLPanel(
                                            &amp;quot;&amp;lt;h1&amp;gt;AUTHENTICATED&amp;lt;/h1&amp;gt;&amp;quot;));
                                } else {
                                        view.setContent(new HTMLPanel(
                                                NOT_AUTHENTICATED_MESSAGE));
                                        Window.Location.replace(HOME_PAGE_URL);
                                }
                        }

                        @Override
                        public void onFailure(Throwable caught) {
                                view.setContent(new HTMLPanel(RPC_FAILURE_MESSAGE));
                        }
                });
    }
&lt;/pre&gt;&lt;br /&gt;
We pop a loading message in as the widget in our &lt;tt&gt;SimplePanel&lt;/tt&gt;, and call the RPC &lt;tt&gt;isAuthenticated&lt;/tt&gt; method.  &lt;tt&gt;Cookies.getCookie&lt;/tt&gt; is a static method which gives us access to the browser&#39;s cookies for our site.  If we get a successful response for our RPC method, and it turns out that our session is marked as authenticated, we put up another message just informing us that we authenticated successfully.  In the future, we&#39;d set the &lt;tt&gt;SimplePanel&lt;/tt&gt;&#39;s widget to some sort of layout panel, and set up our application.  If our session is not marked as authenticated, we redirect the browser back to the login page.  The &lt;tt&gt;Window&lt;/tt&gt; class gives us access to some of the user&#39;s browser&#39;s properties and events.  In this case, &lt;tt&gt;Window.Location&lt;/tt&gt; allows us to change the current &lt;tt&gt;URL&lt;/tt&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;Paying attention&lt;/h3&gt;&lt;br /&gt;
This definitely shows me how, if I&#39;m not paying attention, security lapses can easily slip right by.  Writing the blog entries actually helps me reparse the stuff that I&#39;ve written, and catch some of my mistakes before I move on, but even then I almost blew right past this one.&lt;br /&gt;
&lt;br /&gt;
The &lt;a href=&quot;https://addons.mozilla.org/en-US/firefox/addon/3829/&quot;&gt;Live HTTP Headers plugin for Firefox&lt;/a&gt; made testing and playing with this vulnerability really easy.  I could simply catch a remote call, and then replay it with whatever modifications I wanted to make: header modifications or deletions, payload changes.  It&#39;s pretty smooth.  I also made some packet captures, but this plugin is far easier.&lt;br /&gt;
&lt;br /&gt;
I think it&#39;s time to start making some sort of user configuration wizard, where people can set up their volunteering preferences, or, if they happen to be a volunteer coordinator, the volunteer opportunities for their organization.</content><link rel='replies' type='application/atom+xml' href='http://gaegwtbestpractices.blogspot.com/feeds/5416267311374335049/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://gaegwtbestpractices.blogspot.com/2010/09/authenticating-in-gwt.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3248335039039953287/posts/default/5416267311374335049'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3248335039039953287/posts/default/5416267311374335049'/><link rel='alternate' type='text/html' href='http://gaegwtbestpractices.blogspot.com/2010/09/authenticating-in-gwt.html' title='Authenticating in GWT'/><author><name>John Schutz</name><uri>http://www.blogger.com/profile/12385766703556945544</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiRkpC7-LIVJCQcGKsOoSDO2UZitOmX-n7VCMnXq4HqTQKmeonrbB6vpvwBWw_hKCrK3wFhKstFOC9cObvuJjzpB27YS6SRMGMs9_RT8_GmNKc8KRDMAAqFQaDwZxPhMXo/s220/2009-10-26.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3248335039039953287.post-2719633894212108434</id><published>2010-09-04T04:03:00.001-05:00</published><updated>2010-09-04T04:07:51.759-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="best_practices"/><category scheme="http://www.blogger.com/atom/ns#" term="dao"/><category scheme="http://www.blogger.com/atom/ns#" term="datastore"/><category scheme="http://www.blogger.com/atom/ns#" term="dependency_injection"/><category scheme="http://www.blogger.com/atom/ns#" term="gae"/><category scheme="http://www.blogger.com/atom/ns#" term="guice"/><category scheme="http://www.blogger.com/atom/ns#" term="gwt"/><category scheme="http://www.blogger.com/atom/ns#" term="objectify"/><category scheme="http://www.blogger.com/atom/ns#" term="servlet"/><category scheme="http://www.blogger.com/atom/ns#" term="tutorial"/><title type='text'>Some management servlets</title><content type='html'>So I got StarCraft II, and let&#39;s just say I&#39;ve spent a lot more time with that than I have with this project, what with playing, watching &lt;a href=&quot;http://day9tv.blip.tv/&quot;&gt;Day9 Daily&lt;/a&gt; archives, reading strategy...&lt;br /&gt;
&lt;br /&gt;
And I&#39;m still in the bronze league.  You may now commence laughing.&lt;br /&gt;
&lt;br /&gt;
But a couple of days ago, I picked this stuff back up.  Leaving it for so long is a mistake, as it always takes me a while to remember what&#39;s what, where I was, and where I was going.  I messed around with the interface, trying to get a very basic layout with a working login page.&lt;br /&gt;
&lt;br /&gt;
I learned a few important lessons, like wrapping widgets with &lt;tt&gt;SimplePanel&lt;/tt&gt;s so they could be swapped out easily with just a &lt;tt&gt;setWidget()&lt;/tt&gt; as the interface required, and my previous understanding, as explained in my last entry, of &lt;tt&gt;bind()&lt;/tt&gt; from &lt;tt&gt;mvp4g&lt;/tt&gt; was off.  But I learned a much more important lesson.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;I have no idea what I&#39;m talking about&lt;/h3&gt;&lt;br /&gt;
OK, that&#39;s maybe a bit harsh, but really, I think I was coming at this with a fundamental misconception about how it&#39;ll work.  I was trying to build an application where you could login, or register for the site, or request a reset password, or actually use the site for its intended purpose basically without leaving the layout and original landing page.  Lunacy.  Trying to fit the project to the tool, while not understanding the tool all that well, either.&lt;br /&gt;
&lt;br /&gt;
At first, I realized things should work more like Gmail, where your initial login and registration could be handled on some regular pages, and then you move in to the application itself.  Flipping this switch in my brain alone allowed me to start making much faster progress (obviously helped along because I had moved back to a more familiar paradigm).  Taking that lesson more generally, I shouldn&#39;t force this thing into a single page where widgets are swapped in and out.  If it makes more sense, I should be willing to just move to another page, or sprinkle parts of GWT throughout pages, unless there&#39;s too much of a startup penalty.  &lt;br /&gt;
&lt;br /&gt;
In short, I don&#39;t need to write this as if it were some monolithic desktop application.&lt;br /&gt;
&lt;br /&gt;
On that note, I&#39;m going to focus in this post on the account registration, email confirmation, App Engine cron jobs.  At this time, these all use GAE, but not GWT.  You&#39;ll just have to ignore the GWT client code for now, as it&#39;s in a screwy state that compiles and renders a page, but doesn&#39;t do anything remotely useful.&lt;br /&gt;
&lt;br /&gt;
As always, the code is available &lt;a href=&quot;http://github.com/tofupup/volinfoman/tree/add-static-login&quot;&gt;at github under the add-static-login tag&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;Moving the starting line&lt;/h3&gt;&lt;br /&gt;
The first step was moving the home page, and having it basically static with a login form and a link to the registration page.&lt;br /&gt;
&lt;br /&gt;
&lt;tt&gt;war/WEB-INF/web.xml&lt;/tt&gt;&lt;br /&gt;
&lt;pre class=&quot;brush: html&quot;&gt;&amp;lt;!-- Default page to serve --&amp;gt;
        &amp;lt;welcome-file-list&amp;gt;
               &amp;lt;welcome-file&amp;gt;index.html&amp;lt;/welcome-file&amp;gt;
        &amp;lt;/welcome-file-list&amp;gt;
&lt;/pre&gt;&lt;br /&gt;
I can keep the original &lt;tt&gt;Volinfoman.html&lt;/tt&gt; for later, and this automatically solves one of the issues I had been thinking about, but had put off for later: auto-complete not working in GWT login pages, and using &lt;tt&gt;HTTPS&lt;/tt&gt; to submit the user&#39;s password.&lt;br /&gt;
&lt;br /&gt;
The link to the registration page leads to another simple, static form, &lt;tt&gt;register.html&lt;/tt&gt;.  This submits to a servlet which is written using App Engine, &lt;tt&gt;Register&lt;/tt&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;The making of a servlet&lt;/h3&gt;&lt;br /&gt;
We need to register this servlet with Guice so that injection works.&lt;br /&gt;
&lt;br /&gt;
&lt;tt&gt;src/com/lisedex/volinfoman/server/guice/VolinfomanGuiceModule.java&lt;/tt&gt;&lt;br /&gt;
&lt;pre class=&quot;brush: java&quot;&gt;bind(Register.class).in(Singleton.class);
&lt;/pre&gt;&lt;br /&gt;
&lt;tt&gt;src/com/lisedex/volinfoman/server/guice/VolinfomanServletModule.java&lt;/tt&gt;&lt;br /&gt;
&lt;pre class=&quot;brush: java&quot;&gt;serve(&amp;quot;/volinfoman/register&amp;quot;).with(Register.class);
&lt;/pre&gt;&lt;br /&gt;
In our Guice module, we configure &lt;tt&gt;Register&lt;/tt&gt; as a &lt;tt&gt;Singleton&lt;/tt&gt;, and in Guice&#39;s servlet configuration, we let it know that the URL &lt;tt&gt;/volinfoman/register&lt;/tt&gt; should be sent to &lt;tt&gt;Register&lt;/tt&gt; for handling.  This is, of course, the action we use for the form in &lt;tt&gt;register.html&lt;/tt&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;tt&gt;src/com/lisedex/volinfoman/server/authenticate/Register.java&lt;/tt&gt;&lt;br /&gt;
&lt;pre class=&quot;brush: java&quot;&gt;public class Register extends HttpServlet {
        @Inject
        private Dao dao;

        private static final Logger log = Logger.getLogger(Register.class.getName());

        @Override
        public void doGet(HttpServletRequest req, HttpServletResponse resp)
                        throws IOException {

                PrintWriter output = resp.getWriter();

                // build HTML response page
                resp.setContentType(&amp;quot;text/html&amp;quot;);
                resp.setCharacterEncoding(&amp;quot;utf-8&amp;quot;);
                output.println(&amp;quot;&amp;lt;head&amp;gt;&amp;lt;title&amp;gt;Add initial datastore information&amp;lt;/title&amp;gt;&amp;lt;/head&amp;gt;&amp;quot;);
                output.println(&amp;quot;&amp;lt;body&amp;gt;&amp;quot;);

                String username = req.getParameter(&amp;quot;username&amp;quot;);
                String firstName = req.getParameter(&amp;quot;firstName&amp;quot;);
                String lastName = req.getParameter(&amp;quot;lastName&amp;quot;);
                String password = req.getParameter(&amp;quot;password&amp;quot;);
                String email = req.getParameter(&amp;quot;email&amp;quot;);

                if (!StringSafety.isSafe(username)) {
                        output.println(&amp;quot;&amp;lt;span style=\&amp;quot;color: #ff0000;\&amp;quot;&amp;gt;Username bad, please go back and enter it again&amp;lt;/span&amp;gt;&amp;quot;);
                        output.println(&amp;quot;&amp;lt;/body&amp;gt;&amp;quot;);
                        return;
                }

                if (!StringSafety.isSafe(firstName)) {
                        output.println(&amp;quot;&amp;lt;span style=\&amp;quot;color: #ff0000;\&amp;quot;&amp;gt;First name bad, please go back and enter it again&amp;lt;/span&amp;gt;&amp;quot;);
                        output.println(&amp;quot;&amp;lt;/body&amp;gt;&amp;quot;);
                        return;
                }

                // ... etc, etc. for input safety ...

                if (dao.getUser(username) != null) {
                        output.println(&amp;quot;&amp;lt;span style=\&amp;quot;color: #ff0000;\&amp;quot;&amp;gt;Username already exists, please go back and enter it again&amp;lt;/span&amp;gt;&amp;quot;);
                        output.println(&amp;quot;&amp;lt;/body&amp;gt;&amp;quot;);
                        return;
                }
&lt;/pre&gt;&lt;br /&gt;
This first pass at the servlet is pretty ugly, with plenty of string literals, but what we&#39;re doing is laying the groundwork for the registration by collecting the user&#39;s input.&lt;br /&gt;
&lt;br /&gt;
We extend &lt;tt&gt;javax.servlet.http.HttpServlet&lt;/tt&gt; and override its &lt;tt&gt;doGet()&lt;/tt&gt; method, since at this point we have not declared a method for our &lt;tt&gt;HTML&lt;/tt&gt; form to use and it defaults to &lt;tt&gt;GET&lt;/tt&gt;.  Later, we&#39;ll change this to &lt;tt&gt;doPost()&lt;/tt&gt; since the user is entering their password in the form, and we don&#39;t really need that showing up in the address bar of their browser.  Or our server logs.&lt;br /&gt;
&lt;br /&gt;
We start setting up the page that gets sent back to the user when the servlet completes, by filling out the &lt;tt&gt;HttpServletResponse&lt;/tt&gt; object passed in as &lt;tt&gt;resp&lt;/tt&gt;.  After setting what type of page we&#39;ll be returning, we print to the object line-by-line in the order we would want to build a standard &lt;tt&gt;HTML&lt;/tt&gt; page.&lt;br /&gt;
&lt;br /&gt;
We need to get the user&#39;s input, so we use the &lt;tt&gt;getParameter&lt;/tt&gt; method of &lt;tt&gt;HttpServletRequest&lt;/tt&gt;, which parses the form data for us, and hides the logic needed to handle both &lt;tt&gt;GET&lt;/tt&gt; and &lt;tt&gt;POST&lt;/tt&gt; forms.  After we get the input, we want to protect ourselves from things like &lt;a href=&quot;http://en.wikipedia.org/wiki/SQL_injection&quot;&gt;injection attacks&lt;/a&gt;, so we have to check each piece of input that comes from outside of the servlet.  The static &lt;tt&gt;StringSafety.isSafe()&lt;/tt&gt; method right now is terribly simple, just looking for ; and &amp;amp; characters.  Later, we&#39;d want to do better verification, like making sure the username and password meet whatever requirements we have and that the email address looks basically valid.&lt;br /&gt;
&lt;br /&gt;
If any of the input doesn&#39;t meet our standards, we spit out an error message and abort the servlet.  I check each separately, so I can inform the user of which field doesn&#39;t meet our standards.  Ideally, instead of forcing the user to go back, we&#39;d re-render the form, with the error message integrated into it, but right now we&#39;re doing it the simple way.  I removed some of the repetitious statements for brevity; see the code for the full version.&lt;br /&gt;
&lt;br /&gt;
Finally, we make sure that the username requested doesn&#39;t already exist.  This seems like an expensive way to look this up, and is also open to another account with the same username getting created between that test and the next lines of code.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush: java&quot;&gt;User user = new User(null, username, 
                    User.STATUS_UNCONFIRMED, firstName, lastName, 
                    email, password);
                dao.putUser(user);
&lt;/pre&gt;&lt;br /&gt;
We put the user in the datastore, with a status of &lt;tt&gt;User.STATUS_UNCONFIRMED&lt;/tt&gt;.  This status indicates that the account has been created, but the user has not yet clicked the link included in the email we&#39;re about to send them.  This code has a bug, in that the password is not passed through BCrypt before storage.  We&#39;ll fix this as we come back and fix this class up.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush: java&quot;&gt;Properties props = new Properties();
                Session session = Session.getDefaultInstance(props, null);

                String msgBody = &amp;quot;Thank you for registering a VolunteerIM &amp;quot;
                   + &amp;quot;account!  Please follow the link below to confirm &amp;quot;
                   + &amp;quot;your account:\n\n&amp;quot;;
                Random r = new Random();
                msgBody += &amp;quot;http://lisedexvolinfomantest/volinfoman/emailConfirm?code=&amp;quot;
                    + Long.toString(Math.abs(r.nextLong()), 36)
                    + &amp;quot;\n\n&amp;quot;;
                msgBody += &amp;quot;Note: Please do not reply to this address, as &amp;quot;
                    + &amp;quot;email is thrown away.  If you did not set up a &amp;quot;
                    + &amp;quot;VolunteerIM account, please ignore this email, as the &amp;quot;
                    + &amp;quot;account will be removed automatically in a week.\n&amp;quot;;

                try {
                    Message msg = new MimeMessage(session);
                    msg.setFrom(new InternetAddress(&amp;quot;admin@lisedex.com&amp;quot;, 
                        &amp;quot;VolunteerIM Confirmation&amp;quot;));
                    msg.addRecipient(Message.RecipientType.TO,
                             new InternetAddress(email, firstName + 
                                 &amp;quot; &amp;quot; + lastName));
                    msg.setSubject(&amp;quot;VolunteerIM account confirmation&amp;quot;);
                    msg.setText(msgBody);
                    Transport.send(msg);
                } catch (AddressException e) {
                    output.println(&amp;quot;Bad email address.  Please try again. &amp;quot; +
                        e.toString() + &amp;quot;&amp;lt;/body&amp;gt;&amp;quot;);
                    log.info(&amp;quot;AddressException sending confirmation email: &amp;quot; +
                        e.toString());
                    return;
                } catch (MessagingException e) {
                    output.println(&amp;quot;Error sending confirmation email.  Please try again. &amp;quot;
                        + e.toString() + &amp;quot;&amp;lt;/body&amp;gt;&amp;quot;);
                    log.info(&amp;quot;MessagingException sending confirmation email: &amp;quot;
                        + e.toString());
                    return;
                }

        output.println(&amp;quot;We have sent a confirmation email to &amp;quot; 
             + email + &amp;quot;.  It should arrive shortly.  As soon as you receive &amp;quot; 
             + &amp;quot;it, please &amp;lt;a href=\&amp;quot;/\&amp;quot;&amp;gt;return to the front &amp;quot;
             + &amp;quot;page to log in.&amp;lt;/a&amp;gt;&amp;quot;);
        }
}
&lt;/pre&gt;&lt;br /&gt;
Google&#39;s App Engine has an API for sending emails which uses the &lt;a href=&quot;http://java.sun.com/products/javamail/javadocs/index.html&quot;&gt;JavaMail API&lt;/a&gt;, but we just use a tiny subset.  &lt;br /&gt;
&lt;br /&gt;
We need a reference to a mail &lt;tt&gt;Session&lt;/tt&gt;, which we use to build a &lt;tt&gt;MIME&lt;/tt&gt; compatible email.  First, we build a quick message body, which contains a link with a random string that we&#39;ll use as a confirmation code.&lt;br /&gt;
&lt;br /&gt;
When we set the address that the message will come from, it has to be set as an email address that is listed in the App Engine dashboard as a collaborator on the project.  I found this the hard way, after a &lt;tt&gt;MessagingException&lt;/tt&gt; kept getting thrown during testing.  The code worked fine on the local development server, but when deployed it choked.&lt;br /&gt;
&lt;br /&gt;
Then we specify the recipient and subject, and attach the message body we already generated.&lt;br /&gt;
&lt;br /&gt;
We can still get an exception when sending the email, if we use a &lt;tt&gt;To&lt;/tt&gt; address App Engine doesn&#39;t like, or, as I found out, a &lt;tt&gt;From&lt;/tt&gt; address that&#39;s not registered.  If one of these pop up, we generate an error for the user, and exit the servlet.  At this stage of the code, this will leave an &lt;tt&gt;UNCONFIRMED&lt;/tt&gt; account in the datastore, with no confirmation email sent.  Whooops.&lt;br /&gt;
&lt;br /&gt;
If everything&#39;s gone according to plan, we let the user know that they should keep an eye out for the confirmation email.&lt;br /&gt;
&lt;br /&gt;
Of course, right now, if the user were to click on the link in the email, we&#39;d have to return a &lt;tt&gt;404&lt;/tt&gt;, since there&#39;s no servlet registered at /volinfoman/emailConfirm.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;OK, do you at least have an email account you can read?&lt;/h3&gt;&lt;br /&gt;
Before that, let&#39;s change the registration form to use the &lt;tt&gt;POST&lt;/tt&gt; method.  The &lt;tt&gt;GET&lt;/tt&gt; method puts all of the user&#39;s input into the &lt;tt&gt;URL&lt;/tt&gt;, which include the user&#39;s password in plaintext.  So not only does it show in their address bar, it will live on forever in the server logs.  Fortunately, &lt;tt&gt;HttpServlet&lt;/tt&gt; makes this easy: just add &lt;tt&gt;method=&amp;quot;post&amp;quot;&lt;/tt&gt; to the &lt;tt&gt;form&lt;/tt&gt; declaration in &lt;tt&gt;register.html&lt;/tt&gt;, and change the &lt;tt&gt;doGet()&lt;/tt&gt; method in &lt;tt&gt;Register&lt;/tt&gt; to &lt;tt&gt;doPost()&lt;/tt&gt;.  The class library handles the details.&lt;br /&gt;
&lt;br /&gt;
Now, we want to make sure that the email address the user entered is linked to an account they can read, like every other website in existence with individual accounts.  We&#39;re sending a link with a code embedded to the user after they register, so we&#39;ll need a servlet to manage confirming the accounts.  We could put the confirmation code for each user into the &lt;tt&gt;User&lt;/tt&gt; class, and have the datastore index it so we could run a query, but we also want to expire accounts that haven&#39;t been confirmed after some length of time.  So then we&#39;d need to add an indexed expiration date field to the &lt;tt&gt;User&lt;/tt&gt; class, and it starts to look cleaner to create a datastore table just for this purpose.&lt;br /&gt;
&lt;br /&gt;
&lt;tt&gt;src/com/lisedex/volinfoman/shared/ConfirmationCode.java&lt;/tt&gt;&lt;br /&gt;
&lt;pre class=&quot;brush: java&quot;&gt;public class ConfirmationCode implements Serializable {
        @Id
        private Long id;

        @Indexed
        private String username;

        @Indexed
        private String code;

        @Indexed
        private long expires;

        public ConfirmationCode() {
        }

        /**
         * Constructor
         * @param id Datastore primary key
         * @param username username associated with code
         * @param code confirmation code associated with username
         * @param expires expiration date for code in milliseconds
         */
        public ConfirmationCode(Long id, String username, String code, long expires) {
                setId(id);
                setUsername(username);
                setCode(code);
                setExpires(expires);
        }

        public Long getId() {
                return id;
        }

        public void setId(Long id) {
                this.id = id;
        }

        public String getUsername() {
                return username;
        }

        // ... etc, etc ... it&#39;s getters and setters all the way down
}

&lt;/pre&gt;&lt;br /&gt;
It&#39;s a pretty bare bones data structure, though we should probably add some input verification later to the setters.  We also index all of the elements of the table.  At different times we&#39;ll need to search by the code, and we&#39;ll also need to query for expiration times that are older than a certain time.  Currently, the username does not need to be indexed, but we&#39;ll leave it for now.&lt;br /&gt;
&lt;br /&gt;
In the &lt;a href=&quot;http://gaegwtbestpractices.blogspot.com/2010/07/guice.html&quot;&gt;earlier blog entry about Guice&lt;/a&gt;, I talked some about a &lt;tt&gt;BuildDB&lt;/tt&gt; class which could delete all &lt;tt&gt;User&lt;/tt&gt;s in the datastore, and just leave one admin account.  Well, we also want the option of wiping out our confirmation code table while developing, which requires a new method in our &lt;tt&gt;DAO&lt;/tt&gt;.  We declare it in the &lt;tt&gt;Dao&lt;/tt&gt; interface, and implement it in &lt;tt&gt;DaoGaeDatastore&lt;/tt&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;tt&gt;src/com/lisedex/volinfoman/server/DaoGaeDatastore.java&lt;/tt&gt;&lt;br /&gt;
&lt;pre class=&quot;brush: java&quot;&gt;@Override
        public void deleteUser(Long id) {
                ofy().delete(User.class, id.toString());
        }

        @Override
        public void putConfirmationCode(ConfirmationCode code) {
                ofy().put(code);
        }

        @Override
        public void deleteAllConfirmationCodes() {
                ofy().delete(ofy().query(ConfirmationCode.class).fetchKeys());
        }
&lt;/pre&gt;&lt;br /&gt;
While we&#39;re in there, we added a couple of other methods that we&#39;ll use to work with the confirmation codes right now.  We&#39;ll need a way to delete the users that we put into the datastore, if we get an exception during sending them their confirmation email.  When the exception is caught, we want to throw away the new &lt;tt&gt;User&lt;/tt&gt; object we put in the datastore, so the user can reuse it when they resubmit their registration.  When calling this, we&#39;ll already have the &lt;tt&gt;User&lt;/tt&gt; object, which includes the &lt;tt&gt;Id&lt;/tt&gt; field, so we won&#39;t need to run a query; we can just straight up delete using an Objectify convenience method that builds a datastore &lt;tt&gt;Key&lt;/tt&gt; from the provided class and its &lt;tt&gt;Id&lt;/tt&gt;.&lt;br /&gt;
&lt;br /&gt;
We&#39;ll also need to store new &lt;tt&gt;ConfirmationCode&lt;/tt&gt; objects, and the &lt;tt&gt;deleteAllConfirmationCodes()&lt;/tt&gt; method is pretty much ripped from the &lt;tt&gt;deleteAllUsers()&lt;/tt&gt; method verbatim (just changing the class).&lt;br /&gt;
&lt;br /&gt;
We also need to register the &lt;tt&gt;ConfirmationCode&lt;/tt&gt; class with &lt;tt&gt;Objectify&lt;/tt&gt;, so it knows how to handle it.  We do this in the same place we register &lt;tt&gt;User&lt;/tt&gt;: a static constructor in our &lt;tt&gt;DAO&lt;/tt&gt; implementation.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush: java&quot;&gt;ObjectifyService.register(ConfirmationCode.class);
&lt;/pre&gt;&lt;br /&gt;
Now, in our &lt;tt&gt;BuildDB&lt;/tt&gt; servlet, we can simply call &lt;tt&gt;dao.deleteAllConfirmationCodes&lt;/tt&gt; when we want, and clean out the table.  &amp;quot;Dangerous Database Deletions&amp;quot; are my middle names.&lt;br /&gt;
&lt;br /&gt;
Now, in our &lt;tt&gt;Register&lt;/tt&gt; servlet, we want to add the confirmation code to the datastore.&lt;br /&gt;
&lt;br /&gt;
&lt;tt&gt;src/com/lisedex/volinfoman/server/authenticate/Register.java&lt;/tt&gt;&lt;br /&gt;
&lt;pre class=&quot;brush: java&quot;&gt;public static final int EXPIRATION_FIELD = Calendar.DATE;
    public static final int EXPIRATION_INCREMENT = 7;
&lt;/pre&gt;&lt;br /&gt;
We&#39;ll use the Java &lt;tt&gt;Calendar&lt;/tt&gt; class to calculate our expiration date.  This gives us flexibility to easily change the delay without having to recalculate how many milliseconds it is.  The &lt;tt&gt;EXPIRATION_FIELD&lt;/tt&gt; says which field we&#39;ll be incrementing, where &lt;tt&gt;DATE&lt;/tt&gt; is days, but we could also use &lt;tt&gt;MONTH&lt;/tt&gt; or &lt;tt&gt;HOUR&lt;/tt&gt; or even &lt;tt&gt;YEAR&lt;/tt&gt;.  &lt;tt&gt;Calendar&lt;/tt&gt; will handle the rollover and carrying the ones and the leap years and leap seconds and all of that.  The &lt;tt&gt;EXPIRATION_INCREMENT&lt;/tt&gt;, surprisingly enough, is how much that field will be incremented by.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush: java&quot;&gt;Random r = new Random();
                String code = Long.toString(Math.abs(r.nextLong()), 36);
                Calendar expirationTime = Calendar.getInstance();
                expirationTime.add(EXPIRATION_FIELD, EXPIRATION_INCREMENT);

                ConfirmationCode confCode = new ConfirmationCode(null, username, 
                    code, expirationTime.getTimeInMillis());
                dao.putConfirmationCode(confCode);
&lt;/pre&gt;&lt;br /&gt;
We generate the confirmation code before the message body so we can insert it into the &lt;tt&gt;ConfirmationCode&lt;/tt&gt; table in the datastore.  &lt;tt&gt;Calendar.getInstance()&lt;/tt&gt; gets a &lt;tt&gt;Calendar&lt;/tt&gt; object representing the current time, and we add however much time we&#39;d like to make our expiration time in milliseconds.&lt;br /&gt;
&lt;br /&gt;
In both of the exception handlers for email transmission errors, we add a line to delete the users we just added to the datastore.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush: java&quot;&gt;dao.deleteUser(user.getId());
&lt;/pre&gt;&lt;br /&gt;
&lt;h3&gt;You do know that anyone in the world can delete your datastore, right?&lt;/h3&gt;&lt;br /&gt;
In the &lt;a href=&quot;http://gaegwtbestpractices.blogspot.com/2010/07/guice.html&quot;&gt;Guice blog entry&lt;/a&gt;, we moved &lt;tt&gt;BuildDB&lt;/tt&gt; under control of &lt;tt&gt;Guice&lt;/tt&gt; so we could inject our &lt;tt&gt;DAO&lt;/tt&gt; implementation.  In doing so, we lost the ability to use the App Engine user authentication to control access to the servlet.  As we get further along, this becomes unacceptable, but I&#39;m still not ready to build my own authentication mechanism into the servlet.  We&#39;ll also need the authentication for protecting our cron jobs, so fixing this is not a waste.&lt;br /&gt;
&lt;br /&gt;
In &lt;tt&gt;BuildDB&lt;/tt&gt;, we no longer inject the &lt;tt&gt;Dao&lt;/tt&gt;, we directly instantiate a &lt;tt&gt;DaoGaeDatastore&lt;/tt&gt;.  In the &lt;tt&gt;VolinfomanGuiceModule&lt;/tt&gt; and &lt;tt&gt;VolinfomanServletModule&lt;/tt&gt;, we pull out our definitions for &lt;tt&gt;BuildDB&lt;/tt&gt; (and &lt;tt&gt;CacheStats&lt;/tt&gt;, another servlet that I don&#39;t believe works at the moment).&lt;br /&gt;
&lt;br /&gt;
&lt;tt&gt;war/WEB-INF/web.xml&lt;/tt&gt;&lt;br /&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;&amp;lt;filter-mapping&amp;gt;
            &amp;lt;filter-name&amp;gt;guiceFilter&amp;lt;/filter-name&amp;gt;
            &amp;lt;url-pattern&amp;gt;/volinfoman/*&amp;lt;/url-pattern&amp;gt;
    &amp;lt;/filter-mapping&amp;gt;


    &amp;lt;security-constraint&amp;gt;
        &amp;lt;web-resource-collection&amp;gt;
            &amp;lt;url-pattern&amp;gt;/admin/*&amp;lt;/url-pattern&amp;gt;
        &amp;lt;/web-resource-collection&amp;gt;
        &amp;lt;auth-constraint&amp;gt;
            &amp;lt;role-name&amp;gt;admin&amp;lt;/role-name&amp;gt;
        &amp;lt;/auth-constraint&amp;gt;
    &amp;lt;/security-constraint&amp;gt;

    &amp;lt;servlet&amp;gt;
        &amp;lt;servlet-name&amp;gt;BuildDB&amp;lt;/servlet-name&amp;gt;
        &amp;lt;servlet-class&amp;gt;com.lisedex.volinfoman.server.admin.BuildDB&amp;lt;/servlet-class&amp;gt;
    &amp;lt;/servlet&amp;gt;

        &amp;lt;servlet-mapping&amp;gt;
                &amp;lt;servlet-name&amp;gt;BuildDB&amp;lt;/servlet-name&amp;gt;
                &amp;lt;url-pattern&amp;gt;/admin/builddb&amp;lt;/url-pattern&amp;gt;
        &amp;lt;/servlet-mapping&amp;gt;
&lt;/pre&gt;&lt;br /&gt;
In &lt;tt&gt;web.xml&lt;/tt&gt;, we will change the &lt;tt&gt;Guice&lt;/tt&gt; &lt;tt&gt;url-pattern&lt;/tt&gt; from &lt;tt&gt;/*&lt;/tt&gt; to &lt;tt&gt;/volinfoman/*&lt;/tt&gt;, to allow us some granularity in defining which servlets get processed through &lt;tt&gt;Guice&lt;/tt&gt;.  To get &lt;tt&gt;BuildDB&lt;/tt&gt; out of &lt;tt&gt;Guice&lt;/tt&gt;&#39;s control, we&#39;ll need to change its &lt;tt&gt;URL&lt;/tt&gt; from &lt;tt&gt;/volinfoman/admin/builddb&lt;/tt&gt; to &lt;tt&gt;/admin/builddb&lt;/tt&gt;.&lt;br /&gt;
&lt;br /&gt;
We also set a &lt;tt&gt;security-constraint&lt;/tt&gt; on the &lt;tt&gt;url-pattern&lt;/tt&gt; &lt;tt&gt;/admin/*&lt;/tt&gt; which requires the user have an administrator role for the application.  If the user is not logged in, they will be presented with a login form from Google that allows them to log in with whatever account they&#39;ve used as a collaborator for the project.  If their Google account is not registered as a collaborator on the project, they will get an error and the servlet will not run.  This is also used to protect cron jobs, as they will run as if run by an authenticated admin user.&lt;br /&gt;
&lt;br /&gt;
Finally, we set the &lt;tt&gt;url-pattern&lt;/tt&gt; that configures what &lt;tt&gt;URL&lt;/tt&gt;s &lt;tt&gt;BuildDB&lt;/tt&gt; processes.  Since &lt;tt&gt;/admin/builddb&lt;/tt&gt; matches the &lt;tt&gt;/admin/*&lt;/tt&gt; &lt;tt&gt;security-constraint&lt;/tt&gt; definition, it&#39;s protected by Google&#39;s authentication.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;Get rid of expired confirmation codes&lt;/h3&gt;&lt;br /&gt;
We want to run a cron job that will find any expired confirmation codes, and remove them.  It will also need to look at the &lt;tt&gt;User&lt;/tt&gt; associated with the code, and if it&#39;s still in the &lt;tt&gt;User.UNCONFIRMED&lt;/tt&gt; state, the &lt;tt&gt;User&lt;/tt&gt; needs to be deleted as well.  The reason we double check this, is that I can imagine a time when someone emails support, and support confirms their account, but forgets to remove the associated confirmation code.  If we didn&#39;t check the &lt;tt&gt;User&lt;/tt&gt;&#39;s status, we could delete a confirmed account.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush: java&quot;&gt;@Override
        public void expireCodesBefore(long now) {
                Query&amp;lt;ConfirmationCode&amp;gt; oldCodes = 
                    ofy().query(ConfirmationCode.class).
                        filter(&amp;quot;expires &amp;lt;&amp;quot;, now);
                for (ConfirmationCode code: oldCodes) {
                        User user = getUser(code.getUsername());
                        if (user != null) {
                                // make sure user is still in unconfirmed state
                                if (user.getStatus() == User.STATUS_UNCONFIRMED) {
                                        deleteUser(user.getId());
                                }
                        }

                        deleteConfirmationCode(code);
                }
        }

        @Override
        public ConfirmationCode getConfirmationCode(String code) {
                ConfirmationCode fetched = 
                    ofy().query(ConfirmationCode.class).
                        filter(&amp;quot;code&amp;quot;, code).get();
                return fetched;
        }

        @Override
        public void deleteConfirmationCode(ConfirmationCode code) {
                if (code != null) {
                        ofy().delete(code);
                }
        }

&lt;/pre&gt;&lt;br /&gt;
The &lt;tt&gt;expireCodesBefore()&lt;/tt&gt; method takes a time, in milliseconds, and deletes all confirmation codes with an expiration time that comes before that time.  This can be tested with a relational operator: if the expiration time is less than the time provided, it&#39;s expired.  &lt;br /&gt;
&lt;br /&gt;
The query does just that, and the query itself is &lt;tt&gt;Iterable&lt;/tt&gt;, so we walk through it.  For each expired code, we get the username associated with it, and pull in the &lt;tt&gt;User&lt;/tt&gt; by that name.  If the user exists, and the user&#39;s status is still unconfirmed, we delete the user.  The expired confirmation code is deleted regardless of the user&#39;s state.&lt;br /&gt;
&lt;br /&gt;
&lt;tt&gt;getConfirmationCode()&lt;/tt&gt; will retrieve a confirmation code by the code field, instead of by &lt;tt&gt;Id&lt;/tt&gt;.  To do this, we need to run a query.  Since it should only return one hit, we only return the first one found.  If there were multiple entries with the same username, we&#39;d only work with the first one.&lt;br /&gt;
&lt;br /&gt;
Lastly, we add a method to delete confirmation codes.&lt;br /&gt;
&lt;br /&gt;
In the &lt;tt&gt;Register&lt;/tt&gt; servlet, we move most of the string literals scatted throughout the code into static Strings at the top of the class.  This includes parameter names that are submitted by the registration form, as well as information used to build the email.  &lt;br /&gt;
&lt;br /&gt;
&lt;tt&gt;src/com/lisedex/volinfoman/server/authenticate/Register.java&lt;/tt&gt;&lt;br /&gt;
&lt;pre class=&quot;brush: java&quot;&gt;User user = new User(null, username, User.STATUS_UNCONFIRMED, 
                firstName, lastName, email, null);
            dao.changeUserPassword(user, password);
&lt;/pre&gt;&lt;br /&gt;
We now build the &lt;tt&gt;User&lt;/tt&gt; with a &lt;tt&gt;null&lt;/tt&gt; password, and insert the &lt;tt&gt;User&lt;/tt&gt; into the datastore using the password change method.  This allows us to hash the password before storing it, fixing the bug we introduced in the early version of the code.&lt;br /&gt;
&lt;br /&gt;
&lt;tt&gt;src/com/lisedex/volinfoman/server/cron/ExpireConfirmationCodes.java&lt;/tt&gt;&lt;br /&gt;
&lt;pre class=&quot;brush: java&quot;&gt;public class ExpireConfirmationCodes extends HttpServlet {
        private Dao dao = new DaoGaeDatastore();

    private static final Logger log = Logger.getLogger(ExpireConfirmationCodes.class.getName());

        @Override
        public void doGet(HttpServletRequest req, HttpServletResponse resp)
                throws IOException {

                Calendar now = Calendar.getInstance();
                log.info(&amp;quot;Expiring expiration codes at &amp;quot; + Long.toString(now.getTimeInMillis()));

                dao.expireCodesBefore(now.getTimeInMillis());
        }
}
&lt;/pre&gt;&lt;br /&gt;
The &lt;tt&gt;ExpireConfirmationCodes&lt;/tt&gt; cron job is just a stripped down servlet based on &lt;tt&gt;BuildDB&lt;/tt&gt;.  Since it accepts no parameters, we simply get the current time, and pass its value in milliseconds to the &lt;tt&gt;DAO&lt;/tt&gt; method we wrote above for this purpose.  Note that we&#39;re directly instantiating the &lt;tt&gt;DaoGaeDatastore&lt;/tt&gt; again, as we need to bypass &lt;tt&gt;Guice&lt;/tt&gt; since we need to put our cron jobs behind a &lt;tt&gt;security-constraint&lt;/tt&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;tt&gt;war/WEB-INF/web.xml&lt;/tt&gt;&lt;br /&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;&amp;lt;!-- Cron jobs --&amp;gt;
        &amp;lt;security-constraint&amp;gt;
        &amp;lt;web-resource-collection&amp;gt;
            &amp;lt;url-pattern&amp;gt;/cron/*&amp;lt;/url-pattern&amp;gt;
        &amp;lt;/web-resource-collection&amp;gt;
        &amp;lt;auth-constraint&amp;gt;
            &amp;lt;role-name&amp;gt;admin&amp;lt;/role-name&amp;gt;
        &amp;lt;/auth-constraint&amp;gt;
        &amp;lt;/security-constraint&amp;gt;
  
        &amp;lt;servlet&amp;gt;
                &amp;lt;servlet-name&amp;gt;expireConfirmationCodes&amp;lt;/servlet-name&amp;gt;
                &amp;lt;servlet-class&amp;gt;com.lisedex.volinfoman.server.cron.ExpireConfirmationCodes&amp;lt;/servlet-class&amp;gt;
        &amp;lt;/servlet&amp;gt;

        &amp;lt;servlet-mapping&amp;gt;
                &amp;lt;servlet-name&amp;gt;expireConfirmationCodes&amp;lt;/servlet-name&amp;gt;
                &amp;lt;url-pattern&amp;gt;/cron/expireConfirmationCodes&amp;lt;/url-pattern&amp;gt;
        &amp;lt;/servlet-mapping&amp;gt;
&lt;/pre&gt;&lt;br /&gt;
We add &lt;tt&gt;/cron/*&lt;/tt&gt; &lt;tt&gt;URL&lt;/tt&gt;s to what we hide behind Google&#39;s authentication, and define the &lt;tt&gt;URL&lt;/tt&gt; &lt;tt&gt;/cron/expireConfirmationCodes&lt;/tt&gt; as the one which invokes our expiration servlet.&lt;br /&gt;
&lt;br /&gt;
&lt;tt&gt;war/WEB-INF/cron.xml&lt;/tt&gt;&lt;br /&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;&amp;lt;cronentries&amp;gt;
        &amp;lt;cron&amp;gt;
                &amp;lt;url&amp;gt;/cron/expireConfirmationCodes&amp;lt;/url&amp;gt;
                &amp;lt;description&amp;gt;Expire confirmation codes every day&amp;lt;/description&amp;gt;
                &amp;lt;schedule&amp;gt;every day 02:00&amp;lt;/schedule&amp;gt;
        &amp;lt;/cron&amp;gt;
&amp;lt;/cronentries&amp;gt;
&lt;/pre&gt;&lt;br /&gt;
The cron job definition specifies what &lt;tt&gt;URL&lt;/tt&gt; gets called at what frequency.  If you use a frequency like &amp;quot;every 5 minutes&amp;quot;, the job is started 5 minutes after the last job completed.  For example, if the job took 1 minute to run, the first job would start at 01:00, the next run at 01:06, the next at 01:12, and so on.  If you wanted the jobs to start at 01:00, 01:05, and 01:10, you would need to add the keyword &lt;tt&gt;synchronized&lt;/tt&gt; to the schedule.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;Lastly (aka FINALLY), we can process the confirmation link and login form&lt;/h3&gt;&lt;br /&gt;
This has stretched a bit longer than I expected, but it&#39;s almost over.&lt;br /&gt;
&lt;br /&gt;
&lt;tt&gt;src/com/lisedex/volinfoman/server/authenticate/ConfirmationCodeChecker.java&lt;/tt&gt;&lt;br /&gt;
&lt;pre class=&quot;brush: java&quot;&gt;public class ConfirmationCodeChecker extends HttpServlet {
        private static final String ACCOUNT_NEW = &amp;quot;Sorry, the account you&#39;re trying&amp;quot; +
           &amp;quot; to confirmed has not reached the point where it can be confirmed.  &amp;quot; +
           &amp;quot;Unfortunately, the only way to resolve this is to contact our support&amp;quot; +
           &amp;quot; department, or &amp;lt;a href=\&amp;quot;/register.html\&amp;quot;&amp;gt;apply for a new account&amp;lt;/a&amp;gt;.&amp;quot; +
           &amp;quot;  We apologize for the inconvenience.&amp;quot;;

        private static final String ACCOUNT_INVALID = &amp;quot;&amp;quot;; // another message
        private static final String ALREADY_CONFIRMED = &amp;quot;&amp;quot;; // another message
        private static final String ACCOUNT_CLOSED = &amp;quot;&amp;quot;; // another message
        private static final String BAD_CONFIRMATION_CODE = &amp;quot;&amp;quot;;
        private static final String CONFIRMATION_SUCCESS = &amp;quot;&amp;quot;;

        @Inject
        private Dao dao;

        private static final Logger log = Logger
                        .getLogger(ConfirmationCodeChecker.class.getName());

        private static final String UNSAFE_ERROR = &amp;quot;&amp;lt;span style=\&amp;quot;color: #ff0000;\&amp;quot;&amp;gt;&amp;quot; +
            &amp;quot;There is a problem with the link used to confirm your user account.&amp;quot; +
            &amp;quot;  Please try clicking the link again, and if the problem continues, &amp;quot; +
            &amp;quot;return to the home page and request that the email is sent again.  &amp;quot; +
            &amp;quot;Or you can contact support.  Sorry for the inconvenience!&amp;lt;/span&amp;gt;&amp;quot;;

        @Override
        public void doGet(HttpServletRequest req, HttpServletResponse resp)
                        throws IOException {
                // standard stuff
                output.println(&amp;quot;&amp;lt;head&amp;gt;&amp;lt;title&amp;gt;VolunteerIM account confirmation&amp;lt;/title&amp;gt;&amp;lt;/head&amp;gt;&amp;quot;);
                output.println(&amp;quot;&amp;lt;body&amp;gt;&amp;quot;);

                output.println(&amp;quot;&amp;lt;h2&amp;gt;VolunteerIM account confirmation&amp;lt;/h2&amp;gt;&amp;lt;p&amp;gt;&amp;quot;);

                String username = req.getParameter(&amp;quot;username&amp;quot;);
                String code = req.getParameter(&amp;quot;code&amp;quot;);

                log.info(&amp;quot;Confirmation request with username &amp;quot; + username
                                + &amp;quot; and code &amp;quot; + code);

                if (!StringSafety.isSafe(username)) {
                        output.println(UNSAFE_ERROR + &amp;quot;&amp;lt;/body&amp;gt;&amp;quot;);
                        return;
                }

                if (!StringSafety.isSafe(code)) {
                        output.println(UNSAFE_ERROR + &amp;quot;&amp;lt;/body&amp;gt;&amp;quot;);
                        return;
                }

                ConfirmationCode testCode = dao.getConfirmationCode(code);
                if ((testCode == null) || (testCode.getUsername() == null)
                                || (testCode.getCode() == null)) {
                        output.println(BAD_CONFIRMATION_CODE + &amp;quot;&amp;lt;/body&amp;gt;&amp;quot;);
                        return;
                }

                User fetched = dao.getUser(testCode.getUsername());

                if (fetched.getStatus() == User.STATUS_UNCONFIRMED) {
                        fetched.setStatus(User.STATUS_CONFIRMED);
                        dao.putUser(fetched);

                        // eliminate confirmation code from datastore, as we&#39;re
                        // done with it
                        dao.deleteConfirmationCode(testCode);

                        output.println(CONFIRMATION_SUCCESS + &amp;quot;&amp;lt;/body&amp;gt;&amp;quot;);
                        log.info(&amp;quot;Confirmed username &amp;quot; + fetched.getUsername());
                        return;
                }

                if (fetched.getStatus() == User.STATUS_CLOSED) {
                        output.println(ACCOUNT_CLOSED + &amp;quot;&amp;lt;/body&amp;gt;&amp;quot;);
                        return;
                }

                if (fetched.getStatus() == User.STATUS_CONFIRMED) {
                        output.println(ALREADY_CONFIRMED + &amp;quot;&amp;lt;/body&amp;gt;&amp;quot;);
                        return;
                }

                if (fetched.getStatus() == User.STATUS_INVALID) {
                        output.println(ACCOUNT_INVALID + &amp;quot;&amp;lt;/body&amp;gt;&amp;quot;);
                        return;
                }

                if (fetched.getStatus() == User.STATUS_NEW) {
                        output.println(ACCOUNT_NEW + &amp;quot;&amp;lt;/body&amp;gt;&amp;quot;);
                        return;
                }
        }
}
&lt;/pre&gt;&lt;br /&gt;
This servlet is under &lt;tt&gt;Guice&lt;/tt&gt;&#39;s control, so its &lt;tt&gt;URL&lt;/tt&gt; is defined in &lt;tt&gt;VolinfomanGuiceModule&lt;/tt&gt; and &lt;tt&gt;VolinfomanServletModule&lt;/tt&gt;.  In this case it&#39;s &lt;tt&gt;/volinfoman/emailConfirm&lt;/tt&gt;, as specified in the email sent to the user.  &lt;br /&gt;
&lt;br /&gt;
We define a bunch of messages that may get sent to the user, for different states of the &lt;tt&gt;User&lt;/tt&gt; status, or malformed &lt;tt&gt;URL&lt;/tt&gt; parameters.  We get both a username and code from the &lt;tt&gt;URL&lt;/tt&gt;, which we check for bad data, and also use to cross check whether the code matches the specified username.  If it does, we pull up the &lt;tt&gt;User&lt;/tt&gt;, and based on its status, we return different messages.&lt;br /&gt;
&lt;br /&gt;
If the &lt;tt&gt;User&lt;/tt&gt; was previously unconfirmed, we tell them it&#39;s now confirmed, we mark it as such in the datastore, and they can now log in from the home page.  If it was closed, we let them know they can register for a new account, or contact support to find out why it was closed.  Already confirmed &lt;tt&gt;User&lt;/tt&gt;s get told they can go ahead and log in, and invalid and new users are told to contact support or register new accounts.  In the code above, I&#39;ve stripped the messages since they&#39;re pretty long, but they&#39;re available in the code in the &lt;a href=&quot;http://github.com/tofupup/volinfoman/tree/add-static-login&quot;&gt;repository at github&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;tt&gt;src/com/lisedex/volinfoman/server/authenticate/Login.java&lt;/tt&gt;&lt;br /&gt;
&lt;pre class=&quot;brush: java&quot;&gt;public class Login extends HttpServlet {
        @Inject
        Dao dao;

        private static final Logger log = Logger.getLogger(Login.class.getName());

        @Override
        public void doPost(HttpServletRequest req, HttpServletResponse resp)
                        throws IOException {

                // standard stuff

                String username = req.getParameter(&amp;quot;username&amp;quot;);
                String password = req.getParameter(&amp;quot;password&amp;quot;);

                if ((username == null) || (password == null)) {
                        output.println(&amp;quot;&amp;lt;head&amp;gt;&amp;lt;title&amp;gt;VolunteerIM login&amp;lt;/title&amp;gt;&amp;lt;/head&amp;gt;&amp;quot;);
                        output.println(&amp;quot;&amp;lt;body&amp;gt;Please fill out both username and &amp;quot; +
                            &amp;quot;password fields.&amp;lt;/body&amp;gt;&amp;quot;);
                        return;
                }

                if (!StringSafety.isSafe(username)) {
                        output.println(&amp;quot;&amp;lt;head&amp;gt;&amp;lt;title&amp;gt;VolunteerIM login&amp;lt;/title&amp;gt;&amp;lt;/head&amp;gt;&amp;quot;);
                        output.println(&amp;quot;&amp;lt;body&amp;gt;Username invalid, please go back and &amp;quot; +
                            &amp;quot;try again.&amp;lt;/body&amp;gt;&amp;quot;);
                        return;
                }

                if (!StringSafety.isSafe(password)) {
                        output.println(&amp;quot;&amp;lt;head&amp;gt;&amp;lt;title&amp;gt;VolunteerIM login&amp;lt;/title&amp;gt;&amp;lt;/head&amp;gt;&amp;quot;);
                        output.println(&amp;quot;&amp;lt;body&amp;gt;Password invalid, please go back and try&amp;quot; +
                            &amp;quot; again.&amp;lt;/body&amp;gt;&amp;quot;);
                        return;
                }

                if (dao.checkUserPassword(username, password)) {
                        if (dao.getUser(username).getStatus() == User.STATUS_CONFIRMED) {
                                HttpSession session = req.getSession();
                                resp.sendRedirect(resp.encodeRedirectURL(&amp;quot;/Volinfoman.html&amp;quot;));
                                session.setAttribute(Session.AUTHENTICATEDUSER, username);
                                return;
                        } else {
                                output.println(&amp;quot;&amp;lt;head&amp;gt;&amp;lt;title&amp;gt;VolunteerIM login&amp;lt;/title&amp;gt;&amp;lt;/head&amp;gt;&amp;quot;);
                                output.println(&amp;quot;&amp;lt;body&amp;gt;INSERT CONFIRMATION MESSAGE HERE&amp;lt;/body&amp;gt;&amp;quot;);
                                return;
                        }
                } else {
                        output.println(&amp;quot;&amp;lt;head&amp;gt;&amp;lt;title&amp;gt;VolunteerIM login&amp;lt;/title&amp;gt;&amp;lt;/head&amp;gt;&amp;quot;);
                        output.println(&amp;quot;&amp;lt;body&amp;gt;Username and password do not match.&amp;quot; +
                            &amp;quot;  Please try again.&amp;lt;/body&amp;gt;&amp;quot;);
                        return;
                }
        }
}
&lt;/pre&gt;&lt;br /&gt;
For the login form, we have a &lt;tt&gt;Login&lt;/tt&gt; servlet.  I&#39;ve stripped out some messages and some of the boilerplate code I&#39;ve been using in all of these servlets.  We check the submitted username and password for safety, and then pass them to our previously (way back in the day) developed password checking code in the &lt;tt&gt;DAO&lt;/tt&gt;.  If it&#39;s a match, we also check the &lt;tt&gt;User&lt;/tt&gt;&#39;s status to make sure it&#39;s been confirmed.  If it has, we set a cookie with the &lt;tt&gt;HTTP&lt;/tt&gt; session identifier, mark it in App Engine&#39;s session table as an authorized user, and redirect them to &lt;tt&gt;/Volinfoman.html&lt;/tt&gt;, which is the entry point to our GWT application.  Currently, there is no testing whether the browser visiting &lt;tt&gt;/Volinfoman.html&lt;/tt&gt; is authenticated, so anyone can visit it directly, but this will be rectified in the next bit of code.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;Whew&lt;/h3&gt;&lt;br /&gt;
I realize this is way too long, and maybe showing the revisions as I was figuring this stuff out is not helpful, but I wanted to give a decent introduction to some of the code that will run the site behind the scenes.  I&#39;ve been focusing almost exclusively on GWT code, and as I described above, I&#39;m realizing something that I should have realized a long time ago (except I was blinded by GWT&#39;s novelty).  I should have realized that GWT&#39;s not everything.</content><link rel='replies' type='application/atom+xml' href='http://gaegwtbestpractices.blogspot.com/feeds/2719633894212108434/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://gaegwtbestpractices.blogspot.com/2010/09/some-management-servlets.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3248335039039953287/posts/default/2719633894212108434'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3248335039039953287/posts/default/2719633894212108434'/><link rel='alternate' type='text/html' href='http://gaegwtbestpractices.blogspot.com/2010/09/some-management-servlets.html' title='Some management servlets'/><author><name>John Schutz</name><uri>http://www.blogger.com/profile/12385766703556945544</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiRkpC7-LIVJCQcGKsOoSDO2UZitOmX-n7VCMnXq4HqTQKmeonrbB6vpvwBWw_hKCrK3wFhKstFOC9cObvuJjzpB27YS6SRMGMs9_RT8_GmNKc8KRDMAAqFQaDwZxPhMXo/s220/2009-10-26.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3248335039039953287.post-3542545334431130006</id><published>2010-07-23T00:34:00.007-05:00</published><updated>2010-07-27T00:52:16.063-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="debugging"/><category scheme="http://www.blogger.com/atom/ns#" term="eclipse"/><title type='text'>Unable to install breakpoint due to missing line number attributes</title><content type='html'>I was happily coding along, and didn&#39;t change anything in the Eclipse configuration, just a few lines of code since the last time, and hit F11 to start debugging.  I had set a breakpoint to see the output of the BCrypt hashing implementation I put in.&lt;br /&gt;
&lt;br /&gt;
An error dialog popped up with the message:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;Unable to install breakpoint in 
com.lisedex.volinfoman.server.admin.BuildDB$$FastClassByGuice$$22710124
due to missing line number attributes.  Modify compiler
options to generate line number attributes.

Reason:
Absent Line Number Information
&lt;/pre&gt;&lt;br /&gt;
Crap.  Well, I didn&#39;t change anything, but I thought maybe I hit some key combination that turned off line numbers?  I checked &lt;tt&gt;Window -&gt; Preferences -&gt; Java -&gt; Compiler&lt;/tt&gt;, but sure enough, the line number checkbox was ticked.  So I unticked it, applied the settings, came back and re-ticked it, but no luck.  Did some quick searches, and people recommended checking that option, or adding -g to the compiler options, but that doesn&#39;t really seem to be an option here.&lt;br /&gt;
&lt;br /&gt;
Added a bunch of logging to look at what I wanted to see, and went ahead and ran the program anyways, OKing through the dialog.&lt;br /&gt;
&lt;br /&gt;
It stopped at my breakpoint.&lt;br /&gt;
&lt;br /&gt;
I tried this in several other places in my code, and they all work.  I still get the error, but it doesn&#39;t appear that the problem actually exists.&lt;br /&gt;
&lt;br /&gt;
So if you see this error, my suggestion is, before wasting a bunch of time trying to fix it, see if it&#39;s actually true.  You may just be able to check the box for it to not notify you, and go on with your life.</content><link rel='replies' type='application/atom+xml' href='http://gaegwtbestpractices.blogspot.com/feeds/3542545334431130006/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://gaegwtbestpractices.blogspot.com/2010/07/unable-to-install-breakpoint-due-to.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3248335039039953287/posts/default/3542545334431130006'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3248335039039953287/posts/default/3542545334431130006'/><link rel='alternate' type='text/html' href='http://gaegwtbestpractices.blogspot.com/2010/07/unable-to-install-breakpoint-due-to.html' title='Unable to install breakpoint due to missing line number attributes'/><author><name>John Schutz</name><uri>http://www.blogger.com/profile/12385766703556945544</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiRkpC7-LIVJCQcGKsOoSDO2UZitOmX-n7VCMnXq4HqTQKmeonrbB6vpvwBWw_hKCrK3wFhKstFOC9cObvuJjzpB27YS6SRMGMs9_RT8_GmNKc8KRDMAAqFQaDwZxPhMXo/s220/2009-10-26.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3248335039039953287.post-5572309841552797314</id><published>2010-07-22T16:27:00.004-05:00</published><updated>2010-07-23T00:25:02.341-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="gin"/><category scheme="http://www.blogger.com/atom/ns#" term="gwt"/><category scheme="http://www.blogger.com/atom/ns#" term="mvp"/><category scheme="http://www.blogger.com/atom/ns#" term="mvp4g"/><category scheme="http://www.blogger.com/atom/ns#" term="rpc"/><title type='text'>Converting to the MVP architecture</title><content type='html'>If you see one thing referenced again and again in writings about GWT development, it&#39;s &lt;a href=&quot;http://code.google.com/events/io/2009/sessions/GoogleWebToolkitBestPractices.html&quot;&gt;Ray Ryan&#39;s Google IO 2009 talk on GWT Architecture Best Practices&lt;/a&gt;.  In the talk he describes using the &lt;a href=&quot;http://en.wikipedia.org/wiki/Model-view-presenter&quot;&gt;MVP architecture&lt;/a&gt; to develop GWT applications that allow easier unit testing and clean logic separation.  There&#39;s also a couple of articles about &lt;a href=&quot;http://code.google.com/intl/fr/webtoolkit/articles/mvp-architecture.html&quot;&gt;large scale application development and MVP&lt;/a&gt; also at Google Code.  And there&#39;s a million other things you can read with a quick search.&lt;br /&gt;
&lt;br /&gt;
I&#39;ve used the &lt;a href=&quot;http://en.wikipedia.org/wiki/Model–view–controller&quot;&gt;MVC architecture&lt;/a&gt; for other projects, and it&#39;s treated me pretty well, but you can still end up with some code that&#39;s difficult to follow from beginning to end.  This is generally because of the View cutting the Controller out of the loop in going to the Model.  MVP looks like it could help resolve that, so I wanted to give it a shot while learning GWT.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;Finding a framework&lt;/h3&gt;&lt;br /&gt;
I also didn&#39;t want to do all of the work involved in developing my own framework to support MVP, so after looking around to see what open source projects are available, I came across &lt;a href=&quot;http://code.google.com/p/gwt-presenter/&quot;&gt;gwt-presenter&lt;/a&gt;.  Several blogs and examples talk about this framework, and early on I had decided that I&#39;d use it for this project.  But now that I&#39;m at the stage where I want to implement it, I looked a little closer.&lt;br /&gt;
&lt;br /&gt;
It doesn&#39;t seem to be in active development.  The last release was in August 2009, and GWT 2.0 was not yet released.  There are two branches, and the &lt;a href=&quot;http://borglin.net/gwt-project/&quot;&gt;GAE/GWT development blog&lt;/a&gt; talks about using the &quot;replace&quot; branch, but I&#39;m not sure what stage it&#39;s at.&lt;br /&gt;
&lt;br /&gt;
Though this may be due to my own prejudices, I preferred something that&#39;s currently being hacked, and I found that in &lt;a href=&quot;http://code.google.com/p/mvp4g/&quot;&gt;mvp4g&lt;/a&gt;.  It also looks to have some more features without being bogged down, have some more complete documentation, uses JUnit with pretty high coverage, and a discussion group where the developer regularly responds to questions.  So I&#39;m giving it a shot, at least the 1.2.0 snapshot &lt;a href=&quot;http://code.google.com/p/mvp4g/downloads/detail?name=mvp4g-1.2.0-SNAPSHOT-examples.zip&amp;can=2&amp;q=&quot;&gt;(found in the examples zip)&lt;/a&gt;, since it supports Gin.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;Moving to mvp4g&lt;/h3&gt;&lt;br /&gt;
The code for this stage is available under the &lt;a href=&quot;http://github.com/tofupup/volinfoman/tree/add-mvp4g&quot;&gt;&quot;add-mvp4g&quot; tag on github&lt;/a&gt;, or in a &lt;a href=&quot;http://github.com/tofupup/volinfoman/zipball/add-mvp4g&quot;&gt;zip download&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
I&#39;m going to convert the login page that we currently have working (well, &lt;b&gt;working&lt;/b&gt; is probably a bit too strong) to using this framework.  The &lt;a href=&quot;http://code.google.com/p/mvp4g/wiki/FAQ&quot;&gt;mvp4g FAQ&lt;/a&gt; has some good, quick explanations of their architecture, including a class diagram that I didn&#39;t find terribly useful until I got into implementing it.&lt;br /&gt;
&lt;br /&gt;
The first step, as usual, is to include the jar in your classpath, as well as &lt;a href=&quot;http://commons.apache.org/lang/download_lang.cgi&quot;&gt;commons-lang&lt;/a&gt; and &lt;a href=&quot;http://commons.apache.org/configuration/download_configuration.cgi&quot;&gt;commons-configuration&lt;/a&gt;.  We also need to inherit it in our .gwt.xml module file.&lt;br /&gt;
&lt;br /&gt;
&lt;tt&gt;com/lisedex/volinfoman/Volinfoman.gwt.xml&lt;/tt&gt;&lt;br /&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;&amp;lt;!-- Mvp4g configuration --&amp;gt;
       &amp;lt;inherits name=&#39;com.mvp4g.Mvp4gModule&#39;/&amp;gt;
&lt;/pre&gt;&lt;br /&gt;
We fire it up in our entry point class.&lt;br /&gt;
&lt;br /&gt;
&lt;tt&gt;com/lisedex/volinfoman/client/Volinfoman.java&lt;/tt&gt;&lt;br /&gt;
&lt;pre class=&quot;brush: java&quot;&gt;// code to initialize mvp4g, which handles setting up our
      // Ginjection
      Mvp4gModule module = (Mvp4gModule)GWT.create( Mvp4gModule.class );
      module.createAndStartModule();
      RootPanel.get().add( (Widget)module.getStartView() );
&lt;/pre&gt;&lt;br /&gt;
If you prefer to use a layout panel for your application, the last line can use RootLayoutPanel instead of the RootPanel.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;Event bus&lt;/h3&gt;&lt;br /&gt;
Most of the initial configuration is done through annotations in our event bus class, so we&#39;ll set that up next; apparently you can also set mvp4g up through an XML file, but I didn&#39;t play with that.&lt;br /&gt;
&lt;br /&gt;
&lt;tt&gt;com/lisedex/volinfoman/client/VolinfomanEventBus.java&lt;/tt&gt;&lt;br /&gt;
&lt;pre class=&quot;brush: java&quot;&gt;@Events(startView = LoginView.class, historyOnStart = true,
        ginModule=VolinfomanModule.class)
@Debug( logLevel = LogLevel.DETAILED, logger = 
        Mvp4gLoggerToGwtLogAdapter.class )
public interface VolinfomanEventBus extends EventBus {
        @Event(handlers = LoginPresenter.class)
        public void login();
}
&lt;/pre&gt;&lt;br /&gt;
The &lt;tt&gt;@Events&lt;/tt&gt; annotation declares what View our application will start in, whether it will parse any history information embedded in the URL that starts the application (we currently don&#39;t use this, but I have it enabled anyways), and what Gin module, if any, we want it to use to configure a Ginjector.  Previously we defined the Ginjector in the &lt;tt&gt;onModuleLoad2()&lt;/tt&gt; method in our entry point, but for mvp4g we pull it out and let the framework handle it.&lt;br /&gt;
&lt;br /&gt;
I also turn on debugging using the &lt;tt&gt;@Debug&lt;/tt&gt; annotation, and set the logger to one that I wrote that forwards mvp4g&#39;s logging calls to gwt-log instead of &lt;tt&gt;GWT.log&lt;/tt&gt;.  I wanted all of the logging in one place.  It&#39;s a very simple class, so I&#39;m going to skip it here.&lt;br /&gt;
&lt;br /&gt;
Finally, we declare an interface that extends &lt;tt&gt;EventBus&lt;/tt&gt; which defines all of the events that can be raised on the bus.  The events should not return anything, and can have either one or zero parameters.  We don&#39;t need a parameter for the &lt;tt&gt;login&lt;/tt&gt; event, since the presenter that handles it will have access to the fields the user has filled out.  The &lt;tt&gt;@Event&lt;/tt&gt; annotation declares which classes, separated by a comma, are registered to be notified of the event.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;Presenter&lt;/h3&gt;&lt;br /&gt;
&lt;tt&gt;com/lisedex/volinfoman/client/LoginPresenter.java&lt;/tt&gt;&lt;br /&gt;
&lt;pre class=&quot;brush: java&quot;&gt;@Presenter(view = LoginView.class)
public class LoginPresenter extends BasePresenter
        &amp;lt;LoginPresenter.LoginViewInterface, VolinfomanEventBus&amp;gt; {
&lt;/pre&gt;&lt;br /&gt;
The Presenter will be matched with a View, so we declare that association through the &lt;tt&gt;@Presenter&lt;/tt&gt; annotation, which also lets the mvp4g compiler know that this class defines a Presenter.  Our presenter extends &lt;tt&gt;BasePresenter&lt;/tt&gt;, which is a generic that takes the class defining our View interactions (&lt;tt&gt;LoginViewInterface&lt;/tt&gt;), and our Event Bus class (&lt;tt&gt;VolinfomanEventBus&lt;/tt&gt;) as parameters.&lt;br /&gt;
&lt;br /&gt;
The Presenter class handles the logic that will make the interface displayed to the user actually do something.  In this case, we&#39;re working with a login page, so we need to define what  we need for interactions with the View.  I followed the mvp4g documentation, which defines it using an inner interface.  I don&#39;t see why you couldn&#39;t break it out to a separate file, but it&#39;s a bit more clear having it bound to the Presenter class.  Plus it&#39;s right there to reference when writing your Presenter.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush: java&quot;&gt;public interface LoginViewInterface {
  public String getUsername();
  public String getPassword();
  public void setMessage(String msg);
  public HasClickHandlers getLoginButton();
  public void setLoginButtonEnabled(boolean enabled);
 }
&lt;/pre&gt;&lt;br /&gt;
We need a few things from the user, and we don&#39;t really care how the View gets them.  All we care about is that it can provide the information we need to process the login.  In this case, we want to be able to get the username and password they&#39;ve entered, and we also want to be able to send them messages, in case of an error.  We also need to find out when the user&#39;s told the interface they&#39;ve completed their data entry, so we want to bind to something that throws Click events; we don&#39;t really care if it&#39;s a button or not.  I also need to be able to control whether the user can request to log in; for instance, if they&#39;ve just clicked, until the server responds, I don&#39;t want them to be able to keep clicking.  If the View is not implementing a button, they can use this information in whatever way makes sense.  In fact, writing this, it really shouldn&#39;t be called &lt;tt&gt;setLoginButtonEnabled&lt;/tt&gt;, but something like &lt;tt&gt;setLoginEnabled&lt;/tt&gt; instead.&lt;br /&gt;
&lt;br /&gt;
The Presenter is going to perform the logic we had in our previous &lt;tt&gt;DefaultHomepage&lt;/tt&gt; class, so we need our &lt;tt&gt;UserService&lt;/tt&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush: java&quot;&gt;private UserServiceAsync userService = null;

 @InjectService
 public void setService(UserServiceAsync service) {
  this.userService = service;
 }
&lt;/pre&gt;&lt;br /&gt;
mvp4g supports injecting these services (both RPC and non-RPC) with the &lt;tt&gt;@InjectService&lt;/tt&gt; annotation.  You define them as you normally would, with both &lt;tt&gt;Service&lt;/tt&gt; and the &lt;tt&gt;ServiceAsync&lt;/tt&gt; version.  I didn&#39;t have to change the code at all.&lt;br /&gt;
&lt;br /&gt;
The &lt;tt&gt;bind()&lt;/tt&gt; method is called by the framework for us to do further set up of our interactions with the View.  The &lt;tt&gt;BasePresenter&lt;/tt&gt; we&#39;re extending provides both &lt;tt&gt;view&lt;/tt&gt; and &lt;tt&gt;eventBus&lt;/tt&gt; references so we don&#39;t need to handle that.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush: java&quot;&gt;@Override
 public void bind() {
  super.bind();
  view.getLoginButton().addClickHandler(new ClickHandler() {
   public void onClick(ClickEvent event) {
    view.setLoginButtonEnabled(false);
    eventBus.login();
   }
  });
 }
&lt;/pre&gt;&lt;br /&gt;
All I want right now is to know when the login button (or whatever the View is using) is clicked, and when it is, I turn off the login button and fire a login event onto the Event Bus.  This will allow some flexibility later, if I need to have another class know about logins.&lt;br /&gt;
&lt;br /&gt;
Finally we define the method to handle the login event.  All events are handled by methods that start with &quot;on&quot; followed by the event name.  In this case, it will be &lt;tt&gt;onLogin&lt;/tt&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush: java&quot;&gt;public void onLogin() {
 if (userService == null) {
  Log.fatal(&amp;quot;userService is null, not injected&amp;quot;, 
    new NullPointerException(&amp;quot;LoginPresenter.userService&amp;quot;));
  view.setMessage(&amp;quot;Fatal application error.  \&amp;quot;userService not injected.\&amp;quot;&amp;quot;);
  view.setLoginButtonEnabled(true);
  return;
 }
  
 userService.getUser(view.getUsername(), new AsyncCallback&amp;lt;User&amp;gt;() {
  @Override
  public void onFailure(Throwable caught) {
   view.setMessage(&amp;quot;RPC FAILED&amp;quot;);
   view.setLoginButtonEnabled(true);
  }

  @Override
  public void onSuccess(User result) {
   if (result == null) {
    view.setMessage(&amp;quot;NO SUCH USER&amp;quot;);
   } else {
    view.setMessage(&amp;quot;SUCCESS: &amp;quot; + result.toString());
   }
   view.setLoginButtonEnabled(true);
  }
 });
}
&lt;/pre&gt;&lt;br /&gt;
This is pretty much the click handler that was in &lt;tt&gt;DefaultHomepage&lt;/tt&gt;.  We first do a quick sanity check for our &lt;tt&gt;UserService&lt;/tt&gt;, and if it&#39;s not there, there&#39;s something really wrong.  Then we call our service&#39;s &lt;tt&gt;getUser&lt;/tt&gt; method, and when it returns we either tell the user the user doesn&#39;t exist, or spit out the user&#39;s information.  Then we turn the login button back on.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;View&lt;/h3&gt;&lt;br /&gt;
The View just sets up visual aspect of the interface the user sees.  In this case, we&#39;re still using a &lt;tt&gt;UiBinder&lt;/tt&gt;, copied from the &lt;tt&gt;DefaultHomepage&lt;/tt&gt;, and then adding the methods  to flesh out the View interface we built in the Presenter.&lt;br /&gt;
&lt;br /&gt;
&lt;tt&gt;com/lisedex/volinfoman/client/LoginView.java&lt;/tt&gt;&lt;br /&gt;
&lt;pre class=&quot;brush: java&quot;&gt;public class LoginView extends Composite 
 implements LoginPresenter.LoginViewInterface {

 private static LoginViewUiBinder uiBinder = GWT
  .create(LoginViewUiBinder.class);

 interface LoginViewUiBinder extends UiBinder&amp;lt;Widget, LoginView&amp;gt; {
 }
&lt;/pre&gt;&lt;br /&gt;
The View is going to be a &lt;tt&gt;Composite&lt;/tt&gt; widget, and it will implement the View interface.  We also bind the View with its associated &lt;tt&gt;LoginView.ui.xml&lt;/tt&gt; file, so we can use the fields declared there to provide information to the Presenter.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush: java&quot;&gt;@UiField
 TextBox username;
 
 @UiField
 TextBox password;
 
 @UiField
 Button sendButton;
 
 @UiField
 HTML sendStatus;
 
 public LoginView() {
  initWidget(uiBinder.createAndBindUi(this));
  
  sendButton.setText(&amp;quot;Login&amp;quot;);
  
  sendStatus.setStyleName(&amp;quot;serverResponseLabelError&amp;quot;);
  
  DeferredCommand.addCommand(new Command() {
   public void execute() {
    username.setFocus(true);
   }
  });
  username.selectAll();
 }
&lt;/pre&gt;&lt;br /&gt;
This code is basically the same as what was in &lt;tt&gt;DefaultHomepage&lt;/tt&gt;.  We bind the XML defined user interface, then we set the text for our login button.  We also set the style for the place we&#39;ll send messages to the user, so text shows up red, and then we set the focus on the username field.  Setting focus did not work in previous versions of our code, so after a quick search I found this workaround described in &lt;a href=&quot;http://code.google.com/p/google-web-toolkit/issues/detail?id=1849&quot;&gt;GWT issue 1849&lt;/a&gt;, where they discuss making this the default implementation of &lt;tt&gt;setFocus()&lt;/tt&gt;.&lt;br /&gt;
&lt;br /&gt;
The rest of the class is just implementing the &lt;tt&gt;LoginViewInterface&lt;/tt&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush: java&quot;&gt;@Override
 public String getUsername() {
  return username.getText();
 }

 @Override
 public String getPassword() {
  return password.getText();
 }

        .... etc, etc.
&lt;/pre&gt;&lt;br /&gt;
They&#39;re all just basic getters and setters, so I won&#39;t include them here, but you can take a peek at the &lt;a href=&quot;http://github.com/tofupup/volinfoman/blob/add-mvp4g/src/com/lisedex/volinfoman/client/LoginView.java&quot;&gt;complete code&lt;/a&gt; if you want to see the whole thing.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;Some notes&lt;/h3&gt;&lt;br /&gt;
I like the way mvp4g seems to work, and reading past posts on its discussion group was useful in figuring some of it out.  There&#39;s other functionality I haven&#39;t even touched yet: code-splitting through using different modules for different parts of the interface, lazy loading, and, most importantly, history.  I&#39;ll be adding history as soon as I have a second page or something where history makes sense.  The others seem to be optimizations which we don&#39;t need yet.&lt;br /&gt;
&lt;br /&gt;
I also may want to implement the &lt;a href=&quot;http://en.wikipedia.org/wiki/Command_pattern&quot;&gt;command pattern&lt;/a&gt; later, to have more complete bits of information passed with their event on the event bus.&lt;br /&gt;
&lt;br /&gt;
I&#39;ve been avoiding it long enough, but now it&#39;s time to figure out a good authentication/session design, since I don&#39;t want to require the volunteers using this to have a Google account.  If I did, it would pretty much be implemented for me in the &lt;a href=&quot;http://code.google.com/appengine/docs/java/users/overview.html&quot;&gt;App Engine Users API&lt;/a&gt;.</content><link rel='replies' type='application/atom+xml' href='http://gaegwtbestpractices.blogspot.com/feeds/5572309841552797314/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://gaegwtbestpractices.blogspot.com/2010/07/converting-to-mvp-architecture.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3248335039039953287/posts/default/5572309841552797314'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3248335039039953287/posts/default/5572309841552797314'/><link rel='alternate' type='text/html' href='http://gaegwtbestpractices.blogspot.com/2010/07/converting-to-mvp-architecture.html' title='Converting to the MVP architecture'/><author><name>John Schutz</name><uri>http://www.blogger.com/profile/12385766703556945544</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiRkpC7-LIVJCQcGKsOoSDO2UZitOmX-n7VCMnXq4HqTQKmeonrbB6vpvwBWw_hKCrK3wFhKstFOC9cObvuJjzpB27YS6SRMGMs9_RT8_GmNKc8KRDMAAqFQaDwZxPhMXo/s220/2009-10-26.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3248335039039953287.post-7332110317342088633</id><published>2010-07-20T22:21:00.004-05:00</published><updated>2010-07-26T23:39:11.279-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="dao"/><category scheme="http://www.blogger.com/atom/ns#" term="datastore"/><category scheme="http://www.blogger.com/atom/ns#" term="dependency_injection"/><category scheme="http://www.blogger.com/atom/ns#" term="gae"/><category scheme="http://www.blogger.com/atom/ns#" term="guice"/><category scheme="http://www.blogger.com/atom/ns#" term="refactor"/><category scheme="http://www.blogger.com/atom/ns#" term="rpc"/><category scheme="http://www.blogger.com/atom/ns#" term="servlet"/><title type='text'>Guice</title><content type='html'>&lt;span style=&quot;font-size: x-small;&quot;&gt;&lt;a name=&quot;blogNote&quot;&gt;&lt;/a&gt;&lt;b&gt;NOTE:&lt;/b&gt; There is an intermediate stage for the project between the last posting and this one.  In it, I add a servlet that will populate the datastore with sample data, optionally deleting the datastore first.  It is protected by web.xml directives that no longer work after moving the servlet to Guice, but it&#39;s available as tag &lt;a href=&quot;http://github.com/tofupup/volinfoman/tree/add-objectify&quot; linkindex=&quot;16&quot;&gt;&quot;add-objectify&quot; on github&lt;/a&gt;.&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
Using Guice to inject an appropriate Dao is kind of a no-brainer goal.  Use it to inject one that uses the App Engine datastore for production, and one that&#39;s got a dummy at the back end for testing.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;Prepare to be Guiced&lt;/h3&gt;&lt;br /&gt;
We need to make a &lt;a href=&quot;http://en.wikipedia.org/wiki/Data_access_object&quot;&gt;Data Access Object&lt;/a&gt; interface that the methods needing it can use.  The way that makes most sense to me is to rename our current &lt;tt&gt;DAO&lt;/tt&gt; class to &lt;tt&gt;DaoGaeDatastore&lt;/tt&gt; (correcting the capitalization to match the Java naming conventions), and extract from that a new &lt;tt&gt;Dao&lt;/tt&gt; interface.&lt;br /&gt;
&lt;br /&gt;
Eclipse makes this really easy to accomplish.  The &lt;tt&gt;Refactor-&amp;gt;Rename...&lt;/tt&gt; menu item handles renaming the class, as well as updating all references to it in the code to the new name.  The &lt;tt&gt;Refactor-&amp;gt;Extract Interface...&lt;/tt&gt; applied to &lt;tt&gt;DaoGaeDatastore&lt;/tt&gt; pulls out the interface &lt;tt&gt;Dao&lt;/tt&gt;, also with no real trouble.  While I was at it, I removed the extra Objectify object I had added to the DAO, and used the &lt;tt&gt;ofy()&lt;/tt&gt; method implemented by its parent &lt;tt&gt;DAOBase&lt;/tt&gt;.&lt;br /&gt;
&lt;br /&gt;
To the interface and implementation class, I added a method named &lt;tt&gt;deleteAllUsers()&lt;/tt&gt;, which does exactly what you&#39;d expect based on the name.&lt;br /&gt;
&lt;br /&gt;
&lt;tt&gt;com/lisedex/volinfoman/server/DaoGaeDatastore.java&lt;/tt&gt;&lt;br /&gt;
&lt;pre class=&quot;brush: java&quot;&gt;@Override
        public void deleteAllUsers() {
                ofy().delete(ofy().query(User.class).fetchKeys());
        }
&lt;/pre&gt;&lt;br /&gt;
One of the &lt;tt&gt;Objectify.delete()&lt;/tt&gt; methods accepts an iterable collection of objects that can be a mix of &lt;tt&gt;Key&lt;/tt&gt;s and POJOs (Plain Old Java Object).  Executing a &lt;tt&gt;query()&lt;/tt&gt; on &lt;tt&gt;User.class&lt;/tt&gt; will return all &lt;tt&gt;User&lt;/tt&gt;s, and the &lt;tt&gt;Query.fetchKeys()&lt;/tt&gt; method will return an iterable collection of their &lt;tt&gt;Key&lt;/tt&gt;s.  We can just pass this collection to &lt;tt&gt;Objectify.delete()&lt;/tt&gt;, and all of our &lt;tt&gt;User&lt;/tt&gt;s in the datastore disappear.&lt;br /&gt;
&lt;br /&gt;
We can now convert our &lt;tt&gt;BuildDB&lt;/tt&gt; class, which was built &lt;a href=&quot;#blogNote&quot;&gt;between the last blog entry and this one&lt;/a&gt;, to use &lt;tt&gt;Dao.deleteAllUsers()&lt;/tt&gt;, and remove its dependency on an &lt;tt&gt;Objectify&lt;/tt&gt; object.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;Let the Guicing commence&lt;/h3&gt;&lt;br /&gt;
We need the &lt;tt&gt;guice-servlet&lt;/tt&gt; jar.&lt;br /&gt;
&lt;br /&gt;
I used the &lt;a href=&quot;http://code.google.com/p/google-guice/wiki/GoogleAppEngine&quot;&gt;Guice integration documentation&lt;/a&gt; to see how to get Guice set up for the App Engine environment.  All servlets where we want to use Guice need to be moved from their &lt;tt&gt;web.xml&lt;/tt&gt; declarations into Guice module configurations.  Also, all servlets must be have the &lt;tt&gt;Singleton&lt;/tt&gt; scope, so we can either use the &lt;tt&gt;@Singleton&lt;/tt&gt; notation in the class definitions themselves, or follow the &lt;a href=&quot;http://gaegwtbestpractices.blogspot.com/2010/07/dependency-injection-and-gin.html&quot;&gt;design philosophy we used for Gin&lt;/a&gt; and declare them all in a Guice module.  I chose the latter for consistency.&lt;br /&gt;
&lt;br /&gt;
First we build an &lt;tt&gt;AbstractModule&lt;/tt&gt; called &lt;tt&gt;VolinfomanGuiceModule&lt;/tt&gt;.  Its sole function is to override the &lt;tt&gt;configure()&lt;/tt&gt; method to declare our Guice object bindings.&lt;br /&gt;
&lt;br /&gt;
&lt;tt&gt;com/lisedex/volinfoman/server/guice/VolinfomanGuiceModule.java&lt;/tt&gt;&lt;br /&gt;
&lt;pre class=&quot;brush: java&quot;&gt;public class VolinfomanGuiceModule extends AbstractModule {
        @Override
        protected void configure() {
                // Servlets
                bind(UserServiceImpl.class).in(Singleton.class);
                bind(BuildDB.class).in(Singleton.class);
        }
}
&lt;/pre&gt;&lt;br /&gt;
Since all servlets need to have &lt;tt&gt;Singleton&lt;/tt&gt; scope, and we&#39;re not injecting them with replacement objects, this is all the binding we need.  &lt;br /&gt;
&lt;br /&gt;
If you&#39;ve looked at the code before now, you&#39;ll notice we&#39;re using a library called &lt;a href=&quot;http://code.google.com/p/gwt-log/&quot;&gt;gwt-log&lt;/a&gt; with a remote logging servlet that&#39;s been declared in &lt;tt&gt;web.xml&lt;/tt&gt;.  We&#39;re not moving this to the Guice configuration because it doesn&#39;t need Guice injection right now, and it also produces a warning when it&#39;s not declared in &lt;tt&gt;web.xml&lt;/tt&gt;.  This warning problem is corrected in &lt;a href=&quot;http://code.google.com/p/gwt-log/source/detail?r=504&quot;&gt;r504&lt;/a&gt; of gwt-log, but I&#39;m only using the release version.&lt;br /&gt;
&lt;br /&gt;
Next, we need a &lt;tt&gt;ServletModule&lt;/tt&gt; that sets up what URLs each servlet handles, so we build &lt;tt&gt;VolinfomanServletModule&lt;/tt&gt; and override &lt;tt&gt;configureServlets()&lt;/tt&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;tt&gt;com/lisedex/volinfoman/server/guice/VolinfomanServletModule.java&lt;/tt&gt;&lt;br /&gt;
&lt;pre class=&quot;brush: java&quot;&gt;public class VolinfomanServletModule extends ServletModule {
        @Override protected void configureServlets() {
                serve(&amp;quot;/volinfoman/user&amp;quot;).with(UserServiceImpl.class);
                serve(&amp;quot;/volinfoman/admin/builddb&amp;quot;).with(BuildDB.class);
        }
}
&lt;/pre&gt;&lt;br /&gt;
Each &lt;tt&gt;serve()&lt;/tt&gt; call tells Guice which URL to map to which servlet class.  Each servlet can handle multiple URLs through multiple calls to &lt;tt&gt;serve()&lt;/tt&gt;.&lt;br /&gt;
&lt;br /&gt;
Finally, we need a &lt;tt&gt;GuiceServletContextListener&lt;/tt&gt; that provides a Guice &lt;tt&gt;Injector&lt;/tt&gt; that knows about the modules so it can configure the injection properly.&lt;br /&gt;
&lt;br /&gt;
&lt;tt&gt;com/lisedex/volinfoman/server/guice/VolinfomanGuiceServletContextListener.java&lt;/tt&gt;&lt;br /&gt;
&lt;pre class=&quot;brush: java&quot;&gt;public class VolinfomanGuiceServletContextListener extends
                GuiceServletContextListener {
        @Override
        protected Injector getInjector() {
                return Guice.createInjector(
                                new VolinfomanServletModule(),
                                new VolinfomanGuiceModule());
        }
}
&lt;/pre&gt;&lt;br /&gt;
In &lt;tt&gt;web.xml&lt;/tt&gt; we need to remove references to the servlets we&#39;re configuring through Guice, and add the configuration for Guice in their place.  After taking out the &lt;tt&gt;BuildDB&lt;/tt&gt; and &lt;tt&gt;UserServiceImpl&lt;/tt&gt; declarations, but leaving the &lt;tt&gt;RemoteLoggerServiceImpl&lt;/tt&gt; for gwt-log, we put in the Guice code.&lt;br /&gt;
&lt;br /&gt;
&lt;tt&gt;war/WEB-INF/web.xml&lt;/tt&gt;&lt;br /&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;&amp;lt;!-- Configure Guice servlet, other servlets configured in
                 VolinfomanServletModule --&amp;gt;
        &amp;lt;filter&amp;gt;
                &amp;lt;filter-name&amp;gt;guiceFilter&amp;lt;/filter-name&amp;gt;
                &amp;lt;filter-class&amp;gt;com.google.inject.servlet.GuiceFilter&amp;lt;/filter-class&amp;gt;
        &amp;lt;/filter&amp;gt;

        &amp;lt;filter-mapping&amp;gt;
                &amp;lt;filter-name&amp;gt;guiceFilter&amp;lt;/filter-name&amp;gt;
                &amp;lt;url-pattern&amp;gt;/*&amp;lt;/url-pattern&amp;gt;
        &amp;lt;/filter-mapping&amp;gt;

        &amp;lt;listener&amp;gt;
                &amp;lt;listener-class&amp;gt;com.lisedex.volinfoman.server.guice.VolinfomanGuiceServletContextListener&amp;lt;/listener-class&amp;gt;
        &amp;lt;/listener&amp;gt;
&lt;/pre&gt;&lt;br /&gt;
We set up a &lt;tt&gt;&lt;a href=&quot;http://google-guice.googlecode.com/svn/trunk/javadoc/com/google/inject/servlet/GuiceFilter.html&quot;&gt;GuiceFilter&lt;/a&gt;&lt;/tt&gt; with a URL pattern that describes which URL will be processed by Guice.  We also plug our &lt;tt&gt;GuiceServletContextListener&lt;/tt&gt; as a listener so Guice gets configured when the application is deployed.&lt;br /&gt;
&lt;br /&gt;
Moving the &lt;tt&gt;BuildDB&lt;/tt&gt; servlet to Guice configuration stops the authentication we had set up through a &lt;tt&gt;&amp;lt;security-constraint&amp;gt;&lt;/tt&gt; in the servlet container.  We&#39;ll have to add security in the code itself later, but for now the &lt;tt&gt;BuildDB&lt;/tt&gt; servlet is open to the world.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;Guiced!&lt;/h3&gt;&lt;br /&gt;
All that set up for what will appear to be a pretty small payoff, but we&#39;re ready to use Guice anywhere we want in our servlets, as well as Gin in our GWT client.&lt;br /&gt;
&lt;br /&gt;
We want to inject an instance of &lt;tt&gt;DaoGaeDatastore&lt;/tt&gt; in place of fields declared as &lt;tt&gt;Dao&lt;/tt&gt; in our servlets.  First we need to configure this in &lt;tt&gt;VolinfomanGuiceModule&lt;/tt&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;tt&gt;com/lisedex/volinfoman/server/guice/VolinfomanGuiceModule.java&lt;/tt&gt;&lt;br /&gt;
&lt;pre class=&quot;brush: java&quot;&gt;// Data providers
            bind(Dao.class).to(DaoGaeDatastore.class).in(Singleton.class);
&lt;/pre&gt;&lt;br /&gt;
After adding this to the &lt;tt&gt;configure()&lt;/tt&gt; method, a DaoGaeDatastore singleton will be provided to any servlet that has a class field of type &lt;tt&gt;Dao&lt;/tt&gt; that we also annotate with &lt;tt&gt;@Inject&lt;/tt&gt;.  So we need to inject it in &lt;tt&gt;UserServiceImpl&lt;/tt&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;tt&gt;com/lisedex/volinfoman/server/UserServiceImpl.java&lt;/tt&gt;&lt;br /&gt;
&lt;pre class=&quot;brush: java&quot;&gt;@Inject
    private Dao dao;
&lt;/pre&gt;&lt;br /&gt;
This replaces the previous declaration for &lt;tt&gt;dao&lt;/tt&gt;, and tells Guice to do the injection for that field.  The rest of the class stays the same, since we were already using the &lt;tt&gt;Dao&lt;/tt&gt; interface to define our interaction with the datastore.&lt;br /&gt;
&lt;br /&gt;
In &lt;tt&gt;BuildDB&lt;/tt&gt; we remove the local variable &lt;tt&gt;dao&lt;/tt&gt; in &lt;tt&gt;doGet()&lt;/tt&gt;, and use the same code as above to declare it as a class variable that needs injection.&lt;br /&gt;
&lt;br /&gt;
That&#39;s it.  The project is now Guicified.</content><link rel='replies' type='application/atom+xml' href='http://gaegwtbestpractices.blogspot.com/feeds/7332110317342088633/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://gaegwtbestpractices.blogspot.com/2010/07/guice.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3248335039039953287/posts/default/7332110317342088633'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3248335039039953287/posts/default/7332110317342088633'/><link rel='alternate' type='text/html' href='http://gaegwtbestpractices.blogspot.com/2010/07/guice.html' title='Guice'/><author><name>John Schutz</name><uri>http://www.blogger.com/profile/12385766703556945544</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiRkpC7-LIVJCQcGKsOoSDO2UZitOmX-n7VCMnXq4HqTQKmeonrbB6vpvwBWw_hKCrK3wFhKstFOC9cObvuJjzpB27YS6SRMGMs9_RT8_GmNKc8KRDMAAqFQaDwZxPhMXo/s220/2009-10-26.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3248335039039953287.post-4955399905948963983</id><published>2010-07-20T17:06:00.005-05:00</published><updated>2010-07-20T17:16:42.771-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="datastore"/><category scheme="http://www.blogger.com/atom/ns#" term="dependency_injection"/><category scheme="http://www.blogger.com/atom/ns#" term="gae"/><category scheme="http://www.blogger.com/atom/ns#" term="guice"/><category scheme="http://www.blogger.com/atom/ns#" term="gwt"/><category scheme="http://www.blogger.com/atom/ns#" term="objectify"/><category scheme="http://www.blogger.com/atom/ns#" term="rpc"/><title type='text'>Using Objectify to access the datastore</title><content type='html'>There are several options to choose from when deciding how you want to access the App Engine datastore.  Two options are &lt;a href=&quot;http://java.sun.com/jdo/index.jsp&quot;&gt;Java Data Objects (JDO)&lt;/a&gt; and &lt;a href=&quot;http://java.sun.com/developer/technicalArticles/J2EE/jpa/&quot;&gt;Java Persistence API (JPA)&lt;/a&gt;; both are from Sun and are in the Java SDK.  From searching around the web, the consensus seems to be that both are too heavyweight for most App Engine projects.  They are, however, both standards, and JDO is datastore agnostic, which would make moving your application off of the App Engine platform somewhat easier.  However, you still are stuck with the limitations of the GAE datastore, so it&#39;s not quite so easy bringing JDO code into your GAE project.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;Open source alternatives for Google App Engine&lt;/h3&gt;&lt;br /&gt;
Two GAE specific open source projects are &lt;a href=&quot;http://code.google.com/p/objectify-appengine/&quot;&gt;Objectify&lt;/a&gt; and &lt;a href=&quot;http://code.google.com/p/twig-persist/&quot;&gt;Twig&lt;/a&gt;.  Out of the two, Objectify seems simpler and more aligned with what&#39;s actually happening in the datastore.  As I learn GAE, I feel it&#39;s somewhat imperative to not have the details of the datastore hidden from me.  It&#39;s also modeled after the GAE Python library, which seems pretty clean.  And, last but not least, your data classes can be passed through GWT RPC without modification.  Perhaps in the future I&#39;ll come to a different conclusion if I get tired of having to write too much in the way of nuts and bolts that Twig handles automatically.  Twig also supports parallel queries and merging OR queries, both of which could be pretty nice.&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;http://borglin.net/gwt-project/?page_id=604&quot;&gt;Hearing about the different design philosophies straight from the horses&#39; mouths&lt;/a&gt; gave me some good information I needed to make this decision, too.&lt;br /&gt;
&lt;br /&gt;
The &lt;a href=&quot;http://code.google.com/p/objectify-appengine/wiki/Concepts&quot;&gt;Objectify concepts documentation&lt;/a&gt; is a good starting point for understanding both Objectify, and the GAE datastore.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;Adding Objectify to VolInfoMan&lt;/h3&gt;&lt;br /&gt;
Adding Objectify to the project leads us to the first time that we need a servlet running on App Engine.  The servlet will simply provide a front end to Objectify and the datastore by responding to GWT RPC calls.&lt;br /&gt;
&lt;br /&gt;
&lt;a name=&quot;codeInstructions&quot;&gt;&lt;a href=&quot;http://github.com/tofupup/volinfoman/zipball/add-object-step1&quot;&gt;The code for this state of the project is available&lt;/a&gt; as tag &lt;a href=&quot;http://github.com/tofupup/volinfoman/tree/add-object-step1&quot;&gt;&quot;add-object-step1&quot;&lt;/a&gt; on github.&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;He&#39;s not any kind of program, Sark. He&#39;s a User.&lt;/h3&gt;&lt;br /&gt;
The first step is to define the object we want to pass back and forth between our GWT code and the GAE servlet.  To continue the login page we&#39;ve been working on, we&#39;ll need a User so we can authenticate the session.  Since the User will be shared between GWT and GAE, we&#39;ll put it in the &lt;tt&gt;shared&lt;/tt&gt; package.&lt;br /&gt;
&lt;br /&gt;
(I&#39;m leaving out the package and import statements to conserve space)&lt;br /&gt;
&lt;br /&gt;
&lt;tt&gt;com/lisedex/volinfoman/shared/User.java&lt;/tt&gt;&lt;br /&gt;
&lt;pre class=&quot;brush: java&quot;&gt;public class User implements Serializable {
        @Id
        private Long id;
        @Indexed
        private String username;
        @Indexed
        private long status;
        @Unindexed
        private String firstName;
        @Unindexed
        private String lastName;
        @Unindexed
        private String email;
        @Unindexed
        private String password;
&lt;/pre&gt;&lt;br /&gt;
Objects that will go through RPC need be &lt;tt&gt;Serializable&lt;/tt&gt;.  Later on, I&#39;ll probably add the &lt;tt&gt;@Cached&lt;/tt&gt; annotation to the class so that it will automatically be cached by GAE&#39;s memcache.&lt;br /&gt;
&lt;br /&gt;
You can only run queries on the GAE datastore against fields that are indexed.  More specifically, you need an index for every query you intend to run, so by default all class fields are indexed to make it easier to query on any specific field.  However, &lt;a href=&quot;http://code.google.com/p/objectify-appengine/wiki/BestPractices&quot;&gt;according to the Objectify best practices documentation&lt;/a&gt;, each property that is indexed requires a separate write to the index, which won&#39;t add to latency (writes are done in parallel), but will add to the CPU time used by the application.  So instead, I explicitly declare which fields are to be indexed, and which are not.  The same could be achieved by declaring the whole class &lt;tt&gt;@Indexed&lt;/tt&gt; and only specifying the &lt;tt&gt;@Unindexed&lt;/tt&gt;, or vice versa, but I like to explicitly declare each for easier reading.  The field to be used to create the Key for the object is annotated with &lt;tt&gt;@Id&lt;/tt&gt;.&lt;br /&gt;
&lt;br /&gt;
I put in a couple of getter/setters, and a toString() method for easy logging.  It will need to be fleshed out more later.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;GAE, we need to have a serious talk.  - Love, GWT&lt;/h3&gt;&lt;br /&gt;
Next, we need to define the interface our client uses to access the servlet.  I put GWT code for handling data into &lt;tt&gt;com.lisedex.volinfoman.client.data&lt;/tt&gt;, so I define a &lt;tt&gt;UserService&lt;/tt&gt; interface in that package.&lt;br /&gt;
&lt;br /&gt;
&lt;tt&gt;com/lisedex/volinfoman/client/data/UserService.java&lt;/tt&gt;&lt;br /&gt;
&lt;pre class=&quot;brush: java&quot;&gt;@RemoteServiceRelativePath(&amp;quot;user&amp;quot;)
public interface UserService extends RemoteService {
        User getUser(Long id);
        User getUser(String username);

        void putUser(User user);
}
&lt;/pre&gt;&lt;br /&gt;
The important part is that we declare what URL we&#39;ll be using to access the RPC servlet using the &lt;tt&gt;@RemoteServiceRelativePath&lt;/tt&gt; annotation.  The interface itself extends &lt;tt&gt;RemoteService&lt;/tt&gt;, which all GWT RPC client interfaces should extend, and then we add the RPC functions we want to support.  I want to support grabbing a User by name or id number, or putting a User object back into the datastore.&lt;br /&gt;
&lt;br /&gt;
If you&#39;re using Eclipse, it will complain about the lack of a &lt;tt&gt;UserServiceAsync&lt;/tt&gt; interface, and will build it for you automatically if you let it.  It&#39;s basically the same as above, but doesn&#39;t need to extend &lt;tt&gt;RemoteService&lt;/tt&gt; or declare a &lt;tt&gt;@RemoteServiceRelativePath&lt;/tt&gt;.  You can grab the complete code from github, &lt;a href=&quot;#codeInstructions&quot;&gt;as shown above&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
In the &lt;tt&gt;server&lt;/tt&gt; package, we need a servlet class that implements the &lt;tt&gt;UserService&lt;/tt&gt; interface we just defined.&lt;br /&gt;
&lt;br /&gt;
&lt;tt&gt;com/lisedex/volinfoman/server/UserServiceImpl.java&lt;/tt&gt;&lt;br /&gt;
&lt;pre class=&quot;brush: java&quot;&gt;public class UserServiceImpl extends RemoteServiceServlet implements
                UserService {

    private DAO dao = new DAO();

    public User getUser(String username) {
            return dao.getUser(username);
    }
}
&lt;/pre&gt;&lt;br /&gt;
The real code has stub implementations for &lt;tt&gt;getUser(Long id)&lt;/tt&gt; and &lt;tt&gt;putUser(User user)&lt;/tt&gt; since we&#39;re not using them for now.  We want to abstract away how we&#39;ll be accessing the datastore in case we need to change it, so we put actual interaction in a &lt;a href=&quot;http://en.wikipedia.org/wiki/Data_access_object&quot;&gt;Data Access Object&lt;/a&gt; called...&lt;tt&gt;DAO&lt;/tt&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;tt&gt;com/lisedex/volinfoman/server/DAO.java&lt;/tt&gt;&lt;br /&gt;
&lt;pre class=&quot;brush: java&quot;&gt;public class DAO extends DAOBase {
        static {
                ObjectifyService.register(User.class);
        }

        private Objectify ofy;

        public DAO() {
                ofy = ObjectifyService.begin();
        }
&lt;/pre&gt;&lt;br /&gt;
We don&#39;t have to extend the DAOBase class, but it gives us a couple of functions that we can use without coding, such as a lazily instantiated Objectify object.  Of course, I didn&#39;t know that at the time I wrote the code, so I created my own.  Oops.&lt;br /&gt;
&lt;br /&gt;
You need to register the classes you&#39;ll be using with Objectify, and calling &lt;tt&gt;ObjectifyService.begin()&lt;/tt&gt; returns an &lt;tt&gt;Objectify&lt;/tt&gt; object we&#39;ll use to interact with the datastore.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush: java&quot;&gt;public User getUser(String username) {
                User fetched = ofy.query(User.class)
                    .filter(&amp;quot;username&amp;quot;, username).get();
                return fetched;
        }

        public User getOrCreateUser(String username) {
                User fetched = ofy.query(User.class)
                    .filter(&amp;quot;username&amp;quot;, username).get();
                if (fetched == null) {
                        fetched = new User(null, username, 
                            User.STATUS_INVALID, null, null, null, 
                            null);
                        ofy.put(fetched);
                }
                return fetched;
        }

        public void putUser(User user) {
                ofy.put(user);
        }
}
&lt;/pre&gt;&lt;br /&gt;
To get a &lt;tt&gt;User&lt;/tt&gt; by &lt;tt&gt;username&lt;/tt&gt;, we need to execute a query on the datastore for all &lt;tt&gt;User&lt;/tt&gt; objects, and filter the results by the &lt;tt&gt;username&lt;/tt&gt; field.  If no such object is in the datastore, the &lt;tt&gt;get()&lt;/tt&gt; method will return a &lt;tt&gt;null&lt;/tt&gt;.&lt;br /&gt;
&lt;br /&gt;
Writing the object is easier, since we already know the &lt;tt&gt;Key&lt;/tt&gt; for the object, as it&#39;s encoded in the class (&lt;tt&gt;User.class&lt;/tt&gt;, and &lt;tt&gt;Long id&lt;/tt&gt;).  Just pass the object to Objectify&#39;s &lt;tt&gt;put()&lt;/tt&gt; method.  If the &lt;tt&gt;id&lt;/tt&gt; field in the object is of type &lt;tt&gt;Long&lt;/tt&gt;, and its value when passed to &lt;tt&gt;put()&lt;/tt&gt; is &lt;tt&gt;null&lt;/tt&gt;, Objectify will automatically generate an &lt;tt&gt;id&lt;/tt&gt; for the object.  If the &lt;tt&gt;id&lt;/tt&gt; is of type &lt;tt&gt;long&lt;/tt&gt; or &lt;tt&gt;String&lt;/tt&gt;, the developer is responsible for that themselves.&lt;br /&gt;
&lt;br /&gt;
The &lt;tt&gt;getOrCreateUser()&lt;/tt&gt; function should be modified to use the &lt;tt&gt;get()&lt;/tt&gt; and &lt;tt&gt;put()&lt;/tt&gt; methods in &lt;tt&gt;DAO&lt;/tt&gt;.  Another oops.  And I need to add more data checking, such as if the &lt;tt&gt;User&lt;/tt&gt; sent to &lt;tt&gt;put()&lt;/tt&gt; is &lt;tt&gt;null&lt;/tt&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;Make the Login button do something&lt;/h3&gt;&lt;br /&gt;
Now we need to wire this stuff into the user interface, so that when we hit the Login button, we actually go out to the datastore and try to retrieve the appropriate &lt;tt&gt;User&lt;/tt&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;tt&gt;com/lisedex/volinfoman/client/DefaultHomepage.java&lt;/tt&gt;&lt;br /&gt;
&lt;pre class=&quot;brush: java&quot;&gt;class MyHandler implements ClickHandler {

    @Override
    public void onClick(ClickEvent event) {
        sendStatus.setText(&amp;quot;Sending...&amp;quot; + username.getText() +
                           &amp;quot;/&amp;quot; + password.getText());
        sendButton.setEnabled(false);
        userService.getUser(username.getText(),
            new AsyncCallback&amp;lt;User&amp;gt;() {
                  public void onFailure(Throwable caught) {
                      sendStatus.setText(sendStatus.getText()
                                 + &amp;quot;   FAILED&amp;quot;);
                      sendButton.setEnabled(true);
                  }

                  @Override
                  public void onSuccess(User result) {
                      if (result == null) {
                             sendStatus.setText(sendStatus.getText() 
                                 + &amp;quot;    NO SUCH USER&amp;quot;);
                             sendButton.setEnabled(true);
                             return;
                      }
                      sendStatus.setText(sendStatus.getText()
                          + &amp;quot;    SUCCESS: &amp;quot; + result.toString());
                      sendButton.setEnabled(true);
                  }
              });
    }
}

sendButton.addClickHandler(new MyHandler());
&lt;/pre&gt;&lt;br /&gt;
We obviously have not implemented the &lt;a href=&quot;http://en.wikipedia.org/wiki/Model-view-presenter&quot;&gt;MVP model&lt;/a&gt;, as all of this stuff is in our view.  In the &lt;tt&gt;ClickHandler&lt;/tt&gt; inner class, we call the &lt;tt&gt;UserService.getUser()&lt;/tt&gt; asynchronous method with an &lt;tt&gt;AsyncCallback&lt;/tt&gt; inner class that will handle what happens when the RPC call returns.&lt;br /&gt;
&lt;br /&gt;
If the RPC call fails, &lt;tt&gt;onFailure&lt;/tt&gt; gets called with an exception.  This doesn&#39;t happen if the &lt;tt&gt;username&lt;/tt&gt; doesn&#39;t exist in the User indexes; it only happens with the client is not able to access the servlet for some reason.  &lt;tt&gt;onSuccess&lt;/tt&gt; is called with a &lt;tt&gt;User&lt;/tt&gt; object when the RPC call works.  The &lt;tt&gt;result&lt;/tt&gt; is &lt;tt&gt;null&lt;/tt&gt; if no such &lt;tt&gt;User&lt;/tt&gt; was found, otherwise it&#39;s populated with all stored fields for the object.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;Next up: Guice, security&lt;/h3&gt;&lt;br /&gt;
&lt;a href=&quot;http://code.google.com/p/google-guice/&quot;&gt;Guice&lt;/a&gt; is the basis for &lt;a href=&quot;http://code.google.com/p/google-gin/&quot;&gt;Gin&lt;/a&gt;, except Guice provides dependency injection for Java generally, where Gin is for GWT.  DAO&#39;s are a perfect place to apply dependency injection, since we may want to use the real datastore back end, or a dummy used for testing.  We&#39;ll cover that in the next installment.&lt;br /&gt;
&lt;br /&gt;
Also, there&#39;s no security or authorization needed to call any of the RPC functions.  Anyone that wants can read or write any &lt;tt&gt;User&lt;/tt&gt; object to my datastore, even if they&#39;re not using my client application.  We&#39;ll, uh, get to that sometime.</content><link rel='replies' type='application/atom+xml' href='http://gaegwtbestpractices.blogspot.com/feeds/4955399905948963983/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://gaegwtbestpractices.blogspot.com/2010/07/using-objectify-to-access-datastore.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3248335039039953287/posts/default/4955399905948963983'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3248335039039953287/posts/default/4955399905948963983'/><link rel='alternate' type='text/html' href='http://gaegwtbestpractices.blogspot.com/2010/07/using-objectify-to-access-datastore.html' title='Using Objectify to access the datastore'/><author><name>John Schutz</name><uri>http://www.blogger.com/profile/12385766703556945544</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiRkpC7-LIVJCQcGKsOoSDO2UZitOmX-n7VCMnXq4HqTQKmeonrbB6vpvwBWw_hKCrK3wFhKstFOC9cObvuJjzpB27YS6SRMGMs9_RT8_GmNKc8KRDMAAqFQaDwZxPhMXo/s220/2009-10-26.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3248335039039953287.post-3740247100432596381</id><published>2010-07-18T03:56:00.001-05:00</published><updated>2010-07-20T17:19:18.244-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="dependency_injection"/><category scheme="http://www.blogger.com/atom/ns#" term="gae"/><category scheme="http://www.blogger.com/atom/ns#" term="gin"/><category scheme="http://www.blogger.com/atom/ns#" term="guice"/><category scheme="http://www.blogger.com/atom/ns#" term="gwt"/><category scheme="http://www.blogger.com/atom/ns#" term="mvp"/><category scheme="http://www.blogger.com/atom/ns#" term="tutorial"/><title type='text'>Dependency injection and Gin</title><content type='html'>I was coming across the phrase &quot;dependency injection&quot; quite a bit while reading articles about GWT and &lt;a href=&quot;http://en.wikipedia.org/wiki/Model-view-presenter&quot;&gt;MVP&lt;/a&gt;, and references to the &lt;a href=&quot;http://code.google.com/p/google-guice/&quot; linkindex=&quot;19&quot;&gt;Guice&lt;/a&gt; and &lt;a href=&quot;http://code.google.com/p/google-gin/&quot; linkindex=&quot;20&quot;&gt;Gin&lt;/a&gt; libraries coming out of Google.  I figured it was some new programming paradigm and that I was hopelessly out of date not knowing what it was.  The &lt;a href=&quot;http://en.wikipedia.org/wiki/Dependency_injection&quot;&gt;wikipedia entry&lt;/a&gt; didn&#39;t really help clear it up, nor did &lt;a href=&quot;http://martinfowler.com/articles/injection.html&quot;&gt;other articles I read&lt;/a&gt;.  I kept getting the feeling that I was missing something, and that the concept wasn&#39;t really something new.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;Turns out I was missing something, all right&lt;/h3&gt;&lt;br /&gt;
I was missing that DI is giving a name to something that I and every other object oriented developer have been doing for years.&lt;br /&gt;
&lt;br /&gt;
The short summary in &lt;a href=&quot;http://jamesshore.com/Blog/Dependency-Injection-Demystified.html&quot;&gt;this great article&lt;/a&gt; is what finally made me realize that I could stop trying to make it more complicated than it was: &quot;&lt;b&gt;Dependency injection means giving an object its instance variables. Really. That&#39;s it.&lt;/b&gt;&quot;  If you&#39;re having trouble with the concept, James Shore&#39;s article is invaluable.  &lt;br /&gt;
&lt;br /&gt;
A conceptual example would be something like: you have an object that relies on a reference to some sort of data provider.  In production, the provider will be a database back end, but during testing you want to mock up a provider that is always up and you can precisely control what errors it has.  &lt;br /&gt;
&lt;br /&gt;
You might solve this by constructing a factory that spits out the correct type of provider, and your object can ask that factory to give it a reference to the proper kind of provider.  This works, but your testing has to construct, tear down, and reset the factory to the previous state between each test.  You also have to write a lot of boilerplate code for each of these factories.&lt;br /&gt;
&lt;br /&gt;
DI refers to giving the object a reference to the proper provider, which, during testing, allows you to build a new provider instance which you pass to the object during each test.  As you exit the test, the object and provider are dereferenced automatically, saving you the cleanup.  But in the code itself, you&#39;re now either passing provider references possibly through layer after layer of constructors, or you&#39;re building factories for the initial object which get the proper provider and pass it to the object, and then return the completed object.&lt;br /&gt;
&lt;br /&gt;
Guice and Gin save you from all this repetitive factory building and wiring, and what provider each object needs can be declared in one place, or inline where the instantiated providers will be injected in the object.&lt;br /&gt;
&lt;br /&gt;
I&#39;m certainly not qualified at this time to discuss all of the nuances and capabilities of these libraries, since there are many different ways this concept could be used.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;Instead, I&#39;ll talk about using Gin in this project&lt;/h3&gt;&lt;br /&gt;
&lt;tt&gt;com/lisedex/volinfoman/Volinfoman.gwt.xml&lt;/tt&gt;&lt;br /&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;&amp;lt;!-- Include Gin --&amp;gt;
&amp;lt;inherits name=&amp;quot;com.google.gwt.inject.Inject&amp;quot; /&amp;gt;
&lt;/pre&gt;Adding those lines to your module&#39;s gwt.xml file, and adding the Gin and Guice jar files to your classpath, is all that it takes to enable the Gin annotations and compile time code generation.&lt;br /&gt;
&lt;br /&gt;
If you go to the &lt;a href=&quot;http://www.github.com/tofupup/volinfoman&quot;&gt;VolInfoMan project page on github&lt;/a&gt;, you can go to the &lt;a href=&quot;http://github.com/tofupup/volinfoman/tree/basic-gin&quot;&gt;basic-gin tag and download the source&lt;/a&gt;.  The project is very, very basic at this point, and it&#39;s easy to find the one instance of using Gin.&lt;br /&gt;
&lt;br /&gt;
As practice, I wanted to set up the home page you land on when loading the site to be capable of being changed by dependency injection.  This will allow, later, changing the implementation of the page by making a new class that extends the skeleton &lt;tt&gt;Homepage&lt;/tt&gt; class, and just changing the Gin binding in one place, and all references to that &lt;tt&gt;Homepage&lt;/tt&gt; will be using my new implementation.  Look ma, no factory!&lt;br /&gt;
&lt;br /&gt;
&lt;tt&gt;com/lisedex/volinfoman/client/Homepage.java&lt;/tt&gt;&lt;br /&gt;
&lt;pre class=&quot;brush: java&quot;&gt;package com.lisedex.volinfoman.client;

import com.google.gwt.user.client.ui.Composite;

public class Homepage extends Composite {
}
&lt;/pre&gt;&lt;br /&gt;
First we define our skeleton &lt;tt&gt;Homepage&lt;/tt&gt; class, which will be added to our &lt;tt&gt;RootPanel&lt;/tt&gt; later in &lt;tt&gt;onModuleLoad()&lt;/tt&gt; (actually &lt;tt&gt;onModuleLoad2()&lt;/tt&gt; due to our use of the &lt;a href=&quot;http://code.google.com/p/gwt-log/&quot;&gt;gwt-log&lt;/a&gt; library).&lt;br /&gt;
&lt;br /&gt;
&lt;tt&gt;com/lisedex/volinfoman/client/gin/VolinfomanGinjector.java&lt;/tt&gt;&lt;br /&gt;
&lt;pre class=&quot;brush: java&quot;&gt;package com.lisedex.volinfoman.client.gin;

import com.google.gwt.inject.client.GinModules;
import com.google.gwt.inject.client.Ginjector;
import com.lisedex.volinfoman.client.Homepage;

@GinModules(VolinfomanModule.class)
public interface VolinfomanGinjector extends Ginjector {
 Homepage getHomepage();
}
&lt;/pre&gt;&lt;br /&gt;
We extend the &lt;tt&gt;com.google.gwt.inject.client.Ginjector&lt;/tt&gt; class and use the &lt;tt&gt;@GinModules&lt;/tt&gt; annotation to specify what class contains the binding configuration.  &lt;a name=&quot;injectorMethod&quot;&gt;&lt;/a&gt;We also define the &lt;tt&gt;getHomepage()&lt;/tt&gt; injector method, which we need when using Gin in our application initialization code, because Gin is working at compile time to generate JavaScript, unlike Guice.  Other dependencies that operate below our initialization code will be injected automatically, and won&#39;t require this type of method.&lt;br /&gt;
&lt;br /&gt;
&lt;tt&gt;com/lisedex/volinfoman/client/gin/VolinfomanModule.java&lt;/tt&gt;&lt;br /&gt;
&lt;pre class=&quot;brush: java&quot;&gt;package com.lisedex.volinfoman.client.gin;

import com.google.gwt.inject.client.AbstractGinModule;
import com.lisedex.volinfoman.client.DefaultHomepage;
import com.lisedex.volinfoman.client.Homepage;

public class VolinfomanModule extends AbstractGinModule {
        @Override
        protected void configure() {
                bind(Homepage.class).to(DefaultHomepage.class);
        }
}
&lt;/pre&gt;&lt;br /&gt;
We extend &lt;tt&gt;AbstractGinModule&lt;/tt&gt; to declare our bindings for injection.  Using &lt;tt&gt;AbstractGinModule&lt;/tt&gt; instead of &lt;tt&gt;GinModule&lt;/tt&gt; allows us to drop the &lt;tt&gt;binder.bind()&lt;/tt&gt; syntax in favor of &lt;tt&gt;bind()&lt;/tt&gt;.  In the above code, we&#39;re declaring that when our code asks for a &lt;tt&gt;Homepage&lt;/tt&gt; object, it will get a &lt;tt&gt;DefaultHomepage&lt;/tt&gt; instead.&lt;br /&gt;
&lt;br /&gt;
&lt;tt&gt;com.lisedex.volinfoman.client.DefaultHomepage&lt;/tt&gt; actually contains no references to Gin.  It extends &lt;tt&gt;Homepage&lt;/tt&gt; but doesn&#39;t need to know anything about injection.&lt;br /&gt;
&lt;br /&gt;
&lt;tt&gt;com/lisedex/volinfoman/client/Volinfoman.java&lt;/tt&gt;&lt;br /&gt;
&lt;pre class=&quot;brush: java&quot;&gt;package com.lisedex.volinfoman.client;

import com.lisedex.volinfoman.client.gin.VolinfomanGinjector;
// import GWT objects here...

public class Volinfoman implements EntryPoint {
        public void onModuleLoad() {
                // we set up gwt-log here and
                // use a DeferredCommand to execute
                // onModuleLoad2() */
                // ...
        }

        private void onModuleLoad2() {
                // Create a ginjector
                VolinfomanGinjector ginjector = 
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;GWT.create(VolinfomanGinjector.class);
                // Add the homepage to the rootpanel
                RootPanel.get().add(ginjector.getHomepage());
        }
}
&lt;/pre&gt;&lt;br /&gt;
And our GWT entry point creates the &lt;tt&gt;Ginjector&lt;/tt&gt;, and we use the injector method &lt;tt&gt;getHomepage()&lt;/tt&gt; to inject an instance of &lt;tt&gt;DefaultHomepage&lt;/tt&gt;, thanks to the binding we set up in &lt;tt&gt;VolinfomanModule&lt;/tt&gt;.  &lt;a href=&quot;#injectorMethod&quot;&gt;As mentioned above&lt;/a&gt;, we have to use an injector method due to current limitations in Gin.&lt;br /&gt;
&lt;br /&gt;
That&#39;s it.  Remember to &lt;a href=&quot;http://github.com/tofupup/volinfoman/tree/basic-gin&quot;&gt;grab the basic-gin tag of the source code from github&lt;/a&gt; for a compile-ready Eclipse project where you can immediately play with Gin.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;Other Gin information&lt;/h3&gt;&lt;br /&gt;
Gin supports some of the same annotations as Guice, such as @Inject, @ProvidedBy, @Singleton, @ImplementedBy, all of which allow you to move much of the binding information out to the source where the bindings occur.  Much of this is to provide an alternative to using a &lt;tt&gt;GinModule&lt;/tt&gt; to declare all of the bindings in one place; the latter is the option I prefer, but it&#39;s personal preference for the most part.&lt;br /&gt;
&lt;br /&gt;
Some of the other &lt;tt&gt;bind()&lt;/tt&gt; syntaxes are:&lt;br /&gt;
&lt;pre class=&quot;brush: java&quot;&gt;bind(Something.class).toProvider(SomethingProvider.class);
bind(Something.class).to(SomethingImplementation.class)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.in(Elsewhere.class);
bind(Something.class).annotatedWith(SomethingAnnotation.class);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.to(SomethingImplementation.class);
bind(Something.class).to(SomethingImplementation.class)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.in(Singleton.class);
&lt;/pre&gt;&lt;br /&gt;
&lt;b&gt;Line 1:&lt;/b&gt; When Gin injects an instance of a class normally, it uses the class&#39;s default constructor.  If you need to give extra information to a constructor, you can use the &lt;tt&gt;toProvider()&lt;/tt&gt; syntax.  Gin will inject an instance of &lt;tt&gt;Something&lt;/tt&gt; from the &lt;tt&gt;SomethingProvider&lt;/tt&gt; wherever you need a &lt;tt&gt;Something&lt;/tt&gt; class.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Line 2:&lt;/b&gt; If you only want a binding to apply in &lt;tt&gt;Elsewhere&lt;/tt&gt;, instead of globally, you can use this syntax to specify that scope.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Line 4:&lt;/b&gt; You can also set up custom annotations.  When you want multiple bindings for one type, you can specify which binding to use by setting up an annotation for each.  The annotation and the type will uniquely identify which binding is appropriate.  See the &lt;a href=&quot;http://code.google.com/p/google-guice/wiki/BindingAnnotations&quot;&gt;Guice wiki for more information&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Line 6:&lt;/b&gt; Gin&#39;s injector normally creates a new instance of each &lt;tt&gt;SomethingImplementation&lt;/tt&gt; when it gets injected.  However, sometimes you&#39;ll want to use the Singleton pattern, and only have one instance of &lt;tt&gt;SomethingImplementation&lt;/tt&gt; running around.  Remember that this is a Singleton &lt;i&gt;per &lt;tt&gt;Ginjector&lt;/tt&gt;&lt;/i&gt;, so if you have multiple &lt;tt&gt;Ginjector&lt;/tt&gt;s (which, if you do, you probably need to double check your code) there will be one instance of the &lt;tt&gt;SomethingImplementation&lt;/tt&gt; for each.&lt;br /&gt;
&lt;br /&gt;
There&#39;s a bunch more that can be done with Gin; this is obviously only scratching the surface.  Remember that Gin does the injection at compile-time, so there should be almost no overhead for using it, and it can save you a lot of repetitive coding.  To understand all of the capabilities, it would behoove you to read the &lt;a href=&quot;http://code.google.com/p/google-guice/wiki/Motivation?tm=6&quot;&gt;Guice User&#39;s Guide&lt;/a&gt;, the &lt;a href=&quot;http://code.google.com/p/google-gin/wiki/GinTutorial&quot;&gt;Gin tutorial&lt;/a&gt;, and the &lt;a href=&quot;http://code.google.com/p/google-gin/wiki/GuiceCompatibility&quot;&gt;compatibility information between Gin and Guice&lt;/a&gt;.  The documentation for Guice is much more thorough, but you have to know which ideas can be used in your GWT code.  Guice will be used later for our server side code which runs under GAE.</content><link rel='replies' type='application/atom+xml' href='http://gaegwtbestpractices.blogspot.com/feeds/3740247100432596381/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://gaegwtbestpractices.blogspot.com/2010/07/dependency-injection-and-gin.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3248335039039953287/posts/default/3740247100432596381'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3248335039039953287/posts/default/3740247100432596381'/><link rel='alternate' type='text/html' href='http://gaegwtbestpractices.blogspot.com/2010/07/dependency-injection-and-gin.html' title='Dependency injection and Gin'/><author><name>John Schutz</name><uri>http://www.blogger.com/profile/12385766703556945544</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiRkpC7-LIVJCQcGKsOoSDO2UZitOmX-n7VCMnXq4HqTQKmeonrbB6vpvwBWw_hKCrK3wFhKstFOC9cObvuJjzpB27YS6SRMGMs9_RT8_GmNKc8KRDMAAqFQaDwZxPhMXo/s220/2009-10-26.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3248335039039953287.post-303257661872184890</id><published>2010-07-12T19:12:00.001-05:00</published><updated>2010-07-18T05:40:07.498-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="best_practices"/><category scheme="http://www.blogger.com/atom/ns#" term="bookmarks"/><category scheme="http://www.blogger.com/atom/ns#" term="code_splitting"/><category scheme="http://www.blogger.com/atom/ns#" term="datastore"/><category scheme="http://www.blogger.com/atom/ns#" term="gae"/><category scheme="http://www.blogger.com/atom/ns#" term="gwt"/><category scheme="http://www.blogger.com/atom/ns#" term="max_ross"/><category scheme="http://www.blogger.com/atom/ns#" term="mvp"/><category scheme="http://www.blogger.com/atom/ns#" term="scaling"/><category scheme="http://www.blogger.com/atom/ns#" term="schemas"/><category scheme="http://www.blogger.com/atom/ns#" term="tutorial"/><title type='text'>More research...by watching YouTube videos</title><content type='html'>I&#39;m itching to start hacking code, and I probably will this evening, but I forced myself to spend a good bit of time yesterday doing research: I read &lt;a href=&quot;http://code.google.com/appengine/articles/&quot; linkindex=&quot;14&quot;&gt;articles at the Google App Engine site&lt;/a&gt;, &lt;a href=&quot;http://code.google.com/webtoolkit/doc/latest/tutorial/index.html&quot; linkindex=&quot;15&quot;&gt;GWT tutorials&lt;/a&gt;, and best practices for both &lt;a href=&quot;http://code.google.com/appengine/articles/scaling/overview.html&quot; linkindex=&quot;16&quot;&gt;GAE&lt;/a&gt; and &lt;a href=&quot;http://code.google.com/events/io/2009/sessions/GoogleWebToolkitBestPractices.html&quot; linkindex=&quot;17&quot;&gt;GWT&lt;/a&gt;.  Generally, what I got from those is how much I have to learn about these technologies, which is a little depressing.  Also, that if I&#39;m not really focused, I&#39;m going to get sidetracked by reading about issues I&#39;m not even facing yet.  But the up side is that there&#39;s so much information out there, already available, that most of the problems I&#39;ll run in to someone else has already hit and written about it.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;Google IO videos, or how I learned to stop multitasking and love YouTube&lt;/h3&gt;&lt;br /&gt;
The Google IO talks, &lt;a href=&quot;http://www.youtube.com/user/GoogleDevelopers&quot; linkindex=&quot;18&quot;&gt;presented on the Google Developer&#39;s Channel on YouTube&lt;/a&gt;, were probably some of the most informative stuff I looked at yesterday, as well as exemplifying my tendency to get sidetracked; do I really need to watch an hour and a half of the Google Wave product launch?  I&#39;m not even going to provide a link to that.  It also served to remind me that sometimes &lt;a href=&quot;http://www.npr.org/templates/story/story.php?storyId=1126992&quot; linkindex=&quot;19&quot;&gt; trying to multitask is inefficient&lt;/a&gt;, as when I&#39;d put the videos in the corner of my screen and try to do other work, I&#39;d completely miss huge chunks of the talk.  Having to continuously rewind, and, while the video was getting back to the section I missed, going back to my regular work, only to completely miss the same section again...well, it started to border on the absurd.  Biting the bullet, and telling myself that just watching the video with no distractions was actually most the efficient way to get this done, was also the way to get the most out of these videos.  And there&#39;s a ton of good information there.&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;http://gae-java-persistence.blogspot.com/&quot; linkindex=&quot;20&quot;&gt;Max Ross&lt;/a&gt; easily rose to the top as one of my favorite speakers.  During his videos, I would actually neglect my work and focus completely on the video.  The first talk of his I watched was &lt;a href=&quot;http://www.youtube.com/watch?v=2jW2iSKDipY&quot; linkindex=&quot;21&quot;&gt;an explanation of schemas in the data store&lt;/a&gt;, since I&#39;m not going to have complete SQL functionality while writing an application for the GAE.  I also listened to his &lt;a href=&quot;http://www.javaworld.com/podcasts/jtech/2008/072408jtech.html&quot; linkindex=&quot;22&quot;&gt;(audio only) talk about Hibernate Shards&lt;/a&gt;, even though I won&#39;t be needing that, most likely ever, for this particular project.  Speaking to Max&#39;s skill as a speaker, my girlfriend, who actively tries not to learn about computers almost to the point of Luddism, got sucked in while I was listening.  I believe that, initially, it was her mishearing the word &quot;sharding&quot; as &quot;sharting,&quot; and that hooked her scatalogical mind.  But she ended up putting down her book, and listening with me.  By the end, she actually asked me questions that showed she grasped a lot of what Max was talking about.  She&#39;s a smart girl, but I was pretty surprised.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;Testing&lt;/h3&gt;&lt;br /&gt;
I&#39;m a big proponent of proper testing when I&#39;m developing software, and though I occasionally get some push back for that, it really helps my confidence in code as a project progresses.  So it was nice to hear from Max that he really believes in testing, too, during his talk about &lt;a href=&quot;http://www.youtube.com/watch?v=bcyz3sGa1Qo&amp;amp;feature=channel&quot; linkindex=&quot;23&quot;&gt;GAE Testing Techniques&lt;/a&gt;.  Using the cloud as a test farm is pretty ingenious, so I &lt;strike&gt;wasted&lt;/strike&gt;spent some time looking at &lt;a href=&quot;http://code.google.com/p/cloudcover/&quot; linkindex=&quot;24&quot;&gt;Cloud Cover test harness&lt;/a&gt;.  The code hasn&#39;t been touched since May, but I really like the idea of testing my GAE code actually on the App Engine servers.  This led to &lt;a href=&quot;http://www.youtube.com/watch?v=T_CLzgEL7FA&quot; linkindex=&quot;25&quot;&gt;GWT testing&lt;/a&gt;, which reinforces the idea that the &lt;a href=&quot;http://en.wikipedia.org/wiki/Model-view-presenter&quot; linkindex=&quot;26&quot;&gt;MVP model&lt;/a&gt; will promote not just a clean conceptual layout for the project or make it easier to implement &lt;a href=&quot;http://code.google.com/webtoolkit/doc/latest/DevGuideCodeSplitting.html&quot; linkindex=&quot;27&quot;&gt;code splitting&lt;/a&gt;, but will also make it much easier to have GWT test cases that will run very fast, except for the small percentage that need to test the GUI widgets themselves.  This talk also pointed me to &lt;a href=&quot;http://webdriver.googlecode.com/&quot; linkindex=&quot;28&quot;&gt;WebDriver&lt;/a&gt;, which is the heir apparent to Selenium.  The fact that Selenium ran the testing through JavaScript always seemed a bit too abstracted from the browser itself to provide true test from the user all the way to the data store.  WebDriver will plug directly in to the browser and generate actual key strokes, button clicks, and other events.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;Odds and ends&lt;/h3&gt;&lt;br /&gt;
I also watched a talk about &lt;a href=&quot;http://www.youtube.com/watch?v=vm-RdH_eWK4&amp;amp;feature=channel&quot; linkindex=&quot;29&quot;&gt;some of the new stuff in Google App Engine&lt;/a&gt; (at least new since the 2009 Google IO conference), &lt;a href=&quot;http://www.youtube.com/watch?v=AgaL6NGpkB8&amp;amp;feature=channel&quot; linkindex=&quot;30&quot;&gt;developing complex and scalable apps in GAE&lt;/a&gt;, and &lt;a href=&quot;http://www.youtube.com/watch?v=ofhEyDBpngM&quot; linkindex=&quot;31&quot;&gt;new query capabilities that have been added to the data store&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
And I believe watching all of that in one day kind of made me a bit loopy, and I know that a lot of the specifics ended up getting pushed out by new specifics; but now I have a better idea of what&#39;s out there, so when I do run into an issue or question, I know where to look.  This post is actually more for me, so I have a place to refer with all of these links.  I also have a good idea of how to implement my testing, and how the project should be designed initially.&lt;br /&gt;
&lt;br /&gt;
It was actually time well spent, even though I had to suppress my urge to just start coding, and I&#39;m now in a better position to start without having to feel like I&#39;m going to have to revisit, in the near future, every single decision I make.&lt;br /&gt;
&lt;br /&gt;
And I need to get over my mancrush on Max Ross.  He&#39;s so dreamy.</content><link rel='replies' type='application/atom+xml' href='http://gaegwtbestpractices.blogspot.com/feeds/303257661872184890/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://gaegwtbestpractices.blogspot.com/2010/07/more-researchby-watching-youtube-videos.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3248335039039953287/posts/default/303257661872184890'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3248335039039953287/posts/default/303257661872184890'/><link rel='alternate' type='text/html' href='http://gaegwtbestpractices.blogspot.com/2010/07/more-researchby-watching-youtube-videos.html' title='More research...by watching YouTube videos'/><author><name>John Schutz</name><uri>http://www.blogger.com/profile/12385766703556945544</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiRkpC7-LIVJCQcGKsOoSDO2UZitOmX-n7VCMnXq4HqTQKmeonrbB6vpvwBWw_hKCrK3wFhKstFOC9cObvuJjzpB27YS6SRMGMs9_RT8_GmNKc8KRDMAAqFQaDwZxPhMXo/s220/2009-10-26.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3248335039039953287.post-2985141411903300768</id><published>2010-07-11T10:36:00.000-05:00</published><updated>2010-07-12T19:32:12.346-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="cygwin"/><category scheme="http://www.blogger.com/atom/ns#" term="git"/><category scheme="http://www.blogger.com/atom/ns#" term="tools"/><title type='text'>A short note on Cygwin git</title><content type='html'>I use &lt;a href=&quot;http://git-scm.com/&quot; linkindex=&quot;19&quot;&gt;git&lt;/a&gt; for my source control, and when developing under Windows, I use the &lt;a href=&quot;http://www.cygwin.com/&quot; linkindex=&quot;20&quot;&gt;Cygwin&lt;/a&gt; environment for all of my command line needs. &amp;nbsp;My background is primarily Unix, so I&#39;m used to having a powerful shell prompt and scripting for a lot of tasks. &amp;nbsp;I also use the git available from Cygwin instead of the one from the git site.&lt;br /&gt;
&lt;div&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-family: inherit;&quot;&gt;&lt;br /&gt;
&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-family: inherit;&quot;&gt;You will quickly run into execute permission problems and &lt;tt&gt;CRLF&lt;/tt&gt; vs. &lt;tt&gt;LF&lt;/tt&gt; translation problems.&lt;/span&gt;&lt;br /&gt;
&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-family: inherit;&quot;&gt;&lt;/span&gt;&lt;br /&gt;
&lt;h3&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-family: inherit;&quot;&gt;Execute bit&lt;/span&gt;&lt;/h3&gt;&lt;br /&gt;
&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-family: inherit;&quot;&gt;Windows doesn&#39;t manage the execute bit very well, especially on a FAT filesystem, but I find it even on NTFS.&amp;nbsp; Files created under Cygwin will be created according to your umask, usually as mode 0644, while files from Windows will show up as 0700.&amp;nbsp; This isn&#39;t a real problem except that git tracks the mode of each file in the repository, so changes to the mode will show up as the file being modified.&amp;nbsp; Setting &lt;tt&gt;core.filemode&lt;/tt&gt; to false tells git to ignore the executable bit.&amp;nbsp; If something slips in, you may have to use &lt;a href=&quot;http://www.kernel.org/pub/software/scm/git/docs/git-update-index.html&quot; linkindex=&quot;21&quot;&gt;git update-index&lt;/a&gt; --chmod=-x to correct it.&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-family: inherit;&quot;&gt;&lt;tt&gt;core.filemode&lt;/tt&gt; is set in your .git/config file by &lt;a href=&quot;http://www.kernel.org/pub/software/scm/git/docs/git-init.html&quot; linkindex=&quot;22&quot;&gt;git init&lt;/a&gt; regardless of how it&#39;s set in your system or global configuration, so you will need to set it in your repository configuration.&lt;/span&gt;&lt;br /&gt;
&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-family: inherit;&quot;&gt;&lt;/span&gt;&lt;br /&gt;
&lt;h3&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-family: inherit;&quot;&gt;&lt;tt&gt;CRLF&lt;/tt&gt; vs. &lt;tt&gt;LF&lt;/tt&gt;&lt;/span&gt;&lt;/h3&gt;&lt;br /&gt;
&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-family: inherit;&quot;&gt;Files created under Cygwin will use only &lt;tt&gt;LF&lt;/tt&gt; to mark end of line, while files created under Windows applications will use &lt;tt&gt;CRLF&lt;/tt&gt;. &amp;nbsp;By setting &lt;tt&gt;core.autocrlf&lt;/tt&gt; to true, Cygwin git will convert all &lt;tt&gt;CRLF&lt;/tt&gt;s to &lt;tt&gt;LF&lt;/tt&gt;s on commit operations, and all &lt;tt&gt;LF&lt;/tt&gt;s to &lt;tt&gt;CRLF&lt;/tt&gt;s when checking out.&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-family: inherit;&quot;&gt;I also set &lt;tt&gt;core.safecrlf&lt;/tt&gt; to warn, which checks, before adding a file, that doing the crlf conversions is &quot;safe&quot;.&amp;nbsp; We want to make sure that we&#39;ll get the same file back when it&#39;s checked out.&amp;nbsp; Git cannot &quot;safely&quot; convert files that contain both &lt;tt&gt;LF&lt;/tt&gt;s and &lt;tt&gt;CRLF&lt;/tt&gt;s, so a file that&#39;s been edited under both Windows and Cygwin may produce &lt;tt&gt;warning: LF will be replaced by CRLF in Foo.java&lt;/tt&gt; when running &lt;a href=&quot;http://www.kernel.org/pub/software/scm/git/docs/git-add.html&quot; linkindex=&quot;23&quot;&gt;git add&lt;/a&gt;.&amp;nbsp; This is not a problem with text files, because the file will be cleaned up during its next commit so that all &lt;tt&gt;EOL&lt;/tt&gt;s are the same.&amp;nbsp; However, binary files will be corrupted if they are not properly detected by git.&amp;nbsp; I have never seen git label a binary file as text, but if this does occur you can set the file in &lt;a href=&quot;http://www.kernel.org/pub/software/scm/git/docs/gitattributes.html&quot; linkindex=&quot;24&quot;&gt;.gitattributes&lt;/a&gt; as binary.&amp;nbsp; For example&lt;/span&gt;&lt;br /&gt;
&lt;pre&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-family: inherit;&quot;&gt;&amp;nbsp;&amp;nbsp; *.jpg binary&lt;/span&gt;&lt;/pre&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-family: inherit;&quot;&gt;will make git consider all .jpg files as binary, and will not apply the &lt;tt&gt;CRLF&lt;/tt&gt; conversion, nor will it try a text diff.&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-family: inherit;&quot;&gt;You can also set &lt;tt&gt;core.safecrlf&lt;/tt&gt; to true instead of warn, in which case the error will be fatal, and you will have to correct the problem before you can add the file.&amp;nbsp; You can run a text file through &lt;a href=&quot;http://docs.sun.com/app/docs/doc/816-0210/6m6nb7moe?a=view&quot;&gt;unix2dos&lt;/a&gt;, or add the binary file to .gitattributes, but since git bats a thousand, in my experience, at detecting binary files, it&#39;s a waste of time to have to manually fix all of your text files.&lt;/span&gt;&lt;br /&gt;
&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-family: inherit;&quot;&gt;&lt;/span&gt;&lt;br /&gt;
&lt;h3&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-family: inherit;&quot;&gt;TL;DR&lt;/span&gt;&lt;/h3&gt;&lt;br /&gt;
&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-family: inherit;&quot;&gt;So in conclusion, if developing using git under Cygwin, I run the following commands after creating a repository:&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;pre&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-family: inherit;&quot;&gt;&amp;nbsp;&amp;nbsp; git config core.autocrlf true
&amp;nbsp;&amp;nbsp; git config core.safecrlf warn
&amp;nbsp;&amp;nbsp; git config core.filemode false&lt;/span&gt;&lt;/pre&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-family: inherit;&quot;&gt;Explanations of these settings can be found on the &lt;a href=&quot;http://www.kernel.org/pub/software/scm/git/docs/git-config.html&quot; linkindex=&quot;25&quot;&gt;git-config&lt;/a&gt; man page.&lt;br /&gt;
&lt;/span&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gaegwtbestpractices.blogspot.com/feeds/2985141411903300768/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://gaegwtbestpractices.blogspot.com/2010/07/short-note-on-cygwin-git.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3248335039039953287/posts/default/2985141411903300768'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3248335039039953287/posts/default/2985141411903300768'/><link rel='alternate' type='text/html' href='http://gaegwtbestpractices.blogspot.com/2010/07/short-note-on-cygwin-git.html' title='A short note on Cygwin git'/><author><name>John Schutz</name><uri>http://www.blogger.com/profile/12385766703556945544</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiRkpC7-LIVJCQcGKsOoSDO2UZitOmX-n7VCMnXq4HqTQKmeonrbB6vpvwBWw_hKCrK3wFhKstFOC9cObvuJjzpB27YS6SRMGMs9_RT8_GmNKc8KRDMAAqFQaDwZxPhMXo/s220/2009-10-26.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3248335039039953287.post-6566423952622139162</id><published>2010-07-10T03:20:00.000-05:00</published><updated>2010-07-10T18:20:42.915-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="gae"/><category scheme="http://www.blogger.com/atom/ns#" term="goals"/><category scheme="http://www.blogger.com/atom/ns#" term="gwt"/><category scheme="http://www.blogger.com/atom/ns#" term="gwt-dispatch"/><category scheme="http://www.blogger.com/atom/ns#" term="gwt-presenter"/><category scheme="http://www.blogger.com/atom/ns#" term="tools"/><title type='text'>In the beginning...</title><content type='html'>&lt;b&gt;&lt;span style=&quot;font-size: large;&quot;&gt;What&#39;s all this then?&lt;/span&gt;&lt;/b&gt; &lt;br /&gt;
&lt;br /&gt;
I need to bring my web development skills up to date, instead of living in the all raw-HTML-only-static-pages past.&amp;nbsp; After doing some research, I liked what I read about Google App Engine (GAE), as well as Google Web Toolkit (GWT).&amp;nbsp; Especially that, right now, it will let me focus on the code instead of managing the servers.&lt;br /&gt;
&lt;br /&gt;
Oh, and I can use Java for both the client and server.&lt;br /&gt;
&lt;br /&gt;
I volunteer at a local non-profit, where volunteers track their time with pen and paper, and our contact information is gathered once when we sign up, and then never updated.&amp;nbsp; The hours have to be manually transcribed into a spreadsheet, and it makes it hard to run reports or gather statistics.&amp;nbsp; This seemed like an ideal project that shouldn&#39;t take forever, but also should get into the meat of the two products.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;&lt;span style=&quot;font-size: large;&quot;&gt;OK, but what&#39;s the deal with the blog?&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
Sometimes I work late, and have trouble remembering the next day exactly what I had tried, what research I had done and its conclusions, or just where I am in the project.&amp;nbsp; I usually document this stuff in a text file.&amp;nbsp; While researching best practices for GAE/GWT, I wanted a site that chronicled the creation of an application from the beginning.&amp;nbsp; Initially I didn&#39;t find any.&amp;nbsp; I based my knowledge of best practices on the famous and excellent Google I/O talk: &lt;a href=&quot;http://code.google.com/events/io/2009/sessions/GoogleWebToolkitBestPractices.html&quot; linkindex=&quot;25&quot;&gt;GWT App Architecture Best Practices&lt;/a&gt; by Ray Ryan.&amp;nbsp; It&#39;s invaluable.&lt;br /&gt;
&lt;br /&gt;
Later, I did come across a &lt;a href=&quot;http://borglin.net/gwt-project/&quot; linkindex=&quot;26&quot;&gt;GWT/GAE Development Blog&lt;/a&gt; that seems like a good introduction, though I haven&#39;t worked through much of it yet.&amp;nbsp; I&#39;m sure I&#39;ll use it as a reference as I learn.&lt;br /&gt;
&lt;br /&gt;
But I want to chronicle the fits and starts, both the good and bad ideas, the failures, the dead ends, the screw ups; and then hopefully successes.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;&lt;span style=&quot;font-size: large;&quot;&gt;Basic project details&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: small;&quot;&gt;I haven&#39;t decided on a name yet, so I&#39;ll just call it &quot;the project&quot; for now.&amp;nbsp; It will be hosted on github, as I like git&#39;s branching and DCVS model.&amp;nbsp; I&#39;ll use Java in Eclipse Helios as the language, but still access git from the command line, since I&#39;ve had plenty of problems with the git plug-in.&amp;nbsp; I will probably use the &lt;a href=&quot;http://code.google.com/p/google-gin/&quot; linkindex=&quot;27&quot;&gt;Gin&lt;/a&gt; and &lt;a href=&quot;http://code.google.com/p/google-guice/&quot; linkindex=&quot;28&quot;&gt;Guice&lt;/a&gt;&amp;nbsp;libraries, and base my &lt;a href=&quot;http://en.wikipedia.org/wiki/Model-view-presenter&quot; linkindex=&quot;29&quot;&gt;MVP design&lt;/a&gt; around &lt;/span&gt;the &lt;a href=&quot;http://code.google.com/p/gwt-presenter/&quot; linkindex=&quot;30&quot;&gt;gwt-presenter&lt;/a&gt; and &lt;a href=&quot;http://code.google.com/p/gwt-dispatch/&quot; linkindex=&quot;31&quot;&gt;gwt-dispatch&lt;/a&gt; libraries, though they look like they haven&#39;t been updated in a while.&lt;br /&gt;
&lt;br /&gt;
For testing the pages I&#39;ll use the Google plug-in for Eclipse, as well as the Speed Tracer, Firebug, etc.&lt;br /&gt;
&lt;br /&gt;
Enough procrastinating...</content><link rel='replies' type='application/atom+xml' href='http://gaegwtbestpractices.blogspot.com/feeds/6566423952622139162/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://gaegwtbestpractices.blogspot.com/2010/07/in-beginning.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3248335039039953287/posts/default/6566423952622139162'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3248335039039953287/posts/default/6566423952622139162'/><link rel='alternate' type='text/html' href='http://gaegwtbestpractices.blogspot.com/2010/07/in-beginning.html' title='In the beginning...'/><author><name>John Schutz</name><uri>http://www.blogger.com/profile/12385766703556945544</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiRkpC7-LIVJCQcGKsOoSDO2UZitOmX-n7VCMnXq4HqTQKmeonrbB6vpvwBWw_hKCrK3wFhKstFOC9cObvuJjzpB27YS6SRMGMs9_RT8_GmNKc8KRDMAAqFQaDwZxPhMXo/s220/2009-10-26.jpg'/></author><thr:total>1</thr:total></entry></feed>