Two reasons to prefer Hibernate JPA over EclipseLink on GlassFish

vs.
EclipseLink is the default JPA provider on the GlassFish JEE server. As such, I figured EclipseLink would work well as the persistence provider. However, we began a recent JEE 6 project using GlassFish v3 and chose Hibernate as the JPA provider because of the team's familiarity with Hibernate 3. We later switched to EclipseLink to be compatible with another application running on the server -- and immediately encountered two annoying problems that never occurred with Hibernate. This article is meant to document those EclipseLink problems in case it helps others with similar EclipseLink issues on GlassFish.

The first problem we saw was a sporadic ClassCastException after a module redeploy. The second problem we found was that entity methods marked with @PrePersist were not being called when the entity was being saved to the database. Instead, those fields would remain null when EclipseLink executed the INSERT SQL statement, resulting in constraint violations for our non-nullable columns.

The ClassCastException occurs at a line in our code that assigns an entity to a reference variable declared as the type of its parent class, which is a JPA mapped superclass. A widening cast like that should cause no problem, so this was a head-scratcher. Adding to the mystery is we had no problem with this code when using Hibernate JPA. The entity class in question, ActionTypeLookup, as shown in the stack trace below, directly extends the parent class type that it is being assigned to.

Here are the partial class definitions. The parent class type, CodeLookupValues,
@MappedSuperclass
public abstract class CodeLookupValues implements Serializable {
...
}
is a @MappedSuperclass that the entity ActionTypeLookup directly extends:
@Entity
@Table(name="ACTION_TYPE_LOOKUP")
public class ActionTypeLookup extends CodeLookupValues implements Serializable {
...
}
The persistence code is defined in a module (an EAR file) containing the JPA persistence unit definition. The ClassCastException occurs only if that same module is unloaded and reloaded from the server several times. That is, something you do a lot of during development. We had loaded and unloaded this EAR file many times on GlassFish with no problem when using Hibernate. Shortly after the switch to EclipseLink, we would see this stack trace in the log at deploy time:
javax.enterprise.system.core.com.sun.enterprise.v3.server Exception while loading the app
javax.ejb.EJBException: javax.ejb.CreateException: Initialization failed for Singleton LookupSessionFacade
at com.sun.ejb.containers.AbstractSingletonContainer$SingletonContextFactory.create(AbstractSingletonContainer.java:695)
at com.sun.ejb.containers.AbstractSingletonContainer.instantiateSingletonInstance(AbstractSingletonContainer.java:444)
at org.glassfish.ejb.startup.SingletonLifeCycleManager.initializeSingleton(SingletonLifeCycleManager.java:213)
at org.glassfish.ejb.startup.SingletonLifeCycleManager.initializeSingleton(SingletonLifeCycleManager.java:174)
at org.glassfish.ejb.startup.SingletonLifeCycleManager.doStartup(SingletonLifeCycleManager.java:152)
at org.glassfish.ejb.startup.EjbApplication.start(EjbApplication.java:150)
... [removed several classes involved in installing the EAR] ...
at com.sun.grizzly.SelectionKeyContextTask.call(SelectionKeyContextTask.java:57)
at com.sun.grizzly.ContextTask.run(ContextTask.java:69)
at com.sun.grizzly.util.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:330)
at com.sun.grizzly.util.AbstractThreadPool$Worker.run(AbstractThreadPool.java:309)
at java.lang.Thread.run(Thread.java:619)
Caused by: javax.ejb.CreateException: Initialization failed for Singleton LookupSessionFacade
at com.sun.ejb.containers.AbstractSingletonContainer.createSingletonEJB(AbstractSingletonContainer.java:525)
at com.sun.ejb.containers.AbstractSingletonContainer.access$100(AbstractSingletonContainer.java:74)
at com.sun.ejb.containers.AbstractSingletonContainer$SingletonContextFactory.create(AbstractSingletonContainer.java:693)
... 36 more
Caused by: java.lang.ClassCastException: my.customer.package.shared.datamodel.reference.ActionTypeLookup cannot be cast to my.customer.package.shared.datamodel.reference.CodeLookupValues
at my.customer.package.shared.datamodel.persistence.LookupSessionFacadeBean.reloadCommonLookupValues(LookupSessionFacadeBean.java:127)
at my.customer.package.shared.datamodel.persistence.LookupSessionFacadeBean.readLookupTables(LookupSessionFacadeBean.java:70)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at com.sun.ejb.containers.interceptors.BeanCallbackInterceptor.intercept(InterceptorManager.java:1006)
... [removed several more classes involved in installing the EAR] ...
at com.sun.ejb.containers.interceptors.CallbackChainImpl.invokeNext(CallbackChainImpl.java:61)
at com.sun.ejb.containers.interceptors.InterceptorManager.intercept(InterceptorManager.java:390)
at com.sun.ejb.containers.interceptors.InterceptorManager.intercept(InterceptorManager.java:373)
at com.sun.ejb.containers.AbstractSingletonContainer.createSingletonEJB(AbstractSingletonContainer.java:518)
... 38 more
We see the error at deploy time because the application has a @Singleton EJB that also is annotated with @Startup so the server loads it at deploy time. I don't know if having a non-startup EJB or a non-singleton EJB would make a difference.

It turns out this was the easiest problem to work around -- the ClassCastException goes away if you restart the domain. But I never discovered what causes the exception. At first, I thought the problem must be caused by the two classes being loaded by separate classloaders due to the way we had structured the modules in our application. However, both classes are:
  • Deployed in the same Java package
  • Inside the same JAR
  • Packaged and deployed inside the same EAR.
The ClassCastException does not occur all the time. It just suddenly pops up after a series of deploy/undeploy cycles and does not go away until we restart the GlassFish domain.

My best theory so far as to what causes the ClassCastException is it is a bug in GlassFish's OSGi bundle class loading. The problem appears as if some GlassFish classloader has the parent MappedSuperclass class loaded even after module undeploy but not the child class. Subsequently, when the updated EAR file is deployed again, the new module's newly assigned Archive classloader sees that the parent class is already loaded but needs to load the child classes from the new EAR files's JAR. When the code tries a widening assignment of a child entity to a reference type of the parent class -- kaboom! -- a ClassCastException because the two classes are now unrelated because of the different classloaders.

The GlassFish OSGi implementation gets my suspicion only because EclipseLink is deployed in GlassFish as an OSGi bundle, and the ClassCastException never occurred when using Hibernate, which is deployed as a set of jar files inside the server's shared library classpath.

Better theories are welcome in the comments. I poked around but found no others reporting the same bug or that this problem has been fixed in a GlassFish update (we are not running the latest updates of v3). This ClassCastException is only an annoyance because the workaround is to restart the GlassFish domain/server. A restart always fixes the problem and it goes away, until after another series of deploy/undeploy cycles.

@PrePersist problem

The second and more vexing problem is with the way EclipseLink synchronizes its entity session cache with the database. If I understand the problem correctly from reading various forum postings, if you instantiate a new entity object and call an EntityManager.find() method before the new, detached entity has been persisted with EntityManager.persist(), EclipseLink synchronizes all the detached entity states with the database before performing the query. That synchronization makes sense: the database is going to need to know about the new rows in the tables if the SELECT query is going to find the correct data.

The problem, at least for me, is that when EclipseLink performs the INSERT statement to persist the unsaved entities, it does not call the @PrePersist methods on the entity. I was counting on @PrePersist methods to set values like created-date and last-updated-date, which cannot be null in our schema. OK, I can understand that EclipseLink opted not to call @PrePersist methods when it is merely inserting the data for its convenience and not because EntityManager.persist() was called. But the unexpected behavior required some logic change in our entity code because Hibernate always seemed to call our @PrePersist methods before performing an INSERT on the database. After the switch to EclipseLink, we started seeing database constraint violation errors on these @PrePersist columns.

This second problem with EclipseLink, then, cannot be called a bug, but it was unexpected behavior worth documenting here. I assumed incorrectly that @PrePersist methods, by definition, always would be called before an entity got persisted. Wrong.

Those are the only two unusual problems we saw after switching from Hibernate to EclipseLink. The other problems that occurred were some HQL-specific queries failed, but those could all be converted to standard JPQL with a little alteration.

After complaining about EclipseLink issues, I will profess one preference for EclipseLink: The debugging log messages and error log messages seemed clearer and more straightforward with EclipseLink. When one our our HQL queries failed or I needed to trace what queries were being called and when, EclipseLink's debugging messages just seemed clearer and easier to understand than many of Hibernate's logging messages. I could see queries more clearly being translated from JPQL to Oracle SQL, and see what values were being bound to query substitution parameters. I do hand it to EclipseLink for making its database interactions more transparent than I am used to with Hibernate. So when we had the @PrePersist problem, at least I was able to debug and diagnose them fairly easily.

Clojure’s inventor and author make a case for the new language

Rich Hickey, inventor of the Clojure language, and Stuart Halloway, author of "Programming Clojure," presented introductory and advanced concepts of the young JVM language at Wednesday's Northern Virginia Java Users Group. These are some of my notes from the meeting. The session served to whet interest in learning Clojure, thus these notes do not include a lot of code or explain Clojure's unusual syntax. There are many other sources for that.

Clojure, a Lisp-like language that compiles to Java byte code and runs on the Java virtual machine, was created as a general-purpose programming language that embraces a functional style of software design, rather than the imperative style typical in languages like Java -- and most other general purpose languages in use today. Functional programming languages like Clojure, Scheme and Erlang have been getting a lot of attention at technology conferences over the last few years, which first brought my attention to Clojure. Its functional style and its ability to run alongside and integrate with existing Java code interested me in learning more about Clojure. The fact that its inventor and a technology instructor I highly respect were presenting a free session on Clojure compelled me to attend the JUG meeting.

Rich Hickey released the first version of Clojure in October, 2007, with version 1.0 released May 1, 2009. We are talking about a young language. Still, from what I learned last night, it looks like a powerful language with potential. Clojure is released as open source under the Eclipse Public License 1.0, which makes it easy to use in a non-open source commercial environment.

Stuart Halloway, author of 'Programming Clojure'
Stuart Halloway
Stu Halloway, co-founder of the top-notch professional training and agile consulting company Relevance Inc., began with an introduction to Clojure's features and why a Java developer might want to learn it. Rich then took over and introduced three new features of Clojure (Protocols, Reify and Datatypes) that can be downloaded from the latest source tree but are not part of the current 1.1 release of the language.

According to Stu, some of the compelling features of Clojure are its:
  • Easy interoperability with Java
  • Lisp syntax
  • Functional style
  • Ability to run in a multi-threaded environment with no coding overhead
To demonstrate the syntax benefit, Stu "refactored" the StringUtils.isBlank method from the Apache Commons lang library. He started by showing the full Java source code and then removing all the ceremonial scaffolding code to expose the core logic, then simplified the Java code into the definition of an equivalent Clojure function:
(defn blank? [s]
(every? #(Character/isWhitespace %) s))
I'm not a Clojure programmer (yet) but I think I captured the above syntax correctly. Like Ruby, Clojure uses the question mark to replace the traditional "is" prefix in boolean functions. The # symbol introduces an anonymous function. From what Stu described, the functional programming paradigm in Clojure handles most (all?) corner cases for you. There is no need to write special-case "if" statements to deal with a null parameter, for instance.

For Clojure's interoperability with Java, Clojure code can call Java, and Java code can call Clojure functions. (According to Rich, the integration is implemented with little or no need to use Java reflection at runtime, adding less runtime overhead.)

For Clojure's advantage by using Lisp syntax, Stu referred everyone to Paul Graham's 2001 article, "What Made Lisp Different" as the best explanation. Most languages have "special forms" like imports, scopes, protection definitions, metadata, keywords. These special forms are language features you can use, but you cannot create them yourself and add them to the language. These language features are thus unavailable for reuse. Lisp abandoned this restriction. In a Lisp-like language, special forms of the language look like anything else in the language. All forms of the language are created equal. In Lisp (and Clojure), defining scope, the control flow, method calls, operators, functions, import mechanisms -- they are all lists. Stu said a language's "special forms" restrictions cause a programming language to "crap out," and joked that the restrictions bring about magical cut-and-paste reuse workarounds we call "design patterns."

For Clojure's advantage by being a functional language, Clojure encourages you to write small pieces of code that work well together. Good code has the same shape as pseudo code, he said, and Clojure's functional style lets you create more pseudo-code looking real code. According to Stu, functional languages are simpler to understand. They let you write code that eliminates or reduces what he called "incidental complexity" required by non-functional languages:
  • Corner cases
  • Class definitions
  • Internal exit points
  • Variables
  • Branches
The resulting code is less complex, he said, and simpler to understand by orders of magnitude.

The final benefit he talked about is Clojure's inherent ability to run in a multi-threaded environment with no special concurrency-handling code from the developer. Clojure and other functional programming languages perform this feat by treating data as immutable and producing a new copy of a data structure when data needs to be changed. Two threads never look at the same data at the same time, so there is never any need to synchronize access to code that reads and writes data. Clojure's solution, Stu said, is to separate identify from value. He went on to explain what this means, but maybe the late hour caused me to miss the details.

Rich Hickey

After Stuart set the stage for why learn and use Clojure, Rich Hickey took over to talk about new features he is adding to the language. He said, quite truthfully, that for those in the audience who don't already know Clojure, what he was about to say would not make a lot of sense. These features are Protocols, Reify and Datatypes. As a result of my newness to Clojure, I will pass along what I thought Rich said and hope he and the Clojure crowd forgive my ignorance.
Rich Hickey, inventor of Clojure language, speaking at NovaJUG
Rich Hickey, inventor of Clojure, speaking March 17, 2010 at the
Northern Virginia Java Users Group meeting.
[taken from my phone]


Rich, for an open source programming language inventor, was a refreshingly clear advocate for his new language. Maybe I'm jaded from years of slogging through open-source code, but from my experience, most open source projects release their code with little explanation of how or even why to use it, and then treat users like they are the ones who failed if they misunderstand how to use the code correctly. Rich actually understood where most of us in the audience were coming from. "I know it's a big deal to try to learn a new programming language," he said, but he believes Clojure is worth taking the time to learn and will make our jobs as programmers easier.

Before delving into the new features he is adding, Rich provided a summary of how Clojure is implemented. Part of it is written in Java for performance, and the rest is written in Clojure itself. He said his goal is to eventually write most of Clojure in Clojure once he can get performance boosted to an equivalent level.

Clojure is built using abstractions, with those abstractions written as Java interfaces. The fundamental implementation objectives of Clojure (or at least the ones I picked up on), he said, are to leverage high-performance polymorphism mechanisms of the host environment, to write to abstractions not concrete types, and to enable extension and interoperability with Java.

From what I understood of the new language features, Protocols are named sets of generic functions. Reify allows developers to use the "cool code generation" in the built-in fn function. "I put a lot of work into 'fn' and I wanted to make it reusable," he said. Even though it went over my head, Rich said Reify allows developers to create an instance of an unnamed type that implements protocols, like proxy for protocols. For the new Datatypes feature, if I understood correctly, he said he added a new construct, deftype, to define a name for a type and list of fields in that type.

Additional details that might make sense if you know Clojure:
  • Datatypes fields can be primitives
  • Datatypes support metadata and value-based equality by default
  • In-line method definitions are true methods, no indirection or lookup and calls can be inlined by just-in-time compilers, like Hotspot
  • Keyword-style field lookups can be inlined just like (.field x) calls
Rich concluded by offering more reasons to explore and begin using Clojure. "Closure has dramatically less implicit complexity than other languages," he said. You don't need to write a lot of code simply to support the needs of the language. You spend your time with Clojure focusing on domain complexity, not language complexity, he said. "It has a lot of newness, so the unfamiliarity level is high," he said. "But it is very, very simple."

Adding shutdown hooks to a desktop Griffon application

Technical pride prompted me to write my first Griffon application Tuesday. Griffon is a Groovy-based framework to write Java desktop applications. Groovy takes some of the sting out of writing Java Swing applications and Griffon relieves more of the burden. My pride came into the picture when Manning Publications released its daily Pop Quiz yesterday asking what technique one would use to process the shutdown of a Griffon application running on OS X. Manning posts a new question each day of September, and as of today, I'm running a perfect score. I couldn't let a little question about Griffon stop me. However, since Griffon is so new (its stable release is 0.1.2) and developers are only now starting to play with it, googling around for a simple answer didn't turn up much.

After failing to find sample Griffon code that described the application shutdown process (especially with the question's wrinkle of using OS X), I figured I'd write a simple Griffon desktop application and give the technology a spin. In the category of famous last words, "How hard could it be?" Turns out, thankfully, not that hard.

After downloading the Griffon 0.2-BETA zip file, setting my GRIFFON_HOME environment variable to point to the folder where I unzipped the files, and adding the $GRIFFON_HOME/bin directory to my PATH for convenience, I was ready to create my first Griffon application. I followed the instructions on the Griffon Quick Start page and ran the command:
griffon create-app
and typed in my project name (quiz) to create all the files needed for a basic application. The create-app command generates the application scaffolding along the lines of other modern frameworks like Rails, Grails, App Fuse and even Maven.

Once the create-app command created the skeletal the application files, I followed the sample code on the Griffon Quick Start page to augment the files with code to create a simple desktop application. The application provides a window that lets you type in and execute code in the Groovy shell. Griffon structures its files around the Model-View-Controller pattern, creating subdirectories for "models", "views" and "controllers". Here are the directories in the project's "griffon-app" folder:
$ ls griffon-app
conf/  controllers/  i18n/  lifecycle/  models/  resources/  views/
Figure 1 shows the resulting application in action, which you can build and run using the command griffon run-app. I typed the two Groovy statements into the window and clicked the Execute button.
Griffon sample application screen
Figure 1: Griffon Quick Start application window
One of my first tweaks to the sample application code was to put in place what I learned during my googling around for an answer. I followed the advice of Josh Reed, one of Griffon's six committers. Josh, who uses Griffon in his day job, wrote a blog post this month about how to intercept window closing events that proved quite helpful. I edited the file griffon-app/views/QuizView.groovy to define application properties for defaultCloseOperation and windowClosing so the top of my QuizView.groovy now looked like this:
QuizView.groovy:
import javax.swing.WindowConstants
application(
title:'quiz',
pack:true,
locationByPlatform:true,
iconImage: imageIcon('/griffon-icon-48x48.png').image,
iconImages: [imageIcon('/griffon-icon-48x48.png').image,
imageIcon('/griffon-icon-32x32.png').image,
imageIcon('/griffon-icon-16x16.png').image],
defaultCloseOperation: WindowConstants.DO_NOTHING_ON_CLOSE, // ADDED PROPERTY HERE
windowClosing: { evt ->                                     // AND HERE
println "QuizView.groovy: windowClosing event called!"
System.out.flush()
app.shutdown()
}
In addition to the println statement to tell me my shutdown hook was invoked, I needed to add the call to app.shutdown() since I was now telling Java not to end the application when its main window was closed by setting the defaultCloseOperation property to the DO_NOTHING_ON_CLOSE. I followed Josh's tip on editing the griffon-app/conf/Application.groovy file to set the autoShutdown property to false. This flag is needed so my window-closing event code would be run instead of the default auto-shutdown behavior. (Thanks for the tip, Josh.)
Application.groovy:
application {
title='Quiz'
startupGroups = ['quiz']
// Should Griffon exit when no Griffon created frames are showing?
autoShutdown = true
// If you want some non-standard application class, apply it here
//frameClass = 'javax.swing.JFrame'
}
mvcGroups {
// MVC Group for ""
'quiz' {
model = 'QuizModel'
controller = 'QuizController'
view = 'QuizView'
}
}
Now when I run the application and close the window, the console shows:
$ griffon run-app
Welcome to Griffon 0.2-BETA - http://griffon.codehaus.org/
Licensed under Apache Standard License 2.0
Griffon home is set to: /home/tom/Projects/Griffon/griffon-0.2-BETA
Base Directory: /home/tom/Projects/ManningQuiz/quiz
Running script /home/tom/Projects/Griffon/griffon-0.2-BETA/scripts/RunApp.groovy
Environment set to development
Warning, target causing name overwriting of name default
[groovyc] Compiling 3 source files to /home/tom/.griffon/0.2-BETA/projects/quiz/classes
QuizView.groovy: My windowClosing event called!
That's one way to add a shutdown hook to a Griffon application, by adding a listener to fire when the application's window closes. However, this discovery didn't answer the Manning quiz. None of the available answers showed this technique.

More searching around the web pointed me to the compellingly sounding griffon-app/lifecycle files created by the create-app scaffolding command. One of these auto-generated files is called Shutdown.groovy. It couldn't get more obvious or more easy than that, I suppose. The contents of this file show helpful comments describing how to add shutdown hooks to your application.
griffon-app/lifecycle/Shutdown.groovy
/*
* This script is executed inside the EDT, so be sure to
* call long running code in another thread.
*
* You have the following options
* - SwingBuilder.doOutside { // your code  }
* - Thread.start { // your code }
* - SwingXBuilder.withWorker( start: true ) {
*      onInit { // initialization (optional, runs in current thread) }
*      work { // your code }
*      onDone { // finish (runs inside EDT) }
*   }
*
* You have the following options to run code again inside EDT
* - SwingBuilder.doLater { // your code }
* - SwingBuilder.edt { // your code }
* - SwingUtilities.invokeLater { // your code }
*/
I thought I'd edit this file and add some custom shutdown code. I added this to the end of the above file:
import groovy.swing.SwingBuilder
def swing = new SwingBuilder()
swing.doOutside {
println "doOutside called in the Shutdown.groovy lifecycle"
}
With these few extra lines of code, running the application (griffon run-app) and closing the window resulted in these lines on the console. (I eliminated the Griffon startup information.):
QuizView.groovy: My windowClosing event called!
doOutside called in the Shutdown.groovy lifecycle
Interesting to see that the application's window-closing event occurred before the application shutdown event. That makes perfect sense.

But Wait, There's More

Unfortunately, this solution didn't seem to satisfy any of the available options in the Manning quiz. (Except for the tantalizingly tempting "None of the above" fourth option.) I didn't want to give up yet in finding a solution. The available quiz answers that seemed worthy of looking into talked about defining event handlers for the "ShutdownStart" event or the "ShutdownEnd" event. According to the Release Notes for version 0.1, runtime events may be added to the controller class. The notes list all events that may fired by the application during its life cycle:
  • BootstrapEnd
  • StartupStart
  • StartupEnd
  • ReadyStart
  • ReadyEnd
  • ShutdownStart
Since no event for ShutdownEnd is in the list, I figured the Manning quiz answer was probably defining an event handler for ShutdownStart. Since I wanted to be sure, I added a tiny event handler, with code borrowed from the sample in the Release Notes, to my controller class in griffon-app/controllers/QuizController.groovy:
def onShutdownStart = { app ->
println "Controller onShutdownDown says ${app.config.application.title} is shutting down."
}
I re-ran the application and shut it down, and the console now showed:
QuizView.groovy: My windowClosing event called!
Controller onShutdownDown says Quiz is shutting down.
doOutside called in the Shutdown.groovy lifecycle
The lines show all of my shutdown code successfully got called. So here's what I learned in my foray into Griffon:
  • There are at least THREE ways to handle events that fire when an application is shut down
  • Writing event listeners in Groovy/Griffon is a lot easier than Swing
  • There is no requirement to register the runtime event with the source of the event
  • Griffon (and Groovy) do their share to ease programming by defining conventions over requiring configuration
The nice bonus in playing with Griffon was the scaffolding-building create-app command got me started and running quickly. I was able to create a Griffon desktop Java application, add three ways to capture runtime events, compile and test the application several times -- all in less time it took me to write this blog documenting these facts. I don't know whether Griffon can win the hearts of developers who want to write desktop applications, but I sure think it can win the hearts of Java developers who would otherwise be stuck writing a straight Swing application. If you're a Swing developer, definitely check out what Groovy and now Griffon have to offer in ease of development and simpler code writing. I look forward to seeing what Griffon becomes once it reaches the 1.0 milestone.

Alfresco Software says freeloading corporations led them to closed-source strategy

Alfresco Software said Tuesday it will not release certain new enterprise features of the Alfresco content management products, such as high-availability clustering, as open source as a way to persuade freeloading companies to pay up. John Newton, CTO and co-founder of Alfresco Software Inc., posted notice of this new business strategy in a blog entry that effectively says Fortune 50 users of the free version of Alfresco are cheapskates. He reassures everyone in his blog that core components of Alfresco will remain open source and free. Alfresco thus joins other commercial open source projects, such as MySQL, in bifurcating their source code into a core version licensed as open source, and a version with features important to large businesses (the ones with money) available for a fee.

In a blog last August, Andrew Lampitt called this division an "open-core licensing" model. This division is now officially reflected in Alfresco Software's release Tuesday of Alfresco Enterprise 3.1.

Newton implied in his blog that Alfresco Software was pushed to close-source enterprise features of Alfresco for financial reasons. Many "world-class companies" with "household brands" are using Alfresco's free and open source Labs version, he wrote, and not paying the annual Enterprise subscription. Enterprise subscriptions are a source of revenue for Alfresco Software. Subscribing customers receive a more stable "Enterprise Edition" of the Alfresco content management suite, dedicated support, and optional training and consulting. By releasing enterprise features of Alfresco only to paying customers, Newton said, he hopes Alfresco's largest users will see the benefit of paying Alfresco an annual subscription rather than paying their own developers to figure out how to deploy Alfresco in a high-availability environment or writing their own management tools.

If indeed Alfresco Software won't be able to survive (or at least thrive) without finding a more sustainable revenue source, this move sounds like a good strategy. It will allow Alfresco to continue to improve its already excellent enterprise content management system while allowing smaller companies to use Alfresco's core products for free. If, however, this bifurcated source code strategy is merely a way to return higher profits to Alfresco's founders and funders, the change might negatively impact how Alfresco Software is perceived by developers writing useful extensions and integrations and releasing them as free, open source. Open source developers release their code for the community's benefit as a whole, but might hesitate to do so when a corporation profits indirectly from their work.

There is nothing wrong with a corporation out to make money, and adopting the "open core" model might be the best move for Alfresco's future -- as a product and as a company. I don't know whether much or any of the Alfresco "core" code base was written by non-Alfresco employees. If not, the risk of Alfresco losing contributions from external open source developers is small. Other "community" contributions, like free technical support offered by fellow users on the Alfresco forums, likely will continue unaffected by the licensing change because much of that free exchange of information is among users of the free Labs version.

From what the privately held Alfresco Software says publicly, it isn't hurting for money. Alfresco employees I spoke with last year say the company is profitable. Matt Asay, Alfresco's vice president of business development, said in a CMS Wire story Tuesday by Barb Mosher that sales have been increasing recently by more than 27% from the quarter before. On Monday, Alfresco Software released limited financial information for its fiscal year ended Feb. 28. The news release says Alfresco Software Inc. closed the 2008 fiscal year with a 103% year-over-year revenue growth and a 92% increase in revenue from the fourth quarter of 2008 compared to the same quarter in 2007. It also added 270 paying customers during the 2008 fiscal year.

Newton wrote in his blog that Alfresco Software looked at options other than close-sourcing enterprise features. Alfresco considered "crippling" the open source version, he said, or letting the open source branch become so cutting-edge that it fell "into a destabilized state." But going that route "would make it difficult for certain governments to use our product," he said, and could cause the open source community to create competing forks to fix the crippleware.

Newton said Alfresco's new software model will adhere to these six principals:
  1. Alfresco extensions to create high-availability, clustered systems and provide better monitoring and and administration will be developed as closed source.
  2. The core system and interfaces will remain 100% open source.
  3. Bugs fixed for Enterprise customers will be folded into the next open source Labs release.
  4. Code that Alfresco Software writes for paying customers to integrate Alfresco with proprietary systems will remain closed source.
  5. Integrations to "ubiquitous" proprietary systems, like SharePoint, will remain open source.
  6. Alfresco will continue to support paying customers to the levels of their SLAs.


Installing Groovy from RPM on Fedora

I installed Groovy 1.6 on Fedora from an RPM as offered on the Groovy download page and immediately got an exception stack trace when running groovysh or groovyConsole. I installed the groovy-1.6.0-2.noarch.rpm file, kindly packaged by Federico Pedemonte, then tried to run groovyConsole:
[tom@dev ~]$ groovyConsole
Exception in thread "main" java.lang.NoClassDefFoundError: org/codehaus/groovy/tools/GroovyStarter
Caused by: java.lang.ClassNotFoundException: org.codehaus.groovy.tools.GroovyStarter
at java.net.URLClassLoader$1.run(URLClassLoader.java:200)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
at java.lang.ClassLoader.loadClass(ClassLoader.java:307)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
at java.lang.ClassLoader.loadClass(ClassLoader.java:252)
at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:320)
Could not find the main class: org.codehaus.groovy.tools.GroovyStarter.  Program will exit.
I then tried to run groovysh with no better luck:
[tom@dev ~]$ groovysh
Exception in thread "main" java.lang.NoClassDefFoundError: org/codehaus/groovy/tools/GroovyStarter
Caused by: java.lang.ClassNotFoundException: org.codehaus.groovy.tools.GroovyStarter
at java.net.URLClassLoader$1.run(URLClassLoader.java:200)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
at java.lang.ClassLoader.loadClass(ClassLoader.java:307)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
at java.lang.ClassLoader.loadClass(ClassLoader.java:252)
at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:320)
Could not find the main class: org.codehaus.groovy.tools.GroovyStarter.  Program will exit.
The problem turned out to be quite simple to solve: a missing GROOVY_HOME. The packager adds the environment variable GROOVY_HOME to the shell by adding the file /etc/profile.d/groovy.sh so bash picks it up at startup via the /etc/profile script, but my current shell hadn't had a chance to read in that file yet.

The solution was as easy as exiting and starting a new shell, or manually setting:
[tom@dev ~]$ export GROOVY_HOME=/usr/share/groovy
[tom@dev ~]$ groovyConsole
and everything works.

How to modify the Alfresco Share page footer

Share is Alfresco Software's open source collaboration server. It's a Java web application that stands as a free competitor to Microsoft SharePoint. Since I wrote earlier this month about how to modify the default footer text in the Alfresco content management server's web client, I thought I'd follow up with even simpler instructions on how to modify the global footer in Alfresco Share. You don't even need to stop and restart the web application server for this modification.

Share is part of the open source Alfresco Labs 3 and the commercial Alfresco Enterprise Edition enterprise content management server and is built using Alfresco's Surf web framework. The Share application comes fully functional out of the box (more precisely, out of the share.war file). But it also is highly customizable with a little HTML, JavaScript and FreeMarker script modifications or additions. Thus, the default footer text in Share is much easier to modify than it is in the Alfresco web client I wrote about earlier.

The web client, which is a JavaServer Faces application, embeds the footer text inside a Java tag library inside a JAR file inside the alfresco.war file. You need the source code and a JDK to modify the global footer text. The Share web application, more simply, places its footer text inside a properties file inside the share.war file, which you can customize with a plain text editor. If you deploy Share as a WAR file instead of an exploded WAR, you'll need to unzip the WAR, edit the text file, then rezip. But that's about as complex as this change gets.

To customize the footer text for Share, unzip the share.war file if necessary to a directory of your choosing or just find the exploded WAR file you have currently deployed. If you run Share on Tomcat, your deployed Share application likely is in the directory $CATALINA_HOME/webapps/share (or %CATALINA_HOME%\webapps\share under Windows). The file to modify is in the directory
WEB-INF/classes/alfresco/site-webscripts/org/alfresco/components/footer
called footer.get.properties. On my version of Alfresco Share, this default footer file looks like this (but all on one line):
label.copyright=Supplied free of charge with
<a href='http://www.alfresco.com/services/support/communityterms/#support'>no support</a>,
<a href='http://www.alfresco.com/services/support/communityterms/#certification'>no certification</a>,
<a href='http://www.alfresco.com/services/support/communityterms/#maintenance'>no maintenance</a>,
<a href='http://www.alfresco.com/services/support/communityterms/#warranty'>no warranty</a> and
<a href='http://www.alfresco.com/services/support/communityterms/#indemnity'>no indemnity</a>
by <a href='http://www.alfresco.com'>Alfresco</a> or its
<a href='http://www.alfresco.com/partners/'>Certified Partners</a>.
<a href='http://www.alfresco.com/services/support/'>Click here for support</a>.<br />
Alfresco Software Inc. © 2008-2009 All rights reserved.
This text gets rendered on every Share page as a footer that looks like this:



You can see why someone might want to change this global footer text.

Change the text to anything you like and save footer.get.properties file. Mine now says:
label.copyright=Alfresco Software Inc. © 2008-2009 All rights reserved.
To see your change, you can either restart your web application server if no one is using Share, or you can tell Share to refresh its web components cache without inconveniencing anyone currently using the server. Here's how.

Login to Share as the "admin" user and visit the service /share/service path of the Share application. If you are running Share on your Tomcat local server, the path is probably http://localhost:8080/share/service/. This page will have a button at the bottom labeled "Refresh Web Scripts". Click it, and your footer text change should be loaded the next time the footer component script is called.



Ahh. Much better.

If you build Share from source, you can make your footer text change there by making the change to the "slingshot" project. That apparently was the earlier name of the Share application. The file is config/alfresco/site-webscripts/org/alfresco/components/footer/footer.get.properties

Making this footer change might whet your appetite for making other customizations to your Share application. The Surf component framework makes customization fairly easy once you get the hang of how Surf works. By being able to see your changes quickly by interactively refreshing the web scripts rather than restarting the server, your feedback comes quickly.

How to get rid of the Alfresco Labs default page footer

The web client for the open source Alfresco enterprise content management server ships with a footer that is added to every webpage via a JSP tag. The footer blends marketing and usage tracking with a copyright statement. It can be annoying to see on every page, especially if you are using the web client internally. The web client footer looks like this (from a clipped screen capture):

Alfresco page footer image

The footer says:
Supplied free of charge with no support, no certification, no maintenance,
no warranty and no indemnity by Alfresco or its Certified Partners. Click
here for support. Alfresco Software Inc. © 2005-2009 All rights reserved.
The footer includes Alfresco's logo by reading an image from the Alfresco website. This image is called a web bug, or beacon, and Alfresco can use the image request to their server to help track who is using Alfresco. The footer's underlined text links to pages on the alfresco.com website to provide warnings that the Labs version of Alfresco is not supported by Alfresco Software Inc. The right-side graphic links you to Alfresco's SourceForge download site.

Except for the web beacon image, I don't fault Alfresco Software for adding the footer to its web client. Alfresco Labs is a powerful -- free -- and flexible product. If used within an organization, it is not a bad thing that users are made aware that the product is not the supported Enterprise version. However, since I am using Alfresco on an internal website, and plan to customize the various JSP pages, displaying the warning on every one of my pages seems overkill and unnecessary. And I could do without sending tracking statistics to Alfresco Software.

Removing the global footer isn't a simple process, but it is pretty straightforward. The removal should be as simple as editing a globally included text file. Instead, those clever developers at Alfresco embedded the footer text into a fairly useful JSP tag. This <r:page> JSP tag outputs the skeletal HTML page tags, and includes HTML to pull in global scripts and cascading stylesheets. It also includes code that can log how long it took Alfresco to build the JSP page. To remove the footer from your web pages, you need either to stop using the r:page tag or recompile the page tag to set the text to what you would like, which is what the instructions below cover.

Recompiling the r:page tag that lives in the Alfresco web client JAR file isn't complicated. Assuming you already are using the Alfresco Labs 3 web application, the process should take you a half hour or less, with most of that time spent downloading and compiling code.

Overview

The steps below show you how to obtain the complete source code to Alfresco, make the necessary change to the Java page tag code, rebuild the web client JAR file, then replace the existing JAR file in your Alfresco server with the newly built one.

Prerequisites: You must have a Java compiler, a Subversion client, Apache Ant, and be able to use the command line. For Windows users without Subversion, you can install the SlikSVN client. Others can find a link to download a Subversion client from CollabNet's Tigris.org.

Instructions

Here are the steps to rebuild the Alfresco web client with the altered JSP page tag. (You should be able to cut and paste these lines onto your command line if you remove the prompt character.)
  1. Create an empty directory for the source code
    % mkdir alfresco-labs
    % cd alfresco-labs
    
  2. Use Subversion to download the Alfresco Labs source code
    % svn co svn://svn.alfresco.com/alfresco/HEAD
    
    or use the http protocol if you have internal firewall issues:
    % svn co http://svn.alfresco.com/repos/alfresco-open-mirror/alfresco/HEAD
    
    The svn command will connect to the Alfresco Subversion repository and copy each of the files in the HEAD branch into your local directory. Total download size will be about 734MB. Depending on the speed of your connection, this might be a good time to get coffee, have lunch, check your email. If the Subversion URL doesn't work, check this page to see if Alfresco has changed the repository location.

  3. Edit PageTag.java to change/remove the page footer text
    The PageTag.java file you want to edit is in the directory HEAD/root/projects/web-client/source/java/org/alfresco/web/ui/repo/tag. The commands below will edit the file in Notepad for Windows or vi for Unix/Mac users. But you also can just navigate to the folder and edit the file with any editor.

    For Windows:
    % notepad HEAD\root\projects\web-client\source\java\org\alfresco\web\ui\repo\tag\PageTag.java
    
    For Unix variants:
    $ vi HEAD/root/projects/web-client/source/java/org/alfresco/web/ui/repo/tag/PageTag.java
    
    At this point, you can decide what you want your new page footer text to say. The quickest edit is to completely remove all footer contents by changing line 115:
    private static String alfresco = null;
    
    to:
    private static String alfresco = "";
    
    Why this works is that static alfresco variable holds the contents of the page footer HTML (images and text), which is output by the overridden JSP tag library method doEndTag. If the alfresco variable is null, which it is the first time the code is run, the doEndTag method calls a private helper method, getAlfrescoButton, to populate it.

    If you want to change the footer text to something of your own choosing, or perhaps you want to retain the Alfresco copyright notice because you won't be changing the web client's look and feel, you will want either to set the alfresco variable to the text of your choosing, or edit the getAlfrescoButton method and/or some of the variables it uses to build up the footer text.

    For example, if you want to retain the copyright statement but remove the warning text, edit the ALF_COPY constant on lines 103-112 to remove the text you don't want:
    private final static String ALF_COPY  = "Supplied free of charge with " +
    "<a class='footer' href='http://www.alfresco.com/services/support/communityterms/#support'>no support</a>, " +
    "<a class='footer' href='http://www.alfresco.com/services/support/communityterms/#certification'>no certification</a>, " +
    "<a class='footer' href='http://www.alfresco.com/services/support/communityterms/#maintenance'>no maintenance</a>, " +
    "<a class='footer' href='http://www.alfresco.com/services/support/communityterms/#warranty'>no warranty</a> and " +
    "<a class='footer' href='http://www.alfresco.com/services/support/communityterms/#indemnity'>no indemnity</a> by " +
    "<a class='footer' href='http://www.alfresco.com'>Alfresco</a> or its " +
    "<a class='footer' href='http://www.alfresco.com/partners/'>Certified Partners</a>. " +
    "<a class='footer' href='http://www.alfresco.com/services/support/'>Click here for support</a>. " +
    "Alfresco Software Inc. © 2005-2009 All rights reserved.";
    
    If you want to keep some of the text but remove the web beacon image or SourceForge graphic, you will need to edit the getAlfrescoButton method or set the alfresco variable as discussed previously.

    Fortunately, the getAlfrescoButton method does not add structural HTML text to the footer, so setting the alfresco variable directly with your chosen text is a viable option. If you use HTTPS on your website and you want to use an image in your footer, have a look at the technique used in the getAlfrescoButton method to change the image URL based on the request scheme. It's a handy way to avoid having web browsers complain about an encrypted page using non-encrypted components.

    With the code edited, you are ready to rebuild Alfresco.
  4. Rebuild the web client application
    Here is where you need Ant. Use the build target to rebuild the project modules. The default target will attempt to deploy the rebuilt WAR file, so make sure to use the "build" target.

    For Windows:
    % ant -f HEAD\root\build.xml build
    
    For Unix:
    $ ant -f HEAD/root/build.xml build
    
    You will see several warnings about the use of deprecated methods and the like. But the code should build correctly.

    The Ant task should rebuild the Alfresco JAR files and the WAR file. The rebuilt alfresco-web-client.jar file is the one that contains the repository JSP tag you want to replace. The Ant task creates this JAR file in the directory HEAD/root/projects/web-client/build/dist.
  5. Copy the alfresco-web-client.jar to your Alfresco webapp
    Copy the JAR file to your exploded WAR file directory location under the WEB-INF/lib directory. If you deploy Alfresco as WAR file rather than exploded WAR, you can use the full alfresco.war file that you also will find in the HEAD/root/projects/web-client/build/dist. directory.
You're done. You should now be able to restart your application server and visit your Alfresco website:

Alfresco page without footer

Gone is the footer from this and all Alfresco web client pages.

Weighing risks of storing files in a content management system

I was cleaning out a desk drawer last weekend and found a few old 3 1/2 inch floppy disks. The discovery made me realize that in order to read the data off those disks, I would have to pull the floppy drive from an old computer and install it in a functioning computer -- and hope the new computer had the appropriate data connector, or that I could find an adapter.

The discovery of the floppies, and the realization that the data couldn't easily be read, is parallel to a situation I face today as I debate moving my document and media files to a content management system. The risk today, just like with the floppy disks, is that I might end up storing important data in a format that later becomes unreadable.

I have been contemplating moving a bulk of my personal text files, and perhaps even multimedia files, to an Alfresco CMS running on my home server in order to access my documents conveniently from anywhere. I have been using Evernote to store text notes and web clips, and I looked at Google Docs as an option. Both are great services and dead-simple to manage, but these online services don't provide some of the conveniences of Alfresco.

Alfresco is a free, open source Java web application that is slowly becoming a Swiss army knife for managing content. One of Alfresco's compelling features is the wide variety of file-access protocols it offers to manipulate documents stored in its repository. Alfresco's documents can be accessed via its web client, Java APIs, and CMIS, sure. But more interesting for my current needs is that documents stored in Alfresco also can be read, written and deleted from other computers on the LAN using a CIFS/SMB shared drive, over the web using WebDAV, using NFS, and even FTP.

Because Alfresco can expose its managed content using so many industry-standard protocols, I thought storing my files inside Alfresco would make it easier for me to access my documents no matter where I was, without adding the need for a specialized client application or web connection. I could use a CIFS shared drive at home to access documents from any of my home computers. I could access the documents securely from work using WebDAV over SSL. And I could access documents from a friend's house by logging into my home server from a web browser using Alfresco's native web application. My documents would be stored at home, but also available "in the cloud." I could even use Alfresco's feature to emulate a SharePoint server to version and share my MS Word and other Office documents from Office applications.

Making my documents this accessible would be convenient and (on the geeky side of) cool. I do have a strong concern about whether I want to risk exposing my documents to Internet hackers. But the longer term concern is will I find myself wanting to access my files someday without using Alfresco? Will my content repository one day become the equivalent of a floppy disk?

The risk of storing data in a format that later becomes unreadable is not new, and the problem grows as more of our lives become digitized. I remember a few years ago hearing Grady Booch describe his work preserving seminal software for the Computer History Museum and his labor of love, the Handbook of Software Architecture. He mentioned that software that should be preserved for historical and educational reasons is sometimes stored in once-popular paper or magnetic formats that are difficult to read today. The Library of Congress has been concerned with what digital formats it should use in order to store its electronic archives.

Alfresco stores its content as regular files, which is good. However, those files are named using globally unique identifiers rather than the original file name. The stored documents are mixed with other files used by Alfresco for versioning and other purposes in a series of numbered subdirectories. Do I want to rely on Alfresco being the required middleman to give me the files I need? Using the digital media sustainability factors used by the Library of Congress to rate digital preservation, I would rate Alfresco's storage like this, with High meaning good for sustainability:
  • Disclosure: High
    The files are stored in your native format like ext3 or NTFS. Alfresco itself is open source, and it runs under Java, which can be run on nearly any modern operating system.
  • Adoption: Low
    Despite Alfresco being powerful and free, the file organization and metadata formats are unique to Alfresco.
  • Transparency: High
    Alfresco stores files as regular files, albeit buried within its own directory organization scheme, and the file metadata is stored in a relational database of your choosing.
  • Self-documentation: Low
    Alfresco separates file contents from its metadata using a proprietary storage scheme. Reuniting the two requires Alfresco.
  • External dependencies: Medium-to-High
    Retrieving file data with its metadata requires Java, a web application server, Alfresco, and the database used to store the metadata. Since I use the open source MySQL as my database, and all other dependencies are open source, the external dependencies can be easily assembled. But it would be a pain.
  • Impact of patents: High
    I think all the technology needed to retrieve the data is unencumbered by patents.
  • Technical protection mechanisms: High
    Alfresco's files are stored on the file system without alteration, so no translation or decryption is needed.
Overall, Alfresco scores well for sustainability. The data should be retrievable for the foreseeable future by anyone properly motivated. However, its low Self-documentation and Adoption scores concern me. For example, say a fire completely destroys our home. While we run outdoors, my wife wisely remembers to grab my external backup USB drive. That hard drive -- hopefully -- contains a backup of the Alfresco repository file system and the Alfresco MySQL database containing the content repository metadata, like file name, file path, date created. Whew! My data are safe.

But, hmm, how to find that fire insurance policy while I'm at the local motel's shared lobby computer. Yes, it will be possible to find the file I need through search tools, or by opening the files one by one -- and in the case of binary image files, by changing the file extension from Alfresco's ".bin" to whatever format the file really contains so I can open it with the proper application. But getting my files out of the Alfresco repository, with the file name and directory structure with which the files are usable, will not be as easy as plugging the disk drive into someone else's computer and opening the file with a text editor. It will be in unexpected situations like this when I will have wished I had kept my files stored as regular files in regular directories and just used Samba.

That's where I am now, weighing the advantages of storing my files in a content management system versus the disadvantages and risks. I'm guessing many businesses go through this same struggle whenever they adopt a content management system for their documents. Once a company switches to a content management system, they must jump in with both feet and live with the benefits and problems of storing their documents inside an electronic vault controlled by a piece of non-standard software. At least with Alfresco, the process is reversible through its CIFS interface, and less scary because of its open source nature.

Maybe my solution will be to use Alfresco but to backup my content repository using the CIFS interface. That way, my backups are independent of Alfresco and I preserve the files with their original names and directory locations. I'd lose any extra Alfresco metadata stored with the files, any versioning, any software triggers or rules associated with the files. But I'd still enjoy Alfresco's benefits on my live file system. If you have faced and solved a similar situation when using a content management system, your comments are welcome.

Installing Sun Java JDK 6 Update 12 on Fedora 10

When I set out to install Sun's latest Java development kit on my newly upgraded Fedora 10 development box, I discovered the previous instructions I had used on Fedora 8 from the Fedora FAQ no longer cover installing the Sun JDK. The instructions now refer only to OpenJDK using the java-1.6.0-openjdk package. After a short search, I found a newer installation technique, but unfortunately had to tweak it because it didn't work with JDK 6u12.

The best instructions I found for installing the Sun JDK on Fedora were from Fedora developer Paul Howarth at www.city-fan.org/tips/SunJava6OnFedora. Paul's instructions and his modified jpackage Java 6 RPM package are fantastically helpful. He details how to custom-build Java installation RPMs by rebuilding his RPM with the Sun Microsystems Java 1.6 "bin" installer.

The only roadblock to success was that Paul built his RPM for Java 6 update 7. The RPM spec file doesn't work if you run it with Sun's latest (as of this writing) jdk-6u12-linux-i586.bin file. My first attempt to follow Paul's instructions got me this:
[tom@development Download]$ rpmbuild --rebuild java-1.6.0-sun-1.6.0.7-1.1.cf.nosrc.rpm
Installing java-1.6.0-sun-1.6.0.7-1.1.cf.nosrc.rpm
warning: InstallSourcePackage at: psm.c:246: Header V3 DSA signature: NOKEY, key ID b56a8bac
warning: user paul does not exist - using root
warning: group paul does not exist - using root
warning: user paul does not exist - using root
warning: group paul does not exist - using root
warning: user paul does not exist - using root
warning: group paul does not exist - using root
Executing(%prep): /bin/sh -e /var/tmp/rpm-tmp.W96jt0
+ umask 022
+ cd /home/tom/rpmbuild/BUILD
+ LANG=C
+ export LANG
+ unset DISPLAY
+ rm -rf /home/tom/rpmbuild/BUILD/jdk1.6.0_07
+ export MORE=10000
+ MORE=10000
+ sh /home/tom/rpmbuild/SOURCES/jdk-6u7-linux-i586.bin
sh: /home/tom/rpmbuild/SOURCES/jdk-6u7-linux-i586.bin: No such file or directory
error: Bad exit status from /var/tmp/rpm-tmp.W96jt0 (%prep)
The warnings are harmless. But as you can see, during the "prep" stage, rpmbuild is expecting the bin file to be called jdk-6u7-linux-i586.bin instead of the bin file for update 12. I optimistically hoped I might be able to get around this snag by renaming the newer file to the older name:
[tom@development Download]$ mv ~/rpmbuild/SOURCES/jdk-6u12-linux-i586.bin ~/rpmbuild/SOURCES/jdk-6u7-linux-i586.bin
But that just got me one step farther:
[tom@development Download]$ rpmbuild --rebuild java-1.6.0-sun-1.6.0.7-1.1.cf.nosrc.rpm
Installing java-1.6.0-sun-1.6.0.7-1.1.cf.nosrc.rpm
warning: InstallSourcePackage at: psm.c:246: Header V3 DSA signature: NOKEY, key ID b56a8bac
warning: user paul does not exist - using root
warning: group paul does not exist - using root
warning: user paul does not exist - using root
warning: group paul does not exist - using root
warning: user paul does not exist - using root
warning: group paul does not exist - using root
Executing(%prep): /bin/sh -e /var/tmp/rpm-tmp.1AYQKX
+ umask 022
+ cd /home/tom/rpmbuild/BUILD
+ LANG=C
+ export LANG
+ unset DISPLAY
+ rm -rf /home/tom/rpmbuild/BUILD/jdk1.6.0_07
+ export MORE=10000
+ MORE=10000
+ sh /home/tom/rpmbuild/SOURCES/jdk-6u7-linux-i586.bin
+ cd /home/tom/rpmbuild/BUILD
+ cd jdk1.6.0_07
/var/tmp/rpm-tmp.1AYQKX: line 33: cd: jdk1.6.0_07: No such file or directory
error: Bad exit status from /var/tmp/rpm-tmp.1AYQKX (%prep)
The rpmbuild was able to find and run Sun's (renamed) shell script, but then failed when it tried to switch to the non-existent jdk1.6.0_07 directory in BUILD.

To solve the problem, I had to edit the RPM "spec" file and make two small changes to account for the updated version. Then I continued with Paul's instructions, except using the modified spec file in place of directly using his RPM file. I got the idea of editing the spec file from a blog posting by Nick Lothian.

Here are my modification's to Paul's instructions,
  • Follow Paul's instructions up to and including running the rpmbuild command under the section "Build Java RPM Packages."

  • Begin Detour: After you get the error (shown above) that says "jdk-6u7-linux-i586.bin: No such file or directory," you won't have the RPM files but you will have an RPM spec file stored in ~/rpmbuild/SPECS, called java-1.6.0-sun.spec.

  • Edit this ~/rpmbuild/SPECS/java-1.6.0-sun.spec file by:
    Changing this line (line 37 in my spec file):
    %define buildver        7
    
    to say:
    %define buildver        12
    
    so the buildver is 12 instead of 7, and changing this line (line 45 in my spec file):
    %define toplevel_dir    jdk%{javaver}_0%{buildver}
    
    to say:
    %define toplevel_dir    jdk%{javaver}_%{buildver}
    
    That is, remove the "0" (zero) right before the %{buildver} variable. That second change stumped me at first because Paul apparently had to add a zero-padding in the directory name to get "07" when he was working with Update 7.

  • Run rpmbuild again by using the spec file instead of the rpm file using this command:
    [tom@development Download]$ rpmbuild -ba --rebuild ~/rpmbuild/SPECS/java-1.6.0-sun.spec
    
    This command should succeed with building new RPMs for Sun's JDK.

  • End Detour. Continue with Paul's instructions under "Remove Any Old Cruft."
Now that I have the Sun JDK installed, I'm am curious whether I can find situations where OpenJDK performs differently from Sun's JDK. Thanks to the handy "alternatives" Linux command that lets me easily switch between the different JDK versions, I'll be able to test my Java applications within both environments. After a sudo alternatives --config java, I have:
[tom@development ~]$ java -version
java version "1.6.0_12"
Java(TM) SE Runtime Environment (build 1.6.0_12-b04)
Java HotSpot(TM) Server VM (build 11.2-b01, mixed mode)
Success.

The terribly misunderstood super()

For developers new to Java, here's a tip that could make you look more like a ninja coder than colleagues who have been writing Java for years: learn how super() works within constructors.

I say this because I recently completed a yearlong project with 12 developers and found during the staffing process that about one third of the developers I interviewed, many of whom had been coding Java professionally for years, misunderstood fundamental concepts of Java object creation. Some of these smart developers would insist during the job interview that unless a Java constructor explicitly invokes super(), the parent constructor would never be called. With such an important Java language feature being so terribly misunderstood, I thought I'd dust off this blog with a reminder to those new to Java of how super() works in constructors. A related topic would be how this(...) works, but I'll leave that for another time.

Rule: You never, ever, have to call the no-argument super().

Corollary: It is impossible to instantiate an object without at least one constructor being invoked in all parent classes.

With this rule in mind, here is code to illustrate.
public class A {
public A() {
System.out.println("A says hello");
}
}
public class B extends A {
public B() {
System.out.println("Hello from B");
}
}
If you instantiate class B like this:
B myB = new B();
the console will output:
A says hello
Hello from B
Since B's constructor didn't specify a different constructor in class A by using super with an argument list, the Java runtime invoked A's no-argument constructor by default. No call to super() is needed from within B's constructor in order for the A parent class to be instantiated. In fact, there is absolutely, positively no way to create a B instance without creating an A instance first.

If I modify class B to add super():
public class B extends A {
public B() {
super();
System.out.println("Hello from B");
}
}
the output would be identical to the first version. In fact, the generated Java bytecode would be identical. When there is no explicit call to super() as the first statement in a constructor, the Java compiler implicitly adds a call to the no-argument super() to invoke the no-argument constructor of the parent class. There is never a need to add an explicit call to a no-argument super().

I think a lot of Java developers end up believing you need to call super() in order for the superclass's constructor to be called because so much Java code out there includes extraneous calls to super(). For instance, here is a constructor taken verbatim from a Hello World J2ME coding example from Research in Motion Ltd., the makers of the BlackBerry smart phones.
//create a new screen that extends MainScreen, which provides
//default standard behavior for BlackBerry applications
final class HelloWorldScreen extends MainScreen
{
public HelloWorldScreen()
{
//invoke the MainScreen constructor
super();
//add a title to the screen
LabelField title = new LabelField("HelloWorld Sample", LabelField.ELLIPSIS
| LabelField.USE_ALL_WIDTH);
setTitle(title);
//add the text "Hello World!" to the screen
add(new RichTextField("Hello World!"));
}
If I were a developer just learning Java, I would assume the call to super() is required in order to invoke the parent class's constructor. Why else would the HelloWorldScreen developer code it, and add that comment to explicitly point out the call to the parent class? I searched Krugle for open source projects using calls to the no-argument super() and found 82,347 Java files, including code from major projects like Eclipse and GlassFish. It seems many developers like explicitly invoking super().

I can see one possible reason for doing so. Perhaps there are several constructors in the parent class and the developer wants to call out that he or she is using the no-argument version. The first danger I see with adding extra code that adds no behavior to a program is the risk of adding confusion. For instance, I recently ran across code with constructors that looked something like this:
public class SpecialClass extends RegularClass {
private int x, y;
public SpecialClass() {
super();
}
public SpecialClass(int x) {
super();
this.x = x;
this.y = 0;
}
public SpecialClass(int x, int y) {
this.x = x;
this.y = y;
}
}
The first two constructors explicitly called super(), but the third constructor didn't. Was that a mistake? Did the developer mean to add a call to a different superclass constructor, like super(x, y), but forgot? If not, why did he leave off the third call to super()?

Finding small inconsistencies in code like this waste development time as the reader tracks down whether the inconsistency was the result of harmless oversight or the result of an error that is now a bug. Code that doesn't do anything, without a documented reason for being there, seems way more hazardous to understanding code than any value I can see that might be gained from "documenting" that you really meant the automatic behavior to be taken by writing extra code. Similar to seeing a class that extends java.lang.Object, I end up asking why did the developer do that.

(If you use super() regularly for documentation purposes, I would appreciate hearing your reasons.)

The second danger in adding extraneous calls to super() is that it seems to be teaching a lot of new Java developers that super() is required in order for parent constructors to be invoked. At least that certainly is my recent experience from interviewing Java developers.

The only time super is required is when it takes a non-empty argument list to invoke a constructor in the parent class that requires parameters. For example, here is my base class to represent a knight from the movie Monty Python and the Holy Grail. Note that the constructor requires an argument.
public class EnglishKnight {
private String whatISay;
public EnglishKnight(String saying) {
whatISay = saying;
}
@Override
public String toString() {
return whatISay;
}
}
This following subclass (with an error) is meant to be a certain kind of knight from the movie:
public class KnightWhoSaysNeep extends EnglishKnight {
public KnightWhoSaysNeep() {
// syntax error here.
}
}
You probably see the compile-time error. Since KnightWhoSaysNeep extends EnglishKnight, and since EnglishKnight does not contain a no-argument constructor, the KnightWhoSaysNeep class must override the implicit (and illegal) call to super() in its constructor. Here's the error from Eclipse:
Implicit super constructor EnglishKnight() is undefined.
Must explicitly invoke another constructor
To fix KnightWhoSaysNeep we need to call one of the valid constructors in the superclass. In this case, there is only one constructor in the superclass, which takes a String as an argument.
public class KnightWhoSaysNeep extends EnglishKnight {
public KnightWhoSaysNeep() {
super("Neep!");
}
}
The corrected KnightWhoSaysNeep class demonstrates the proper use of super -- one that takes a parameter to override default behavior.