Monday, June 17, 2013

Sitemesh 403 Error

We just tried to restart a service that was working on Friday, and we were met with this error:
Clicking on the URL for `sitemesh_1_5_decorators.dtd` resolved to a redirect to a page stating that sitemesh is no longer developed.

To buy time to actually fix the issue I downloaded a copy of `sitemesh_1_5_decorators.dtd` from GitHub and hosted it locally using `python -m SimpleHTTPServer 80`, and redirected opensymphony.com to localhost it `/etc/hosts`.

Not the cleanest solution, but it will buy us time to remove those dead references from our code.

Monday, June 3, 2013

Gradle War Overlay Plugin (and why you don't need one)

Please excuse the (somewhat) misleading title, but I wanted to grab anyone actually searching for a Gradle-based solution to using the maven-war-overlay-plugin.

First some background. Certain spring-based products (such as Jasig's CAS) recommend integrating via the maven-war-overlay-plugin. Our project uses Gradle, and I wanted to replicate that behavior using that tool so we could use the same tool for everything.

Of course the first thing I looked for was the string "gradle-war-overlay-plugin." Turns out because Gradle is so flexible, I didn't need a separate plugin for this behavior. Honestly I was debating whether or not to write this functionality into a plugin, but I opted to write this post instead. First, this is what the pom.xml file would look like using the recommended setup:

Basically if you use Maven, you can declare your project as an overlay onto an existing war file that's retrieved from maven central. Here is how you reproduce that behavior in gradle:

What I've done is introduced the concept of an 'underlay'. Several reasons for this:

  • In Gradle it seemed easier to customize the behavior of my dependencies than to introduce a new project type of 'overlay'
  • Calling the dependency an 'overlay' dependency would imply that the files in those dependencies over-write the files that you've developed
  • It is a real word
So a few key differences to point out:
  • 2 new configurations:
    • warUnderlay - any war file that we want to expand "under" our project (i.e., without overwriting anything
    • pomWorkaround - In maven, a vanilla dependency will pick up a pom artifact type, but Gradle always assumes a jar dependency type. These are for dependencies that don't exist in jar form, but the transient dependencies for cas-server-webapp (for example) list them without a classifier. As far as I can tell this is the only way to override the artifact type for this dependency
  • To expand a bit more on the pomWorkaround section:
    • Download everything in pom form
    • Prevent the compile dependency from attempting to download the jar files that don't exist by subtracting the version number and '.pom' from the filename using regex
  • The compile configuration includes all the transient dependencies of the warUnderlay configuration, but does not extract them from an actual war file. An alternative to this approach is to include cas-server-core, but doing it this way matches the maven method more closely.
  • The war task has one more action tacked onto it:
    • First unwar all the warUnderlay dependencies. Gradle's AntBuilder is really helpful here
    • Then unwar the war file that was generated for us by Gradle before - the one without the war underlay. We want this to *overlay* what we've already *underlayed* in the previous step
    • Finally re-create the war file using ant again
This isn't the most terse code, nor is it the most readable either. This seemed to be the most generic I could make it..Use the following to test:

$ gradle jettyRunWar # or gradle tomcatRunWar

Here are some known weaknesses to this method:
  • IDEs will not pick up on it and will most likely generate a TON of errors
  • The project cannot be run in "exploded" mode; you must build the war and deploy it every time
  • Related to the first point, this isn't following a proper Gradle lifecycle. I'm sure there's a way to define this so that Gradle recognizes the war files as inputs into generating the final war file. I haven't spent too much time on it (past dabbling a bit with zipTree), so if you can figure this part out you could probably solve all three of these issues.
So... should I make it into a plugin? Please let me know what you think, contribute, and feel free to use this code in your project if you need to.

Cheers!
Joe

Monday, April 1, 2013

Flyway and Cloud Foundry

Here is an interesting problem...

For work we're using flyway to migrate our databases at runtime. So far we're pretty happy about it, but we ran into a peculiar issue when attempting to use auto-reconfiguration features in Cloud Foundry.

For those who are unfamiliar, when you deploy a Spring webapp to Cloud Foundry, by default one bean of type javax.sql.DataSource is re-written at deployment to connect to the provisioned resource (in my case, a postgresql database). It's fairly nice in that I don't have to worry about what the username/password is for my JDBC connection, and when it works it allows me to focus on features instead of configuration

But if you have a flyway bean defined in your application context, most likely you have it as a dependency for your session factory or entity manager factory. Since flyway depends on your data source bean, it becomes ineligible for auto-reconfiguration by Cloud Foundry. You'll get something like the following error message:

Caused by: com.googlecode.flyway.core.api.FlywayException: Unable to obtain Jdbc connection from DataSource
        at com.googlecode.flyway.core.util.jdbc.JdbcUtils.openConnection(JdbcUtils.java:56)
        at com.googlecode.flyway.core.Flyway.execute(Flyway.java:1232)
        at com.googlecode.flyway.core.Flyway.migrate(Flyway.java:820)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
        at java.lang.reflect.Method.invoke(Unknown Source)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeCustomInitMethod(AbstractAutowireCapableBeanFactory.java:1581)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1522)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1452)
        ... 38 more
Quite frustrating. So far I've only begun to understand the nature of this problem. The workaround would be to have your session factory or entity manager factory not depend on the flyway bean, but I'm not quite sure how we would go about ensuring the database is up-to-date.

*EDIT: it looks like this is the symptom, not the cause. Removing the flyway bean makes that error go away, but when I try to run a command that connects to the database I get the same issue. It's back to the drawing board for me.

*EDIT 2: So the short of the story is that if your dataSource bean is already a dependency of another bean that implements BeanFactoryPostProcessor, the dataSource bean is ineligible for the cloud foundry auto-reconfiguration support. That might not be worded 100% correctly, but that's the behavior I'm observing. Tomorrow I will attempt to explicitly configure my webapp for cloud foundry.

*EDIT 3: So this may have been an issue with the cloud foundry provider. I tried again the next day and everything worked as expected. So my theories above are incorrect, and dataSource should work just fine.