Tuesday, December 16, 2008
SacGRU the Sacramento GRoovy (GRails) Users
http://groups.google.com/group/sacgru
If you are interested, please sign up so that I can notify you of plans, upcoming events, etc. I'm very excited at the prospect of bringing the Development Productivity of Groovy and the Grails platform to the Sacramento Area. Imagine the cost savings to the California State Government if they built all their Web Applications using Grails. Goodbye 42 Billion dollar debt!
Sunday, December 14, 2008
Who wants a Sacramento Groovy User's Group?
My basic plan is to meet once a month and do the following types of sessions:
- Vendors coming in pimping their products that use groovy in some way
- Developers showing off the work they're doing with Groovy
- Talks on various Groovy related technologies (Grails, Griffon, etc)
- Open discussion
- Lightning talks
- Code reviews
- Mini-code camps ("let's build something that does X")
In order to make this work primarily I need interested people. I've already got ideas for sponsors (facilities, food, etc.).
Please hit me up if you're interested in participating in any way.
Wednesday, November 19, 2008
Netbeans 6.5 Goes Gold and Gets Groovy
Just a few weeks back when it had come out with it's last milestone release, I used Netbeans 6.5 it at a client site to train them in the development of a Grails application that I had been working on for them. Just three of us were able to get it configured (along with Grails, Groovy, and Java) on all the classroom's systems in no time (well maybe an hour or so). I only had the chance to play with it for a few hours but it seemed completely useable with code highlighting, error checking, code completion and running Grails applications in the IDE itself. I would also say that the overall look and feel of Netbeans and it's responsiveness were highly improved over older versions. Older versions always felt clunky to me.
Netbeans won't be replacing Intellij for me anytime soon but I'm always glad to see competition in the marketplace and more choices for my clients.
Tuesday, November 18, 2008
Is it okay to violate OO or Relational principles when mapping objects to the database?
My opinion is that the goal of using an ORM tool such as hibernate is to simplify the job of translating business object into the relational persistence model. In the good-old-days I used to seperate my DOAs from my "rich" domain model objects. My DAOs were essentially dumb objects that were only used to make JDBC queries. The Domain model was rich with behavior and was not directly influenced by the relational model. Of course then I had an issue of how the Domain model communicated with these DAOs. The DAOs weren't really Objects in the pure sense because I had isolated them from implementing any business behavior.
An ORM purist might argue that you could change the model above by mapping those Domain objects to the relational model. This is possible and I've worked on a number of projects that did exactly that. The difficulty there is that you have what I call the "big mapping" layer that deals with the logic of mapping these domain objects to the relational model. This quickly becomes one of the most complex pieces of the application and usually only one or two experts in that particular ORM tool have any idea of what is really going on. ORM purists might argue that this is just fine and that you business developers shouldn't need to know what is going on with the persistence layer. But in practice I have found that those experts disappear and eventually whatever developes are left standing end up inheriting this mapping layer.
Maybe because of similar experience to mine, the current trend seems to be a "compromised" ORM approach. If you take the Grails framework as an example (I believe the same tends to hold true for Seam or JEE 5 applications using annotations), the mapping layer is quite simple and is held within the Domain objects themselves. Although capable of mapping in the same big mapping solution as before, typically a developer builds the Objects before the relational model and the relational model is generated from the Domain objects. I've found in practice that the Domain model tends to intrude on the Relational model, although the technology doesn't neccessitate this as Grails just uses hibernate under the covers and is certainly capable of using the big mapping approach.
I would argue that this compromise is the best of all worlds for most situations. I have found that you are able to achieve an intelligent and simple Domain model and an efficient, normalized relational model. In this compromised approach, you will almost certainly end up with Domain ojects that don't make a lot of business sense (such as List of Value objects) and with a Relational model that may have compromised certain normative principles to encourage simplification of the mapping. I argue that this isn't necessarily a bad thing. What you end up with is a much simpler application that everyone can wrap their heads around which, from my perspective, is the primary purpose anyway.
What do you all think?
Friday, November 14, 2008
Grails 1.04 Release Helps Me Finish My Project
So now I'm in a situation where I have dozens of stakeholders sitting around a table and I'm trying to explain to them that this particular FATAL exception is really no big deal and not a threat to the application. Needless to say this ends up on the issue tracker with a HIGH priority....
But Grails 1.04 comes along with a fix to the this issue. No sooner had I read about it then I upgraded. Curiously enough after I set up Intellij (Yes version 8) with the 1.04 version of Grails it notified me that I was using different versions and did I want to upgrade. After affirming the upgrade I was ready to try running the application, running all my unit, integration and webtests (functional tests), and now it has been deployed to the client test environment for the QA team. So far I'm very pleased and haven't hit any regressions (knock on wood). Great work G2One! Oops...I mean SpringSource ;).
Wednesday, November 12, 2008
Groovy and Grails support continues to improve with Intellij IDEA 8
<disclosure>I am a member of the Jetbrains "Seeder Program" which means I could potentially recieve schwag from Jetbrains. However, everything I write here is purely my opinion with no input for the Jetbrains folks</disclosure>
Back in those heady days of IDEA 7.0 all I really cared about was the Groovy and Grails support. There really wasn't a good IDE for doing Groovy and Grails. Netbeans was almost non-existent for Groovy and Grails support, the Eclipse Groovy plugin was maybe the best prior to IDEA 7.0 but by their own admission really needed more resources to make it a solid experience. This maybe wasn't so bad for Groovy because Dynamic OO languages (since Smalltalk) always seemed to be for VI fan-boys. But Netbeans (and Intellij) had done some work on a Ruby plugin that wasn't Great but was better than anything Groovy had.
Then months before IDEA 7.0 came out, the Jetbrains folks announced support for a Groovy and Grails plugin for Intellij IDEA. Oddly enough, just prior to them making the announcement I had written on the forums promising that if they would support Groovy I would pay in advance for a 7.0 license. I doubt that had a darned thing to do with their decision but seemingly out of the Ether they announce that they are creating a Groovy and Grails plugin.
I was so freakin' excited I immediately downloaded the source code and built the plugin...I remember it being quite difficult in those days and when I finally figured it out I posted again on their forums explaining to others how to do the same, although I unfortunately got the directions not quite right (oh well...).
The Groovy and Grails support in 7.0 FAR exceeded my expectations. Even at launch it was very usable and it continued to improve. I realized just recently how awesome it was when I was writing a Grails application with webservices in one IDEA window and the Client in the other and I had them both running in debug mode at one point making changes, setting break points... Just a fantastic experience!
So as not to disappoint, idea 8.0 is packed full of new features for Groovy and Grails. But I would still call them incremental. Perhaps one ommitance is a Grails Webflow visual editor. I noticed months ago they were doing something like that for Seam and it seemed (no pun intended) like they could have done the same for Grails. Maybe with Grails it is un-needed as Grails has an easy to use DSL. Maybe the visual editor would have detracted from rather than improved upon the workflow...
What has been added? Features I've already used and really like are the visual Grails plugin manager, Completion for Domain classes' dynamic methods and finders and the ability to invoke Grails scripts straight from Intellij. The funny thing is that none of these features let you do anything that you couldn't do before they just make it so much easier. Anyone who has been a long time intellij user remembers before intellij when you couldn't use a class from the Java standard libraries without having the Javadocs open because you had no idea what methods their were. Then intellij came along and we could just press ctrl-space and see a list of all methods. Now you can do that with dynamic methods added by Grails. Yes I sit around with my Grails documentation open these days trying to remember which methods I can call. No longer! In addition this means less mistakes. Likewise, the script quick invoker allows you to press ctrl-alt-g and get a text box to invoke grails scripts. Press your trusty ctl-space buttons and voila! All the commands are listed out for you.
I really appreciate the attention to detail shown by these changes and the lack of simply adding features that you already got from the command line without improving on them. My workflow has continued to improve since the first releases of the Groovy and Grails plugin for intellij. These changes are evolutionary instead of revolutionary but don't let that fool you. IDEA continues to be far and away the best IDE for Groovy and Grails development.
Sunday, November 9, 2008
Intellij IDEA 8 Released
<disclosure>I am a member of the Jetbrains "Seeder Program" which means I could potentially recieve schwag from Jetbrains. However, everything I write here is purely my opinion with no input for the Jetbrains folks</disclosure>
Jetbrains has released Intellij IDEA 8 with lots of new features. The interesting thing about IDEA (indeed any of the major Java IDEs) is that it is going to be a different tool to different developers. In the days of IDEA 2, I loved it purely for it's great Java code completion, refactoring tools, and code analysis. Later, (I want to say IDEA 4 and 5) I loved it for it's best-in-the-market HTML, XML, Struts, and Javascript editing because I'm primarily a Enterprise Java Web Application developer. I was excited about IDEA 7 because it was the best editor for Groovy and Grails which I now use daily. I can't say I care at all about it's Swing tools, Ruby tools, etc. but there are clearly developers out there that do.
But that being said, what every developer cares about is having a responsive code editor that works with them at the speed at which they think without being distracted from business issues by the technical issue of how to accomplish something with the editor. The relationship a developer has with his IDE is lke the relationship between a carpenter and his tools. If a carpenter has hand picked every tool he uses and has it right where he wants it so that he doesn't even have to think about it as it comes to his hand then his productivity and the quality of his work will be better. If he is constantly worrying about where his tools are, what tools to use for what tasks, and whether or not his tools will perform appropriately, then his job performance will be degraded. The carpenter has a system for where he keeps all his hammers, his nails, saws, etc. The goal is to get to the point where you don't even have to think about it and it is instinctual. It is also how we train in martial arts. If you have to think about blocking then you're going to get punched. If you can make blocking instinctual then you're a lot safer. A master has honed his skills to the point where even the counter attack is instinctual.
Which brings me to my point. Although I have been an avid Intellij IDEA user since IDEA 2, there has at least been the perception that the performance has degraded in certain ways. Although I think IDEA always performed well compared to other IDEs on the market, I had noticed a number of areas where the performance had become a distraction. Slow start up times were definately an issue but I can live with that. Probably the most annoying thing for me were long page rendering when opening a new page or switching from one to another. Or more commonly, when bringing IDEA back into focus after working with another tool (browser, app server console, etc.). This coding editor that always worked at the speed at which I thought was becoming senile before it's time.
It appears that perhaps the most important new feature (to me anyway) is the "reworked engine" that is supposed to improve the performance overall but particularly the startup. My experience so far has been very positive. Startup time I can testify has been HUGELY improved. IDEA parses all your project files, indexing references, creating a cache, etc. to improve performance. After starting it a couple of times the performance is going to improve because it doesn't have to rebuild all it's indexing and cache. I can report that with my completely un-scientific test of timing the startup times of IDEA 7.04 and IDEA 8 here are my results:
IDEA 7.04 - 35 seconds
IDEA 8.0 - 19 seconds
This was for the same project (a Grails project) and I started both versions a number of times to make sure it wasn't an issue of creating the indexes or cache. Keep in mind that is for one project and depending on the complexity of your project it may take more or less time. Also, the first time I started either of them took much longer, like 4 or 5 times longer, while it built the cache, indexes, ect. It also seemed to make some difference how many tabs I had opened up but not much.
Checking on other performance, going from page to page seems to be quite zippy. All the keyboard shortcuts seem to perform instantly. No problems to report so far when jumping focus from other apps to intellij. I'll definitely be working with it and commenting on my blog over the next couple of weeks if I notice an issue, but so far very positive! Great job Jetbrainers...Jetbrainsers...Jetbrainsonians...whatever...
Next I'll check out the new Groovy and Grails features and report back my experiences there.
Tuesday, September 30, 2008
Will the G1 Android Phone be an iPhone Killer?
The argument, at least as made by Java Posse proponents, is that the usability of the iPhone allows user's to do things that they may have never done before, or at least never wanted to do with any prior phone, citing examples such as browsing the internet and text-voice-mail (no idea how this works as I don't own an iPhone). The point was that it was not the features but the implementation that mattered. Prior to the iPhone there were many phones with browsers but none that implemented the browsing experience well enough to compel users to use it regularly. To be fair to Joe and the others, they were not making comparisons between the iPhone and the G1 and Joe (despite being an admitted all-things-apple-fanboy) made the point that he wanted to use it prior to passing judgement.
However, since the announcement of the G1, there have been numerous articles and blogs written about why the G1 does not measure up to the iPhone or other competitors. The most agregeous that I saw was one published by Macworld UK called10 fails for the T-Mobile G1 (Google Android phone). In the article, it discusses 10 features that the G1 doesn't have that it should. According to this article, the G1 fails to measure up because it doesn't have things like Multi-touch, desktop syncing and Exchange email. These are nice features of the iPhone that the G1 doesn't have, therefore the G1 is inferior.
I think these folks are missing the point entirely, as Steve Jobs himself said, "Inovation is not about saying yes to everything. It is about saying no to all but the most crucial features." The point is that the G1 will succeed or fail not based on what features it has but based on how well it implements the crucial features. it doesn't implement multi-touch, well maybe it doesn't need it with the keyboard and scrollball. It doesn't have Exchange email. Well for me Gmail is all I need and using Gmail and google Calendar to manage my contacts and schedule sounds like a God-send. I think Gmail may be the best application ever built, not because it lets me get my email and manage contacts online but because the implementation is freakin' fantastic! Other application I love are google maps and google calendar, all of which are supposed to have first-class implemenation in Android. I would be happy NOT to have to use any sort of desktop syncing if it's all automatically synced with my gmail.
I'm suggesting that you wait until you have held a G1 in your hands and used it for a few days before you make any judgement. Oh, and as an aside, if Apple/ATT or HTC/T-Mobile would like to send me one of their fine communication devices so that I, as an impartial observer, can write a review on it, I would be willing to bear this cross.
Monday, September 29, 2008
Do AJAX Web Applications Break the MVC Pattern?
If I understood his argument is was basically that if anything but the controller handles an event then the MVC pattern has been broken. Clearly we do this in an AJAX application where some user events would be handled by Javascript libraries and others would ultimately make requests to the application and therefore be handled by the controller.
It seems to me that the same argument could be made of any web application that uses DHTML that reacts to user input. So even graphical buttons that change state based on whether a user is hovering over it or clicking it is an event that causes a change NOT handled by the controller. Even something this trivial would break the MVC pattern.
So maybe the more important question is to what degree or in what fashion is it okay to break or modify the MVC pattern. From my point of view it makes sense for events that invoke ONLY view logic to be handled by the view. So to have sort of a "Smart View" instead of the traditional dumb document or form that is usually returned in an MVC web application. This allows for much more responsive views that will immediately respond to user input. This level of responsiveness is not generally possible in a web application where events are processed over http.
So where do you draw the line? A "Smart View" should only contain view logic. There are obvious security risks to allowing business logic to be handled in the view so obviously that road is closed. I'm curious if this has been a common theme to others implementing AJAX on their web apps.
Wednesday, May 21, 2008
Mapping a document from byte[] to BLOB in Grails on Oracle
My second thought was to change the object type in my domain object to blob:
static mapping = {
document type: 'blob'
}
But that didn't work either because it couldn't automatically convert a byte[] to a BLOB. I was able to find others who had had similar issues on the Grails nabble and a blog posting by a gentleman named Ryan using hibernate to convert byte[] to BLOB were old (by Grails standards) and I wanted to see if Grails could handle this using the new ORM DSL instead of hibernate mapping files. This is what I came up with:
import java.sql.Blob
import java.sql.SQLException
import org.hibernate.Hibernate
class Documentation extends ConfigurationItem {
int docVersion
byte[] document
Blob documentBlob
String fileType
String fileName
String fileSize
String title
String abstraction
DocumentationType documentationType
Date dateCreated
Date lastUpdated
static transients = ["document"]
static belongsTo = DocumentationType
static constraints = {
docVersion(nullable: true)
document(nullable: true)
documentBlob(nullable: true)
fileType(nullable: true)
fileName(nullable: true)
fileSize(nullable: true)
title(nullable: true)
abstraction(nullable: true)
documentationType(nullable: true)
}
static mapping = {
documentBlob type: 'blob'
}
def getDocument() {
if (documentBlob == null)
return null;
return toByteArray(getDocumentBlob());
}
def setDocument(document) {
setDocumentBlob(Hibernate.createBlob(document));
}
boolean equals(obj) {
if (this == obj) return true;
if (!obj || obj.class != this.class) return false;
return id?.equals(obj.id)
}
int hashCode() {
return id ? id.hashCode() : super.hashCode()
}
private byte[] toByteArray(Blob fromBlob) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
return toByteArrayImpl(fromBlob, baos);
} catch (SQLException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
if (baos != null) {
try {
baos.close();
} catch (IOException ex) {
}
}
}
}
private byte[] toByteArrayImpl(Blob fromBlob, ByteArrayOutputStream baos) {
byte[] buf = new byte[4000];
InputStream is = fromBlob.getBinaryStream();
try {
while (true) {
int dataSize = is.read(buf);
if (dataSize == -1)
break;
baos.write(buf, 0, dataSize);
}
} finally {
if (is != null) {
try {
is.close();
} catch (IOException ex) {
throw ex;
}
}
}
return baos.toByteArray();
}
String toString() {
return "${name}"
}
}
Notice all I had to do was create an ORM mapping for documentBlob. So far so good! It seems to be working in our testing in both HSQL and Oracle 9i. Has anyone else ran into this issue?
Thursday, May 8, 2008
Notes from the Groovy and Grails meetup
It started with Guilluime giving the state of the Union on Groovy and Grails. Then came folks from LinkedIn talking about their experiences using Grails and finally by Mathew Porter of Contegix talking about why they use Grails internally instead of Rails as they do a tremendous
amount of Rails hosting. This was very interesting and he made two big points.
- Grails scales for less cost (hardware)
- The Grails platform is more stable and they don't have to constantly upgrade the technologies, re-train developers, etc.
It was nice to meet and talk to folks there. I met Dave Klein JUG leader (from where evades me), Jeff Brown and Guillaume Laforge of G2One fame, Dierk Konig , writer of the fantastic Groovy In Action (GINA) and my beloved Webtest plugin for Grails, and my favorite Tech writer Scott Davis who is also the Chief Editor of aboutgroovy.com (correction, I had before stated that Scott Davis was the CE of groovyblogs.org which is Glen Smith). All very cool people to talk too and very passionate about groovy and grails.
Tuesday, May 6, 2008
JavaOne: Day 1 (Tuesday): Keynote
- Java is on 3 Billion Devices(mostly phones)
- Card scanners, blah blah blah, old news
- John Gage just made a joke (sorta) about not having a social security card with an electronic identifier, or as he said:
"Hi, I'm an American, please detonate the closest device." - CO2 sensor, used for mining, etc. Can be used for monitoring water usage, power usage, unsafe gas levels such as CO2 etc.]
- Gas meater can monitor 40 different times of gas
- Amazon Kindle (little electronic book) done in Java
- Very cool mash-up Demo of a multi-media app done in JavaFX on applet, desktop, and mobile
- one thing was cool is that they were able to drag the applet to the desktop and drag photos from the desktop to the application, it felt very integrated
- They were having ridiculous network issues, then again so am I
- Very cool 3D high-def stuff, very fast, very cool looking stuff
- Java 6 update 10 preview release
- JavaFX Desktop SDK EAP (July)
- JavaFX Desktop 1.0 (Fall)
- JavaFX Mobile and TV (Spring 2009)
- Glassfish V3 Kernal
- 98KB
- Modular Design
- Okay I'm a glassfish fanboy
- 48,000,000 JRE downloads a month
- Project Hydrazine
- Find
- Merge
- Deploy
- Share
- Monetize
- Project Insight
- JavaFX platform will have "Instrumentation", the ability to monitor data of users and passing data to users.
- I don't really understand what that means.
- Holy Crap, Neil Young is up on stage
- He's talking about how the Blue Ray sound is so much superior to anything prior
- He's wearing shades, what the...
- That was very cool suff, best Blue Ray Demo Ever
Wednesday, April 23, 2008
Deploying Grails and Hudson on OracleAS 10g R3
Ironically, my Grails application would not load with these settings. So I tried loading it without changing the deployment plan and Viola! It loaded with no issues.
The article did go to some good use, however. Hudson would not load without changing the setting to "Search local classes first."
Be warned, the current configuration UI is not identical to the one in the article.
Tuesday, April 22, 2008
Creating multiple Jetty Server instances
For my current project I was required to create multiple Jetty Server instances to run different applications and I found the documentation to be lacking.
It's possible that someone documented this somewhere but I couldn't find much info using the normal channels (google searches, etc).
I'm very hopeful that this will start some conversation and people will let me know if there are better ways to do this.
We're running Jetty on a Solaris 10 instance which I access using SSH. The requirements I had were:
- Jetty had to run in a shell-less mode. Meaning that when I logged out the server didn't shut down.
- I needed multiple instances of the Server to run different applications.
It wasn't required for them to run in seperate JVMs which frankly I have yet to try.
Running Jetty shell-less is as simple as using the jetty.sh start script. This is probably obvious to old-time Jetty
users but it took me some researching to find this out. Starting Jetty this way requires you to be in the $JETTY_HOME directory and sexecute the script as follows:
./bin/jetty.sh start
./bin/jetty.sh
...which will spit out all the usable commands.
Well that was easy enough, so how about starting multiple instances? Jetty uses the jetty.xml file (sometimes refered to generically as config.xml) found in the $JETTY_HOME/etc directory as the default configuration file for the server. You can override the default and pass in multiple files from the command line just by typing their path after the start command:
./bin/jetty.sh start $JETTY_HOME/env/foo.xml $JETTY_HOME/env/bar.xml
What will Jetty do with two configuration files? Well there is line very early in each config.xml file that identifies the server instance:
If the id for each file is the same then Jetty will combine both files. If they are different then Jetty creates multiple instances.
So I created a new file called jetty-cdr-instance.xml by copying the jetty.xml file. The first thing I did was change the line above:
After that all you have to do is look through the file and change ports or whatever other setting you need or want to change. I changed the port from 8080 to 8081 and changed all the other ports (not sure if I needed to):
<call name="addConnector">
<arg>
<new class="org.mortbay.jetty.nio.SelectChannelConnector">
<set name="port"><systemproperty name="jetty.port" default="8081"></systemproperty>
<set name="maxIdleTime">30000</set>
<set name="Acceptors">2</set>
<set name="statsOn">false</set>
<set name="confidentialPort">9453</set>
<set name="lowResourcesConnections">5000</set>
<set name="lowResourcesMaxIdleTime">5000</set>
</set>
</new>
</arg></call>
There are a number of other settings that can be configured but one that is important is where to find deployment archives (WAR files). By default Jetty looks into the $JETTY_HOME/webapps directory, which is fine for the default instance. But for my second instance I want to deploy different apps so I created a webapps2 directory and changed the configuration setting:
<call name="addLifeCycle">
<arg>
<new class="org.mortbay.jetty.deployer.WebAppDeployer">
<set name="contexts"><ref id="Contexts"></ref>
<set name="webAppDir"><systemproperty name="jetty.home" default=".">/webapps2</systemproperty>
<set name="parentLoaderPriority">false</set>
<set name="extract">true</set>
<set name="allowDuplicates">false</set>
<set name="defaultsDescriptor"><systemproperty name="jetty.home" default=".">/etc/webdefault.xml</systemproperty>
</set>
</set>
</set></new></arg></call>
And that's about it! So now if I feed both files at the command line I get two server instances, one running on the default port 8080 and the other 8081 and each running the applications in their respective webAppDir directory. But what if I don't want to pass the file names in the command line?
I'm sure there is a better way to do this, probably involving the jetty.conf file, but I was unable to find sufficient documentation. In the jetty.sh script you will find the lines:
#####################################################
# Run the standard server if there's nothing else to run
#####################################################
if [ -z "$CONFIGS" ]
then
CONFIGS="${JETTY_HOME}/etc/jetty.xml"
fi
I changed:
CONFIGS="${JETTY_HOME}/etc/jetty.xml"
to:
CONFIGS="${JETTY_HOME}/etc/jetty.xml ${JETTY_HOME}/etc/jetty-cdr-instance.xml"
and now by simply typing:
./bin/jetty.sh
I get both instances running.
Wednesday, April 16, 2008
CDR (Configuration Data Repository) Open Sourced!
CDR is an extremely simple Configuration Management Database. It is meant to be a starting point for an organization that is taking the initial steps toward getting their Configuration Management under control. As such, it's not meant to compete with the big CMDB vendors in features and functionality (yet).
I'm building CDR using the following:
- Grails 1.01
- Jsecurity Plugin (the latest 0.2 snapshot)
- Jasper Reports Plugin (0.7.5)
- WebTest Plugin(0.3)
Delegata released it under the new BSD license so it can pretty much be used by whoever wants it for whatever purpose they have for it. Our goal is by open sourcing this product, it will continue to bring value to or clients beyond the initial release as they will be able to take advantage of any improvements made by me or anyone else.
I am still actively developing CDR, adding tests of all sorts, making UI improvements, and enhancing functionality. The source is available at:
http://code.google.com/p/configuration-data-repository/