Friday, April 13, 2012

Near-Zero Effort Default Spring 3.1 Bean Profiles

A client of mine is performing a code migration over an extended time during which the "old" bits need to be executed unless the "new" bits are desired.  Naturally, we leveraged Spring 3.1 bean profiles to perform this amazing feat.

Now, the currently documented way of doing this is to use Spring 3.1's well-known Java system property spring.profiles.default. Since by default, the old bits should be active, all you need to do is ensure that somewhere in your target environment you're specifying the Java system property spring.profiles.default with a value that is a comma-delimited list of Spring bean profile name(s) that reflect the old bits. When you want the new bits, set spring.profiles.active with the profile name(s) that activate those new bits, thereby overriding the default profiles.  Note that you can, of course, have both properties defined at the same time, since one is for the default activation and the other is for explicit activation.  (BTW, you could instead use native OS environment variables as well.)

Pretty nice, but there are just a couple of issues with that, IMHO.
  • It requires changes to the target environments.  If you have many such environments or there is a resistance to changing system properties or environment variables, you must deal with that friction.
  • It's not immediately obvious what the default profiles are.  You can't tell from the XML configuration file elements or @Configuration classes alone which profiles are intended as your default ones.  You have to know which target environments are setting which default profiles, if any, since they're set per environment.
As for the first issue, the more agile your organization, the easier it is to change configuration like this, so not really a huge deal.  However, as I see it, the fewer unnecessary changes per release, the better.

With regard to the second issue, if there were a way to make it obvious which profiles were intended to be activated by default when viewing Spring XML configuration files or @Configuration classes, it'd be just jim-dandy.  Well, it turns out that you can.

There's a rather underdocumented feature in Spring 3.1 that defines a reserved default profile name, namely "default", that will serve to activate a beans element that includes it in its profile.  For example, if you're using "old" and "new" as your profile names corresponding to the bits you want to activate, and the old bits are your default, simply define your beans elements like this:

<beans>
  
  <!-- define beans here that don't change by profile -->
  
  <beans profile="old,default">
    <!--
      Define beans reflecting the "old" bits here.
      
      They'll be active unless any profiles are activated explicitly
      via spring.profiles.default, spring.profiles.active, or other
      means.
    -->
  </beans>
  <beans profile="new">
    <!--
      Define beans reflecting the "new" bits here.
      
      They'll be inactive unless they're explicitly activated.
    -->
  </beans>
</beans>

It's worth noting that if any bean profiles are activated by any means (PropertySource, spring.profiles.default or spring.profiles.active), the reserved "default" profile is ignored.

By leveraging Spring 3.1's reserved default profile name "default", you don't need to change your target deployment environments and you can see very quickly & clearly in your Spring configuration files which profiles are intended to be your default(s).

Nice!

Thursday, April 05, 2012

Official JPA 2.0 API jar in Maven Central

I'd love to see an official JPA 2.0 API jar, with sources & javadoc, available in Maven Central as javax.persistence:jpa-api:2.0, so I entered this issue:
http://java.net/jira/browse/JPA_SPEC-19

Right now, you have to dig to find various implementations' groupId:artifactId:version values.

If you want that, too, then vote for the issue!

Tuesday, April 03, 2012

Spring & JPA over Hibernate: Don't forget SpringSessionContext!

NB:  I'm writing this one down so that I don't forget.

If you're using Spring 3.1.1 and JPA with Hibernate as your JPA provider, you're probably using Spring's LocalContainerEntityManagerFactoryBean with a persistenceProviderClass of org.hibernate.ejb.HibernatePersistence.

I was having trouble getting Hibernate's Session to get hooked up to Spring's JpaTransactionManager.  To fix this, include the following entry in your Hibernate properties:
hibernate.current_session_context_class=org.springframework.orm.hibernate3.SpringSessionContext

It's configured automatically when you're using Spring & pure Hibernate with either LocalSessionFactoryBean or AnnotationSessionFactoryBean, but it doesn't appear to be so when using LocalContainerEntityManagerFactoryBean.

Now, hopefully neither your nor I will forget again.

Sunday, March 11, 2012

BitBucket offers free unlimited git PRIVATE repos

I am really growing to like git and the distributed way of version control and I have to acknowledge GitHub as a very popular place for social coding.  I've noticed one thing, though, that's prevented me from using it: you have to pay for private repositories (US$7/mo as of this writing).

Fortunately, I noticed that BitBucket, from Atlassian, not only has added git support (I was using it with Mercurial), they also support unlimited public and private repositories and up to five users for free, with reasonable paid plans from there.

In fact, all plans support unlimited public & private repos, and it seems to have most if not all of the same bells & whistles (issue tracking, wiki, pull requests, etc). I think it beats GitHub for small projects that you don't want to share just yet (if ever).

I wonder what would have happened if BitBucket had offered git before GitHub did -- maybe BitBucket would have been the new, cool place to store your code!

Anyway, just thought I'd let people know about the great service.

Saturday, March 10, 2012

Running TestNG & JUnit 4 tests in the same Maven project without duplication

Update on 16 Mar '12:  Cedric has informed me that the TestNG master branch now supports JUnit 4.  Try it out!

As of this writing, TestNG still doesn't support JUnit4.  Problem is, Spring Roo uses JUnit 4 and I happen to be developing a liking for TestNG lately.

Many folks out there have suggested defining two Surefire executions, one for JUnit 4 & one for TestNG.  I did that, but every time I do, it looks like the default configuration of Surefire runs first, then whatever other test executions that you've defined next, often resulting in duplicate tests being run.

I noticed that the default Surefire execution id is default-test.  By simply defining an execution with id default-test and setting skipTests to true, I was able to avoid the unwanted default run.  You could either redefine the default-test execution or defeat it and define whatever other test executions you want to.

Here's the POM snippet that defeats the default-test execution and defines one for JUnit 4 & TestNG:

    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-surefire-plugin</artifactId>
        <version>2.11</version>
        <executions>
            <execution>
                <id>default-test</id>
                <configuration>
                    <skipTests>true</skipTests>
                </configuration>
                <goals>
                    <goal>test</goal>
                </goals>
            </execution>
            <execution>
                <id>junit-tests</id>
                <goals>
                    <goal>test</goal>
                </goals>
                <configuration>
                    <testNGArtifactName>none:none</testNGArtifactName>
                    ...
                </configuration>
            </execution>
            <execution>
                <id>testng-tests</id>
                <goals>
                    <goal>test</goal>
                </goals>
                <configuration>
                    <junitArtifactName>none:none</junitArtifactName>
                    ...
                </configuration>
            </execution>
        </executions>
    </plugin>

I hope this helps you some time.