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.