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.