Showing posts with label Code generator. Show all posts
Showing posts with label Code generator. Show all posts

Wednesday, April 28, 2010

MongoDB with Sculptor - Data Mapper

This post is part of a series of articles describing the support for MongoDB in Sculptor. This post explains how the mapping between domain objects and MongoDB data objects is performed.

When working with mongoDB Java API the DBObject plays a central role. It is like a key value Map. Values can be of most types and also collections and other DBObjects for nested documents. Sculptor also adds support for Enums and Joda date time classes.

Implementing mapping of domain objects to DBObjects is a tedious and error prone programming task that is a perfect candidate for automation. Sculptor comes in handy for this. Sculptor generates data mapper classes that converts domain objects to DBObjects. It is useful to not have to write those mappers by hand.

An early definition of a BlogPost in the blog sample might look like this in Sculptor model:

Entity BlogPost {
String slug key
String title
String body
}


From this Sculptor generates domain object class in java with appropriate getters, setters, constructors, equals, etc. Sculptor also generates a mapper for converting the BlogPost to/from DBObject. The generated code looks like this:


public DBObject toData(BlogPost from) {
if (from == null) {
return null;
}

DBObject result = new BasicDBObject();

if (from.getId() != null) {
ObjectId objectId = ObjectId.massageToObjectId(from.getId());
result.put("_id", objectId);
}

result.put("slug", from.getSlug());
result.put("title", from.getTitle());
result.put("body", from.getBody());

return result;
}


and in the other direction:


public BlogPost toDomain(DBObject from) {
if (from == null) {
return null;
}

String slug = (String) from.get("slug");

BlogPost result = new BlogPost(slug);

if (from.containsField("_id")) {
ObjectId objectId = (ObjectId) from.get("_id");
String idString = objectId.toStringMongod();
IdReflectionUtil.internalSetId(result, idString);
}

if (from.containsField("title")) {
result.setTitle((String) from.get("title"));
}
if (from.containsField("body")) {
result.setBody((String) from.get("body"));
}

return result;
}


I'm happy that I don't have to write and especially maintain that kind of code. The generation is not a one time shot. When you change the model the domain objects and the mappers are regenerated.

Domain objects can of course also have behavior, otherwise it wouldn't be a rich domain model. The behavior logic is always written manually. Separation of generated and manually written code is done by a generated base class and manually written subclass, a gap class. It is in the subclass you add methods to implement the behavior of the domain object. The subclass is also generated, but only once, it will never be overwritten by the generator.

You might need to add some hand written code to customize the mappers. That is easy. In the model you can specify that you need a subclass that you can implement yourself. This is very useful for doing data migration.

It can also be good to know that fields marked with transient are are not stored, but they are loaded if they exist in the retrieved documents. This can also be used for data migration.

By default the names in the data store are the same as the names in the Java domain objects. In case you need to use other names it is possible to define that in model with databaseTable and databaseColumn.


Entity BlogPost {
databaseTable="BlogEntries"
String slug key
String title
String body databaseColumn="content"
}


You have probably selected MongoDB for its low latency. Then you want the data mapper to be fast also. The mappers provided by Sculptor are generated code that runs at full speed. Alternative solutions using reflection or intermediate String JSON format is probably not as fast.

Tuesday, February 9, 2010

Customization of webflows

With the new 1.7 release of sculptor the possibilities to customize flows for the web client is much better.
With our Library example here is what you can do.
For example, lets say you want to implement filter functionality for the library list feature. Here is the steps to do that:

1) Specify that you want gap-files for the list library feature, so in your model.guidesign:

gui Library for Library {
Module for media {
ListTask for Library {
gap
}
}
}

Now you have a bunch of files for the feature:
  • src/generated/java/org...library/ListLibraryActionBase.java
  • src/main/java/org...library/ListLibraryAction.java -> gap
  • src/generated/java/org...library/ListLibraryForm.java
  • src/WEB-INF/generated/flows/media/listLibrary/listLibrary-base.xml
  • src/WEB-INF/generated/flows/media/listLibrary/list_include.xhtml
  • src/WEB-INF/flows/media/listLibrary/listLibrary-flow.xml -> gap
  • src/WEB-INF/flows/media/listLibrary/list.xhtml -> gap

2) Edit the media/listLibrary flow:


<?xml version="1.0" encoding="UTF-8"?>
<flow xmlns="http://www.springframework.org/schema/webflow"
xmlns:ns0="http://www.w3.org/2001/XMLSchema-instance"
ns0:schemaLocation="http://www.springframework.org/schema/webflow
http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd"
parent="media/listLibraryBase">
<view-state id="list">
<transition on="filterLibrary" to="listByFilter" />
</view-state>
<view-state id="listByFilter" model="listLibraryForm"
view="/WEB-INF/flows/media/listLibrary/list.xhtml" parent="media/listLibraryBase#list">
<on-render>
<evaluate
expression="listLibraryAction.findByFilter(flowRequestContext)" />
</on-render>
</view-state>
</flow>

3) Edit the ListLibraryAction, add the method:


public String findByFilter(RequestContext ctx) {
getRepository().clear();

List<library> allLibraries = getLibraryService().findAll(ServiceContextStore.get());
List<library> filtered = new ArrayList<library>();
String filter = ctx.getRequestParameters().get("libraryFilter");
for (Library library : allLibraries) {
if (library.getName().startsWith(filter)) {
filtered.add(library);
}
}
formObject(ctx).setAllLibraries(filtered);
return "success";
}

4) Add a simple form with a text field and a button to the media/listLibrary/list.xhtml-file:


<html xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core" xmlns:t="http://myfaces.apache.org/tomahawk"
xmlns:h="http://java.sun.com/jsf/html" xmlns:c="http://java.sun.com/jstl/core"
xmlns:a="ApplicationTaglib">
<body>
<ui:composition template="/WEB-INF/common/template.xhtml">
<ui:define name="content">
<h1>
<h:outputFormat value="#{msg['list.header']}">
<f:param
value="#{msgMedia['model.DomainObject.Library.plural']}" />
</h:outputFormat>
</h1>
<h:form xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:f="http://java.sun.com/jsf/core"
xmlns:t="http://myfaces.apache.org/tomahawk"
xmlns:h="http://java.sun.com/jsf/html" xmlns:c="http://java.sun.com/jstl/core"
xmlns:a="ApplicationTaglib">
<div>
<label for="_libraryFilter">#{msgMedia['model.DomainObject.Library.filter']}: </label>
<input type="text" value="#{requestParameters.libraryFilter}" name="libraryFilter" id="_libraryFilter"/>
<h:commandButton value="#{msgMedia['model.DomainObject.Library.filterButton']}" action="filterLibrary" />
</div>
</h:form>
<ui:include
src="/WEB-INF/generated/flows/media/listLibrary/list_include.html" />
</ui:define>
</ui:composition>
</body>
</html>

Note that the default generated table showing the list result is used by including the generated file. In case you need to modify the generated content you can either overwrite the code generation templates in WebSpecialCases.xpt or simply copy the generated file and maintain it manually

That's it.

Wednesday, October 21, 2009

Even Weird Naming Conventions are Good

The good thing with naming conventions are that they are toolable. The DBAs at my department have strong opinions about database naming. They have good reasons for that, even though I don't fully understand all of them :-)
  • Table names should be prefixed with application/component identifier.
  • Primary key id column should be prefixed with table name (without application prefix) and followed by _GID.
  • Underscore to separate words.
  • Foreign key column is concatenation of role name and primary key column name of target table, except when role and table have the same name.
Does this mean that we have to specify each and every name twice, once for Java and once for the database. Argh... NO, we are using Sculptor. With a straightforward customization I implemented these conventions in the generator and we could continue with natural (java point of view) naming and please the preferences of the DBAs without additional effort.

Naming conventions are important for software quality. Supporting the conventions with a tool is the best way to make sure that they are applied in a consistent way.

Monday, October 19, 2009

Sculptor and Agile

For us, the agile way has always been the way of getting things done. Even before there where fancy names for it we did things in a way that enabled us to deliver the right things on the right time. This hasn't change, but now we call it scrum :-)
Sculptor will help you if you want to work agile.

Things change
They do. There is nothing anybody can do about it. And when things change, there will be delays. Doing things in an agile manner helps minimize those delays. Using sculptor helps you even more.
I'll show you by an example. Lets say you have an application that is almost done for production. Testing is done. No more bugs (yeah, right). On the final demo, suddenly, one of the stake holders realize that the customer object needs another attribute, lets call it ICE (In Case of Emergency). And, of course according to the stake holder you can't go into production without it.
So, what are the changes that needs to be done?
From the bottom up:
  • Database schema
  • Persistence layer (JPA, Hibernate, etc) :
  • Domain object
  • Service layer (if we have any logic tied to the attribute)
  • Back office Client (for creating a customer object)
  • Public Client (for viewing a customer object)
Most of these changes are boiler plate code. Changing declarations etc. Without Sculptor these changes takes time and are error prone. If we assume that every change is a risk and then calculate and compare all changes we need to do with and without Sculptor we end up with the following table where every point is a code change needed to do to accomplish the last minute requirement:








Without SculptorWith Sculptor
model:01
db script:10
db migration script:11
property in domain object:10
jpa annotation för property:10
logic in service or domain object:11
back office client (create/update/list/view):70
public client (view):11
junit test (save, find):22
documentation (class diagrams):10
sum:166

So, being simple, lets say that each point is equally valued. By this assumption we reduce risk and time by 150%

This can be very valuable when change comes along. And it does, doesn't it...?

Sunday, October 4, 2009

Sculptor in the Cloud

Now you can use Sculptor to speed up and simplify development of applications running in the Google App Engine cloud.


Powered by App Engine


Let's start with a demo of how easy it is to create a new application and deploy it.



For this we are using Sculptor maven archetype for App Engine. Try it yourself:
  1. mvn archetype:generate -DarchetypeGroupId=org.fornax.cartridges -DarchetypeArtifactId=fornax-cartridges-sculptor-archetype-appengine -DarchetypeVersion=1.7.0-SNAPSHOT -DarchetypeRepository=http://www.fornax-platform.org/archiva/repository/snapshots/

  2. cd to the new directory

  3. mvn clean

  4. mvn generate-sources

  5. mvn eclipse:eclipse

  6. Import the project in Eclipse

Without any changes the new project is ready to run in the local development server or to be deployed at appspot.com. The sample in the demo is available here: http://sculptor-helloworld.appspot.com

The archetype creates a sample of of a RESTful Spring 3.0 Controller and JSP pages for the CRUD operations.




















The archetype also creates a simple sample model, from which Sculptor generates Entity, Repository and Service with the default CRUD operations; findById, findAll, save, and delete.

The model is defined in a textual DSL, with an intuitive syntax, from which Sculptor generates high quality Java code and configuration. It is not a one time shot. The application can be developed incrementally with an efficient round trip loop. The generator is part of the build process (maven).



Sculptor generates JPA mapping annotations for the domain objects defined in the design model. Relations are very limited in App Engine, since the datastore (BigTable) is not a relational database.

Owned and embedded associations are supported and mapped as ordinary JPA associations. They are specified with aggregate and BasicType in the Sculptor model.



Unowned associations are handled with id references and you must lookup the objects with findById when needed.



Services and Repositories are implemented as Spring components with @Autowired dependency injection. Spring AOP is used for error handling and transaction management.



Behavior is implemented with hand written code in subclass, separated from re-generated code in base class. In the above example the sayHello method is typically implemented in the Service by first using the generated findByKey method in the Repository. Note that the name attribute of the Planet is marked as key.

Sculptor also provides support for JUnit testing with the local App Engine environment. I will cover that in another article some day soon.

Thursday, October 1, 2009

Maven Archetype for App Engine

I have developed a maven archetype for Google App Engine projects. The generated project supports:
  • All dependency jar files are downloaded from maven repositories and copied to lib directory as required by App Engine Eclipse plugin, and local development server.

  • Eclipse project is created with mvn eclipse:eclipse. The resulting Eclipse project has the necessary settings for App Engine Eclipse plugin.

  • Entity classes are processed by DataNucleus enhancer in the build lifecycle.

  • JUnit tests with local App Engine environment can be run from maven.

Setting up all of this is not trivial and therefore I would like to share the solution and I hope you find it useful.

Eclipse Project
The maven eclipse plugin need a lot of configuration.
<build>
<outputDirectory>war/WEB-INF/classes</outputDirectory>
<plugins>
<plugin>
<artifactId>maven-eclipse-plugin</artifactId>
<version>2.5.1</version>
<configuration>
<!--
buildOutputDirectory doesn't work due to
http://jira.codehaus.org/browse/MECLIPSE-422 An workaround is the
outputDirectory at project/build level
<buildOutputDirectory>war/WEB-INF/classes</buildOutputDirectory>
-->
<testOutputDirectory>target/test-classes</testOutputDirectory>
<classpathContainers>
<classpathContainer>com.google.appengine.eclipse.core.GAE_CONTAINER</classpathContainer>
</classpathContainers>
<buildcommands>
<buildcommand>org.eclipse.jdt.core.javabuilder</buildcommand>
<buildcommand>com.google.gdt.eclipse.core.webAppProjectValidator</buildcommand>
<buildcommand>com.google.appengine.eclipse.core.enhancerbuilder</buildcommand>
<buildcommand>com.google.appengine.eclipse.core.projectValidator</buildcommand>
</buildcommands>
<additionalProjectnatures>
<projectnature>org.eclipse.jdt.core.javanature</projectnature>
<projectnature>com.google.appengine.eclipse.core.gaeNature</projectnature>
<projectnature>com.google.gdt.eclipse.core.webAppNature</projectnature>
</additionalProjectnatures>
<excludes>
<!-- Included in GAE_CONTAINER -->
<exclude>com.google.appengine:appengine-api-1.0-sdk</exclude>
<exclude>com.google.appengine:appengine-api-1.0-labs</exclude>
<exclude>com.google.appengine.orm:datanucleus-appengine</exclude>
<exclude>org.datanucleus:datanucleus-jpa</exclude>
<exclude>org.datanucleus:datanucleus-core</exclude>
<exclude>org.apache.geronimo.specs:geronimo-jpa_3.0_spec</exclude>
<exclude>org.apache.geronimo.specs:geronimo-jta_1.1_spec</exclude>
<exclude>javax.jdo:jdo2-api</exclude>
</excludes>
</configuration>
</plugin>


Some dependencies must be excluded, since they are part of GAE_CONTAINER, otherwise JUnit tests will not work when running inside Eclipse. The output directory is changed to war/WEB-INF/classes. There is a bug (MECLIPSE-422) which cause the test classes to not be separated if buildOutputDirectory is used. The local development server doesn't like the test classes. The trick is to define the output at the top build level and define testOutputDirectory.

Copy Dependencies
When running the local development server and deploying to App Engine all dependent jar files must be located in war/WEB-INF/lib. I have used the maven dependency plugin to copy the jar files during the maven clean phase.

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy</id>
<phase>clean</phase>
<goals>
<goal>copy</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
<outputDirectory>war/WEB-INF/lib</outputDirectory>
</artifactItem>
<!-- more ... -->
<artifactItem>
<groupId>com.google.appengine</groupId>
<artifactId>appengine-api-1.0-sdk</artifactId>
<version>${appengine.version}</version>
<outputDirectory>war/WEB-INF/lib</outputDirectory>
</artifactItem>
<artifactItem>
<groupId>com.google.appengine</groupId>
<artifactId>appengine-api-1.0-labs</artifactId>
<version>${appengine.version}</version>
<outputDirectory>war/WEB-INF/lib</outputDirectory>
</artifactItem>
<artifactItem>
<groupId>com.google.appengine.orm</groupId>
<artifactId>datanucleus-appengine</artifactId>
<version>1.0.3</version>
<outputDirectory>war/WEB-INF/lib</outputDirectory>
</artifactItem>
<artifactItem>
<groupId>org.datanucleus</groupId>
<artifactId>datanucleus-jpa</artifactId>
<version>1.1.5</version>
<outputDirectory>war/WEB-INF/lib</outputDirectory>
</artifactItem>
<artifactItem>
<groupId>org.datanucleus</groupId>
<artifactId>datanucleus-core</artifactId>
<version>1.1.5</version>
<outputDirectory>war/WEB-INF/lib</outputDirectory>
</artifactItem>
<artifactItem>
<groupId>org.apache.geronimo.specs</groupId>
<artifactId>geronimo-jpa_3.0_spec</artifactId>
<version>1.1.1</version>
<outputDirectory>war/WEB-INF/lib</outputDirectory>
</artifactItem>
<artifactItem>
<groupId>org.apache.geronimo.specs</groupId>
<artifactId>geronimo-jta_1.1_spec</artifactId>
<version>1.1.1</version>
<outputDirectory>war/WEB-INF/lib</outputDirectory>
</artifactItem>
<artifactItem>
<groupId>javax.jdo</groupId>
<artifactId>jdo2-api</artifactId>
<version>2.3-eb</version>
<outputDirectory>war/WEB-INF/lib</outputDirectory>
</artifactItem>
</artifactItems>
<!-- other configurations here -->
</configuration>
</execution>
</executions>
</plugin>



DataNucleus Enhancer
Running the JUnit tests from maven was a primary goal as I would like to run tests from continous build server. Th JUnit tests are using local App Engine environment with in-memory datastore. Therefore the classes must be processed by DataNucleus enhancer after ordinary compilation.

<plugin>
<groupId>org.datanucleus</groupId>
<artifactId>maven-datanucleus-plugin</artifactId>
<version>1.1.4</version>
<configuration>
<api>JPA</api>
<mappingIncludes>**/*.class</mappingIncludes>
<log4jConfiguration>${basedir}/src/main/resources/log4j.properties</log4jConfiguration>
<verbose>false</verbose>
</configuration>
<executions>
<execution>
<phase>process-classes</phase>
<goals>
<goal>enhance</goal>
</goals>
</execution>
</executions>
</plugin>


Archetype
All of this is packaged in a maven archetype. Try it like this.
  1. mvn archetype:generate -DarchetypeGroupId=org.fornax.cartridges -DarchetypeArtifactId=fornax-cartridges-sculptor-archetype-appengine -DarchetypeVersion=1.7.0-SNAPSHOT -DarchetypeRepository=http://www.fornax-platform.org/archiva/repository/snapshots/

  2. cd to the new directory

  3. mvn clean

  4. mvn eclipse:eclipse

  5. Import the project in Eclipse

As an extra bonus your new project is configured for Spring 3.0 with a sample of a RESTful controller.

Sculptor code generator tool is of course also configured and ready to be used in the new project. I will soon write another article about Sculptor's support for App Engine.

Wednesday, September 16, 2009

Customer Specific Addon: Deep Merge

This article illustrates the possibility to add your own features to the Sculptor code generator.

In my customer project we have a need to merge two object graphs. We have a persistent domain model and we receive messages from production systems when changes occur. There are several production systems sending the data in slightly different format and semantics.

We designed this as a first step that converts the production messages to new transient domain object instances.

Next step is to merge that object graph with present persistent objects.

This feels like a tedious and repetitive programming task. If done manually it will require some maintenance when we do changes.

At first I took a look at Dozer, but pretty soon things got complicated and required a lot of XML mapping files. So we gave up that idea.

At home, it struck me that we already have the tool we need. We are already using Sculptor, and it should be a simple addition to generate the merge methods in the domain objects.

Next morning I implemented it like this...

The final java code to be generated looks like this in each domain object. It copies attributes and new associated objects. It traverses existing associations.

  public void deepMerge(Item other) {
Set<Object> processed = new HashSet<Object>();
deepMerge(other, processed);
}

public void deepMerge(Item other, Set<Object> processed) {
if (processed.contains(this)) {
return;
}
processed.add(this);

if (other.getEstimatedTimeOfArrival() != null) {
setEstimatedTimeOfArrival(other.getEstimatedTimeOfArrival());
}

deepMergeShipment(other, processed);

deepMergeEvents(other, processed);

}

public void deepMergeShipment(Item other, Set<Object> processed) {
Shipment currentValue = getShipment();
if (other.getShipment() != null) {
if (currentValue == null) {
setShipment(other.getShipment());
} else {
currentValue.deepMerge(other.getShipment(), processed);
}
}
}

public void deepMergeEvents(Item other, Set<Object> processed) {
for (TrackingEvent each : other.getEvents()) {
if (getEvents().contains(each)) {
TrackingEvent currentValue = eventForKey(other.getKey());
currentValue.deepMerge(each, processed);
} else {
addEvent(each);
}
}
}

protected TrackingEvent eventForKey(Object key) {
for (TrackingEvent each : getEvents()) {
if (each.getKey().equals(key)) {
return each;
}
}
return null;
}
I developed this as a project specific addon, i.e. I invoked a code generation template from SpecialCases.xpt:
«AROUND templates::DomainObject::keyGetter FOR DomainObject»
«targetDef.proceed()»

«EXPAND templates::DeepMerge::deepMerge»
«ENDAROUND»
I started with the simple attributes.
«DEFINE deepMerge FOR DomainObject»

«EXPAND deepMergeMethod»

«ENDDEFINE»

«DEFINE deepMergeMethod FOR DomainObject»
public void deepMerge(«getDomainPackage()».«name» other) {
«EXPAND deepMergeAttribute FOREACH attributes.reject(e | !e.changeable)»
«ENDDEFINE»

«DEFINE deepMergeAttribute FOR Attribute»
if (other.«getGetAccessor()»() != null) {
set«name.toFirstUpper()»(other.«getGetAccessor()»());
}
«ENDDEFINE»
I generated and looked at the result.

I noticed that the auditable fields were included. Ok, then I can use the helper function isSystemAttribute() to skip those.
«EXPAND deepMergeAttribute FOREACH attributes
.reject(e | !e.changeable || e.isSystemAttribute())»

The tricky part is the associations and I could imagine that we would have some corner cases that wouldn't be covered by the generated pattern. Therefore I created separate methods for each association so that it will be possible to override the generated methods in gap classes and handle eventual special cases manually.

I added the templates for references. Starting with the to-one references:

«DEFINE deepMergeOneReference FOR Reference»
public void deepMerge«name.toFirstUpper()»(«from.getDomainPackage()».«from.name» other) {
«to.getDomainPackage()».«to.name» currentValue = get«name.toFirstUpper()»();
if (other.get«name.toFirstUpper()»() != null) {
if (currentValue == null) {
set«name.toFirstUpper()»(other.get«name.toFirstUpper()»());
} else {
currentValue.deepMerge(other.get«name.toFirstUpper()»());
}
}
}
«ENDDEFINE»

Continuing with the to-many case. It is a little bit more tricky, since we need to grab existing instance for collection. Added a helper method for that.
«DEFINE deepMergeManyReference FOR Reference»
public void deepMerge«name.toFirstUpper()»(«from.getDomainPackage()».«from.name» other) {
for («getTypeName()» each : other.get«name.toFirstUpper()»()) {
if (get«name.toFirstUpper()»().contains(each)) {
«to.getDomainPackage()».«to.name» currentValue = «name.singular()»ForKey(other.getKey());
currentValue.deepMerge(each);
} else {
add«name.toFirstUpper().singular()»(each);
}
}
}

protected «to.getDomainPackage()».«to.name» «name.singular()»ForKey(Object key) {
for («to.getDomainPackage()».«to.name» each : get«name.toFirstUpper()»()) {
if (each.getKey().equals(key)) {
return each;
}
}
return null;
}
«ENDDEFINE»

I was testing this using the Library sample in Sculptor. I noticed problem with extended objects, such as Book, Movie that extends Media. The getKey method is not defined in Media. However, I just ignore this for now, since we don't have that kind of association in our model, and the intention is not to develop a general purpose solution.

Not completely done yet. We have the classical case with circular references. To avoid infinite recursion I added a collection that was passed as parameter to keep track of which objects that have been processed.

All this took me 2 hours to implement, probably much less than implementing it manually in all domain objects. The big benefit is that it is much less risk of manual faults and requires zero maintenance when making changes to the domain objects.

The final template file below, in case you are interested in implementing something similar:

«IMPORT sculptormetamodel»
«EXTENSION extensions::helper»
«EXTENSION extensions::dbhelper»
«EXTENSION extensions::properties»


«DEFINE deepMerge FOR DomainObject»
«IF !isImmutable()»
«EXPAND deepMergeMethod»

«EXPAND deepMergeOneReference FOREACH references.select(r | !r.many).reject(e | !e.changeable)»
«EXPAND deepMergeManyReference FOREACH references.select(r | r.many)»
«ENDIF»
«ENDDEFINE»

«DEFINE deepMergeMethod FOR DomainObject»
public void deepMerge(«getDomainPackage()».«name» other) {
java.util.Set<Object> processed = new java.util.HashSet<Object>();
deepMerge(other, processed);
}

public void deepMerge(«getDomainPackage()».«name» other, java.util.Set<Object> processed) {
if (processed.contains(this)) {
return;
}
processed.add(this);

«EXPAND deepMergeAttribute FOREACH attributes.reject(e | !e.changeable || e.isSystemAttribute())»

«FOREACH references.reject(e | !e.changeable) AS ref»
deepMerge«ref.name.toFirstUpper()»(other, processed);
«ENDFOREACH»
}
«ENDDEFINE»



«DEFINE deepMergeAttribute FOR Attribute»
«IF isPrimitive() -»
set«name.toFirstUpper()»(other.«getGetAccessor()»());
«ELSE-»
if (other.«getGetAccessor()»() != null) {
set«name.toFirstUpper()»(other.«getGetAccessor()»());
}
«ENDIF-»
«ENDDEFINE»


«DEFINE deepMergeOneReference FOR Reference»
public void deepMerge«name.toFirstUpper()»(«from.getDomainPackage()».«from.name» other, java.util.Set<Object> processed) {
if (other.get«name.toFirstUpper()»() != null) {
«IF to.isImmutable()»
if (!other.get«name.toFirstUpper()»().equals(get«name.toFirstUpper()»())) {
set«name.toFirstUpper()»(other.get«name.toFirstUpper()»());
}
«ELSE»
«to.getDomainPackage()».«to.name» currentValue = get«name.toFirstUpper()»();
if (currentValue == null) {
set«name.toFirstUpper()»(other.get«name.toFirstUpper()»());
} else {
currentValue.deepMerge(other.get«name.toFirstUpper()»(), processed);
}
«ENDIF»
}
}
«ENDDEFINE»

«DEFINE deepMergeManyReference FOR Reference»
public void deepMerge«name.toFirstUpper()»(«from.getDomainPackage()».«from.name» other, java.util.Set<Object> processed) {
for («getTypeName()» each : other.get«name.toFirstUpper()»()) {
if (get«name.toFirstUpper()»().contains(each)) {
«IF to.isImmutable()»
// replace
remove«name.toFirstUpper().singular()»(each);
add«name.toFirstUpper().singular()»(each);
«ELSE»
«to.getDomainPackage()».«to.name» currentValue = «name.singular()»ForKey(each.getKey());
currentValue.deepMerge(each, processed);
«ENDIF»
} else {
add«name.toFirstUpper().singular()»(each);
}
}
}

protected «to.getDomainPackage()».«to.name» «name.singular()»ForKey(Object key) {
for («to.getDomainPackage()».«to.name» each : get«name.toFirstUpper()»()) {
if (each.getKey().equals(key)) {
return each;
}
}
return null;
}
«ENDDEFINE»

Thursday, August 27, 2009

Screencast: Introduction to Sculptor

During the summer I have published a series of articles that illustrate basic usage of Sculptor. They include screencasts so that you get a feeling of what it looks like when using Sculptor.

If you are totally new to Sculptor you might need to read What is Sculptor? before looking at the practical example.

The series illustrates the following, step-by-step:

  1. Jump Start - Initial creation of maven and eclipse projects. Persistent entity and CRUD GUI are created in a few minutes.


  2. The World is Changing - Adding more to the application. Quick development round trip, short feedback loop, it is not a one time shot.


  3. Testing is Simple - Testability is crucial and is of course supported.


  4. Adding Behaviour - Generated code is well separated from hand written code.


  5. Say Hello - Entity, Repository and Service are some of the available building blocks. Yes, it is real DDD-style.


  6. Introducing a Type - Developing a high quality domain model is the core of Sculptor. Small type objects are typically part of a good domain model.


  7. Refactoring - How is refactoring done when having a mix of hand written and generated code?


Tuesday, August 25, 2009

Refactoring

Sometimes I get the question "How is refactoring done when having a mix of hand written and generated code?" It is a good question, since refactoring is very important. The intention is that existing IDE refactoring tools will continue to serve you when using Sculptor.

When doing initial prototyping and you don't have any (or little) hand written code you can easily change in the model and re-generate. When you have hand written code you start with using the refactoring tools in the IDE, as you are used to. Thereafter you do corresponding change in the model and re-generate.

Eventual mistakes will normally be caught by the compiler and JUnit tests.

The following screencast illustrates how to rename Planet to Planet2.




Alternative video format (mpg)

Sculptor doesn't make it more difficult to do refactoring. Sometimes it makes refactoring easier, when the change only affects generated code.

Thursday, August 20, 2009

Introducing a Type

An important building block when creating a high quality domain model is to create small type objects. In this article we will create a Length type for the diameter of the Planet of the helloworld application.



Alternative video format (mpg)

Length is a typical Quantity with a value and unit, e.g. meter, kilometer.

In the design model it looks like this:
BasicType Length {
BigDecimal value min="0"
-@LengthUnit unit
}

enum LengthUnit {
cm, m, km
}

Entity Planet {
gap
scaffold
String name key
Long population min="0"
-@Length diameter nullable
-Set<@Moon> moons opposite planet

Repository PlanetRepository {
findByKey;
}
}


We also need to convert between different units. The behaviour expressed as a JUnit test:
public class LengthTest {

@Test
public void shouldConvertFromMeterToKilometer() {
Length length = new Length(new BigDecimal("31000"), m);
Length lengthInKilometer = length.to(km);
assertEquals(new Length(new BigDecimal("31"), km),
lengthInKilometer);
}

@Test
public void shouldConvertFromKilometerToMeter() {
Length length = new Length(new BigDecimal("44"), km);
Length lengthInMeter = length.to(m);
assertEquals(new Length(new BigDecimal("44000"), m),
lengthInMeter);
}

@Test
public void shouldNotConvertSameUnit() {
Length length = new Length(new BigDecimal("17"), km);
Length length2 = length.to(km);
assertSame(length, length2);
}
}

BasicType objects may contain business logic in the same way as other domain objects. The following screencast illustrates how to implement the conversion.



Alternative video format (mpg)

BasicType is a stored in the same table as the Domain Object referencing it. It corresponds to JPA @Embeddable.

There are a lot of cases when it is a good idea to introduce types.
  • Identifers, natural business keys. It is more readable to pass around an identifier type instead of a plain String or Integer
  • Money
  • Range
  • Quantity
I can recommend reading When to Make a Type, Martin Fowler.

Saturday, August 15, 2009

Say Hello

In previous article our Planet is capable of constructing a greeting message. This article shows how to make it possible for a client application to say hello to the Planet.



Alternative video format (mpg)

Let us create a PlanetService to expose the sayHello method to clients. We lookup the Planet from its name using the built in findByKey repository operation. In Sculptor model file this looks like this:
      Service PlanetService {
String sayHello(String planetName) throws PlanetNotFoundException;
}

Entity Planet {
gap
scaffold
String name key
Long population min="0"
Long diameter min="0" nullable
-Set<@Moon> moons opposite planet

Repository PlanetRepository {
findByKey;
}
}

All hand written java code we need to add is for testing and two trivial lines in PlanetServiceImpl:
    public String sayHello(ServiceContext ctx, String planetName)
throws PlanetNotFoundException {

Planet planet = getPlanetRepository().findByKey(planetName);
return planet.greeting();
}

Tuesday, August 11, 2009

Adding Behaviour

In previous articles we created a simple helloworld application without any hand written code. This article shows how to add some hand written business logic.



Alternative video format (mpg)

The behaviour to implement is that the Planet should be able to construct a greeting message based on its population. Test for this behaviour looks like this:
public class PlanetTest {

@Test
public void shouldSayHelloWhenHasPopulation() {
Planet earth = new Planet("Earth");
earth.setPopulation(7000000000L);
String message = earth.greeting();
assertEquals("Hello from Earth", message);
}

@Test
public void shouldBeQuietWhenNoPopulation() {
Planet pluto = new Planet("Pluto");
String message = pluto.greeting();
assertEquals("", message);
}
}
The video illustrates how to implement this.

As you see nothing special, you add the business logic in Java as usual.

Separation of generated and manually written code is done by a generated base class and manually written subclass, a gap class. It is in the subclass you add methods to implement the behavior of the Domain Object. The subclass is also generated, but only once, it will never be overwritten by the generator.

The gap class is not generated initially. When you need a gap class you specify that in the DSL with gap keyword.

Saturday, August 8, 2009

Testing is Simple

In previous articles we created a simple helloworld application without any tests. This article shows how to do integration testing of services with Sculptor.



Alternative video format (mpg)

We turn on generation of junit test and complete the failing tests from previous articles. For each Service there is a generated JUnit test class that we are encouraged to implement. It uses Spring transactional test fixtures and DbUnit.

Spring beans are injected in the test with ordinary @Autowired annotations.

public class PlanetServiceTest extends AbstractDbUnitJpaTests
implements PlanetServiceTestBase {
private PlanetService planetService;

@Autowired
public void setPlanetService(PlanetService planetService) {
this.planetService = planetService;
}

@Test
public void testFindById() throws Exception {
Planet found = planetService.findById(getServiceContext(), 1L);
assertEquals("Earth", found.getName());
}

@Test
public void testFindAll() throws Exception {
List<Planet> found = planetService.findAll(getServiceContext());
assertEquals(2, found.size());
}

@Test
public void testSave() throws Exception {
int countBefore = countRowsInTable("PLANET");
Planet planet = new Planet("Pluto");
planet.setPopulation(0L);
planetService.save(getServiceContext(), planet);
assertEquals(countBefore + 1, countRowsInTable("PLANET"));
}

@Test
public void testDelete() throws Exception {
int countBefore = countRowsInTable("PLANET");
Planet planet = planetService.findById(getServiceContext(), 2L);
planetService.delete(getServiceContext(), planet);
assertEquals(countBefore - 1, countRowsInTable("PLANET"));
}
}


The initial test data is defined in DbUnit xml file. The database is refreshed for each test method.
<?xml version="1.0" encoding="UTF-8"?>

<dataset>
<PLANET ID="1" NAME="Earth" POPULATION="7000000000" VERSION="0"/>
<PLANET ID="2" NAME="Jupiter" POPULATION="0" VERSION="0"/>
<MOON/>
</dataset>


Above tests only covers the normal cases so far and we should of course do more tests for exceptional cases and validation boundaries, such as negative population.

The tests illustrated here are kind of integration tests and you should do ordinary unit tests for domain objects and other classes of importance.

Tuesday, July 28, 2009

The World is Changing

In the previous article the simple helloworld application consisted of one single domain object, the Planet. In this article I will add some more features to the picture, including an association to the Moons of the Planet.



Alternative video format (mpg)

It is good to have a natural business key, which is used for equals and hashCode. The name of the Planet is a candidate. Let us add some properties for the population and diameter of the Planet also. Hibernate validator is supported and validations can be defined directly in the model, e.g. min="0".



We add the Moon Entity and its association to Planet.



Build and start Jetty. The changes are immediately reflected in the generated CRUD GUI.



The DSL and the code generation drives the development and is not a one time shot. The application can be developed incrementally with an efficient round trip loop.

Wednesday, July 22, 2009

Jump Start

This article illustrates the first basic steps of how to use Sculptor. We will create a simple hello world application. It will only have one simple domain object and some CRUD operations.



Alternative video format (mpg)

We start by firing off some maven archetype commands to create the business and presentation tier projects. The sculptor archetypes creates maven pom files with needed dependencies. We generate eclipse projects from those maven projects using the ordinary maven-eclipse-plugin. These projects are imported into Eclipse. You find the commands for this in the wiki.

We open the design model that is the input to the code generator. It is a textual DSL that defines the application structure. We define the Planet Entity in a Module.



We also need some CRUD operations to be able to do something with the Planet in the to be generated GUI. It is easiest to add those with the scaffold keyword. Note that the DSL editor has support for code completion, error highlight and outline view (including ctrl+O).

Now it is time to generate code, but first we will turn off one feature. Sculptor promotes writing junit tests, but for this demo we turn off that by adding a property to sculptor-generator.properties.
generate.test=false
We build the application by executing mvn install from the -parent project. The code generation is part of the maven build cycle.

We start the web application server by running mvn jetty:run from the -web project. Note that this is also a maven command, and no specific installation of an application server and database is required. Hsqldb inmemory database is used.

The generated CRUD GUI is available at http://localhost:8080/helloworld-web/



That was everything needed to create a minimal application. It illustrates how easy it is to get started. Within 15 minutes you can go from scratch to a running application, including build scripts, Eclipse projects, domain model, JPA persistence, services, Web Flow application and much more. Thereafter you can continue by evolving the design, add manual code and regenerate.

This was an extremely simple example. Sculptor can be used for much more advanced applications. The CRUD GUI is useful for administrative sections of the application or to serve as a scaffolding for your manually crafted user interface. The major strength of Sculptor is in the business tier when developing typical enterprise or web applications that benefit from a rich and persistent domain model.

Saturday, June 27, 2009

What is Sculptor?

Sculptor is an open source productivity tool that applies the concepts from Domain-Driven Design and Domain Specific Languages.

You express your design intent in a textual DSL, from which Sculptor generates high quality Java code and configuration. It is not a one time shot. The application can be developed incrementally with an efficient round trip loop.

Sculptor is useful when developing typical enterprise or web applications that benefit from a rich and persistent domain model. Sculptor also provides a sophisticated CRUD GUI for administrative sections of the application or to serve as a scaffolding for your manually created pages.

The generated code is based on well-known frameworks, such as JPA, Hibernate, Spring Framework, Spring Web Flow, JSF, RCP, and Java EE.

Product characteristics:
  • Easy to learn, intuitive syntax of the textual DSL, editor with error highlight, code completion, and outline
  • Quick development round trip, it is not a one time generation
  • Existing IDE tools, such as refactoring, code assist and debugger will continue to be of service to you
  • High quality of generated code, based on well known frameworks, best practices, and a lot of experience
  • Great extensibility and customization options
  • Easy to remove the tool, no runtime magic
Visit the Fornax Sculptor web site to learn more and try it.