Gaelyk: Lightweight Groovy on the Google App Engine

2,780 views
2,703 views

Published 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 GAE.

Published in: Technology, News & Politics
0 Comments
5 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
2,780
On SlideShare
0
From Embeds
0
Number of Embeds
292
Actions
Shares
0
Downloads
0
Comments
0
Likes
5
Embeds 0
No embeds

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.











  • Gaelyk: Lightweight Groovy on the Google App Engine

    1. 1. Lightweight on the by Tim Berglund
    2. 2. Tell me about aGreat Application Language
    3. 3. Tell me a Happy Tale of Deployment
    4. 4. and Make it Fast
    5. 5. Tell me about the Googl eApp Engine
    6. 6. An App Server
    7. 7. In the CLoud
    8. 8. Google Infrastructure
    9. 9. Usually Free!
    10. 10. Quotas http://code.google.com/appengine/docs/quotas.html
    11. 11. Quotas http://code.google.com/appengine/docs/quotas.html
    12. 12. Pricing
    13. 13. Languages
    14. 14. Languages
    15. 15. Languages
    16. 16. Languages
    17. 17. Services
    18. 18. Services It’s an app server
    19. 19. Services With a few extra features
    20. 20. Services
    21. 21. Services Datastore
    22. 22. Services Datastore Authentication
    23. 23. Services Datastore Authentication Caching
    24. 24. Services Datastore Authentication Caching XMPP
    25. 25. Services Datastore Authentication Caching XMPP Email
    26. 26. Services Datastore Task Queue Authentication Caching XMPP Email
    27. 27. Services Datastore Task Queue Authentication Image API Caching XMPP Email
    28. 28. Services Datastore Task Queue Authentication Image API Caching URL Fetching XMPP Email
    29. 29. Services Datastore Task Queue Authentication Image API Caching URL Fetching XMPP OAuth Email
    30. 30. Services Datastore Task Queue Authentication Image API Caching URL Fetching XMPP OAuth Email Blobstore
    31. 31. But It’s not Perfect
    32. 32. Limitations
    33. 33. Limitations The JDK whitelist
    34. 34. Limitations The JDK whitelist http://code.google.com/appengine/docs/java/jrewhitelist.html
    35. 35. Limitations The JDK whitelist http://code.google.com/appengine/docs/java/jrewhitelist.html No Hibernate
    36. 36. Limitations The JDK whitelist http://code.google.com/appengine/docs/java/jrewhitelist.html No Hibernate Only JPA and JDO
    37. 37. Limitations The JDK whitelist http://code.google.com/appengine/docs/java/jrewhitelist.html No Hibernate Only JPA and JDO Grails is limited
    38. 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. 39. Tell me about
    40. 40. Groovy-based
    41. 41. Groovy-based Lightweight
    42. 42. Groovy-based Lightweight Page-centric
    43. 43. Groovy-based Lightweight Page-centric Tightly coupled to the Google App Engine
    44. 44. Groovy-based Lightweight Page-centric Tightly coupled to the Google App Engine A better GAE than the GAE
    45. 45. Setting up Gaelyk
    46. 46. Serving Content
    47. 47. Serving Content
    48. 48. Serving Content Static content (like in a WAR)
    49. 49. Serving Content Static content (like in a WAR) Groovy templates
    50. 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. 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. 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. 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. 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. 55. Templates
    56. 56. Templates Lightweight, Groovy JSPs
    57. 57. Templates Lightweight, Groovy JSPs Downside: Scriptlets
    58. 58. Templates Lightweight, Groovy JSPs Downside: Scriptlets Request attributes bound from Groovlets
    59. 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. 60. Groovlets
    61. 61. Groovlets Groovy script, meet Servlet
    62. 62. Groovlets Groovy script, meet Servlet Intrinsic variables
    63. 63. Groovlets Groovy script, meet Servlet Intrinsic variables Forwarding
    64. 64. Groovlets Groovy script, meet Servlet Intrinsic variables Forwarding Redirecting
    65. 65. Groovlets Groovy script, meet Servlet Intrinsic variables Forwarding Redirecting MarkupBuilder
    66. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 76. URL Mapping
    77. 77. URL Mapping Templates in war/*.gtpl
    78. 78. URL Mapping Templates in war/*.gtpl Groovlets in war/WEB-INF/groovy/*.groovy
    79. 79. URL Mapping Templates in war/*.gtpl Groovlets in war/WEB-INF/groovy/*.groovy Pretty URLs with war/WEB-INF/routes.groovy
    80. 80. The Datastore
    81. 81. The Datastore
    82. 82. The Datastore Google’s BigTable infrastructure
    83. 83. The Datastore Google’s BigTable infrastructure Schemaless
    84. 84. The Datastore Google’s BigTable infrastructure Schemaless Named entities (property bundles)
    85. 85. The Datastore Google’s BigTable infrastructure Schemaless Named entities (property bundles) Indexed, queryable, sortable
    86. 86. The Datastore Google’s BigTable infrastructure Schemaless Named entities (property bundles) Indexed, queryable, sortable Transactional
    87. 87. The Datastore Google’s BigTable infrastructure Schemaless Named entities (property bundles) Indexed, queryable, sortable Transactional Great Groovy wrapper
    88. 88. Datastore Indexes
    89. 89. Datastore Indexes Automatically generated by default
    90. 90. Datastore Indexes Automatically generated by default Override in WEB-INF/datastore-indexes.xml
    91. 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. 92. Tell me about Other Services
    93. 93. Email in Java
    94. 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. 95. Inbound Email
    96. 96. Inbound Email Enable in appengine-web.xml
    97. 97. Inbound Email Enable in appengine-web.xml Enable built-in servlet in web.xml
    98. 98. Inbound Email Enable in appengine-web.xml Enable built-in servlet in web.xml email.groovy Groovlet
    99. 99. Inbound Email Enable in appengine-web.xml Enable built-in servlet in web.xml email.groovy Groovlet The “message” intrinsic variable
    100. 100. Memcache in Java
    101. 101. Memcache with Gaelyk def countryFr = new Country(name: 'France') memcache['FR'] = countryFr if ('FR' in memcache) { countryFromCache = memcache['FR'] }
    102. 102. URL Fetch in Java
    103. 103. URL Fetch with Gaelyk def response = urlFetch.fetch(new URL("http://example.com")) def text = new String(response.content)
    104. 104. Task Queues queue.add countdownMillis: 10000, url: '/task/emailTaskList', taskName: 'Email current story assignments', method: 'PUT', params: [date: new Date()]
    105. 105. Task Queues
    106. 106. Task Queues Timed HTTP requests
    107. 107. Task Queues Timed HTTP requests Configure in war/WEB-INF/queue.xml
    108. 108. Task Queues Timed HTTP requests Configure in war/WEB-INF/queue.xml http://code.google.com/appengine/docs/java/config/queue.html
    109. 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. 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. 111. XMPP
    112. 112. XMPP User notification
    113. 113. XMPP User notification XML payloads for distributed communication
    114. 114. XMPP User notification XML payloads for distributed communication Send messages with a Groovier API
    115. 115. XMPP User notification XML payloads for distributed communication Send messages with a Groovier API Receive messages through a servlet
    116. 116. XMPP Sending in Java
    117. 117. XMPP Sending with Gaelyk
    118. 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. 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. 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. 121. XMPP Receiving
    122. 122. XMPP Receiving Enable in appengine-web.xml
    123. 123. XMPP Receiving Enable in appengine-web.xml Enable built-in servlet in web.xml
    124. 124. XMPP Receiving Enable in appengine-web.xml Enable built-in servlet in web.xml /war/WEB-INF/groovy/jabber.groovy
    125. 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. 126. Plugins
    127. 127. Plugins
    128. 128. Plugins Analogous to Grails plugins
    129. 129. Plugins Analogous to Grails plugins Mini Gaelyk apps
    130. 130. Plugins Analogous to Grails plugins Mini Gaelyk apps plugins/xxxPluginDescriptor.groovy
    131. 131. Plugins Analogous to Grails plugins Mini Gaelyk apps plugins/xxxPluginDescriptor.groovy Bundled as ZIP files
    132. 132. Plugins Analogous to Grails plugins Mini Gaelyk apps plugins/xxxPluginDescriptor.groovy Bundled as ZIP files Ad-hoc discovery and distribution
    133. 133. Development
    134. 134. What about Services in development?
    135. 135. GAE Emulation
    136. 136. GAE Emulation Datastore simulated in filesystem
    137. 137. GAE Emulation Datastore simulated in filesystem General Administration
    138. 138. GAE Emulation Datastore simulated in filesystem General Administration http://localhost:8080/_ah/admin
    139. 139. GAE Emulation Datastore simulated in filesystem General Administration http://localhost:8080/_ah/admin Authentication
    140. 140. GAE Emulation Datastore simulated in filesystem General Administration http://localhost:8080/_ah/admin Authentication http://localhost:8080/_ah/login
    141. 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. 142. Code
    143. 143. On github git@github.com:tlberglund/august-stories.git git@github.com:tlberglund/august-demo.git
    144. 144. Thank You Tim Berglund www.augusttechgroup.com tim.berglund@augusttechgroup.com @tlberglund Using Your Existing Team
    145. 145. Photo Credits Parchment Background http://www.photoshopstar.com/effects/recreate-indiana-jones-text-effect/

    ×