Feb
14
2010
0

Serving Grails static content with UI-Performance and Cloud Foundry

I am excited about Cloud Foundry. It promises to greatly simplify java application deployment to Amazon’s Elastic Compute Cloud (EC2). With a few clicks, you can deploy an J2EE application (e.g. Grails) to the container of your choice (Tomcat 5.5tc Server, and more) on a fresh EC2 Linux instance backed by a MySQL database on Amazon’s Elastic Block Store (EBS). It doesn’t get much easier than this. The instance configuration automatically configures your container behind an Apache instance, which gives you some great options for hosting static content or configuring side-by-side application platforms.

I’ve been using Burt Beckwith’s UI Performance plugin for Grails to bundle and compress my static content (images, js, css) during the build, so I can save on outbound bandwith and improve the response time of my application. In order to take advantage of this build-time optimization, we need to modify the Apache configuration to serve static content instead of proxying those requests to the container.

  1. $ grails install-plugin ui-performance
  2. Configure your app to use the plugin’s taglibs and to bundle resources if necessary. (See http://www.grails.org/plugin/ui-performance)
  3. Add a handler for the CreateWarEnd event in your scripts/Events.groovy file (make one if you don’t have one). This handler will package all the bundled and gzipped resources into a static zip file that you can upload to Cloud Foundry:
    eventCreateWarEnd = {warName, stagingDir ->
        def directory = new File(warName).parent
    
        ant.zip(destfile:"${directory}/static.zip") {
            fileset(dir:"${stagingDir}") {
                include name: "images/**"
                include name: "css/**"
                include name: "js/**"
            }
        }
    }
    
  4. Create a “container initialization script” for Cloud Foundry to execute when your instance starts up. This script will tell Apache to directly serve the following directories and to add the appropriate Content-Encoding header for content that is already gzipped. The name doesn’t really matter at this point, but I called mine proxyexcludes.sh.
    cat > /etc/httpd/conf.d/_proxyexcludes.conf <<END
    ProxyPass /images !
    ProxyPass /css !
    ProxyPass /js !
    
    SetEnvIf Request_URI "\\.gz\\.(css|js|jpg|gif|png|ico)\$" already_compressed=true
    Header set Content-Encoding gzip env=already_compressed
    END
    

    I found that keeping this file up to date was easier if I just let grails rebuild it every time after the WAR is created. I modified my eventCreateWarEnd handler to take care of this for me:

    eventCreateWarEnd = {warName, stagingDir ->
        def directory = new File(warName).parent
    
        ant.zip(destfile:"${directory}/static.zip") {
            fileset(dir:"${stagingDir}") {
                include name: "images/**"
                include name: "css/**"
                include name: "js/**"
            }
        }
    
        ant.echo "Creating Proxy exclusion list: ${directory}/proxyexcludes.sh"
        new File("${directory}/proxyexcludes.sh") << """cat > /etc/httpd/conf.d/_proxyexcludes.conf <<END
    ProxyPass /images !
    ProxyPass /css !
    ProxyPass /js !
    
    SetEnvIf Request_URI "\\.gz\\.(css|js|jpg|gif|png|ico)\$" already_compressed=true
    Header set Content-Encoding gzip env=already_compressed
    END
    """
    
    }
    
  5. $ grails war
  6. Upload the static.zip file produced during the build as “static content” when you upload your app to Cloud Foundry:

    Cloud Foundry Upload Static Content

  7. When creating a deployment, upload your “proxyexcludes.sh” container initialization script:

    Cloud Foundry Deployment

  8. Fire up your instance and check out the performance gains!
Nov
05
2009
0

Groovy deserialization troubles

In a grails application, I needed to persist a complicated expression tree to the database, but I didn’t want to have Hibernate generate a database table for the information as it would greatly affect read performance (lots of joins) and I don’t need to have relational access to subsets of the tree. I decided to serialize the entire tree into a BLOB.

After creating a Hibernate userType to handle the serialization/deserialization, I ran into a ClassNotFoundException when attempting to deserialize my object.  I found out that this is due to java using the “last defined ClassLoader” when deserializing with an ObjectInputStream, which might not be the right ClassLoader in a Groovy environment (see: http://jira.codehaus.org/browse/GROOVY-1627.

The solution? Subclass ObjectInputStream to allow you to pass in a predefined ClassLoader and override the resolveClass(ObjectStream classDesc) to use this ClassLoader parameter:


public class ClassLoaderAwareObjectInputStream extends ObjectInputStream {

 private ClassLoader myClassLoader;

 public ClassLoaderAwareObjectInputStream(ClassLoader myClassLoader) throws IOException, SecurityException {
 super();
 this.myClassLoader = myClassLoader;
 }

 public ClassLoaderAwareObjectInputStream(InputStream in, ClassLoader myClassLoader) throws IOException {
 super(in);
 this.myClassLoader = myClassLoader;
 }

 @Override
 protected Class resolveClass(ObjectStreamClass desc) throws IOException,
 ClassNotFoundException {
 String name = desc.getName();
 return Class.forName(name, false, myClassLoader);
 }
}

Thanks to Satish Gunnu for the tip.

Powered by WordPress | Theme: Aeros 2.0 by TheBuckmaker.com