Archive

Archive for March, 2008

Adventures in Grails – Running more than one version of your app

March 29, 2008 Andrew Hahn 1 comment

Update: Posted a patch to the Grails JIRA http://jira.codehaus.org/browse/GRAILS-2771

Grails provides a simple versioning system for your application with the app.version property in application.properties. When you build a .war for deployment the file name is given by "${app.name}-${app.version}.war". For me this is great, I frequently need to run multiple versions of an app to support multiple client development branches. However, when I dropped myApp-0.2.war into tomcat’s webapp dir along side myApp-0.1.war the deployment failed. On tomcat 6.0.16 the error is cryptic and far from helpful SEVERE: Error listenerStart. Tomcat 5.5.26 provided a much more useful error message. It turns out that Grails only includes the version number in the .war file name and not in web.xml where it uses only the app name.

To fix this all you need to do is edit the web.xml of one of the versions and replace the app name with something else. Well, thats not very Groovy! To have Grails do this for you it is a simple change to the Package.groovy script that ships with Grails.

The lines (264-266):


Ant.copy(file:"${grailsHome}/src/war/WEB-INF/web${servletVersion}.template.xml", tofile:tmpWebXml)

                Ant.replace(file:tmpWebXml, token:"@grails.project.key@", value:"${grailsAppName}")

get changed to:


Ant.copy(file:"${grailsHome}/src/war/WEB-INF/web${servletVersion}.template.xml", tofile:tmpWebXml, overwrite:true)

Ant.replace(file:tmpWebXml, token:"@grails.project.key@", value:"${grailsAppName}-${grailsAppVersion}")

The overwrite:true in the copy task is necessary so that when the version is changed, web.xml is updated. This will break you if you rely on hand made changes to web.tmp.xml if your ~/.grails directory. Though, you might not want to do things that way anyway.On a side note, you may want to define more environments in grails-app/config/DataSources.groovy if you have changed your app’s DB schema to prevent errors.

Categories: grails, groovy, programming Tags: ,

MySQL install fails on Ubuntu with static IP

March 12, 2008 Andrew Hahn 2 comments

Update:

It was not the static IP address, but the fact that due to a typo in /etc/network/interfaces the loopback device was not active.

Installing MySQL server on Ubuntu fails if the machine has a static IP address.

sudo apt-get install mysql-server

Results in:

Setting up mysql-server-5.0 (5.0.45-1ubuntu3.1) ...
* Stopping MySQL database server mysqld
...done.
* Starting MySQL database server mysqld
...fail!
invoke-rc.d: initscript mysql, action "start" failed.
dpkg: error processing mysql-server-5.0 (--configure):
subprocess post-installation script returned error exit status 1
dpkg: dependency problems prevent configuration of mysql-server:
mysql-server depends on mysql-server-5.0; however:
Package mysql-server-5.0 is not configured yet.
dpkg: error processing mysql-server (--configure):
dependency problems - leaving unconfigured
Processing triggers for libc6 ...
ldconfig deferred processing now taking place
Errors were encountered while processing:
mysql-server-5.0
mysql-server
E: Sub-process /usr/bin/dpkg returned an error code (1)

To fix this edit /etc/mysql/my.conf and change the bind-address to the static address of the machine. Then rerun apt-get to finish the configuration of the packages.

sudo apt-get -f install

Reported to Ubuntu launchpad

Adventures in Grails – Debugging

March 11, 2008 Andrew Hahn 1 comment

Update :

It was pointed out to me on the Grails user list that thegrails-debugcommand can be used to do what I posted below. However, the code below is slightly more flexible in that you can control the port, transport and suspend flag with environmental variables.

Working with Grails so far has been great. However, there is no easy way to (that I could find) to fire up Grails and attach a debugger. So I made a few changes to the startup script $GRAILS_HOME/bin/startGrails to handle a debug argument. This is *NIX systems only (including OS X), I haven’t worked with batch files in so long (and have no need for it) that I did not make the changes to $GRAILS_HOME/bin/startGrails.bat. Perhaps someone that uses that other OS to develop on could port the changes.

The script uses the environmental variables:

  • JPDA_ADDRESS #the port to listen on, default 8000
  • JPDA_TRANSPORT #transport to use, default ‘dt_socket’
  • JPDA_WAIT #if ‘y’ then wait for the debugger to attach, default ‘n’

If anyone of these is not set it’s default value is used.

To start the VM for debugging add ‘debug’ as the first arg to the grails command.

grails debug run-app

The changes are in two places. First:


if [ "$1" = "-cp" ] || [ "$1" = "-classpath" ]; then
  CP=$2
  shift 2
fi

ARGUMENTS=$@

becomes


#Test for and setup debug option
if [ ! $JPDA_ADDRESS ]
then
   JPDA_ADDRESS=8000
fi

if [ ! $JPDA_TRANSPORT ]
then
   JPDA_TRANSPORT="dt_socket"
fi

if [ ! $JPDA_WAIT ]
then
   JPDA_WAIT="n"
fi

if [ $# -ne 0 ]
then
   if [ $1 == "debug" ]
   then
      shift
      GRAILS_DEBUG_OPTS="-Xdebug -Xrunjdwp:transport=${JPDA_TRANSPORT},address=${JPDA_ADDRESS},server=y,suspend=${JPDA_WAIT}"
   fi
fi

if [ "$1" = "-cp" ] || [ "$1" = "-classpath" ]; then
  CP=$2
  shift 2
fi

ARGUMENTS=$@

and in the startGrails function:

startGrails() {
  CLASS=$1
  shift
  JAVA_OPTS="-server -Xmx512M $JAVA_OPTS"
  # Start the Profiler or the JVM
  if $useprofiler; then
      runProfiler
  else
  	if [ $# -eq 0 ] ; then         # no argument given
         exec "$JAVACMD" $JAVA_OPTS \
          -classpath "$STARTER_CLASSPATH" \
          -Dprogram.name="$PROGNAME" \
          -Dgroovy.starter.conf="$GROOVY_CONF" \
          -Dgrails.home="$GRAILS_HOME" \
          -Dbase.dir="." \
          -Dtools.jar="$TOOLS_JAR" \
          $STARTER_MAIN_CLASS \
          --main $CLASS \
          --conf "$GROOVY_CONF" \
          --classpath "$CP"
  	else
         exec "$JAVACMD" $JAVA_OPTS \
          -classpath "$STARTER_CLASSPATH" \
          -Dprogram.name="$PROGNAME" \
          -Dgroovy.starter.conf="$GROOVY_CONF" \
          -Dgrails.home="$GRAILS_HOME" \
          -Dbase.dir="." \
          -Dtools.jar="$TOOLS_JAR" \
          $STARTER_MAIN_CLASS \
          --main $CLASS \
          --conf "$GROOVY_CONF" \
          --classpath "$CP" \
          "${ARGUMENTS}"
  	fi
  fi
}

becomes

startGrails() {
  CLASS=$1
  shift
  JAVA_OPTS="-server -Xmx512M $JAVA_OPTS"
  # Start the Profiler or the JVM
  if $useprofiler; then
      runProfiler
  else
  	if [ $# -eq 0 ] ; then         # no argument given
         exec "$JAVACMD" $JAVA_OPTS \
          -classpath "$STARTER_CLASSPATH" \
          "$GRAILS_DEBUG_OPTS" \
          -Dprogram.name="$PROGNAME" \
          -Dgroovy.starter.conf="$GROOVY_CONF" \
          -Dgrails.home="$GRAILS_HOME" \
          -Dbase.dir="." \
          -Dtools.jar="$TOOLS_JAR" \
          $STARTER_MAIN_CLASS \
          --main $CLASS \
          --conf "$GROOVY_CONF" \
          --classpath "$CP"
  	else
         exec "$JAVACMD" $JAVA_OPTS \
          -classpath "$STARTER_CLASSPATH" \
          "$GRAILS_DEBUG_OPTS" \
          -Dprogram.name="$PROGNAME" \
          -Dgroovy.starter.conf="$GROOVY_CONF" \
          -Dgrails.home="$GRAILS_HOME" \
          -Dbase.dir="." \
          -Dtools.jar="$TOOLS_JAR" \
          $STARTER_MAIN_CLASS \
          --main $CLASS \
          --conf "$GROOVY_CONF" \
          --classpath "$CP" \
          "${ARGUMENTS}"
  	fi
  fi
}
Categories: grails, groovy, programming

Adventures in Grails – WS-Security Part 2

March 11, 2008 Andrew Hahn 2 comments

This post builds on the previous post WS-Secutiry Part 1 by adding inHandlers that populate an acegi security context.

Integrating acegi

It turned out that initial integration of acegi with xfire + WSS was even easier than hooking up WSS for xfire in Grails. Though I can’t claim much original work here. In his blog Propagating Acegi’s Security Context in a WSS UsernameToken SOAP Header via XFire using wss4j Michael Vorbuger provides everything necessary to get it working.

To get it running I added the three classes from Michael’s code acegi-ws-security-xfire-example to the appropriate packages in src/java/ in my Grails app.

  • ch.vorburger.acegiwss.server.PasswordHandler
  • ch.vorburger.acegiwss.server.ForgivingWSS4jInHandler
  • ch.vorburger.acegiwss.server.ValidateUserTokenHandler

and changed the inhandlers to use these classes in XfireGrailsPlugin.groovy.


"xfire.passHandler"(ch.vorburger.acegiwss.server.PasswordHandler) { bean ->
        }

"xfire.DOMhandler"(org.codehaus.xfire.util.dom.DOMInHandler) { bean ->
        }

"xfire.WSS4JHandler"(ch.vorburger.acegiwss.server.ForgivingWSS4jInHandler) {
     properties = ["passwordCallbackRef":ref("xfire.passHandler"),
                    "action":"UsernameToken"]
        }

"xfire.ValidateUserTokenHandler"(ch.vorburger.acegiwss.server.ValidateUserTokenHandler) {}

That makes the SecurityContext avaliable in the service. To see it work I paraphrased Michael’s example in the test service.

import org.acegisecurity.Authentication
import org.acegisecurity.context.SecurityContextHolder

class TestService {

    static expose=['xfire']

    boolean transactional = true

    String serviceMethod() {

        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
		 if (auth == null || auth.getName() == null || auth.getName().length() == 0) {
			 // In a real service, this would be a proper SOAP Fault, NOT an IllegalArgumentException
			 throw new IllegalArgumentException(NOAUTH_FAULT_TEXT);
		 }

       return "You did it ${auth.getName()}!!!"
    }
}

Thats it!

Categories: grails, groovy, java, programming Tags:

Adventures in Grails – WS-Security Part 1

March 1, 2008 Andrew Hahn 5 comments

The next step in rewriting the application was to secure the web services with WS-Security. In this post I get a grails version of the xfire wss example of User Token Authentication up and running. To do this I use (of course) the grails xfire plugin.

After creating a grails project and installing the xfire plugin, the first thing to do is to configure the inHandlers:

to do this add the following to the doWithSpring closure in XfireGrailsPlugin.groovy


"xfire.passHandler"(org.codehaus.xfire.demo.PasswordHandler) { bean ->
        }

"xfire.DOMhandler"(org.codehaus.xfire.util.dom.DOMInHandler) { bean ->
        }

"xfire.WSS4JHandler"(org.codehaus.xfire.security.wss4j.WSS4JInHandler) {
    properties = ["passwordCallbackRef":ref("xfire.passHandler"),
                  "action":"UsernameToken"]
        }
"xfire.ValidateUserTokenHandler"(org.codehaus.xfire.demo.ValidateUserTokenHandler) {
        }

ValidateUserTokenHandler and PasswordHandler are part of the example code distributed with xfire. I just copied them into the correct package in src/java/ in this simple grails app. And then add the inHandlers to the org.grails.xfire.ServiceBean


inHandlers = [ref("xfire.DOMhandler"),
              ref("xfire.WSS4JHandler"),
              ref("xfire.ValidateUserTokenHandler")]

See the complete listing for doWithSpring at the end of this post. Now any service you expose with xfire will require (and print) a username and password in a WSS UsernameToken header. The simple service I used to test this is:


class TestService {

    static expose=['xfire']

    boolean transactional = true

    String serviceMethod() {
       return "You did it!!!"
    }
}

I use soapUI to test, here is the request it generated:


      foo
      bar
      2008-03-01T19:49:03.627Z

This is obviously not a perfect solution. You may not want to secure all the web services in your project or at least not all in the same way. After I finish with this project I will have a more general solution to contribute to the grails xfire plugin.But first, I need to do something with the user credentials I am now receiving. Next up, integrating with acegi through the grails acegi plugin

Full doWithSpring listing:


def doWithSpring = {

        "xfire.serviceRegistry"(org.codehaus.xfire.service.DefaultServiceRegistry) { bean->
            bean.getBeanDefinition().setSingleton(true)
        }

        "xfire.transportManager"(org.codehaus.xfire.transport.DefaultTransportManager){ bean->
            bean.getBeanDefinition().setSingleton(true)
            bean.getBeanDefinition().setInitMethodName("initialize")
            bean.getBeanDefinition().setDestroyMethodName("dispose")
        }

        "xfire"(org.codehaus.xfire.DefaultXFire,
                 ref("xfire.serviceRegistry"),
                 ref("xfire.transportManager")) { bean ->
            bean.getBeanDefinition().setSingleton(true)
        }

        "xfire.typeMappingRegistry"(org.codehaus.xfire.aegis.type.DefaultTypeMappingRegistry){ bean ->
            bean.getBeanDefinition().setSingleton(true)
            bean.getBeanDefinition().setInitMethodName("createDefaultMappings");
        }

        "xfire.aegisBindingProvider"(org.codehaus.xfire.aegis.AegisBindingProvider,
            ref("xfire.typeMappingRegistry")) { bean ->
            bean.getBeanDefinition().setSingleton(true)
        }

        "xfire.serviceFactory"(org.codehaus.xfire.service.binding.ObjectServiceFactory,
            ref("xfire.transportManager"), ref("xfire.aegisBindingProvider")) { bean ->
            bean.getBeanDefinition().setSingleton(true)
        }

        "xfire.servletController"(org.codehaus.xfire.transport.http.XFireServletController,
            ref("xfire")) { bean ->
            bean.getBeanDefinition().setSingleton(true)
        }

        "grails.xfire"(org.grails.xfire.ServiceFactoryBean, "grails.xfire") { bean ->
            bean.getBeanDefinition().setInitMethodName("initialize")
            transportManager = ref("xfire.transportManager")
            grailsApplication = ref("grailsApplication", true)
        }

        "xfire.passHandler"(org.codehaus.xfire.demo.PasswordHandler) { bean ->
        }

        "xfire.DOMhandler"(org.codehaus.xfire.util.dom.DOMInHandler) { bean ->
        }

        "xfire.WSS4JHandler"(org.codehaus.xfire.security.wss4j.WSS4JInHandler) {
            properties = ["passwordCallbackRef":ref("xfire.passHandler"),
                          "action":"UsernameToken Timestamp"]
        }

        "xfire.ValidateUserTokenHandler"(org.codehaus.xfire.demo.ValidateUserTokenHandler) {}

        if(application.serviceClasses) {
            application.serviceClasses.each { service ->
                def serviceClass = service.getClazz()
                def exposeList = GrailsClassUtils.getStaticPropertyValue(serviceClass, 'expose')
                if(exposeList!=null && exposeList.contains('xfire')) {
                    def sName = service.propertyName.replaceFirst("Service","XFire")
                    //
                    "${sName}"(org.grails.xfire.ServiceBean){
                        //
                        xfire = ref("xfire")
                        //
                        serviceBean = ref("${service.propertyName}")
                        //
                        serviceClass = service.getClazz()
                        //
                        serviceFactory = ref("grails.xfire")

                        inHandlers = [ref("xfire.DOMhandler"),
                                ref("xfire.WSS4JHandler"),
                                ref("xfire.ValidateUserTokenHandler")]
                    }
                }
            }
        }
    }