Gaelyk: Lightweight Groovy on the Google App Engine

  • 2,500 views
Uploaded on

Gaelyk is a lightweight Groovy web application framework built specifically for the Google App Engine. It provides a fast, easy way to build simply applications in Groovy and host them for free on the …

Gaelyk is a lightweight Groovy web application framework built specifically for the Google App Engine. It provides a fast, easy way to build simply applications in Groovy and host them for free on the GAE.

  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
No Downloads

Views

Total Views
2,500
On Slideshare
0
From Embeds
0
Number of Embeds
0

Actions

Shares
Downloads
0
Comments
0
Likes
5

Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide

  • On the JDK, we’ve got options, and they’re growing. Groovy is a better Java focused on strong Java interop, an easy migration path, and easy application coding.
  • Often the Java deployment story is less than happy. The Ops team gets it and feels great about it, but the PHP guys don’t know what to do with us. We’ve got great framework choices,
  • It should be an easy framework to learn and use.
  • Demo starting up a Gaelyk app.

  • You’re running your app on Google’s nearly infinitely scalable infrastructure. Go ahead, DDOS it!
  • You’re running your app on Google’s nearly infinitely scalable infrastructure. Go ahead, DDOS it!
  • You’re running your app on Google’s nearly infinitely scalable infrastructure. Go ahead, DDOS it!
  • You can sign up and get started and not pay a dime. You get certain free quotas.
  • GAE offers more than just transfer in and out and CPU. All services have their own quotas described at that URL. Since we’re talking about Gaelyk, not GAE, we’ll move on.
  • If you grow beyond the free quotas, you can always buy more. Internet scale, baby!
  • Started at Python. Now any language of the JVM, within limitations.
  • Started at Python. Now any language of the JVM, within limitations.
  • Started at Python. Now any language of the JVM, within limitations.
  • Started at Python. Now any language of the JVM, within limitations.
  • Started at Python. Now any language of the JVM, within limitations.
  • Started at Python. Now any language of the JVM, within limitations.

  • Remember, it’s basically an app server.
  • Remember, it’s basically an app server.










  • Remember, it’s basically an app server.
  • Most of java.awt, javax.imageio, some java.nio, etc. Performance problems in Java have to do with first-request performance after a delay of no requests.
  • Most of java.awt, javax.imageio, some java.nio, etc. Performance problems in Java have to do with first-request performance after a delay of no requests.
  • Most of java.awt, javax.imageio, some java.nio, etc. Performance problems in Java have to do with first-request performance after a delay of no requests.
  • Most of java.awt, javax.imageio, some java.nio, etc. Performance problems in Java have to do with first-request performance after a delay of no requests.
  • Most of java.awt, javax.imageio, some java.nio, etc. Performance problems in Java have to do with first-request performance after a delay of no requests.
  • Most of java.awt, javax.imageio, some java.nio, etc. Performance problems in Java have to do with first-request performance after a delay of no requests.






  • All examples will be with TextMate and the Console. No Eclipse plugin necessary!













































  • Attachments are optional.
  • Attachments are optional.


























  • A discussion
  • PluginDescriptor can add bindings, etc.
  • PluginDescriptor can add bindings, etc.
  • PluginDescriptor can add bindings, etc.
  • PluginDescriptor can add bindings, etc.
  • PluginDescriptor can add bindings, etc.

  • The GAE SDK contains an embedded Jetty instance that emulates all appropriate services. The Eclipse plugin does this as well, but you don’t have to use it.
  • It’s also easy to deploy to GAE from the command line.











Transcript

  • 1. Lightweight on the by Tim Berglund
  • 2. Tell me about aGreat Application Language
  • 3. Tell me a Happy Tale of Deployment
  • 4. and Make it Fast
  • 5. Tell me about the Googl eApp Engine
  • 6. An App Server
  • 7. In the CLoud
  • 8. Google Infrastructure
  • 9. Usually Free!
  • 10. Quotas http://code.google.com/appengine/docs/quotas.html
  • 11. Quotas http://code.google.com/appengine/docs/quotas.html
  • 12. Pricing
  • 13. Languages
  • 14. Languages
  • 15. Languages
  • 16. Languages
  • 17. Services
  • 18. Services It’s an app server
  • 19. Services With a few extra features
  • 20. Services
  • 21. Services Datastore
  • 22. Services Datastore Authentication
  • 23. Services Datastore Authentication Caching
  • 24. Services Datastore Authentication Caching XMPP
  • 25. Services Datastore Authentication Caching XMPP Email
  • 26. Services Datastore Task Queue Authentication Caching XMPP Email
  • 27. Services Datastore Task Queue Authentication Image API Caching XMPP Email
  • 28. Services Datastore Task Queue Authentication Image API Caching URL Fetching XMPP Email
  • 29. Services Datastore Task Queue Authentication Image API Caching URL Fetching XMPP OAuth Email
  • 30. Services Datastore Task Queue Authentication Image API Caching URL Fetching XMPP OAuth Email Blobstore
  • 31. But It’s not Perfect
  • 32. Limitations
  • 33. Limitations The JDK whitelist
  • 34. Limitations The JDK whitelist http://code.google.com/appengine/docs/java/jrewhitelist.html
  • 35. Limitations The JDK whitelist http://code.google.com/appengine/docs/java/jrewhitelist.html No Hibernate
  • 36. Limitations The JDK whitelist http://code.google.com/appengine/docs/java/jrewhitelist.html No Hibernate Only JPA and JDO
  • 37. Limitations The JDK whitelist http://code.google.com/appengine/docs/java/jrewhitelist.html No Hibernate Only JPA and JDO Grails is limited
  • 38. Limitations The JDK whitelist http://code.google.com/appengine/docs/java/jrewhitelist.html No Hibernate Only JPA and JDO Grails is limited Performance problems?
  • 39. Tell me about
  • 40. Groovy-based
  • 41. Groovy-based Lightweight
  • 42. Groovy-based Lightweight Page-centric
  • 43. Groovy-based Lightweight Page-centric Tightly coupled to the Google App Engine
  • 44. Groovy-based Lightweight Page-centric Tightly coupled to the Google App Engine A better GAE than the GAE
  • 45. Setting up Gaelyk
  • 46. Serving Content
  • 47. Serving Content
  • 48. Serving Content Static content (like in a WAR)
  • 49. Serving Content Static content (like in a WAR) Groovy templates
  • 50. Templates <% include '/WEB-INF/includes/header.gtpl' %> <h1>Look Up an Artist</h1> <form action="/artistLookup.groovy" method="POST"> <input type="text" size="50" name="artist"/> <input type="submit" value="Lookup"/> </form> <ul> <% request.artists.each { artist -> %> <li>${artist.name}</li> <% } %> </ul> <% include '/WEB-INF/includes/footer.gtpl' %>
  • 51. Templates <% include '/WEB-INF/includes/header.gtpl' %> <h1>Look Up an Artist</h1> <form action="/artistLookup.groovy" method="POST"> <input type="text" size="50" name="artist"/> <input type="submit" value="Lookup"/> </form> <ul> <% request.artists.each { artist -> %> <li>${artist.name}</li> <% } %> </ul> <% include '/WEB-INF/includes/footer.gtpl' %>
  • 52. Templates <% include '/WEB-INF/includes/header.gtpl' %> <h1>Look Up an Artist</h1> <form action="/artistLookup.groovy" method="POST"> <input type="text" size="50" name="artist"/> <input type="submit" value="Lookup"/> </form> <ul> <% request.artists.each { artist -> %> <li>${artist.name}</li> <% } %> </ul> <% include '/WEB-INF/includes/footer.gtpl' %>
  • 53. Templates <% include '/WEB-INF/includes/header.gtpl' %> <h1>Look Up an Artist</h1> <form action="/artistLookup.groovy" method="POST"> <input type="text" size="50" name="artist"/> <input type="submit" value="Lookup"/> </form> <ul> <% request.artists.each { artist -> %> <li>${artist.name}</li> <% } %> </ul> <% include '/WEB-INF/includes/footer.gtpl' %>
  • 54. Templates <% include '/WEB-INF/includes/header.gtpl' %> <h1>Look Up an Artist</h1> <form action="/artistLookup.groovy" method="POST"> <input type="text" size="50" name="artist"/> <input type="submit" value="Lookup"/> </form> <ul> <% request.artists.each { artist -> %> <li>${artist.name}</li> <% } %> </ul> <% include '/WEB-INF/includes/footer.gtpl' %>
  • 55. Templates
  • 56. Templates Lightweight, Groovy JSPs
  • 57. Templates Lightweight, Groovy JSPs Downside: Scriptlets
  • 58. Templates Lightweight, Groovy JSPs Downside: Scriptlets Request attributes bound from Groovlets
  • 59. Groovlets import java.net.URLEncoder if(params.artist) { def response = urlFetch.fetch(new URL("http://musicbrainz.org/ ws/1/artist/?type=xml&name=${URLEncoder.encode(params.artist)}")) def text = new String(response.content) def xml = new XmlSlurper().parseText(text) request.artists = xml.'artist-list'.artist } else { request.artists = [] } forward '/artistLookup.gtpl'
  • 60. Groovlets
  • 61. Groovlets Groovy script, meet Servlet
  • 62. Groovlets Groovy script, meet Servlet Intrinsic variables
  • 63. Groovlets Groovy script, meet Servlet Intrinsic variables Forwarding
  • 64. Groovlets Groovy script, meet Servlet Intrinsic variables Forwarding Redirecting
  • 65. Groovlets Groovy script, meet Servlet Intrinsic variables Forwarding Redirecting MarkupBuilder
  • 66. import java.net.URLEncoder if(params.artist) { def response = urlFetch.fetch(new URL("http:// musicbrainz.org/ws/1/artist/?type=xml&name=$ {URLEncoder.encode(params.artist)}")) def text = new String(response.content) def xml = new XmlSlurper().parseText(text) request.artists = xml.'artist-list'.artist } else { request.artists = [] } forward '/artistLookup.gtpl'
  • 67. import java.net.URLEncoder if(params.artist) { def response = urlFetch.fetch(new URL("http:// musicbrainz.org/ws/1/artist/?type=xml&name=$ {URLEncoder.encode(params.artist)}")) def text = new String(response.content) def xml = new XmlSlurper().parseText(text) request.artists = xml.'artist-list'.artist } else { request.artists = [] } forward '/artistLookup.gtpl'
  • 68. import java.net.URLEncoder if(params.artist) { def response = urlFetch.fetch(new URL("http:// musicbrainz.org/ws/1/artist/?type=xml&name=$ {URLEncoder.encode(params.artist)}")) def text = new String(response.content) def xml = new XmlSlurper().parseText(text) request.artists = xml.'artist-list'.artist } else { request.artists = [] } forward '/artistLookup.gtpl'
  • 69. import java.net.URLEncoder if(params.artist) { def response = urlFetch.fetch(new URL("http:// musicbrainz.org/ws/1/artist/?type=xml&name=$ {URLEncoder.encode(params.artist)}")) def text = new String(response.content) def xml = new XmlSlurper().parseText(text) request.artists = xml.'artist-list'.artist } else { request.artists = [] } forward '/artistLookup.gtpl'
  • 70. import com.google.appengine.api.datastore.* import static com.google.appengine.api.datastore.FetchOption if(!user) { redirect users.createLoginURL(request.requestURL.toString( return } def query = new Query("project") query.addSort("dateCreated", Query.SortDirection.ASCENDING) PreparedQuery preparedQuery = datastore.prepare(query) request.projects = preparedQuery.asList( withOffset(0) ) forward "/WEB-INF/views/project/list.gtpl"
  • 71. import com.google.appengine.api.datastore.* import static com.google.appengine.api.datastore.FetchOption if(!user) { redirect users.createLoginURL(request.requestURL.toString( return } def query = new Query("project") query.addSort("dateCreated", Query.SortDirection.ASCENDING) PreparedQuery preparedQuery = datastore.prepare(query) request.projects = preparedQuery.asList( withOffset(0) ) forward "/WEB-INF/views/project/list.gtpl"
  • 72. import com.google.appengine.api.datastore.* import static com.google.appengine.api.datastore.FetchOption if(!user) { redirect users.createLoginURL(request.requestURL.toString( return } def query = new Query("project") query.addSort("dateCreated", Query.SortDirection.ASCENDING) PreparedQuery preparedQuery = datastore.prepare(query) request.projects = preparedQuery.asList( withOffset(0) ) forward "/WEB-INF/views/project/list.gtpl"
  • 73. import com.google.appengine.api.datastore.* import static com.google.appengine.api.datastore.FetchOption if(!user) { redirect users.createLoginURL(request.requestURL.toString( return } def query = new Query("project") query.addSort("dateCreated", Query.SortDirection.ASCENDING) PreparedQuery preparedQuery = datastore.prepare(query) request.projects = preparedQuery.asList( withOffset(0) ) forward "/WEB-INF/views/project/list.gtpl"
  • 74. import com.google.appengine.api.datastore.* import static com.google.appengine.api.datastore.FetchOption if(!user) { redirect users.createLoginURL(request.requestURL.toString( return } def query = new Query("project") query.addSort("dateCreated", Query.SortDirection.ASCENDING) PreparedQuery preparedQuery = datastore.prepare(query) request.projects = preparedQuery.asList( withOffset(0) ) forward "/WEB-INF/views/project/list.gtpl"
  • 75. import com.google.appengine.api.datastore.* import static com.google.appengine.api.datastore.FetchOption if(!user) { redirect users.createLoginURL(request.requestURL.toString( return } def query = new Query("project") query.addSort("dateCreated", Query.SortDirection.ASCENDING) PreparedQuery preparedQuery = datastore.prepare(query) request.projects = preparedQuery.asList( withOffset(0) ) forward "/WEB-INF/views/project/list.gtpl"
  • 76. URL Mapping
  • 77. URL Mapping Templates in war/*.gtpl
  • 78. URL Mapping Templates in war/*.gtpl Groovlets in war/WEB-INF/groovy/*.groovy
  • 79. URL Mapping Templates in war/*.gtpl Groovlets in war/WEB-INF/groovy/*.groovy Pretty URLs with war/WEB-INF/routes.groovy
  • 80. The Datastore
  • 81. The Datastore
  • 82. The Datastore Google’s BigTable infrastructure
  • 83. The Datastore Google’s BigTable infrastructure Schemaless
  • 84. The Datastore Google’s BigTable infrastructure Schemaless Named entities (property bundles)
  • 85. The Datastore Google’s BigTable infrastructure Schemaless Named entities (property bundles) Indexed, queryable, sortable
  • 86. The Datastore Google’s BigTable infrastructure Schemaless Named entities (property bundles) Indexed, queryable, sortable Transactional
  • 87. The Datastore Google’s BigTable infrastructure Schemaless Named entities (property bundles) Indexed, queryable, sortable Transactional Great Groovy wrapper
  • 88. Datastore Indexes
  • 89. Datastore Indexes Automatically generated by default
  • 90. Datastore Indexes Automatically generated by default Override in WEB-INF/datastore-indexes.xml
  • 91. Datastore Indexes Automatically generated by default Override in WEB-INF/datastore-indexes.xml <?xml version="1.0" encoding="utf-8"?> <datastore-indexes   autoGenerate="true">     <datastore-index kind="Employee" ancestor="false">         <property name="lastName" direction="asc" />         <property name="hireDate" direction="desc" />     </datastore-index>     <datastore-index kind="Project" ancestor="false">         <property name="dueDate" direction="asc" />     </datastore-index> </datastore-indexes>
  • 92. Tell me about Other Services
  • 93. Email in Java
  • 94. Email with Gaelyk mail.send sender: 'story-admin@augusttechgroup.com', to: 'tlberglund@gmail.com', subject: 'Story Complete', textBody: "A story you edited has increased in priority. Check it out."
  • 95. Inbound Email
  • 96. Inbound Email Enable in appengine-web.xml
  • 97. Inbound Email Enable in appengine-web.xml Enable built-in servlet in web.xml
  • 98. Inbound Email Enable in appengine-web.xml Enable built-in servlet in web.xml email.groovy Groovlet
  • 99. Inbound Email Enable in appengine-web.xml Enable built-in servlet in web.xml email.groovy Groovlet The “message” intrinsic variable
  • 100. Memcache in Java
  • 101. Memcache with Gaelyk def countryFr = new Country(name: 'France') memcache['FR'] = countryFr if ('FR' in memcache) { countryFromCache = memcache['FR'] }
  • 102. URL Fetch in Java
  • 103. URL Fetch with Gaelyk def response = urlFetch.fetch(new URL("http://example.com")) def text = new String(response.content)
  • 104. Task Queues queue.add countdownMillis: 10000, url: '/task/emailTaskList', taskName: 'Email current story assignments', method: 'PUT', params: [date: new Date()]
  • 105. Task Queues
  • 106. Task Queues Timed HTTP requests
  • 107. Task Queues Timed HTTP requests Configure in war/WEB-INF/queue.xml
  • 108. Task Queues Timed HTTP requests Configure in war/WEB-INF/queue.xml http://code.google.com/appengine/docs/java/config/queue.html
  • 109. Task Queues Timed HTTP requests Configure in war/WEB-INF/queue.xml http://code.google.com/appengine/docs/java/config/queue.html Can update without redeploying
  • 110. Task Queues Timed HTTP requests Configure in war/WEB-INF/queue.xml http://code.google.com/appengine/docs/java/config/queue.html Can update without redeploying Can be secured
  • 111. XMPP
  • 112. XMPP User notification
  • 113. XMPP User notification XML payloads for distributed communication
  • 114. XMPP User notification XML payloads for distributed communication Send messages with a Groovier API
  • 115. XMPP User notification XML payloads for distributed communication Send messages with a Groovier API Receive messages through a servlet
  • 116. XMPP Sending in Java
  • 117. XMPP Sending with Gaelyk
  • 118. XMPP Sending with Gaelyk def recipient = 'tlberglund@gmail.com' if (xmpp.getPresence(recipient).isAvailable()) { def status = xmpp.send(to: recipient, body: "Glad you're into Gaelyk!") if (!status.isSuccessful()) {
  • 119. XMPP Sending with Gaelyk def recipient = 'tlberglund@gmail.com' if (xmpp.getPresence(recipient).isAvailable()) { def status = xmpp.send(to: recipient, body: "Glad you're into Gaelyk!") if (!status.isSuccessful()) { // ...
  • 120. XMPP Sending with Gaelyk def recipient = 'tlberglund@gmail.com' if (xmpp.getPresence(recipient).isAvailable()) { def status = xmpp.send(to: recipient, body: "Glad you're into Gaelyk!") if (!status.isSuccessful()) { // ... } }
  • 121. XMPP Receiving
  • 122. XMPP Receiving Enable in appengine-web.xml
  • 123. XMPP Receiving Enable in appengine-web.xml Enable built-in servlet in web.xml
  • 124. XMPP Receiving Enable in appengine-web.xml Enable built-in servlet in web.xml /war/WEB-INF/groovy/jabber.groovy
  • 125. XMPP Receiving Enable in appengine-web.xml Enable built-in servlet in web.xml /war/WEB-INF/groovy/jabber.groovy The “message” intrinsic variable
  • 126. Plugins
  • 127. Plugins
  • 128. Plugins Analogous to Grails plugins
  • 129. Plugins Analogous to Grails plugins Mini Gaelyk apps
  • 130. Plugins Analogous to Grails plugins Mini Gaelyk apps plugins/xxxPluginDescriptor.groovy
  • 131. Plugins Analogous to Grails plugins Mini Gaelyk apps plugins/xxxPluginDescriptor.groovy Bundled as ZIP files
  • 132. Plugins Analogous to Grails plugins Mini Gaelyk apps plugins/xxxPluginDescriptor.groovy Bundled as ZIP files Ad-hoc discovery and distribution
  • 133. Development
  • 134. What about Services in development?
  • 135. GAE Emulation
  • 136. GAE Emulation Datastore simulated in filesystem
  • 137. GAE Emulation Datastore simulated in filesystem General Administration
  • 138. GAE Emulation Datastore simulated in filesystem General Administration http://localhost:8080/_ah/admin
  • 139. GAE Emulation Datastore simulated in filesystem General Administration http://localhost:8080/_ah/admin Authentication
  • 140. GAE Emulation Datastore simulated in filesystem General Administration http://localhost:8080/_ah/admin Authentication http://localhost:8080/_ah/login
  • 141. GAE Emulation Datastore simulated in filesystem General Administration http://localhost:8080/_ah/admin Authentication http://localhost:8080/_ah/login http://localhost:8080/_ah/logout
  • 142. Code
  • 143. On github git@github.com:tlberglund/august-stories.git git@github.com:tlberglund/august-demo.git
  • 144. Thank You Tim Berglund www.augusttechgroup.com tim.berglund@augusttechgroup.com @tlberglund Using Your Existing Team
  • 145. Photo Credits Parchment Background http://www.photoshopstar.com/effects/recreate-indiana-jones-text-effect/