Posts Tagged ‘Grails’

Grails Solr Plugin Progress Screencast

March 3rd, 2010

I started work on this plugin back in December based on the work I did for Patheos.com and by the graces of my employer Avalon Consulting LLC and Patheos, they allowed me to open source the plugin and continue working on it. This week I had some time to get back to it, and today I started creating a basic reference implementation application that will accompany the documentation. Below is a screencast demo of this application which indexes an export of songs from my iTunes library metadata and makes them searchable.

The code for the plugin is at Github and is still a work in progress.  I’m pushing towards a 0.1 release next week with the bulk of the work I still need to do in the form of documentation and clean-up.

So please watch the screencast, and I would love feedback.  I will certainly take offers to help continue the development of the plugin but would like to get 0.1 out first to round out my train of thought and not further delay that basic milestone.

Grails Solr Plugin pre-0.1 Demo from mbrevoort on Vimeo.

Annotations with Grails

December 29th, 2009

Recently I’ve been heads down writing a Grails Solr plugin for using Solr to index and search Grails domain objects, content and other documents. I share more on that in the next several day and weeks.  While doing so I implement annotations for the first time. In this example, I create an annotation type that lets you annotate a class property to specify a custom Solr index field name.  In usage it looks like this:

import org.grails.plugins.solr.Solr
class aDomain {
  @Solr(field="thedomainstitle")
  String title
}

The imported class above is actually an interface that defines the annotation:

package org.grails.plugins.solr
import java.lang.annotation.*

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Solr {
  String field();
}

This is fairly self explanatory. The name of the annotation is “Solr” and there is one propery field with a value of String. Almost identical to the java equivalent.

Now to access the annotation value for the field, you simply need the get a reference to the annotation and call the method on it defined above: field(). So with a bit of context below, I define an indexSolr() method for each domain class, loop through each property and check if there is an annotation of Solr defined for the field, if so I set the value of field() to the docKey which is used as the Solr field name for the property.

application.domainClasses.each { dc ->
  dc.metaClass.indexSolr < < { ->
    //.......
    application.getArtefact(DomainClassArtefactHandler.TYPE, delegate.class.name).getProperties().each {
      //.......
      def theField = delegate.class.declaredFields.find{ field -> field.name == it.name}

      if(theField.isAnnotationPresent(Solr)) {
        docKey = theField.getAnnotation(Solr).field()
      }
      //........
    }
    //........
  }
}

Powerful, yet simple.

Definitive Guide to Grails, Second Edition

January 21st, 2009

Today I received my pre-ordered copy of the Definitive Guide to Grails, Second Edition. This is the first book I’ve ever pre-ordered on from Amazon. The book seems to be a total rewrite of the first edition and is much thicker as well.

Grails has changed significantly since 0.4 (the version the first edition was based); the new book is based on Grails 1.1 that ’s scheduled to be released of beta within a number or weeks. I’m knee deep in a project now that’s using Grails 1.1 beta2 so this book is very timely!

Though I haven’t read much of the book yet the first thing I noticed was testing is address throughout the entire book, generally near the end of each chapter, demonstrating how to test the features covered in each chapter.  This is fantastic!  Also, I read the chapter on URL Mapping (since my new application relies heavily on custom URL Mappings and I’ve battled through several Grails 1.1 beta bugs) and the material was comprehensive.  So my early impression of this book is very good; it appears to be very thorough and a significant contribution to the Grails community that was much needed. Excellent work Graeme and Jeff.

Making Relative URLs Absolute with Groovy

December 21st, 2008

I’m working on a project where I am using Grails as a content delivery layer for an XML based content management system.  The content management system has the ability to publish XHTML content with inline links and images; however the inline img and a tags reference the content as relative paths.  I need the links to internal pages to be absolute from the server root (ie /sub/content.html rather than sub/content.html) and I am hosting all static content like images at Amazon S3 so I need the image links to be absolute to a different DNS name (ie http://media.mywebsite.com/images/image.jpg rather than images/image.jpg).

Instead of implementing some hacks in the CMS itself, I opted to do the transformation in Grails as the content was rendered allowing greater flexibility.  I created a taglib for rendering the XHTML block that does a Groovy replaceAll with regular expressions.  The tag gets called like this: <g:processXHTML content=”${content.mainBody.text()}”/> .  My GSP has reference to a content variable that is an XMLSlurped xml document with a mainBody tag.  Here is the code for the tag:

/**
 * Given XHTML content will return the same content with any relative a tag hrefs prefixed with a /
 * and any relative images prefixed with the mediaURL
 */

def processIngeniuxXHTML = {attrs ->
  def content = attrs.content
  def rootURL = grailsApplication.config.mediaURL

  if (content) {
    // first look for links within the content that are relative URLS and prefix with a slash so they are
    // resolved from the root rather than the current context
    def regex = /(< \s*a\s+[^>]*href\s*=\s*[\"'])(?!http)([^\"'>]+)[\"'>]/
    def replace = { fullMatch, a, b ->
      if (b[0] != "/"){
        // for some reason it's not outputing the closing quote so I added it at then end explicitly
        "${a}/${b}\""
      } else
        "${fullMatch}"
    }

    content = content.replaceAll(regex, replace)

    // second if there are any images referenced with relative URLs, prefix with the DNS for the S3 bucket
    regex = /(< \s*img\s+[^>]*src\s*=\s*[\"'])(?!http)([^\"'>]+)[\"'>]/
    replace = { fullMatch, a, b ->
      if (b[0] != "/"){
        // for some reason it's not outputing the closing quote so I added it at then end explicitly
        "${a}${rootURL}/${b}\""
      } else
        "${fullMatch}"
    }

    out < < content.replaceAll(regex, replace)
  }
}

And here are the test conditions to demonstrate what’s expected:

void testProcessXHTML() {
  ContentUtilTagLib tl = new ContentUtilTagLib()

  def i = """<div><a href="Explore/test"/>"""
  def o = """<div><a href="/Explore/test"/></div>"""

  tl.processXHTML(content: i)
  assertEquals o, out.toString()
  out.getBuffer().setLength(0)

  i = """<div><a alt="blah" href="Explore/test"/></div>"""
  o = """<div><a alt="blah" href="/Explore/test"/></div>"""

  tl.processXHTML(content: i)
  assertEquals o, out.toString()
  out.getBuffer().setLength(0)

  i = """<div><a href="Explore/test"/><img width="100" src="myImage/a.jpg"/></div>"""
  o = """<div><a href="/Explore/test"/><img width="100" src="http://stub/myImage/a.jpg"/></div>"""

  tl.processXHTML(content: i)
  assertEquals o, out.toString()
  out.getBuffer().setLength(0)

  i = """<div><a href="/Explore/test"/></div>"""
  o = """<div><a href="/Explore/test"/></div>"""

  tl.processXHTML(content: i)
  assertEquals o, out.toString()
  out.getBuffer().setLength(0)

  i = """<div><a href="http://Explore/test"/><img width="100" src="http://anotherhost/myImage/a.jpg"/></div>"""
  o = """<div><a href="http://Explore/test"/><img width="100" src="http://anotherhost/myImage/a.jpg"/></div>"""

  tl.processXHTML(content: i)
  assertEquals o, out.toString()
  out.getBuffer().setLength(0)

}

More related to this project to come…

NFJS Rocky Mountain Software Symposium Debrief

November 17th, 2008

This weekend I attended the NFJS Rocky Mountain Software Symposium in Lone Tree, CO.  It was hard to pass up a NFJS conference that’s 15 minutes from home.  Overall the sessions were excellent.  There wasn’t a single session I attended that sucked or was a waste of time, a first for any conference I’ve attended.  I seemed to gravitate toward sessions by Ken Sipe and Stu Halloway followed by Groovy and Grails presentations by Jeff Brown and Scott Davis.

I gleaned several consistent overarching themes at the conference:

  • Java is essentially Dead (Stu Halloway leading this sentiment)
  • BUT the JVM is alive and well.  In fact the JVM has transitioned from a write once run Java anywhere runtime to a dynamic platform for many languages (Groovy, JRudy, Scala, Clojure, Javascript, etc…. oh yeah and Java)
  • Not doing Test Driven Development is basically irresponsible, especially with the explosion of dynamic languages
  • Not doing Continuous Integration is basically irresponsible

I learned the Jeff Brown has developed and is about to announce a new Hudson plugin for building Grails projects in Hudson, a big and welcome improvement on specifying new ant targets, using the shell script option or whatever.  Jeff also echoed much of what has already been said about the recent Spring Source acquisition of G2:

  • Deeper integration between Grails and Sprint MVC
  • The Groovy Spring bean builder will be moved into Spring
  • And of course there will be closer collaboration between the Groovy, Grails, and Spring development teams, though the team will remain distinct.

I’m super excited about Git though Matt McCullough urged me to take a look at Mercurial

Anyway, fantastic event and kudos to No Fluff Just Stuff.

Rocky Mountain Software Symposium

November 14th, 2008

This weekend I’m attending the NFJS Rocky Mountain Software Symposium.  Today I attended a few really good sessions specifically Java Memory, Performance and the Garbage Collector presented by Ken Sipe and a very enthusiastic Groovy Metaprogramming tour by Scott Davis.  Though I’ve been using Groovy for quite a while, primarily inconjuction with Grails, Scott’s presentation really helped fill some gaps for me.   Ken’s presentation on JVM memory managemant was a great primer.  I’m looking forward to profiling a few of the Grails apps I’m working on vith VisualVM to see how I can optimize New vs Old space as well as preset the PermGen space to speed up Grails start-up.

I’m looking forward to the coming two days of sessions though I REALLY could have used a weekend.  At least the conference is 15 minutes from home.  I’ll post my thoughts/reactions to the other session this weekend.

After a nudging from Ken during his presentation to ‘increase your digital footprint’ and Cannon-Brookes return to blogging (congrats on your recent position on the Gartner Magic Quadrant for Social Software – well deserved…) I’m renewing my commitment to start blogging again and this time more focused on my “work” life, choosing to use Facebook for sharing personal updates.

Congrats to G2 for the Spring Source aquisition and the release today of Grails 1.0.4.  And I just saw that the Groovy/Grails Experience has been scheduled for February 2009… in Denver!

Colorado Springs Open Source Software Meetup Group

February 29th, 2008

Tonight I headed down to the Springs for the first meeting of the Colorado Springs Open Source Software Meetup Group. I learned about the group from this post on the Atlassian developer blog. Though it was a bit slow tonight, overall it was a good event and I think it will develop into a solid user group. My only concern is that the topic is too broad to successfully satisfy everyone’s interest. Tonight’s speaker was Kirstan Vandersluis who demoed the open source data integration platform XAware. There seems to be a lot of momentum in the commercially backed open source BI/EAI space lately with others including Pentaho and Apatar.

I walked away tonight with a free license of Intellij IDEA so thanks to Dave Booth from Jetbrains for the free swag! Definitely my favorite IDE, and I was actually in need of a license! And thanks TEKsystems for the food and the other sponsors as well!

I’m scheduled to present on Grails in May so if you’re in the area feel free to come heckle! Wow, four sentences ending in exclamation points (make that five)!

Part 2: Terracotta: 1, Grails Searchable Plugin+Me: 1

January 29th, 2008

Round Two of my quest to cluster the Grails Searchable Plugin index with Terracotta! In my first post I outlined my intent to use Terracotta to cluster the Grails Searchable Plugin (Lucene + Compass) using the Lucene RAMDirectory approach. Incidentally, my first attempt failed miserably.

As a quick confidence booster before setting out again, I successfully clustered the Grails Searchable Plugin using a JDBCDirectory store for the index. Since my last attempt I received some guidance from the guys at Terracotta and learned that Shay Banon is working with Terrocotta to develop a Compass module for Terracotta. Good stuff… check out the details of that here.

The first hurdle I needed to overcome was the classloader error:

Classloader name not set, instances defined from this
loader not supported in Terracotta
(loader: org.codehaus.groovy.grails.cli.support.GrailsRootLoader)

Before an object can be shared by Terracotta, the ClassLoader that loaded the object’s class has to be known to Terracotta. For core java and supported frameworks/container Terracotta takes care of registering relevant classloaders. For the GrailsRootLoader I can do this one of two ways:

  1. hack the grails source where the class loader is instantiated, registering it with Terracotta
  2. this can be converted into a config module that will inject the proper byte code at runtime so as to avoid any code tweaking.

The guys at Terracotta offered to help with #2 if I could get #1 working.

So I checked out the Grail trunk from SVN, and added these lines after the GrailsRootLoader is created:

 // create loader and execute main class
GrailsRootLoader loader = new GrailsRootLoader(lc);
((com.tc.object.loaders.NamedClassLoader) loader).__tc_setClassLoaderName("Grail Classloader");
com.tc.object.bytecode.hook.impl.ClassProcessorHelper.registerGlobalLoader((com.tc.object.loaders.NamedClassLoader) loader);

I built the Grails jars and replaced them in my grails-1.0-final-SNAPSHOT/dist directory. That resolved my classloader error! Next I went through about 100 iterations of root and lock configuration for my tc-config.xml as I dug and spun my way around the Compass source. I finally ended up with this for my tc-config.xml (keep in mind this can surely be optimized and is pretty brute force):

<?xml version="1.0" encoding="UTF-8" ?>

<tc:tc-config xmlns:tc="http://www.terracotta.org/config">
  <system>
    <configuration-model>development</configuration-model>
  </system>

  <servers>
    <server name="localhost" />
  </servers>

  <clients>
    <logs>%d/client-logs-%h</logs>
    <dso>

       <debugging>
        <runtime-logging>
          <lock-debug>true</lock-debug>
        </runtime-logging>
        <runtime-output-options>
          <full-stack>true</full-stack>
        </runtime-output-options>
      </debugging>

    </dso>
    <modules>
      <module name="clustered-lucene-2.0.0" version="2.5.0"/>
    </modules>
  </clients>

  <application>
    <dso>
      <instrumented-classes>
        <include>
          <class-expression>org.compass.core.lucene.engine..*</class-expression>
        </include>
        <include>
          <class-expression>org.apache.lucene..*</class-expression>
        </include>
      </instrumented-classes>
      <roots>
        <root>
          <field-name>org.compass.core.lucene.engine.store.RAMLuceneSearchEngineStore.ramIndexes</field-name>
        </root>
      </roots>
      <locks>
	<named-lock>
          <method-expression>* org.compass.core.lucene.engine.manager.DefaultLuceneSearchEngineIndexManager.*(..)</method-expression>
          <lock-level>write</lock-level>
          <lock-name>theLockName</lock-name>
        </named-lock>
        </locks>
     </dso>
  </application>
</tc:tc-config>

It worked! I was able to test with the Terracotta server running, and two Grails apps running in separate JVMs that all of the Domain class data that the first JVM indexed, could be searched by the second JVM and they both returned identical results. I also verified the existence of the objects under the root in the Terracotta Admin GUI. And finally, I shut both Grails app JVMs down and brought them back up and my index was still available.

What’s next? Work to get a config module for Terracotta and optimize my root and locking configuration. I’m very interested in what will come of the promised Compass/Terracotta integration. Also Clustering Grails is a much taller mountain. I was told that past attempts to cluster Groovy with Terracotta had not been too successful. For now I’m happy just to have climbed my little hill…

Using JDBCDirectory with the Grails Searable Plugin

January 24th, 2008

In my last post I mentioned that one of the options I was considering for clustering the Grails Searchable plugin, though not the preferred option, was to use the JDBCDirectory implementation of Lucene. I had this working in just 15 minutes and though it’s probably not the optimal approach, it is by far the simplest.

Just to set the stage, I’m running Grails RC3, Groovy 1.5.1, JDK 1.6, grails-searchable-0.4-SNAPSHOT and MySQL 5.0.45. I will configure the Compass to use the JdbcDirectory connection instead of file or ram and test that it can be updated and read by two instances of my Grails application.

First I created compass.cfg.xml in grails-app/conf (just needs to be somewhere in the classpath) with jdbc connection details:

<compass-core-config xmlns="http://www.opensymphony.com/compass/schema/core-config"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://www.opensymphony.com/compass/schema/core-config
          http://www.opensymphony.com/compass/schema/compass-core-config.xsd">
   <compass name="default">
     <connection>
         <jdbc dialect="org.apache.lucene.store.jdbc.dialect.MySQLDialect">
             <dataSourceProvider>
                 <driverManager url="jdbc:mysql://localhost/search?emulateLocators=true"
                                      username="root" password="password"
                                      driverClass="com.mysql.jdbc.Driver" />
             </dataSourceProvider>
             <fileEntries>
                 <fileEntry name="__default__">
                     <indexInput bufferSize="4096" />
                     <indexOutput bufferSize="4096" />
                 </fileEntry>
             </fileEntries>
         </jdbc>
     </connection>
   </compass>
</compass-core-config>

One thing I missed at first was the dialect setting in the jdbc tag. Make sure you select the correct dialect for your database. I used this as a reference.
In SearchableConfiguration.groovy I set compassConnection to null

String compassConnection = null

I started my grails app and hit just one more snag, I got a “packet too large” error while it was building the index. Turns out the default max_allowed_packet in MySQL on the server is 1M and since Lucene stores parts of the index in BLOBs I needed to up the max. I found this article and set the max_allowed_packet setting to 16M. I did this in my.ini:

[mysqld]
max_allowed_packet=16M

That was it! I tested by running two instances of my Grails application simultaneously and I modified one of my Domain class instances and executed a search on the other instance and within a few seconds, the new value was returned in my search.

The last thing I considered was configuring a Local Directory Cache in Compass. This seemed like it could offer some good optimization though it was unclear from the documentation how much control you have over the size of the cache, what gets cached, the TTL, etc. Also, I soon realized that this was introduced in the latest version of Compass (1.2) and isn’t the version that is bundled in the Searchable plugin.

Terracotta: 1, Grails Searchable Plugin+Me: 0

January 22nd, 2008

I’m on a quest to figure out how to cluster the Grails Searchable plugin that’s based on Lucene/Compass across multiple nodes with as little intrusion and in a way that’s as turnkey as possible. I’m in immediate need of a solution, and I think it would be a good contribution to the Searchable plugin.

To start here’s what I’m considering:

  1. Manage a local index per node. Probably disable mirrorChanges via Compass::GPS and rebuild the index on someinterval. This is not very desirable since it would eliminate the Compass:GPS capability, and there just must be a more elegant way…
  2. Use the JDBCDirectory implementation of Lucene and store the index in a sql database. This option is viable but has obvious performance implications that somewhat defeat the purpose. The configuration would be fairly clean using the native Compass XML config. This maybe a good fall back option to do some A/B testing against.
  3. Use Terracotta and RAMDirectory to handle synchronization of the index across nodes. This has a lot of promise and will be the path I head down first. Though I have no experience whatsoever with Terracotta

Another possibility is to look at Compass’ new support for Gigaspaces. Thanks Marcos for passing that on. This sounds interesting but “feels” fairly heavy compared to the others, though I know nothing about GigaSpaces or Coherence so I’m just talking out of my arse at this point!

I made my first attempt with Terracotta and Compass today. I dug deep enough into Compass to find that it manages the RAMDirectory objects in this class: org.compass.core.lucene.engine.store.RAMLuceneSearchEngineStore and manages each subindex RAMDirectory in a java.util.HashMap called ramIndexes. The first issue I had to overcome was that HashMap isn’t a synchronized data structure, and I was having problems configuring the locking in Terracotta for ramIdexes. I kept getting UnlockedSharedObjectException exceptions not matter what I tried.

After a little help from the Terracotta users list I ended up with a named lock. My tc-config.xml looked like this:

<?xml version="1.0" encoding="UTF-8" ?>
<tc:tc-config xmlns:tc="http://www.terracotta.org/config">
  <system>
    <configuration-model>development</configuration-model>
  </system>

  <servers>
    <server name="localhost" />
  </servers>

  <clients>
    <logs>%d/client-logs-%h</logs>
    <dso>
      <debugging>
        <runtime-logging>
          <lock-debug>true</lock-debug>
        </runtime-logging>
        <runtime-output-options>
          <full-stack>true</full-stack>
        </runtime-output-options>
      </debugging>
    </dso>
    <modules>
      <module name="clustered-lucene-2.0.0" version="2.5.0"/>
    </modules>
  </clients>

  <application>
    <dso>
      <instrumented-classes>
        <include>
          <class-expression>org.compass.core.lucene.engine.store.RAMLuceneSearchEngineStore</class-expression>
        </include>
      </instrumented-classes>
      <roots>
        <root>
          <field-name>org.compass.core.lucene.engine.store.RAMLuceneSearchEngineStore.ramIndexes</field-name>
        </root>
      </roots>
      <locks>
	<named-lock>
          <method-expression>* org.compass.core.lucene.engine.store.RAMLuceneSearchEngineStore.*(..)</method-expression>
          <lock-level>write</lock-level>
          <lock-name>theLockName</lock-name>
        </named-lock>
      </locks>
     </dso>
  </application>
</tc:tc-config>

That at least got me past the UnlockSharedObjectException, but tonight I hit a bigger roadblock. Terracotta is choking on Grails custom ClassLoader. I did a preliminary search on the error message below but came up short. I followed up on the Terracotta users’ list but I thought I’d share my progress and my intentions here in case anyone out there has any ideas…

Error creating bean with name 'compassGps': Cannot resolve reference to
bean'compass' while setting bean property 'compass'; nested exception is
org.springframework.beans.factory.BeanCreationException: Error creating
bean with name 'compass': FactoryBean threw exception on object creation;
nested exception is java.lang.IllegalStateException: Classloader name not set,
instances defined from this loader not supported in Terracotta (loader:
org.codehaus.groovy.grails.cli.support.GrailsRootLoader):
java.lang.IllegalStateException: Classloader name not set, instances
defined from this loader not supported in Terracotta (loader:
org.codehaus.groovy.grails.cli.support.GrailsRootLoader)

Stay tuned…

Update (Jan 29, 2008): See part 2 here

Powered by Web Design Company Plugins

Switch to our mobile site