Simple CRUD with Gaelyk

October 21, 2010 at 7:26 pm | Posted in GAE, Groovy, Search | 5 Comments
Tags: , , , , , , ,

After having written a Grails application for Google App Engine I know exactly what the pain points are in production: Cold startup times make using the application an unenjoyable experience. App Engine calls for a lightweight application with far less JAR baggage and fast startup times. Yesterday I attended Guillaume LaForge’s session “Gaelyk, a lightweight Groovy toolkit for Google App Engine” at SpringOne2GX presenting the nuts and bolts of Gaelyk. The session was reason enough to think about rewriting my application to Gaelyk. To get started I whipped up a simple CRUD example in Gaelyk. All you need to do is to download the Gaelyk template application and you are ready to go. The test application will have four simple use cases: show all entities of a certain type (in my case a “Goal” entity), create new ones, update and delete them.

Gaelyk doesn’t support multiple closures in a controller class to automatically expose URLs by convention like Grails does. Instead you have to define your URL routing for each operation separately.

war/WEB-INF/routes.groovy:

get  "/goals",                forward:  "/showGoals.groovy"
get  "/goals/add",            forward:  "/WEB-INF/pages/goal.gtpl"
post "/goals/insert",         forward:  "/insertGoal.groovy"
get  "/goals/delete/@id",     forward:  "/deleteGoal.groovy?id=@id"
get  "/goals/edit/@id",       forward:  "/editGoal.groovy?id=@id"
post "/goals/update",         forward:  "/updateGoal.groovy"

To show all Goals I defined a Groovlet named showGoals.groovy that uses the App Engine datastore low-level API to retrieve them and forward to the view goals.gtpl. In the view you can add a new goal or edit/delete existing goals.

war/WEB-INF/groovy/showGoals.groovy:

import com.google.appengine.api.datastore.Query
import com.google.appengine.api.datastore.PreparedQuery
import static com.google.appengine.api.datastore.FetchOptions.Builder.*

log.info "Getting all goals"

def query = new Query("Goal")
PreparedQuery preparedQuery = datastore.prepare(query)
query.addSort("created", Query.SortDirection.DESCENDING)
def goals = preparedQuery.asList(withDefaults())

request.goals = goals

forward '/WEB-INF/pages/goals.gtpl'

war/WEB-INF/pages/goals.gtpl:

<% include '/WEB-INF/includes/header.gtpl' %>

<h2>Goals</h2>

<a href="/goals/add">Add Goal</a>
<br><br>
<table border="1">
  <thead>
     <tr>
        <th>Name</th>
        <th>Description</th>
        <th>Created</th>
        <th>&nbsp;</th>
     </tr>
  </thead>
  <tbody>
     <% request.goals.each { goal -> %>
        <tr>
           <td>${goal.name}</td>
           <td>${goal.description}</td>
           <td>${goal.created}</td>
           <td><a href="/goals/delete/${goal.key.id}">Delete</a> | <a href="/goals/edit/${goal.key.id}">Edit</a></td>
        </tr>
     <% } %>
  </tbody>
</table>

<% include '/WEB-INF/includes/footer.gtpl' %>

The operations insert and edit share a common view named goal.gtpl. Based on whether the request contains the attribute goal the view will be in insert or edit mode. In case an existing Goal entity was provided the form gets populated with the entity data and is ready to be updated. The Goal to be edited will be retrieved by providing the entity’s ID in the editGoal.groovy Groovlet.

war/WEB-INF/groovy/editGoal.groovy:

import com.google.appengine.api.datastore.KeyFactory
import com.google.appengine.api.datastore.Key

log.info "Editing goal"

def id = Long.parseLong(params.id)
Key key = KeyFactory.createKey("Goal", id)
def goal = datastore.get(key)

request.goal = goal
forward '/WEB-INF/pages/goal.gtpl'

war/WEB-INF/pages/goal.gtpl:

<% include '/WEB-INF/includes/header.gtpl' %>

<%
  def goal = request.getAttribute("goal")
  boolean existingKey = goal?.key
  String action = !existingKey ? 'Add' : 'Update'
%>

<h2>${action} Goal</h2>

<form action="/goals/${!existingKey ? 'insert' : 'update'}" method="POST">
   <table border="0">
      <tbody>
         <tr>
            <td>Name:</td>
            <td><input type="text" name="name" value="${goal?.name ? goal.name : ''}"></td>
         </tr>
         <tr>
            <td>Description:</td>
            <td><input type="text" name="description" value="${goal?.description ? goal.description : ''}"></td>
         </tr>
      </tbody>
      <% if(existingKey) { %>
         <input type="hidden" name="id" value="${goal.key.id}">
      <% } %>
   </table>
   <br>
   <input type="submit" value="${action}">
   <input type="button" value="Cancel" onclick="javascript:document.location.href = '/goals';">
</form>

<% include '/WEB-INF/includes/footer.gtpl' %>

At last we need to implement the Groovlets for persisting the changes to your entity. All of them are quite straight forward to implement. Gaelyk’s abstractions for the datastore work quite nicely.

war/WEB-INF/groovy/insertGoal.groovy:

import com.google.appengine.api.datastore.Entity

log.info "Inserting goal"

def goal = new Entity("Goal")
goal.name = params.name
goal.description = params.description
goal.created = new Date()
goal.save()

redirect '/goals'

war/WEB-INF/groovy/updateGoal.groovy:

import com.google.appengine.api.datastore.KeyFactory
import com.google.appengine.api.datastore.Key

log.info "Updating goal"

def id = Long.parseLong(params.id)
Key key = KeyFactory.createKey("Goal", id)
def goal = datastore.get(key)
goal.name = params.name
goal.description = params.description
goal.save()

redirect '/goals'

war/WEB-INF/groovy/deleteGoal.groovy:

import com.google.appengine.api.datastore.KeyFactory
import com.google.appengine.api.datastore.Key

log.info "Deleting goal"

def id = Long.parseLong(params.id)
Key key = KeyFactory.createKey("Goal", id)
key.delete()

redirect '/goals'

Configuring Solr 1.4 logging with Log4J in Tomcat

January 6, 2010 at 9:15 pm | Posted in Java, Search, Software Development | 2 Comments
Tags: , , ,

Solr 1.4 logging is based on the “SLF4J” API. To configure Solr to use Log4J as standard logging implementation deploy the Solr web application to your Tomcat. Once the Solr web application got started add the libraries “slf4j-log4j12-1.5.5.jar” and “log4j-1.2.15.jar” to $CATALINA_HOME/webapps/solr/WEB-INF/lib. Delete the library “slf4j-jdk14-1.5.5.jar” from $CATALINA_HOME/webapps/solr/WEB-INF/lib.

Create the directory $CATALINA_HOME/webapps/solr/WEB-INF/classes and add the file log4j.properties. The file should hold your Log4J configuration. Here’s an example using a rolling log file:

log4j.rootLogger=ERROR, logfile

log4j.appender.logfile=org.apache.log4j.DailyRollingFileAppender
log4j.appender.logfile.File=${catalina.home}/logs/solr.log

log4j.appender.logfile.DatePattern='.'yyyy-MM-dd
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern=%d %p [%c{3}] - [%t] - %X{ip}: %m%n

Alternatively, you can prepare your Solr web application WAR file to reflect the steps mentioned above. The Solr logging page (http://<host&gt;:<port>/solr/admin/logging) that comes with the web app will not reflect these changes. It only works for JDK logging that we just configured not to be used anymore. The Tomcat instance has to be restarted to reflect the changes.

Create a free website or blog at WordPress.com.
Entries and comments feeds.