<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-3314601722322098765</id><updated>2012-01-22T00:13:03.553+01:00</updated><category term='Roo'/><category term='springframework'/><category term='JPA'/><category term='Seminar'/><category term='Visualization'/><category term='scalability'/><category term='REST'/><category term='Release'/><category term='Comparison'/><category term='Hibernate'/><category term='Customization'/><category term='GAE'/><category term='Webflow'/><category term='Sculptor'/><category term='Internal DSL'/><category term='System design'/><category term='Java'/><category term='OO'/><category term='NoSQL'/><category term='Refactoring'/><category term='Quality'/><category term='MongoDB'/><category term='test'/><category term='Productivity'/><category term='Scala'/><category term='Success Story'/><category term='integration'/><category term='TDD'/><category term='Agile'/><category term='CQRS'/><category term='Maven'/><category term='BDD'/><category term='DSL'/><category term='Code generator'/><category term='EDA'/><category term='Akka'/><category term='JSF'/><category term='DDD'/><category term='Spring'/><category term='automation'/><category term='JEE'/><title type='text'>Sculptor Team Blog</title><subtitle type='html'>Sculptor is an open source productivity tool. You express your design intent in a textual DSL, from which Sculptor generates high quality Java code and configuration.</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://fornax-sculptor.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://fornax-sculptor.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Sculptor</name><uri>http://www.blogger.com/profile/15660077990398682094</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://4.bp.blogspot.com/_0g7ky1dgnS4/SkKTtArD_yI/AAAAAAAAAAM/ruLNzCTSrYk/S220/Sculptor.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>69</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-3314601722322098765.post-6737736825033216817</id><published>2012-01-21T23:49:00.003+01:00</published><updated>2012-01-22T00:13:03.583+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Sculptor'/><category scheme='http://www.blogger.com/atom/ns#' term='Release'/><title type='text'>Sculptor 2.1.0 Released</title><content type='html'>One very useful new feature is the generated finder methods. It is now possible to specify the query in the model file and let sculptor generate the code for the repository operations. In some cases the query can be derived from the method signature only.&lt;br /&gt;&lt;br /&gt;This will generate a query returning all persons with the specified first name.&lt;br /&gt;&lt;pre class="brush:text"&gt;&lt;br /&gt;Entity Person {&lt;br /&gt;  String firstName&lt;br /&gt;  String lastName&lt;br /&gt;  Date birthDate&lt;br /&gt;&lt;br /&gt;  PersonRepository {&lt;br /&gt;    findBy(String firstName);&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;More advanced conditions can also be specified&lt;br /&gt;&lt;pre class="brush:text"&gt;&lt;br /&gt;&lt;br /&gt;  PersonRepository {&lt;br /&gt;    findBornBefore(Date bd) condition="birthDate &lt; :bd" orderBy="firstName";&lt;br /&gt;    findByName(String name) condition="lastName i= :name or firstName i= :name";&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Sculptor generates findByCondition expressions and will report at generation time if there are any errors in the queries.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Another nice addition is the generated builder classes that provides a fluent interface to build domain objects in a manner that can be easier to work with and read.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:java"&gt;&lt;br /&gt;    Book book = book()&lt;br /&gt;        .createdBy("me")&lt;br /&gt;        .createdDate(now)&lt;br /&gt;        .title("Ender's Game")&lt;br /&gt;        .isbn("Some-ISBN")&lt;br /&gt;        .addMediaCharacter(mediaCharacter()&lt;br /&gt;            .name("Ender")&lt;br /&gt;            .build())&lt;br /&gt;        .build();&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://fornax.itemis.de/confluence/display/fornax/0.+What%27s+New+%28CSC%29#0.What%27sNew%28CSC%29-Version2.1.x"&gt;Read more about the release&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3314601722322098765-6737736825033216817?l=fornax-sculptor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fornax-sculptor.blogspot.com/feeds/6737736825033216817/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fornax-sculptor.blogspot.com/2012/01/sculptor-210-released.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/6737736825033216817'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/6737736825033216817'/><link rel='alternate' type='text/html' href='http://fornax-sculptor.blogspot.com/2012/01/sculptor-210-released.html' title='Sculptor 2.1.0 Released'/><author><name>Sculptor</name><uri>http://www.blogger.com/profile/15660077990398682094</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://4.bp.blogspot.com/_0g7ky1dgnS4/SkKTtArD_yI/AAAAAAAAAAM/ruLNzCTSrYk/S220/Sculptor.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3314601722322098765.post-3319031008389698810</id><published>2011-03-14T21:24:00.002+01:00</published><updated>2011-03-14T21:54:14.866+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Sculptor'/><title type='text'>Sculptor 2.0 is out</title><content type='html'>This is the 11th major release of Sculptor. &lt;br /&gt;&lt;br /&gt;Three new and noteworthy features:&lt;br /&gt;&lt;br /&gt;1. The new &lt;a href="http://fornax.itemis.de/confluence/x/BACB"&gt;REST support&lt;/a&gt; in Sculptor makes it very easy to expose restful services by using conventions and easy delegation to Services to reduce boilerplate coding. &lt;a href="http://static.springsource.org/spring/docs/3.0.x/reference/mvc.html"&gt;Spring MVC&lt;/a&gt; is the underlaying REST framework. &lt;br /&gt;&lt;br /&gt;2. &lt;a href="http://fornax-sculptor.blogspot.com/2011/02/mixin-composition.html"&gt;Mixin composition&lt;/a&gt; is something Java developers are missing, but with Sculptor it becomes available.&lt;br /&gt;&lt;br /&gt;3. The services can now easily be used with &lt;a href="http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/remoting.html"&gt;Spring Remoting&lt;/a&gt;. This is convenient for the &lt;a href="http://fornax.itemis.de/confluence/x/zQk"&gt;RCP client&lt;/a&gt;, but can be used for other clients also.&lt;br /&gt;&lt;br /&gt;In addition to these and several other new features we have also improved the technical quality.&lt;br /&gt;&lt;br /&gt;We have upgraded support for latest versions of most of the underlaying tools and frameworks. Most important is Xtext 1.0, Eclipse Helios, Maven 3, Spring 3.0.&lt;br /&gt;&lt;br /&gt;The logging API has been changed  from Commons Logging with Log4j to &lt;a href="http://www.slf4j.org/"&gt;SLF4J&lt;/a&gt; with &lt;a href="http://logback.qos.ch/"&gt;Logback&lt;/a&gt;. SLF4J allow many backends and also have special bridges for many (legacy) logging frameworks. SLF4J with Logback is considered to be best logging framework available.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3314601722322098765-3319031008389698810?l=fornax-sculptor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fornax-sculptor.blogspot.com/feeds/3319031008389698810/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fornax-sculptor.blogspot.com/2011/03/sculptor-20-is-out.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/3319031008389698810'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/3319031008389698810'/><link rel='alternate' type='text/html' href='http://fornax-sculptor.blogspot.com/2011/03/sculptor-20-is-out.html' title='Sculptor 2.0 is out'/><author><name>Patrik</name><uri>http://www.blogger.com/profile/11876565293495127382</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3314601722322098765.post-3191590422601808388</id><published>2011-02-03T22:00:00.003+01:00</published><updated>2011-02-03T22:21:26.801+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Scala'/><category scheme='http://www.blogger.com/atom/ns#' term='Sculptor'/><category scheme='http://www.blogger.com/atom/ns#' term='DDD'/><category scheme='http://www.blogger.com/atom/ns#' term='OO'/><title type='text'>Mixin Composition</title><content type='html'>I think one of the best features in Scala is traits. Using traits it is possible to compose small pieces of behavior and state in an elegant way. I miss traits when I use Java. To mitigate that problem I have implemented support for traits in Sculptor. This article illustrates how this tool can be used for designing good, rich, domain models with traits.&lt;br /&gt;&lt;br /&gt;Traits provide a mixin composition mechanism that is missing in Java. Similar to interfaces in Java, traits are used to define object types by specifying the signature of the supported methods. Unlike interfaces, traits can be partially implemented; i.e. it is possible to define implementations for some methods. Similar to abstract classes, but you don't have to wast your single inheritance opportunity.&lt;br /&gt;&lt;br /&gt;How many times have you stared at java code like this:&lt;br /&gt;if (p1.compareTo(p2) &lt;= 0)&lt;br /&gt;&lt;br /&gt;Why not spell it out, to make the code more readable:&lt;br /&gt;if (p1.lessThanOrEquals(p2))&lt;br /&gt;&lt;br /&gt;We seldom do that, because implementing 4 methods, lessThan, lessThanOrEquals, greaterThan and greaterThanOrEquals, in each and every class that needs to be compared is too much work. We stick to the cryptical compareTo.&lt;br /&gt;&lt;br /&gt;What if I say you only have to implement them once and then you can easily mixin the methods in each and every class that needs to be compared or sorted in some way.&lt;br /&gt;&lt;br /&gt;I hope I don't have to elaborate on how bad idea it is to try to use inheritance (base class) for these kind of reusable pieces of code.&lt;br /&gt;&lt;br /&gt;In Sculptor's textual DSL model traits are defined like this:&lt;br /&gt;&lt;pre class="brush:text"&gt;&lt;br /&gt;        Trait Ordered {&lt;br /&gt;            def boolean greaterThan(@Ordered other);&lt;br /&gt;            def boolean greaterThanOrEquals(@Ordered other);&lt;br /&gt;            def boolean lessThan(@Ordered other);&lt;br /&gt;            def boolean lessThanOrEquals(@Ordered other);&lt;br /&gt;            def abstract protected int compare(@Ordered other);&lt;br /&gt;        }&lt;br /&gt;        &lt;br /&gt;        Entity Product with Ordered {&lt;br /&gt;            String name&lt;br /&gt;        }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;After code generation this means that you can implement the 4 comparison methods once, in the Ordered trait, and they will be available in Product and other domain objects that are defined 'with Ordered'. The compare method must still be implemented in Product, because it is there you know what to compare with. The internal, generated, implementation is based on delegation, i.e. no magic.&lt;br /&gt;&lt;br /&gt;Let us define another trait, which illustrates that traits also can hold state.&lt;br /&gt;&lt;pre class="brush:text"&gt;&lt;br /&gt;        Trait PriceTag {&lt;br /&gt;            - protected Money normalPrice&lt;br /&gt;            protected double discount&lt;br /&gt;            def Money price;&lt;br /&gt;            def Money price(String currency);&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        Entity Product with PriceTag {&lt;br /&gt;            String name&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        BasicType Money with Ordered {&lt;br /&gt;            BigDecimal amount&lt;br /&gt;            String currency&lt;br /&gt;        }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This means that normalPrice and discount will be mixed in to Product. You implement the price methods in the PriceTag trait. Product and all other domain objects that are defined 'with PriceTag' will have those fields and methods.&lt;br /&gt;&lt;br /&gt;Wouldn't it be nice to be able to compare product by price? Let us do that by combining the two traits. First add the compare method in PriceTag, so that you only have to implement it at one place.&lt;br /&gt;&lt;pre class="brush:text"&gt;&lt;br /&gt;        Trait PriceTag {&lt;br /&gt;            - protected Money normalPrice&lt;br /&gt;            protected double discount&lt;br /&gt;            def Money price;&lt;br /&gt;            def Money price(String currency);&lt;br /&gt;            def protected int compare(Ordered other);&lt;br /&gt;        }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Then mixin both traits into Product&lt;br /&gt;&lt;pre class="brush:text"&gt;&lt;br /&gt;        Entity Product with Ordered with PriceTag {&lt;br /&gt;            String name&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;That's it. We have designed products with a rich price and compare interface.&lt;br /&gt;Note that compareTo is no longer implemented in Product, only in PriceTag.&lt;br /&gt;&lt;br /&gt;Try this new feature in latest &lt;a href="http://sculptor.fornax-platform.org"&gt;Sculptor&lt;/a&gt; 2.0.0-SNAPSHOT.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3314601722322098765-3191590422601808388?l=fornax-sculptor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fornax-sculptor.blogspot.com/feeds/3191590422601808388/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fornax-sculptor.blogspot.com/2011/02/mixin-composition.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/3191590422601808388'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/3191590422601808388'/><link rel='alternate' type='text/html' href='http://fornax-sculptor.blogspot.com/2011/02/mixin-composition.html' title='Mixin Composition'/><author><name>Patrik</name><uri>http://www.blogger.com/profile/11876565293495127382</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3314601722322098765.post-5104858795303078447</id><published>2011-01-27T08:32:00.005+01:00</published><updated>2011-01-27T09:01:29.176+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='EDA'/><category scheme='http://www.blogger.com/atom/ns#' term='scalability'/><title type='text'>EDA in Massive Web Sites</title><content type='html'>This is a comment to &lt;a href="http://twitter.com/h3nk3"&gt;@h3nk3&lt;/a&gt;'s latest blog post on &lt;a href="http://r-c-r.tumblr.com/post/2947120096/concurrency-parallelism-and-actors"&gt;Concurrency, Parallelism and Actors&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;I totally agree that there is no single solution for everything. Regarding massive web sites I would like to add a few things to your conclusions, which makes asynchronous event driven solutions important for web sites also.&lt;br /&gt;&lt;br /&gt;You should try to do as little as possible in the request thread, i.e. minimize latency. Threads in them selves are a scarce resource. You can do the essential validation and then hand off the rest of the work to a task queue (e.g. Flickr). You can precompute everything and cache it so that serving the requests becomes trivial (e.g. Reddit). Both these techniques can benefit from using event driven solutions in the backend.&lt;br /&gt;&lt;br /&gt;We see great improvements in the area of truly asynchronous web solutions, which means that you don't have to deliver a synchronous response to every request. The result can be pushed to the clients asynchronously. Then it becomes natural to use event driven solutions in the server. The reasons for using Actors it is not only scalability and concurrency, it is also much about using a simpler concurrency model than threads and locks.&lt;br /&gt;&lt;br /&gt;Of course it is also important to use HTTP as designed, since HTTP caching is a key ingredient in highly scalable web sites.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3314601722322098765-5104858795303078447?l=fornax-sculptor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fornax-sculptor.blogspot.com/feeds/5104858795303078447/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fornax-sculptor.blogspot.com/2011/01/eda-in-massive-web-sites.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/5104858795303078447'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/5104858795303078447'/><link rel='alternate' type='text/html' href='http://fornax-sculptor.blogspot.com/2011/01/eda-in-massive-web-sites.html' title='EDA in Massive Web Sites'/><author><name>Patrik</name><uri>http://www.blogger.com/profile/11876565293495127382</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3314601722322098765.post-2594880917831499104</id><published>2010-10-29T22:00:00.002+02:00</published><updated>2010-10-29T22:15:25.567+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Sculptor'/><category scheme='http://www.blogger.com/atom/ns#' term='EDA'/><category scheme='http://www.blogger.com/atom/ns#' term='scalability'/><category scheme='http://www.blogger.com/atom/ns#' term='CQRS'/><title type='text'>Event Sourcing with Sculptor - Snapshots</title><content type='html'>&lt;a href="http://fornax-sculptor.blogspot.com/2010/10/event-sourcing-with-sculptor.html"&gt;Yesterday I described&lt;/a&gt; how events can be used as storage mechanism. Instead of storing current state we store each change as a Domain Event. Current state is reconstructed by loading and replaying all historical events.  For Entities with a long life cycle it can be too many events. This can be solved with an optimization that is based on periodically storing a rolling snapshot of current state.  &lt;br /&gt;&lt;br /&gt;Let us look at the code in the Sculptor port of the &lt;a href="http://github.com/gregoryyoung/m-r"&gt;Simple CQRS Example&lt;/a&gt; to understand what this means in practice. &lt;br /&gt;&lt;br /&gt;When loading InventoryItem we start by applying latest snapshot, if any, and thereafter replaying the events after the snapshot. This is the code in the Repository:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:java"&gt;&lt;br /&gt;    @Override&lt;br /&gt;    public InventoryItem findByKey(String itemId)&lt;br /&gt;            throws InventoryItemNotFoundException {&lt;br /&gt;        InventoryItem result = super.findByKey(itemId);&lt;br /&gt;&lt;br /&gt;        loadFromHistory(result);&lt;br /&gt;&lt;br /&gt;        return result;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    private void loadFromHistory(InventoryItem entity) {&lt;br /&gt;        InventoryItemSnapshot snapshot = getInventoryItemSnapshotRepository()&lt;br /&gt;                .getLatestSnapshot(entity.getItemId());&lt;br /&gt;        entity.applySnapshot(snapshot);&lt;br /&gt;        long snapshotVersion = snapshot == null ? 0 : snapshot.getVersion();&lt;br /&gt;&lt;br /&gt;        List&amp;lt;InventoryItemEvent&amp;gt; history = getInventoryItemEventRepository()&lt;br /&gt;                .findAllAfter(entity.getItemId(), snapshotVersion);&lt;br /&gt;        entity.loadFromHistory(history);&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;That is how the snapshots are used when loading InventoryItem. Let us see how they are saved. We define the Snapshot Value Object for InventoryItem like this&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:text"&gt;&lt;br /&gt;        ValueObject InventoryItemSnapshot {&lt;br /&gt;            String itemId index&lt;br /&gt;            boolean activated&lt;br /&gt;            Long version&lt;br /&gt;            &lt;br /&gt;            Repository InventoryItemSnapshotRepository {&lt;br /&gt;                @InventoryItemSnapshot getLatestSnapshot(String itemId);&lt;br /&gt;                protected findByCondition(PagingParameter pagingParameter);&lt;br /&gt;                save;&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Here we store the state as explicit attributes, which is simple with Sculptor, since we got the persistence (to MongoDB or JPA) for free, but it can also be stored as a blob (ecoded as xml, protobuf, or whatever you prefer). In this example the only state is the activated flag, but it can be much more in a real application. &lt;br /&gt;&lt;br /&gt;The storage of the snapshot is triggered by a subscriber on the ordinary Domain Event flow. Simply defined as this in the model:&lt;br /&gt;&lt;pre class="brush:text"&gt;&lt;br /&gt;        Service InventoryItemSnapshotter {&lt;br /&gt;            subscribe to inventoryItemTopic&lt;br /&gt;            inject @InventoryItemRepository&lt;br /&gt;            inject @InventoryItemSnapshotRepository&lt;br /&gt;        }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The implementation calculates how many events has passed since previous snapshot by comparing version numbers. When the delta exceeds a threshold (e.g. 100 events) a snapshot is created and saved.&lt;br /&gt;&lt;pre class="brush:java"&gt;&lt;br /&gt;    public void receive(Event event) {&lt;br /&gt;        if (!(event instanceof InventoryItemEvent)) {&lt;br /&gt;            return;&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        InventoryItemEvent inventoryItemEvent = (InventoryItemEvent) event;&lt;br /&gt;        String itemId = inventoryItemEvent.getItemId();&lt;br /&gt;&lt;br /&gt;        InventoryItemSnapshot snapshot = getInventoryItemSnapshotRepository()&lt;br /&gt;                .getLatestSnapshot(itemId);&lt;br /&gt;        long snapshotVersion = snapshot == null ? 1 : snapshot.getVersion();&lt;br /&gt;        long eventVersion = inventoryItemEvent.getAggregateVersion() == null ? 1&lt;br /&gt;                : inventoryItemEvent.getAggregateVersion();&lt;br /&gt;        if (eventVersion - snapshotVersion &amp;gt;= VERSION_DELTA) {&lt;br /&gt;            takeSnapshot(itemId);&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    private void takeSnapshot(String itemId) {&lt;br /&gt;        InventoryItem item;&lt;br /&gt;        try {&lt;br /&gt;            item = getInventoryItemRepository().findByKey(itemId);&lt;br /&gt;        } catch (InventoryItemNotFoundException e) {&lt;br /&gt;            log.warn(&amp;quot;takeSnapshot failed: &amp;quot; + e.getMessage());&lt;br /&gt;            return;&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        InventoryItemSnapshot snapshot = item.createSnapshot();&lt;br /&gt;        getInventoryItemSnapshotRepository().save(snapshot);&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;By using snapshots we can dramatically improve performance for loading Entities with many historical changes. However, you can always start development without snapshotting and add it later, as a performance enhancement.&lt;br /&gt;&lt;br /&gt;Also, note that snapshots and event are immutable and therefore we have great opportunities for using caching for improving performance.&lt;br /&gt;&lt;br /&gt;The complete source code for this example is available here: &lt;a href="http://github.com/patriknw/sculptor-simplecqrs/"&gt;http://github.com/patriknw/sculptor-simplecqrs/&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3314601722322098765-2594880917831499104?l=fornax-sculptor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fornax-sculptor.blogspot.com/feeds/2594880917831499104/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fornax-sculptor.blogspot.com/2010/10/event-sourcing-with-sculptor-snapshots.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/2594880917831499104'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/2594880917831499104'/><link rel='alternate' type='text/html' href='http://fornax-sculptor.blogspot.com/2010/10/event-sourcing-with-sculptor-snapshots.html' title='Event Sourcing with Sculptor - Snapshots'/><author><name>Patrik</name><uri>http://www.blogger.com/profile/11876565293495127382</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3314601722322098765.post-3673667237291561134</id><published>2010-10-28T14:40:00.001+02:00</published><updated>2010-10-28T14:44:04.876+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Sculptor'/><category scheme='http://www.blogger.com/atom/ns#' term='EDA'/><category scheme='http://www.blogger.com/atom/ns#' term='CQRS'/><title type='text'>Event Sourcing with Sculptor</title><content type='html'>A while a go Greg Young published the &lt;a href="http://github.com/gregoryyoung/m-r"&gt;Super Simple CQRS Example&lt;/a&gt;. There are also some good documents of how to use events as storage mechanism at the &lt;a href="http://cqrs.wordpress.com/documents/"&gt;CQRS Info Site&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;I have implemented the same example with Java and Sculptor. In this post I will describe how the Event Sourcing is implemented. Even though my implementation is facilitated by using Sculptor and MongoDB the design can easily be done with other techniques, such as JPA or plain JDBC, and without Sculptor.&lt;br /&gt;&lt;br /&gt;A domain model typically holds current state of the world. Event Sourcing makes it possible to see how we got to this state. Essentially it means that we have to capture all changes to an application state as a sequence of events. These events can be used to reconstruct current and past states.&lt;br /&gt;&lt;br /&gt;The sample application is a simplified inventory of items. In line with CQRS it has a strict separation of commands and queries. Changes to the InventoryItem domain object are published as Domain Events. The InventoryItem can be modified with a few commands that I have represented as operations in a Service.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:text"&gt;&lt;br /&gt;    Service InventoryFacade {&lt;br /&gt;        inject @InventoryItemRepository&lt;br /&gt;        createInventoryItem(String itemId, String name);&lt;br /&gt;        deactivateInventoryItem(String itemId);&lt;br /&gt;        renameInventoryItem(String itemId, String newName);&lt;br /&gt;        checkInItemsToInventory(String itemId, int count);&lt;br /&gt;        removeItemsFromInventory(String itemId, int count);&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;All these commands are handled in the same way, except createInventoryItem, which is slightly different. The Service looks up the Entity and calls corresponding method on the domain object. &lt;br /&gt;&lt;pre class="brush:java"&gt;&lt;br /&gt;    public void deactivateInventoryItem(String itemId) {&lt;br /&gt;        InventoryItem item = tryGetItem(itemId);&lt;br /&gt;        item.deactivate();&lt;br /&gt;        getInventoryItemRepository().save(item);&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The Entity performs validation and creates Domain Event for state changes.&lt;br /&gt;&lt;pre class="brush:java"&gt;&lt;br /&gt;    public void deactivate() {&lt;br /&gt;        if (!isActivated())&lt;br /&gt;            throw new IllegalStateException("already deactivated");&lt;br /&gt;        applyChange(new InventoryItemDeactivated(new Date(), getItemId()));&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;In this case InventoryItem has a flag (state) indicating that the item is activated or not. Note that this state is not changed directly, but it is changed later as a result of the InventoryItemDeactivated event. All changes are done with Domain Events, which is important, because we don't save current state, we only save the Domain Events. The activated flag is not stored explicitly.&lt;br /&gt;&lt;br /&gt;The DomainEvent is applied and added to a list of changes, which later will be stored and published.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:java"&gt;&lt;br /&gt;    private void applyChange(InventoryItemEvent event) {&lt;br /&gt;        applyChange(event, true);&lt;br /&gt;    }&lt;br /&gt;    &lt;br /&gt;    private void applyChange(InventoryItemEvent event, boolean isNew) {&lt;br /&gt;        DynamicMethodDispatcher.dispatch(this, event, &amp;quot;apply&amp;quot;);&lt;br /&gt;        if (isNew) {&lt;br /&gt;            changes.add(event);&lt;br /&gt;        } else {&lt;br /&gt;            setVersion(event.getAggregateVersion());&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public void apply(InventoryItemDeactivated event) {&lt;br /&gt;        setActivated(false);&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;When saving, the InventoryItem instance is saved, but only as a key and version. The version is used for detecting  concurrent modifications (optimistic locking). Additionally, the changes, the Domain Events are stored. The version number of InventoryItem is stored in Each Domain Event. An additional sequence number is also used to ensure the correct order of the events.&lt;br /&gt;&lt;br /&gt;We need to override the default save method in the Repository to handle these sequence numbers and also save the events.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:java"&gt;&lt;br /&gt;    @Override&lt;br /&gt;    public InventoryItem save(InventoryItem entity) {&lt;br /&gt;        InventoryItem saved = super.save(entity);&lt;br /&gt;&lt;br /&gt;        List&amp;lt;InventoryItemEvent&amp;gt; changes = entity.getUncommittedChanges();&lt;br /&gt;        changes = applyVersionToChanges(changes, saved.getVersion());&lt;br /&gt;        for (InventoryItemEvent each : changes) {&lt;br /&gt;            getInventoryItemEventRepository().save(each);&lt;br /&gt;        }&lt;br /&gt;        entity.markChangesAsCommitted();&lt;br /&gt;&lt;br /&gt;        return saved;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    private List&amp;lt;InventoryItemEvent&amp;gt; applyVersionToChanges(&lt;br /&gt;            List&amp;lt;InventoryItemEvent&amp;gt; changes, long version) {&lt;br /&gt;        List&amp;lt;InventoryItemEvent&amp;gt; result = new ArrayList&amp;lt;InventoryItemEvent&amp;gt;();&lt;br /&gt;        long sequence = version * 1000;&lt;br /&gt;        for (InventoryItemEvent each : changes) {&lt;br /&gt;            result.add(each.withAggregateVersion(version).withChangeSequence(&lt;br /&gt;                    sequence));&lt;br /&gt;            sequence++;&lt;br /&gt;        }&lt;br /&gt;        return result;&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;When saving the Domain Events they are also published to a topic, which the read side subscribes on. That is handled with the publish/subscribe mechanism in Sculptor. In the model we simply need to specifiy publish on the save method.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:text"&gt;&lt;br /&gt;        abstract DomainEvent InventoryItemEvent {&lt;br /&gt;            persistent&lt;br /&gt;            String itemId index&lt;br /&gt;            Long aggregateVersion nullable&lt;br /&gt;            Long changeSequence nullable&lt;br /&gt;            &lt;br /&gt;            Repository InventoryItemEventRepository { &lt;br /&gt;                save publish to inventoryItemTopic;&lt;br /&gt;                List&amp;lt;@InventoryItemEvent&amp;gt; findAllForItem(String itemId);&lt;br /&gt;                protected findByCondition;&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        DomainEvent InventoryItemDeactivated extends @InventoryItemEvent {&lt;br /&gt;        }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;In the read side we add subscribers to this topic.&lt;br /&gt;&lt;pre class="brush:text"&gt;&lt;br /&gt;    Service InventoryListView {&lt;br /&gt;        subscribe to inventoryItemTopic&lt;br /&gt;        inject @InventoryItemListRepository&lt;br /&gt;    }&lt;br /&gt;    &lt;br /&gt;    Service InventoryItemDetailView {&lt;br /&gt;        subscribe to inventoryItemTopic&lt;br /&gt;        inject @InventoryItemDetailsRepository&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Alright, then we are almost done. One more thing though, when retrieving a InventoryItem we must replay all events to recreate current state. We do that by overriding the default findByKey method in the Repository.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:java"&gt;&lt;br /&gt;    @Override&lt;br /&gt;    public InventoryItem findByKey(String itemId)&lt;br /&gt;            throws InventoryItemNotFoundException {&lt;br /&gt;        InventoryItem result = super.findByKey(itemId);&lt;br /&gt;&lt;br /&gt;        loadFromHistory(result);&lt;br /&gt;&lt;br /&gt;        return result;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    private void loadFromHistory(InventoryItem entity) {&lt;br /&gt;        List&amp;lt;InventoryItemEvent&amp;gt; history = getInventoryItemEventRepository()&lt;br /&gt;                .findAllForItem(entity.getItemId());&lt;br /&gt;        entity.loadFromHistory(history);&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;To retrieve all events we use a simple query in the InventoryItemEventRepository.&lt;br /&gt;&lt;pre class="brush:java"&gt;&lt;br /&gt;    public List&amp;lt;InventoryItemEvent&amp;gt; findAllForItem(String itemId) {&lt;br /&gt;        List&amp;lt;ConditionalCriteria&amp;gt; criteria = criteriaFor(&lt;br /&gt;                InventoryItemEvent.class).withProperty(itemId()).eq(itemId)&lt;br /&gt;                .orderBy(changeSequence()).build();&lt;br /&gt;        return findByCondition(criteria);&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The loaded events are applied to the InventoryItem Domain Object.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:java"&gt;&lt;br /&gt;    public void loadFromHistory(List&amp;lt;InventoryItemEvent&amp;gt; history) {&lt;br /&gt;        for (InventoryItemEvent each : history) {&lt;br /&gt;            applyChange(each, false);&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    private void applyChange(InventoryItemEvent event, boolean isNew) {&lt;br /&gt;        DynamicMethodDispatcher.dispatch(this, event, &amp;quot;apply&amp;quot;);&lt;br /&gt;        if (isNew) {&lt;br /&gt;            changes.add(event);&lt;br /&gt;        } else {&lt;br /&gt;            setVersion(event.getAggregateVersion());&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public void apply(InventoryItemCreated event) {&lt;br /&gt;        setActivated(true);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public void apply(InventoryItemDeactivated event) {&lt;br /&gt;        setActivated(false);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public void apply(Object other) {&lt;br /&gt;        // ignore&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;In this example we have used a naive approach when loading the InventoryItem by replaying all historical events. For Entities with a long life cycle it can be too many events. Then we can use a snapshot technique, which I will describe in a separate blog post.&lt;br /&gt;&lt;br /&gt;The complete source code for this example can be found here: &lt;a href="http://github.com/patriknw/sculptor-simplecqrs/tree/event_sourcing_without_snapshots"&gt;http://github.com/patriknw/sculptor-simplecqrs/tree/event_sourcing_without_snapshots&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3314601722322098765-3673667237291561134?l=fornax-sculptor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fornax-sculptor.blogspot.com/feeds/3673667237291561134/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fornax-sculptor.blogspot.com/2010/10/event-sourcing-with-sculptor.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/3673667237291561134'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/3673667237291561134'/><link rel='alternate' type='text/html' href='http://fornax-sculptor.blogspot.com/2010/10/event-sourcing-with-sculptor.html' title='Event Sourcing with Sculptor'/><author><name>Patrik</name><uri>http://www.blogger.com/profile/11876565293495127382</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3314601722322098765.post-2489424932413474765</id><published>2010-10-21T17:27:00.002+02:00</published><updated>2010-10-21T17:32:52.646+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Sculptor'/><category scheme='http://www.blogger.com/atom/ns#' term='MongoDB'/><title type='text'>Switching an existing app to MongoDB</title><content type='html'>Ron has switched project from using Hibernate/MySQL to MongoDB and wrote notes on the changes and gotchas. &lt;br /&gt;Read it: &lt;a href="http://rpstechnologies.net/ron/blog/2010/09/fornax-sculptor-dsl-tool-switching-an-existing-app-to-mongodb/"&gt;Switching an existing app to MongoDB&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3314601722322098765-2489424932413474765?l=fornax-sculptor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fornax-sculptor.blogspot.com/feeds/2489424932413474765/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fornax-sculptor.blogspot.com/2010/10/switching-existing-app-to-mongodb.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/2489424932413474765'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/2489424932413474765'/><link rel='alternate' type='text/html' href='http://fornax-sculptor.blogspot.com/2010/10/switching-existing-app-to-mongodb.html' title='Switching an existing app to MongoDB'/><author><name>Sculptor</name><uri>http://www.blogger.com/profile/15660077990398682094</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://4.bp.blogspot.com/_0g7ky1dgnS4/SkKTtArD_yI/AAAAAAAAAAM/ruLNzCTSrYk/S220/Sculptor.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3314601722322098765.post-4207052381058948361</id><published>2010-09-18T09:36:00.001+02:00</published><updated>2010-09-18T09:41:59.151+02:00</updated><title type='text'>Photo from San Francisco</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_qr2e_ywa0-I/TJRs4ybXt0I/AAAAAAAABTg/QNjnzcTIHvY/s1600/DSC00928.JPG"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 400px; height: 69px;" src="http://1.bp.blogspot.com/_qr2e_ywa0-I/TJRs4ybXt0I/AAAAAAAABTg/QNjnzcTIHvY/s400/DSC00928.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5518155166300747586" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://kthoms.wordpress.com/"&gt;Karsten&lt;/a&gt; saw this in San Francisco :-)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3314601722322098765-4207052381058948361?l=fornax-sculptor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fornax-sculptor.blogspot.com/feeds/4207052381058948361/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fornax-sculptor.blogspot.com/2010/09/photo-from-san-francisco.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/4207052381058948361'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/4207052381058948361'/><link rel='alternate' type='text/html' href='http://fornax-sculptor.blogspot.com/2010/09/photo-from-san-francisco.html' title='Photo from San Francisco'/><author><name>Patrik</name><uri>http://www.blogger.com/profile/11876565293495127382</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_qr2e_ywa0-I/TJRs4ybXt0I/AAAAAAAABTg/QNjnzcTIHvY/s72-c/DSC00928.JPG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3314601722322098765.post-5031883624207744795</id><published>2010-09-09T21:00:00.000+02:00</published><updated>2010-09-09T21:08:08.075+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Sculptor'/><category scheme='http://www.blogger.com/atom/ns#' term='EDA'/><category scheme='http://www.blogger.com/atom/ns#' term='CQRS'/><title type='text'>EDA CQRS Betting Sample</title><content type='html'>For todays &lt;a href="http://jwsdsl09sep.eventbrite.com/"&gt;Xtext/Sculptor&lt;/a&gt; seminar we have prepared a new example that illustrates some of the new event-driven features in Sculptor. It highlights a good case for using an architecture in line with &lt;a href="http://cqrs.wordpress.com/"&gt;Command and Query Responsibility Segregation&lt;/a&gt; (CQRS) pattern.&lt;br /&gt;&lt;br /&gt;Consider an online betting system. It receives a massive amount of bets. A simple model for that could be:&lt;br /&gt;&lt;pre class="brush:text"&gt;&lt;br /&gt;        ValueObject Bet {&lt;br /&gt;            String betOfferId&lt;br /&gt;            String customerId&lt;br /&gt;            Double amount&lt;br /&gt;            &lt;br /&gt;            Repository BetRepository {&lt;br /&gt;                save;&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;        &lt;br /&gt;        Service BettingEngine {&lt;br /&gt;            inject @BetRepository&lt;br /&gt;            &lt;br /&gt;            placeBet(@Bet bet);&lt;br /&gt;        }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The BettingEngine process the Bet and stores the information.&lt;br /&gt;&lt;br /&gt;Questions pop up:&lt;ul&gt;&lt;br /&gt;&lt;li&gt;How many bets have been done by customer A?&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Which customers place the higest bets, on average?&lt;/li&gt;&lt;br /&gt;&lt;li&gt;What are the top 10 high stakes?&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;It would be rather easy to develop support for those kind of queries in the master betting engine domain, but problems will soon bubble up.&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Poor performance - the structure of the domain model is not optimized for all types of queries.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Not scalable - single centralized database will become a bottleneck.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Hard to change - too much functionality in one monolithic system.&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;Command-Query Responsibility Segregation (CQRS) comes to the rescue. Simplified it is about separating commands (that change the data) from the queries (that read the data). Separate subsystems take care of answering queries (reporting) and the domain for processing and storing updates can stay focused. The result of the commands are published to query subsystems, each optimized for its purpose.&lt;br /&gt;&lt;br /&gt;Back to the betting sample. We are aiming for a design as illustrated in next drawing. Command side on the left (green) and Query side on the right (blue).&lt;br /&gt; &lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_qr2e_ywa0-I/TIfiYIUg0GI/AAAAAAAABTQ/kpjVLq_Qa-8/s1600/betting_demo.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 400px; height: 263px;" src="http://2.bp.blogspot.com/_qr2e_ywa0-I/TIfiYIUg0GI/AAAAAAAABTQ/kpjVLq_Qa-8/s400/betting_demo.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5514625172916719714" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;We publish domain events when bets are placed. There are several ways to do that in Sculptor, but one easy way is to mark a Service operation with the publish keyword in the model.&lt;br /&gt;&lt;pre class="brush:text"&gt;&lt;br /&gt;        DomainEvent BetPlaced {&lt;br /&gt;            - Bet bet&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        Service BettingPublisher {&lt;br /&gt;            publishEvent(@BetPlaced betEvent) publish to jms:topic:bet; &lt;br /&gt;        }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Then, in the hand-written java code simply invoke this method. In this case a one-liner in BettingEngine.&lt;br /&gt;&lt;br /&gt;That's all for the command side, now let us take a look at the query side. We define a new module or even better a completely new business component.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:text"&gt;&lt;br /&gt;   Module customer {&lt;br /&gt;        Consumer BettingConsumer {&lt;br /&gt;            inject @CustomerStatisticsRepository&lt;br /&gt;            subscribe to jms:topic:bet&lt;br /&gt;        }&lt;br /&gt;        &lt;br /&gt;        Service BettingQueryService {&lt;br /&gt;            getHighBetters =&amp;gt; CustomerStatisticsRepository.findHighAverageCustomers; &lt;br /&gt;        }&lt;br /&gt;   &lt;br /&gt;        Entity CustomerStatistics {&lt;br /&gt;            gap&lt;br /&gt;            String customerId key&lt;br /&gt;            int numberOfBets&lt;br /&gt;            double averageAmount index&lt;br /&gt;            &lt;br /&gt;            Repository CustomerStatisticsRepository { &lt;br /&gt;                findByKey;&lt;br /&gt;                save;&lt;br /&gt;                List&amp;lt;@CustomerStatistics&amp;gt; findHighAverageCustomers(double limit);&lt;br /&gt;                protected findByCondition;&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;   }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;We receive the events published from the betting engine by defining the subscribe keyword in the BettingConsumer.&lt;br /&gt;&lt;br /&gt;In the query side we use domain objects that are optimized for the views and reports that are needed. We denormalize the data to minimize the number of joins needed when retrieving the data. Data is calculated ahead of time. In this case we calculate the average amount for the CustomerStatistics.&lt;br /&gt;&lt;br /&gt;Since we are using publish/subscribe via a message bus, it might take a while until the query side is updated with the latest data, but that is not a problem. Most systems can be eventually consistent on the query side.&lt;br /&gt;&lt;br /&gt;The full source code for the example is available here: &lt;a href="http://github.com/patriknw/sculptor-betting"&gt;http://github.com/patriknw/sculptor-betting&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;In the example we have chosen MongoDB as persistence store, Camel together with ActiveMQ as event bus. It is a matter of simple configuration to use something else, such as Oracle with JPA/Hibernate and Spring Integration for the event bus. &lt;br /&gt;&lt;br /&gt;I was pretty impressed myself when I counted the number of lines of code that was required to implement this example. 70 lines! 40 in the model and 30 lines of hand written java code. User interface not counted.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3314601722322098765-5031883624207744795?l=fornax-sculptor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fornax-sculptor.blogspot.com/feeds/5031883624207744795/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fornax-sculptor.blogspot.com/2010/09/eda-cqrs-betting-sample.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/5031883624207744795'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/5031883624207744795'/><link rel='alternate' type='text/html' href='http://fornax-sculptor.blogspot.com/2010/09/eda-cqrs-betting-sample.html' title='EDA CQRS Betting Sample'/><author><name>Patrik</name><uri>http://www.blogger.com/profile/11876565293495127382</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_qr2e_ywa0-I/TIfiYIUg0GI/AAAAAAAABTQ/kpjVLq_Qa-8/s72-c/betting_demo.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3314601722322098765.post-8919668119372565637</id><published>2010-09-09T20:50:00.003+02:00</published><updated>2010-09-09T21:07:00.791+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Sculptor'/><title type='text'>Sculptor Presentation</title><content type='html'>Today we had a Sculptor presentation as part of the seminar 'Developing Domain-Specific Languages with Xtext'. The slides from this presentation is available at slideshare: &lt;a href="http://www.slideshare.net/patriknw/sculptor"&gt;http://www.slideshare.net/patriknw/sculptor&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3314601722322098765-8919668119372565637?l=fornax-sculptor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fornax-sculptor.blogspot.com/feeds/8919668119372565637/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fornax-sculptor.blogspot.com/2010/09/sculptor-presentation.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/8919668119372565637'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/8919668119372565637'/><link rel='alternate' type='text/html' href='http://fornax-sculptor.blogspot.com/2010/09/sculptor-presentation.html' title='Sculptor Presentation'/><author><name>Patrik</name><uri>http://www.blogger.com/profile/11876565293495127382</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3314601722322098765.post-2738036158317730000</id><published>2010-09-02T08:28:00.003+02:00</published><updated>2010-09-02T08:38:27.251+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Sculptor'/><category scheme='http://www.blogger.com/atom/ns#' term='Seminar'/><category scheme='http://www.blogger.com/atom/ns#' term='DSL'/><title type='text'>Free Seminar: Developing DSLs with Xtext</title><content type='html'>Next Thursday, September 09, in Stockholm, we  will talk about Sculptor in the context of how to develop textual Domain-Specific Languages with Xtext. In this talk Sven Efftinge, original creator of Xtext, will present Xtext. I'm looking forward to listen to Sven's presentation.&lt;br /&gt;&lt;br /&gt;More information and registration here: &lt;a href="http://jwsdsl09sep.eventbrite.com/"&gt;http://jwsdsl09sep.eventbrite.com/&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3314601722322098765-2738036158317730000?l=fornax-sculptor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fornax-sculptor.blogspot.com/feeds/2738036158317730000/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fornax-sculptor.blogspot.com/2010/09/free-seminar-developing-dsls-with-xtext.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/2738036158317730000'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/2738036158317730000'/><link rel='alternate' type='text/html' href='http://fornax-sculptor.blogspot.com/2010/09/free-seminar-developing-dsls-with-xtext.html' title='Free Seminar: Developing DSLs with Xtext'/><author><name>Sculptor</name><uri>http://www.blogger.com/profile/15660077990398682094</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://4.bp.blogspot.com/_0g7ky1dgnS4/SkKTtArD_yI/AAAAAAAAAAM/ruLNzCTSrYk/S220/Sculptor.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3314601722322098765.post-3608473173118270507</id><published>2010-08-26T10:00:00.001+02:00</published><updated>2010-08-26T10:11:34.614+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Sculptor'/><category scheme='http://www.blogger.com/atom/ns#' term='EDA'/><category scheme='http://www.blogger.com/atom/ns#' term='Akka'/><category scheme='http://www.blogger.com/atom/ns#' term='scalability'/><title type='text'>EDA Akka as EventBus</title><content type='html'>Some time since &lt;a href="http://fornax-sculptor.blogspot.com/2010/08/eda-events-over-system-boundaries-with.html"&gt;last entry&lt;/a&gt; in the EDA-sequence, but here we are again. Today we are going to do something really interesting. A friend and colleague of us, &lt;a href="http://jonasboner.com/"&gt;Jonas Bonér&lt;/a&gt;, is the creator of a super interesting framework called &lt;a href="http://akkasource.org/"&gt;Akka&lt;/a&gt;.&lt;br /&gt;Akka is an event driven platform for constructing highly scalable and fault tolerant applications. It is built with &lt;a href="http://www.scala-lang.org/"&gt;Scala&lt;/a&gt;, but also have a rich API for java. It follows the &lt;a href="http://en.wikipedia.org/wiki/Actor_model"&gt;Actor Model&lt;/a&gt; and together with &lt;a href="http://en.wikipedia.org/wiki/Software_transactional_memory"&gt;Software Transactional Memory&lt;/a&gt; (STM), it raises the abstraction level and provides an easy to use tool for building highly concurrent applications.&lt;div&gt;So, today we are going to take advantage of the java API in Akka to do our own EventBus implementation.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;First, update your pom with the stuff needed for Akka (repo's and dependency):&lt;/div&gt;&lt;pre class="brush:xml"&gt;&amp;lt;repository&amp;gt;&lt;br /&gt;&amp;lt;id&amp;gt;Akka&amp;lt;/id&amp;gt;&lt;br /&gt;&amp;lt;name&amp;gt;Akka Maven2 Repository&amp;lt;/name&amp;gt;&lt;br /&gt;&amp;lt;url&amp;gt;http://www.scalablesolutions.se/akka/repository/ &amp;lt;/url&amp;gt;&lt;br /&gt;&amp;lt;/repository&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;repository&amp;gt;&lt;br /&gt;&amp;lt;id&amp;gt;Multiverse&amp;lt;/id&amp;gt;&lt;br /&gt;&amp;lt;name&amp;gt;Multiverse Maven2 Repository&amp;lt;/name&amp;gt;&lt;br /&gt;&amp;lt;url&amp;gt;http://multiverse.googlecode.com/svn/maven-repository/releases/&amp;lt;/url&amp;gt;&lt;br /&gt;&amp;lt;/repository&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;repository&amp;gt;&lt;br /&gt;&amp;lt;id&amp;gt;GuiceyFruit&amp;lt;/id&amp;gt;&lt;br /&gt;&amp;lt;name&amp;gt;GuiceyFruit Maven2 Repository&amp;lt;/name&amp;gt;&lt;br /&gt;&amp;lt;url&amp;gt;http://guiceyfruit.googlecode.com/svn/repo/releases/ &amp;lt;/url&amp;gt;&lt;br /&gt;&amp;lt;/repository&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;repository&amp;gt;&lt;br /&gt;&amp;lt;id&amp;gt;JBoss&amp;lt;/id&amp;gt;&lt;br /&gt;&amp;lt;name&amp;gt;JBoss Maven2 Repository&amp;lt;/name&amp;gt;&lt;br /&gt;&amp;lt;url&amp;gt;https://repository.jboss.org/nexus/content/groups/public/ &amp;lt;/url&amp;gt;&lt;br /&gt;&amp;lt;/repository&amp;gt;&lt;br /&gt;&lt;br /&gt;...&lt;br /&gt;&lt;br /&gt;&amp;lt;dependency&amp;gt;&lt;br /&gt;&amp;lt;groupId&amp;gt;se.scalablesolutions.akka&amp;lt;/groupId&amp;gt;&lt;br /&gt;&amp;lt;artifactId&amp;gt;akka-core_2.8.0&amp;lt;/artifactId&amp;gt;&lt;br /&gt;&amp;lt;version&amp;gt;0.10&amp;lt;/version&amp;gt;&lt;br /&gt;&amp;lt;/dependency&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;div&gt;Second, create your implementation of the bus, AkkaEventBus.java:&lt;/div&gt;&lt;br /&gt;&lt;pre class="brush:java"&gt;&lt;br /&gt;package org.foo;&lt;br /&gt;&lt;br /&gt;import org.fornax.cartridges.sculptor.framework.event.Event;&lt;br /&gt;import org.fornax.cartridges.sculptor.framework.event.EventBus;&lt;br /&gt;import org.fornax.cartridges.sculptor.framework.event.EventSubscriber;&lt;br /&gt;&lt;br /&gt;import se.scalablesolutions.akka.actor.ActorRef;&lt;br /&gt;import se.scalablesolutions.akka.actor.ActorRegistry;&lt;br /&gt;import se.scalablesolutions.akka.actor.UntypedActor;&lt;br /&gt;import se.scalablesolutions.akka.actor.UntypedActorFactory;&lt;br /&gt;&lt;br /&gt;public class AkkaEventBus implements EventBus {&lt;br /&gt;&lt;br /&gt; public boolean subscribe(final String topic, final EventSubscriber subscriber) {&lt;br /&gt;  UntypedActor.actorOf(new UntypedActorFactory() {&lt;br /&gt;   public UntypedActor create() {&lt;br /&gt;    return new ActorListener(topic, subscriber);&lt;br /&gt;   }&lt;br /&gt;  }).start();&lt;br /&gt;  &lt;br /&gt;  return true;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public boolean unsubscribe(String topic, EventSubscriber subscriber) {&lt;br /&gt;  // TODO : implement mapping between arbitrary subscriber and actor in registry&lt;br /&gt;  return true;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public boolean publish(String topic, Event event) {&lt;br /&gt;  ActorRef[] actorsForTopic = ActorRegistry.actorsFor(topic);&lt;br /&gt;  for (int i = 0; i &amp;lt; actorsForTopic.length; i++) {&lt;br /&gt;   actorsForTopic[i].sendOneWay(event);&lt;br /&gt;  }&lt;br /&gt;  return true;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; @SuppressWarnings("unchecked")&lt;br /&gt; private class ActorListener extends UntypedActor {&lt;br /&gt;  final String topic;&lt;br /&gt;  final EventSubscriber subscriber;&lt;br /&gt;&lt;br /&gt;  ActorListener(String topic, EventSubscriber subscriber) {&lt;br /&gt;   this.topic = topic;&lt;br /&gt;   this.subscriber = subscriber;&lt;br /&gt;   this.getContext().setId(topic);&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  @Override&lt;br /&gt;  public void onReceive(Object message) throws Exception {&lt;br /&gt;   this.subscriber.receive((Event) message);&lt;br /&gt;  }&lt;br /&gt; &lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;div&gt;As you can see, I lack the unsubscribe implementation and I left out equals and hashCode overrides, but that I leave to you.&lt;/div&gt;&lt;div&gt;Third, add it as our bus implementation through spring config:&lt;/div&gt;&lt;br /&gt;&lt;pre class="brush:xml"&gt;&lt;br /&gt;&amp;lt;bean id="akkaEventBus" class="org.foo.AkkaEventBus"/&amp;gt;&lt;br /&gt;&amp;lt;alias name="akkaEventBus" alias="eventBus"/&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;div&gt;What we have done now is built an highly scalable and concurrent EventBus that dispatches event asynchronously.&lt;/div&gt;&lt;div&gt;Pretty easy, right? :-)&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;It doesn't run over the network, but Akka has some nice modules for that as well, so that is our task for next time.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3314601722322098765-3608473173118270507?l=fornax-sculptor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fornax-sculptor.blogspot.com/feeds/3608473173118270507/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fornax-sculptor.blogspot.com/2010/08/eda-akka-as-eventbus.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/3608473173118270507'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/3608473173118270507'/><link rel='alternate' type='text/html' href='http://fornax-sculptor.blogspot.com/2010/08/eda-akka-as-eventbus.html' title='EDA Akka as EventBus'/><author><name>Andreas</name><uri>http://www.blogger.com/profile/08618614943051927546</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3314601722322098765.post-9111250543319973945</id><published>2010-08-11T18:07:00.000+02:00</published><updated>2010-08-11T18:59:10.799+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Sculptor'/><category scheme='http://www.blogger.com/atom/ns#' term='EDA'/><category scheme='http://www.blogger.com/atom/ns#' term='integration'/><category scheme='http://www.blogger.com/atom/ns#' term='scalability'/><title type='text'>EDA events over system boundaries - with camel</title><content type='html'>&lt;a href="http://fornax-sculptor.blogspot.com/2010/08/eda-events-over-system-boundaries.html"&gt;Last time&lt;/a&gt; we examined how we could make our events travel over system boundaries with the help of &lt;a href="http://en.wikipedia.org/wiki/Java_Message_Service"&gt;JMS&lt;/a&gt; and &lt;a href="http://www.springsource.org/spring-integration"&gt;Spring-integration&lt;/a&gt;.&lt;div&gt;Today we will do exactly the same but we will use Apache Camel as event engine instead. The task is to enable application domain events over system boundaries through JMS. If you don't remember our model, here it is again:&lt;/div&gt;&lt;pre class="brush:java"&gt;Application Universe {&lt;br /&gt;basePackage=org.helloworld&lt;br /&gt;&lt;br /&gt;Module milkyway {&lt;br /&gt;  Service PlanetService {&lt;br /&gt;    @BigLandingSuccess landOnPlanet(String planetName, String astronautName)&lt;br /&gt;      publish to milkywayChannel;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  DomainEvent BigLandingSuccess {&lt;br /&gt;    String planetName&lt;br /&gt;    String astronautName&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;Module houston {&lt;br /&gt;  Service GroundControlService {&lt;br /&gt;    subscribe to milkywayChannel&lt;br /&gt;    bringOutTheChampagne(int noOfBottles);&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;}&lt;/pre&gt;&lt;div&gt;To use Apache Camel as event engine, you need to specify it in the sculptor-generator.properties file:&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style=" color: rgb(51, 51, 51); line-height: 16px; "&gt;&lt;span class="Apple-style-span"  style="font-size:medium;"&gt;integration.product=camel&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;And add the needed dependencies to the projects pom-file:&lt;/div&gt;&lt;pre class="brush:xml"&gt;&lt;br /&gt;&amp;lt;dependency&amp;gt;&lt;br /&gt;&amp;lt;groupId&amp;gt;org.apache.camel&amp;lt;/groupId&amp;gt;&lt;br /&gt;&amp;lt;artifactId&amp;gt;camel-core&amp;lt;/artifactId&amp;gt;&lt;br /&gt;&amp;lt;version&amp;gt;2.3.0&amp;lt;/version&amp;gt;&lt;br /&gt;&amp;lt;/dependency&amp;gt;&lt;br /&gt;&amp;lt;dependency&amp;gt;&lt;br /&gt;&amp;lt;groupId&amp;gt;org.apache.camel&amp;lt;/groupId&amp;gt;&lt;br /&gt;&amp;lt;artifactId&amp;gt;camel-jms&amp;lt;/artifactId&amp;gt;&lt;br /&gt;&amp;lt;version&amp;gt;2.3.0&amp;lt;/version&gt;&lt;br /&gt;&amp;lt;/dependency&amp;gt;&lt;br /&gt;&amp;lt;dependency&amp;gt;&lt;br /&gt;&amp;lt;groupId&amp;gt;org.apache.camel&amp;lt;/groupId&amp;gt;&lt;br /&gt;&amp;lt;artifactId&amp;gt;camel-spring&amp;lt;/artifactId&amp;gt;&lt;br /&gt;&amp;lt;version&amp;gt;2.3.0&amp;lt;/version&amp;gt;&lt;br /&gt;&amp;lt;/dependency&amp;gt;&lt;br /&gt;&amp;lt;dependency&amp;gt;&lt;br /&gt;&amp;lt;groupId&amp;gt;org.apache.activemq&amp;lt;/groupId&amp;gt;&lt;br /&gt;&amp;lt;artifactId&amp;gt;activemq-camel&amp;lt;/artifactId&amp;gt;&lt;br /&gt;&amp;lt;version&amp;gt;5.3.2&amp;lt;/version&amp;gt;&lt;br /&gt;&amp;lt;/dependency&amp;gt;&lt;br /&gt;&amp;lt;!-- xbean is required for ActiveMQ broker configuration in the spring xml file --&amp;gt;&lt;br /&gt;&amp;lt;dependency&amp;gt;&lt;br /&gt;&amp;lt;groupId&amp;gt;org.apache.xbean&amp;lt;/groupId&amp;gt;&lt;br /&gt;&amp;lt;artifactId&amp;gt;xbean-spring&amp;lt;/artifactId&amp;gt;&lt;br /&gt;&amp;lt;version&amp;gt;3.7&amp;lt;/version&amp;gt;&lt;br /&gt;&amp;lt;/dependency&amp;gt;&lt;br /&gt;&amp;lt;dependency&amp;gt;&lt;br /&gt;&amp;lt;groupId&amp;gt;javax.xml.bind&amp;lt;/groupId&amp;gt;&lt;br /&gt;&amp;lt;artifactId&amp;gt;jaxb-api&amp;lt;/artifactId&amp;gt;&lt;br /&gt;&amp;lt;version&amp;gt;2.1&amp;lt;/version&amp;gt;&lt;br /&gt;&amp;lt;/dependency&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;div&gt;Regenerate and you will have a new camel.xml file in src/main/resources. This file is only generated once, and you can use it to add configuration for Camel.&lt;br /&gt;&lt;/div&gt;&lt;div&gt;To do the same as we did with spring-integration, i.e. put a domain event on a jms topic, we just have to add route rule to the camel.xml file:&lt;/div&gt;&lt;pre class="brush:xml"&gt;&lt;br /&gt;&amp;lt;camel:route&amp;gt;&lt;br /&gt; &amp;lt;camel:from uri="direct:shippingChannel"/&amp;gt;&lt;br /&gt; &amp;lt;camel:to uri="jms:topic:shippingEvent"/&amp;gt;&lt;br /&gt;&amp;lt;/camel:route&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And that's it. Now we are publishing our domain event to an ActiveMQ topic. Yes, a bit strange that it works, but it gets clearer if we look in camel.xml and see whats was already there:&lt;br /&gt;&lt;pre class="brush:xml"&gt;&lt;br /&gt;&amp;lt;beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:camel="http://camel.apache.org/schema/spring" xmlns:broker="http://activemq.apache.org/schema/core" xsi:schemaLocation="         http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd         http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd         http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd         http://activemq.apache.org/schema/core http://activemq.apache.org/schema/core/activemq-core.xsd"&amp;gt;&lt;br /&gt;&amp;lt;bean id="camelEventBusImpl" class="org.fornax.cartridges.sculptor.framework.event.CamelEventBusImpl"/&amp;gt;&lt;br /&gt;&amp;lt;alias name="camelEventBusImpl" alias="eventBus"/&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;camel:camelContext id="camel"&amp;gt;&lt;br /&gt;  &amp;lt;camel:package&amp;gt;org.sculptor.shipping&amp;lt;/camel:package&amp;gt;&lt;br /&gt;  &amp;lt;camel:template id="producerTemplate"/&amp;gt;&lt;br /&gt;&amp;lt;/camel:camelContext&amp;gt;&lt;br /&gt;&amp;lt;!--&lt;br /&gt;  Camel ActiveMQ to use the ActiveMQ broker&lt;br /&gt; --&amp;gt;&lt;br /&gt;&amp;lt;bean id="jms" class="org.apache.activemq.camel.component.ActiveMQComponent"&amp;gt;&lt;br /&gt;  &amp;lt;property name="brokerURL" value="tcp://localhost:61616"/&amp;gt;&lt;br /&gt;&amp;lt;/bean&amp;gt;&lt;br /&gt;&amp;lt;/beans&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;We have a bean that wires the jms api's to the actual ActiveMQ instance together. So you have to have an ActiveMQ instance up and running listening on port 61616.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;All for today, next time...we'll see what the subject is...&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3314601722322098765-9111250543319973945?l=fornax-sculptor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fornax-sculptor.blogspot.com/feeds/9111250543319973945/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fornax-sculptor.blogspot.com/2010/08/eda-events-over-system-boundaries-with.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/9111250543319973945'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/9111250543319973945'/><link rel='alternate' type='text/html' href='http://fornax-sculptor.blogspot.com/2010/08/eda-events-over-system-boundaries-with.html' title='EDA events over system boundaries - with camel'/><author><name>Andreas</name><uri>http://www.blogger.com/profile/08618614943051927546</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3314601722322098765.post-4553690774095741230</id><published>2010-08-02T22:00:00.001+02:00</published><updated>2010-08-02T22:00:01.209+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Sculptor'/><category scheme='http://www.blogger.com/atom/ns#' term='EDA'/><category scheme='http://www.blogger.com/atom/ns#' term='integration'/><category scheme='http://www.blogger.com/atom/ns#' term='scalability'/><title type='text'>EDA events over system boundaries</title><content type='html'>In the last &lt;a href="http://fornax-sculptor.blogspot.com/2010/07/eda-why-event-bus-in-sculptor.html"&gt;post&lt;/a&gt; we explained why we have the 'event bus' notion. And earlier we have seen how to use it to publish and subscribe, both through the dsl in the model, or through plain java code.&lt;div&gt;Today we thought we should look how you can make domain events part of the public api of your business component and publish them to the rest of the world.&lt;/div&gt;&lt;div&gt;To do this we will use the event bus implementation that is based on spring integration. We will use its &lt;a href="http://static.springsource.org/spring-integration/reference/htmlsingle/spring-integration-reference.html#jms-outbound-channel-adapter"&gt;JMS outbound channel adapter&lt;/a&gt; to publish the 'BigLandingSuccess' domain event to a jms topic that the rest of the world can listen to.&lt;/div&gt;&lt;div&gt;If you don't remember, here is the model:&lt;/div&gt;&lt;br /&gt;&lt;pre class="brush:java"&gt;Application Universe {&lt;br /&gt; basePackage=org.helloworld&lt;br /&gt;&lt;br /&gt; Module milkyway {&lt;br /&gt;   Service PlanetService {&lt;br /&gt;     @BigLandingSuccess landOnPlanet(String planetName, String astronautName)&lt;br /&gt;       publish to milkywayChannel;&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   DomainEvent BigLandingSuccess {&lt;br /&gt;     String planetName&lt;br /&gt;     String astronautName&lt;br /&gt;   }&lt;br /&gt; }&lt;br /&gt; Module houston {&lt;br /&gt;   Service GroundControlService {&lt;br /&gt;     subscribe to milkywayChannel&lt;br /&gt;     bringOutTheChampagne(int noOfBottles);&lt;br /&gt;   }&lt;br /&gt; }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;div&gt;But first, how do we replace the default event bus implementation with the spring-integration based?&lt;/div&gt;&lt;div&gt;In &lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;sculptor-generator.properties&lt;/span&gt;, add the property:&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;integration.product=spring-integration&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;And in the project pom file, add the dependency:&lt;/div&gt;&lt;pre class="brush:xml"&gt;&amp;lt;dependency&amp;gt;&lt;br /&gt; &amp;lt;groupId&amp;gt;org.springframework.integration&amp;lt;/groupId&amp;gt;&lt;br /&gt; &amp;lt;artifactId&amp;gt;org.springframework.integration&amp;lt;/artifactId&amp;gt;&lt;br /&gt; &amp;lt;version&amp;gt;1.0.4.RELEASE&amp;lt;/version&amp;gt;&lt;br /&gt;&amp;lt;/dependency&amp;gt;&lt;/pre&gt;&lt;div&gt;Regenerate, and you are home. So, now all event routing will use spring-integration as engine. So without any other changes, it works exactly as before.&lt;br /&gt;Now you have to decide how your public api should look like, and in our case we decides that the 'BigLandingSuccess' domain event should be made available for other applications to listen to.&lt;/div&gt;&lt;div&gt;And now we take advantage of the power of spring-integration.  When we regenerated with spring-integration enabled, we ended up with the file:&lt;br /&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;src/main/resources/spring-integration.xml&lt;/span&gt;&lt;/div&gt;&lt;div&gt;This is the place where all spring-integration definitions lands. It is generated once, so you are now free to edit it. Open the file and add:&lt;/div&gt;&lt;pre class="brush:xml"&gt;&amp;lt;jms:outbound-channel-adapter id="publicMilkywayChannel"  destination="publicMilkywayTopic" channel="milkywayChannel"/&amp;gt;&lt;/pre&gt;&lt;div&gt;You will need a &lt;a href="http://static.springsource.org/spring/docs/2.5.x/reference/jms.html"&gt;spring jms bean&lt;/a&gt; named 'publicMilkywayTopic' and that bean needs to map to an actual message broker instance, for example &lt;a href="http://activemq.apache.org/"&gt;ActiveMQ&lt;/a&gt;. But the spring bean and message broker setup is out scope for this blog entry, so that one we leave to you. &lt;/div&gt;&lt;div&gt;The only caveat now is that we are exposing our java objects in the message. This can be cured with a transformation step before the jms adapter. So add a transformer to your spring-integration configuration:&lt;/div&gt;&lt;pre class="brush:xml"&gt;&amp;lt;object-to-string-transformer input-channel="milkywayChannel" output-channel="milkywayMessagesAsStringChannel"/&amp;gt;&lt;br /&gt;&lt;/pre&gt;And change the jms outbound channel to wire up with the new channel:&lt;pre class="brush:xml"&gt;&amp;lt;jms:outbound-channel-adapter id="publicMilkywayChannel"  destination="publicMilkywayTopic" channel="milkywayMessagesAsStringChannel"/&amp;gt;&lt;br /&gt;&lt;/pre&gt;Spring-integration has a lot of transformers you can use. The above just transforms a java object to a string through its toString method. That is of course not always what you want, but it works for this example.&lt;div&gt;&lt;br /&gt;&lt;div&gt;So that was all that had to be done to make a domain event part of your public api. Of course, you need to have proper documentation to give others a fair chance of finding your event.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;If we take one step back and consider what we have done.&lt;br /&gt;First, we declared the 'BigLandingSuccess' domain event and publishes it internally.&lt;/div&gt;&lt;div&gt;Second, we add an adapter that listens to the channel and in its turn publishes it on a jms topic, i.e. makes it public.&lt;/div&gt;&lt;div&gt;Third, we added a transformation step before doing a public publish to remove the dependency to our classes.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;Now, this is nice, isn't it? We can have a lot of domain events internally in our application and by that take advantage of all the nice attributes of EDA. And we can by choice make a domain event public with 'just' configuration changes.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Quite long post, but we got pretty much done. Next time we will look how to do the same with Apache Camel&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3314601722322098765-4553690774095741230?l=fornax-sculptor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fornax-sculptor.blogspot.com/feeds/4553690774095741230/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fornax-sculptor.blogspot.com/2010/08/eda-events-over-system-boundaries.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/4553690774095741230'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/4553690774095741230'/><link rel='alternate' type='text/html' href='http://fornax-sculptor.blogspot.com/2010/08/eda-events-over-system-boundaries.html' title='EDA events over system boundaries'/><author><name>Andreas</name><uri>http://www.blogger.com/profile/08618614943051927546</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3314601722322098765.post-9186278111728015942</id><published>2010-08-01T15:26:00.000+02:00</published><updated>2010-08-02T00:26:50.538+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Sculptor'/><category scheme='http://www.blogger.com/atom/ns#' term='EDA'/><category scheme='http://www.blogger.com/atom/ns#' term='scalability'/><title type='text'>EDA why the event bus in sculptor?</title><content type='html'>We have come to our seventh entry about &lt;a href="http://en.wikipedia.org/wiki/Event-driven_architecture"&gt;Event Driven Architecture&lt;/a&gt;. We are in the topic of how Sculptor supports EDA. In previous posts we have covered how to publish and subscribe, both through the dsl in the model, and through plain java code.&lt;div&gt;Today we will talk a bit more on the thin layer we call 'the event bus'.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;As stated before, there are a lot of different approaches to EDA. You can use/implement it locally or apply it too the entire enterprise. But also, in its simplest form its about the observer pattern, regardless if we talk about big or small implementations. This leads us to the main motivations of our event bus abstraction.&lt;/div&gt;&lt;div&gt;We want to keep it simple, but at the same time still have the power of doing event handling big and small. In the Enterprise, or locally. And doing this with the same programming interfaces, i.e. keeping it simple.&lt;/div&gt;&lt;div&gt;So, based on the above we have the event bus api. And with the risk of repeating my self, it is a very simple one. Methods for publishing, subscribing, and un-subscribing.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;It also ships with a default implementation named (you got it) simple event bus. This one is really easy (hey, I found another word instead of simple) to use and fully functional on its own. Though, if you need to integrate with another system you are going to need another implementation. &lt;/div&gt;&lt;div&gt;And that is one of our other motivation for the event bus, it should be easy to swap implementations.&lt;/div&gt;&lt;div&gt;Beside the default, we currently have two implementations based on &lt;a href="http://www.springsource.org/spring-integration"&gt;Spring-integration&lt;/a&gt; and &lt;a href="http://camel.apache.org/"&gt;Apache Camel&lt;/a&gt;. Both of these are what's called "lightweight integration frameworks". Supporting these two frameworks brings a lot of power to the solution when it comes to integration.&lt;/div&gt;&lt;div&gt;And that brings us to the next topic of this series. Some examples of how to bring your events to life over system boundaries, i.e. integration stuff.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3314601722322098765-9186278111728015942?l=fornax-sculptor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fornax-sculptor.blogspot.com/feeds/9186278111728015942/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fornax-sculptor.blogspot.com/2010/07/eda-why-event-bus-in-sculptor.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/9186278111728015942'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/9186278111728015942'/><link rel='alternate' type='text/html' href='http://fornax-sculptor.blogspot.com/2010/07/eda-why-event-bus-in-sculptor.html' title='EDA why the event bus in sculptor?'/><author><name>Andreas</name><uri>http://www.blogger.com/profile/08618614943051927546</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3314601722322098765.post-3286759805736084508</id><published>2010-07-30T15:01:00.001+02:00</published><updated>2010-07-30T15:24:03.707+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Sculptor'/><category scheme='http://www.blogger.com/atom/ns#' term='EDA'/><category scheme='http://www.blogger.com/atom/ns#' term='scalability'/><title type='text'>EDA pub/sub with plain java</title><content type='html'>In a couple of posts we have seen how to &lt;a href="http://fornax-sculptor.blogspot.com/2010/07/eda-simple-example-with-sculptor.html"&gt;publish&lt;/a&gt; and &lt;a href="http://fornax-sculptor.blogspot.com/2010/07/eda-simple-example-with-sculptor_29.html"&gt;subscribe&lt;/a&gt; to events. I showed how to do that through the dsl in the model. But you have of course also the possibility to do pub/sub by plain java code as well. The key thing is to have a handle to the event bus.&lt;div&gt;The easiest way to explain it is to show an example, so, a simple service that publish an event as part of it's business logic:&lt;/div&gt;&lt;br /&gt;&lt;pre class="brush:java"&gt;&lt;br /&gt;@Service&lt;br /&gt;public class GroundControlService {&lt;br /&gt;&lt;br /&gt; @Autowired&lt;br /&gt; @Qualifier("eventBus")&lt;br /&gt; private EventBus eventBus;&lt;br /&gt;&lt;br /&gt; @Override&lt;br /&gt; public void receive(Event event) {&lt;br /&gt;   if (event instanceof BigLandingSuccess) {&lt;br /&gt;     eventBus.publish("earthChannel", new Celebration("We did it!"));&lt;br /&gt;     this.bringOutTheChampagne(ALL);&lt;br /&gt;     eventBus.publish("supplierChannel", new MissingResource("Champagne"));&lt;br /&gt;   }&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;And for the other end, the subscriber:&lt;br /&gt;&lt;pre class="brush:java"&gt;&lt;br /&gt;@Service&lt;br /&gt;public class SupplierService {&lt;br /&gt;&lt;br /&gt;@Autowired&lt;br /&gt;@Qualifier("eventBus")&lt;br /&gt;private EventBus eventBus;&lt;br /&gt;&lt;br /&gt;public void init() {&lt;br /&gt;  eventBus.subscribe("supplierChannel", new EventSubscriber() {&lt;br /&gt;    @Override&lt;br /&gt;    public void receive(Event event) {&lt;br /&gt;      if (event instanceof MissingResource) {&lt;br /&gt;        raceForContract(event);&lt;br /&gt;      }&lt;br /&gt;    }&lt;br /&gt;  });&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;div&gt;Not so hard.&lt;/div&gt;&lt;div&gt;Now, here and there I've used the term 'event bus', and you have also seen it in java code above. Next time we will look closer into the bus. Why is there an abstraction and what can you do with it?&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3314601722322098765-3286759805736084508?l=fornax-sculptor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fornax-sculptor.blogspot.com/feeds/3286759805736084508/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fornax-sculptor.blogspot.com/2010/07/eda-pubsub-with-plain-java.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/3286759805736084508'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/3286759805736084508'/><link rel='alternate' type='text/html' href='http://fornax-sculptor.blogspot.com/2010/07/eda-pubsub-with-plain-java.html' title='EDA pub/sub with plain java'/><author><name>Andreas</name><uri>http://www.blogger.com/profile/08618614943051927546</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3314601722322098765.post-6053180084389590426</id><published>2010-07-29T14:45:00.004+02:00</published><updated>2010-07-29T15:17:12.498+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Sculptor'/><category scheme='http://www.blogger.com/atom/ns#' term='EDA'/><category scheme='http://www.blogger.com/atom/ns#' term='scalability'/><title type='text'>EDA a simple example with Sculptor continued</title><content type='html'>Lets continue with our simple example from the last &lt;a href="http://fornax-sculptor.blogspot.com/2010/07/eda-simple-example-with-sculptor.html"&gt;post&lt;/a&gt;. We declared a domain event in the model and we marked a service to publish that event to a channel.&lt;div&gt;Now, what about getting notified when such an event is published?&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Continuing with our model from last time, its just a matter of editing it. Lets create another module with a service that is interested of the event:&lt;/div&gt;&lt;br /&gt;&lt;pre class="brush:java"&gt;Application Universe {&lt;br /&gt; basePackage=org.helloworld&lt;br /&gt;&lt;br /&gt; Module milkyway {&lt;br /&gt;   Service PlanetService {&lt;br /&gt;     @BigLandingSuccess landOnPlanet(String planetName, String astronautName)&lt;br /&gt;       publish to milkywayChannel;&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   DomainEvent BigLandingSuccess {&lt;br /&gt;     String planetName&lt;br /&gt;     String astronautName&lt;br /&gt;   }&lt;br /&gt; }&lt;br /&gt; Module houston {&lt;br /&gt;   Service GroundControlService {&lt;br /&gt;     subscribe to milkywayChannel&lt;br /&gt;     bringOutTheChampagne(int noOfBottles);&lt;br /&gt;   }&lt;br /&gt; }&lt;br /&gt;}&lt;/pre&gt;&lt;div&gt;&lt;br /&gt;The result is that the GroundControl service will implement the EventSubscriber interface and be marked with @Subscribe annotation. That means that the GroundControl service will automatically be added as subscriber to milkywayChannel. It will be notified, receive method called, when events are published to that channel.&lt;br /&gt;&lt;/div&gt;&lt;div&gt;You will get a stub of the receive method of the EventSubscriber interface.&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;pre class="brush:java"&gt;&lt;br /&gt;@Override&lt;br /&gt;public void receive(Event event) {&lt;br /&gt; // TODO Auto-generated method stub&lt;br /&gt; throw new UnsupportedOperationException("receive not implemented");&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;div&gt;So that you will need to implement with your logic:&lt;/div&gt;&lt;br /&gt;&lt;pre class="brush:java"&gt;&lt;br /&gt;@Override&lt;br /&gt;public void receive(Event event) {&lt;br /&gt; if (event instanceof BigLandingSuccess) {&lt;br /&gt;   this.bringOutTheChampagne(999999);&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;div&gt;Ok, so now we covered some very simple notations for publishing and subscribing to events. Next time I'll show how to do the same but in plain java code.&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3314601722322098765-6053180084389590426?l=fornax-sculptor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fornax-sculptor.blogspot.com/feeds/6053180084389590426/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fornax-sculptor.blogspot.com/2010/07/eda-simple-example-with-sculptor_29.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/6053180084389590426'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/6053180084389590426'/><link rel='alternate' type='text/html' href='http://fornax-sculptor.blogspot.com/2010/07/eda-simple-example-with-sculptor_29.html' title='EDA a simple example with Sculptor continued'/><author><name>Andreas</name><uri>http://www.blogger.com/profile/08618614943051927546</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3314601722322098765.post-5489881456827994839</id><published>2010-07-26T18:00:00.003+02:00</published><updated>2010-07-26T23:33:26.100+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Sculptor'/><category scheme='http://www.blogger.com/atom/ns#' term='EDA'/><category scheme='http://www.blogger.com/atom/ns#' term='DSL'/><category scheme='http://www.blogger.com/atom/ns#' term='scalability'/><title type='text'>EDA a simple example with Sculptor</title><content type='html'>Now we have gone through EDA in general, and briefly covered how we support it in Sculptor. It's time to see how it can be used.&lt;div&gt;We start with a simple example where we will show how to declare a domain event and how that event is published. &lt;/div&gt;&lt;div&gt;Let us use our old, well known, hello world example.&lt;/div&gt;&lt;pre class="brush:java"&gt;&lt;br /&gt;Application Universe {&lt;br /&gt;basePackage=org.helloworld&lt;br /&gt;&lt;br /&gt;Module milkyway {&lt;br /&gt; Service PlanetService {&lt;br /&gt;   void landOnPlanet(String planetName, String astronautName);&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;That is our model. So, what might the rest of the 'universe' be interested of here? Well, if NASA sent out a space ship with an astronaut on it who's mission was to land on some far far away planet, wouldn't they be interested in the event of a landing? I think so. So, when a astronaut lands on a planet, we would like the service to publish an event of this happening.&lt;div&gt;Lets start with re-defining the model with the event:&lt;/div&gt;&lt;pre class="brush:java"&gt;Application Universe {&lt;br /&gt;basePackage=org.helloworld&lt;br /&gt;&lt;br /&gt;Module milkyway {&lt;br /&gt; Service PlanetService {&lt;br /&gt;   void landOnPlanet(String planetName, String astronautName);&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; DomainEvent BigLandingSuccess {&lt;br /&gt;   String planetName&lt;br /&gt;   String astronautName&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;div&gt;DomainEvents may contain attributes and references in the same way as ValueObjects and Entities. DomainEvents are always immutable and not persistent.&lt;br /&gt;&lt;br /&gt;Events are about something happening at a point in time, so it's natural for events to contain time information. Sculptor automatically adds two timestamps, occurred and recorded. The time the event occurred in the world and the time the event was noticed.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Ok, so now we have the event defined, now we want it to be published. The easiest way of doing this is to mark the service in the dsl (you can of course publish events programmatically, but more about that in a later blog entry) . So, once again, lets re-define our model:&lt;/div&gt;&lt;br /&gt;&lt;pre class="brush:java"&gt;Application Universe {&lt;br /&gt;basePackage=org.helloworld&lt;br /&gt;&lt;br /&gt;Module milkyway {&lt;br /&gt; Service PlanetService {&lt;br /&gt;   @BigLandingSuccess landOnPlanet(String planetName, String astronautName)&lt;br /&gt;      publish to milkywayChannel;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; DomainEvent BigLandingSuccess {&lt;br /&gt;   String planetName&lt;br /&gt;   String astronautName&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;}&lt;/pre&gt;&lt;div&gt;The operation must return a DomainEvent or take a DomainEvent as parameter. That event is published to the defined channel when the operation has been invoked.&lt;br /&gt;&lt;br /&gt;As an alternative the DomainEvent instance can be created from the return value or parameters. The DomainEvent must have a matching constructor.&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;The result of the above declarative way of publishing events is a generated annotation @Publish on the method. It will trigger the Spring AOP advice PublishAdvice that is part of Sculptor framework.&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Recap: We have declared an event in our model, and further marked our service to publish this event when it happens. As an API, I think this is very neat. Everyone who is interested can see that when an astronaut lands on a planet, he or she can be notified. &lt;/div&gt;&lt;div&gt;And that is what we will talk about next time:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;How can I be notified when things happens?&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3314601722322098765-5489881456827994839?l=fornax-sculptor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fornax-sculptor.blogspot.com/feeds/5489881456827994839/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fornax-sculptor.blogspot.com/2010/07/eda-simple-example-with-sculptor.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/5489881456827994839'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/5489881456827994839'/><link rel='alternate' type='text/html' href='http://fornax-sculptor.blogspot.com/2010/07/eda-simple-example-with-sculptor.html' title='EDA a simple example with Sculptor'/><author><name>Andreas</name><uri>http://www.blogger.com/profile/08618614943051927546</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3314601722322098765.post-1090854070725364800</id><published>2010-07-19T18:00:00.000+02:00</published><updated>2010-07-19T21:50:07.103+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Sculptor'/><category scheme='http://www.blogger.com/atom/ns#' term='EDA'/><category scheme='http://www.blogger.com/atom/ns#' term='scalability'/><title type='text'>EDA sculptor support</title><content type='html'>&lt;div&gt;In the previous &lt;a href="http://fornax-sculptor.blogspot.com/2010/07/eda-overview.html"&gt;post&lt;/a&gt; we gave an overview of &lt;a href="http://en.wikipedia.org/wiki/Event-driven_architecture"&gt;Event Driven Architecture&lt;/a&gt;. It is a very big area, and you can use it to a lot of things.&lt;br /&gt;It is a very good complement to DDD, and is a corner stone when building scalable systems.&lt;br /&gt;When constructing systems it is a very nice match to accomplish loosely coupled modules and bounded contexts, i.e. business components. And much more.&lt;br /&gt;&lt;br /&gt;We strive for simplicity, but at the same time not restricting us.&lt;br /&gt;&lt;br /&gt;So, how can you use with Sculptor?&lt;br /&gt;In the 1.9.0 release, we have focused on support for &lt;a href="http://en.wikipedia.org/wiki/Publish/subscribe"&gt;Publish/Subscribe&lt;/a&gt;, &lt;a href="http://www.udidahan.com/2009/12/09/clarified-cqrs/"&gt;Comman-Query Responisbility Segregation (CQRS)&lt;/a&gt; and &lt;a href="http://martinfowler.com/eaaDev/EventSourcing.html"&gt;Event Sourcing. &lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The most useful part is of course the pub/sub support. Event sourcing is an architectural style that has its niche. CQRS is also an architectural style that there has been some publicity around lately. CQRS uses pub/sub and can with advantage be constructed to use Event Sourcing.&lt;br /&gt;&lt;br /&gt;To support the above, there are three central parts in our implementation:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;An event bus abstraction&lt;/li&gt;&lt;li&gt;DomainEvent&lt;/li&gt;&lt;li&gt;CommandEvent&lt;/li&gt;&lt;/ul&gt;&lt;span style="font-weight: bold;"&gt;"The bus"&lt;/span&gt;&lt;br /&gt;The event bus is an extremely simple API, with three different implementations (in 1.9.0), "Simple", &lt;a href="http://www.springsource.org/spring-integration"&gt;Spring Integration&lt;/a&gt; and &lt;a href="http://camel.apache.org/"&gt;Apache Camel&lt;/a&gt;. The idea is that the central parts, i.e. pub/sub, should be easy to use. The only thing you have to work with is an event bus where you publish and subscribes to and from events. And if you need some non functional behavior (asynchronism, over the wire, etc) for your events, you plug in an event bus that can handle this requirements.&lt;br /&gt;And as stated above, the easiest way to accomplish that with the 1.9.0 release is to use Spring Integration or Apache Camel. But you can also choose to implement your own event bus.&lt;br /&gt;You can publish and subscribe to and from the bus either declarative through the DSL, or programatically through the event bus API.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;DomainEvent vs CommandEvent&lt;/span&gt;    &lt;/div&gt;&lt;div&gt;&lt;div&gt;CommandEvent is an instruction for something to happen. The system  processes a CommandEvent and takes appropriate actions.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;DomainEvent  states fact - that something has happen. This fact is published to the  rest of the world and the publisher just lets it go with no further  interest in what happens to the event, i.e. who receives it and what they  do.&lt;br /&gt;&lt;br /&gt;If you compare it to an application API, CommandEvent could be the input API, while the DomainEvent is the output API for the application. I.e. the CommandEvent is what you can make the application perform, while the DomainEvent is what the application reports has happen.&lt;br /&gt;&lt;br /&gt;As you probably figured out, CommandEvent is for implementing CQRS and Event Sourcing.&lt;br /&gt;&lt;br /&gt;Finally, let us take quick look at how the notion for DomainEvents look like in the DSL.&lt;br /&gt;&lt;pre class="code-java"&gt;DomainEvent ShipHasArrived {&lt;br /&gt;  - ShipId ship&lt;br /&gt;  - UnLocode port&lt;br /&gt;}&lt;br /&gt;   &lt;br /&gt;DomainEvent ShipHasDepartured {&lt;br /&gt;  - ShipId ship&lt;br /&gt;  - UnLocode port&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;As you can see, you define a domain event in the same way as for entities or value objects.&lt;br /&gt;&lt;br /&gt;And to declare pub/sub:&lt;br /&gt;&lt;pre class="code-java"&gt;Service TrackingService {&lt;br /&gt;  @ShipHasArrived recordArrival(DateTime occurred, @Ship ship, @Port port)&lt;br /&gt;      publish to shippingChannel;&lt;br /&gt;}&lt;br /&gt;Service Statistics {&lt;br /&gt;  subscribe to shippingChannel&lt;br /&gt;  &lt;span class="code-object"&gt;int&lt;/span&gt; getShipsInPort(@UnLocode port);&lt;br /&gt;  reset;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;&lt;span style="font-family:georgia;"&gt;That was all for today, next time will it be more concrete with examples of how to use it.&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3314601722322098765-1090854070725364800?l=fornax-sculptor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fornax-sculptor.blogspot.com/feeds/1090854070725364800/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fornax-sculptor.blogspot.com/2010/07/eda-sculptor-support.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/1090854070725364800'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/1090854070725364800'/><link rel='alternate' type='text/html' href='http://fornax-sculptor.blogspot.com/2010/07/eda-sculptor-support.html' title='EDA sculptor support'/><author><name>Andreas</name><uri>http://www.blogger.com/profile/08618614943051927546</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3314601722322098765.post-8601669826783066199</id><published>2010-07-15T18:00:00.004+02:00</published><updated>2010-07-15T20:56:39.688+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Sculptor'/><category scheme='http://www.blogger.com/atom/ns#' term='EDA'/><category scheme='http://www.blogger.com/atom/ns#' term='scalability'/><title type='text'>EDA overview</title><content type='html'>&lt;div&gt;&lt;span class="Apple-style-span"  style="font-size:medium;"&gt;&lt;span class="Apple-style-span"  style="font-family:verdana;"&gt;As stated in the &lt;/span&gt;&lt;/span&gt;&lt;a href="http://fornax-sculptor.blogspot.com/2010/07/eda-intro.html"&gt;&lt;span class="Apple-style-span"  style="font-size:medium;"&gt;&lt;span class="Apple-style-span"  style="font-family:verdana;"&gt;last post&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;span class="Apple-style-span"  style="font-size:medium;"&gt;&lt;span class="Apple-style-span"  style="font-family:verdana;"&gt;, we would like to talk a bit (very shortly) about EDA in a broader sense to give an overview of it and to give you a sense for what we read into the term EDA. This is quite important as a background when we go further and explain our interpretation and implementation of it.&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-size:medium;"&gt;&lt;span class="Apple-style-span"  style="font-family:verdana;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;span class="Apple-style-span"  style="font-size:medium;"&gt;&lt;span class="Apple-style-span"  style="font-family:verdana;"&gt;Event Driven Architecture is a very broad area. But in a short sentence its about:&lt;/span&gt;&lt;/span&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-size:medium;"&gt;&lt;span class="Apple-style-span"  style="font-family:verdana;"&gt;Applications and systems that produce, consume and reacts on events.&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-size:medium;"&gt;&lt;span class="Apple-style-span"  style="font-family:verdana;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-size:medium;"&gt;&lt;span class="Apple-style-span"  style="font-family:verdana;"&gt;And even if there is a lot of attention on EDA as means of implementing integration between applications and systems, EDA can be applied within applications and even in parts of applications.&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-size:medium;"&gt;&lt;span class="Apple-style-span"  style="font-family:verdana;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-size:medium;"&gt;&lt;span class="Apple-style-span"  style="font-family:verdana;"&gt;The benefits of an EDA is:&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;&lt;span class="Apple-style-span"  style="font-size:medium;"&gt;&lt;span class="Apple-style-span"  style="font-family:verdana;"&gt;loosely coupled systems (or internals of a system) &lt;/span&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span class="Apple-style-span"  style="font-size:medium;"&gt;&lt;span class="Apple-style-span"  style="font-family:verdana;"&gt;high performance (fire and forget)&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span class="Apple-style-span"  style="font-size:medium;"&gt;&lt;span class="Apple-style-span"  style="font-family:verdana;"&gt;high scalability&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-size:medium;"&gt;&lt;span class="Apple-style-span"  style="font-family:verdana;"&gt;Of course this comes with a trade-off. An extra abstraction is added, i.e. it becomes more complex.&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-size:medium;"&gt;&lt;span class="Apple-style-span"  style="font-family:verdana;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-size:medium;"&gt;&lt;span class="Apple-style-span"  style="font-family:verdana;"&gt;In it's simplest form, EDA is about the &lt;/span&gt;&lt;/span&gt;&lt;a href="http://en.wikipedia.org/wiki/Observer_pattern"&gt;&lt;span class="Apple-style-span"  style="font-size:medium;"&gt;&lt;span class="Apple-style-span"  style="font-family:verdana;"&gt;Observer Pattern&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;span class="Apple-style-span"  style="font-size:medium;"&gt;&lt;span class="Apple-style-span"  style="font-family:verdana;"&gt;. Something happens somewhere, and 0 to n parties is interested in that happening. From this simple pattern, all interpretations, implementations and usages of EDA are spawn.&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-size:medium;"&gt;&lt;span class="Apple-style-span"  style="font-family:verdana;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-size:medium;"&gt;&lt;span class="Apple-style-span"  style="font-family:verdana;"&gt;So, before we go into how we have implemented support for EDA in Sculptor, we will give you some examples of  what shapes we think EDA can take. Big and small...here we go:&lt;/span&gt;&lt;/span&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:verdana;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;span class="Apple-style-span"  style="font-family:verdana;"&gt;GUI's&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:verdana;"&gt;Swing uses the Observer pattern. Many different GUI frameworks uses an event driven approach. Java Server Faces works with events, event handlers and event actions. Etc...&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:verdana;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;span class="Apple-style-span"  style="font-family:verdana;"&gt;DomainEvent&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:verdana;"&gt;A &lt;/span&gt;&lt;a href="http://martinfowler.com/eaaDev/DomainEvent.html"&gt;&lt;span class="Apple-style-span"  style="font-family:verdana;"&gt;Domain Event&lt;/span&gt;&lt;/a&gt;&lt;span class="Apple-style-span"  style="font-family:verdana;"&gt; is registration of something that has happen. It might be of interest to 0 to n consumers. The thing is; The producer doesn't care, it just tell the world that this thing has happen and happily goes on with it's life. &lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:verdana;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;span class="Apple-style-span"  style="font-family:verdana;"&gt;PubSub&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:verdana;"&gt;Publisher and Subscribers is a central part of all event driven integrations. It is an implementation of the Observer pattern. It often comes with persistent events. Topics in the Java/JMS tech domain is an implementation of it.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:verdana;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;span class="Apple-style-span"  style="font-family:verdana;"&gt;SOA2.0&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:verdana;"&gt;Now when SOA has been around for some years the next thing is SOA2.0. In the 2.0 version of SOA events plays a central role as a complement to services.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="  line-height: 19px; "&gt;&lt;span class="Apple-style-span"  style="font-size:medium;"&gt;&lt;span class="Apple-style-span"  style="font-family:verdana;"&gt; An event-driven system typically consists of event emitters (or agents) and event consumers (or sinks). Sinks have the responsibility of applying a reaction as soon as an event is presented. The reaction might or might not be completely provided by the sink itself. For instance, the sink might just have the responsibility to filter, transform and forward the event to another component or it might provide a self contained reaction to such event. The first category of sinks can be based upon traditional components such as message oriented middleware while the second category of sinks (self contained online reaction) might require a more appropriate transactional executive framework, for example an ESB.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:verdana;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;span class="Apple-style-span"  style="font-family:verdana;"&gt;Event servers&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:verdana;"&gt;Event servers are servers that are specialized in processing events. They often provide filter/query capabilities for events. &lt;/span&gt;&lt;a href="http://download-llnw.oracle.com/docs/cd/E13213_01/wlevs/docs20/"&gt;&lt;span class="Apple-style-span"  style="font-family:verdana;"&gt;Weblogic Event Server&lt;/span&gt;&lt;/a&gt;&lt;span class="Apple-style-span"  style="font-family:verdana;"&gt; is an example of this kind of product.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:verdana;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;span class="Apple-style-span"  style="font-family:verdana;"&gt;Event sourcing&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:verdana;"&gt;All changes to an application is recorded as a series of events. An applications current state can be queried. But not only that, an application state can be rolled forward or backwards as you wish, giving you a lot of power. &lt;/span&gt;&lt;a href="http://martinfowler.com/"&gt;&lt;span class="Apple-style-span"  style="font-family:verdana;"&gt;Martin Fowler&lt;/span&gt;&lt;/a&gt;&lt;span class="Apple-style-span"  style="font-family:verdana;"&gt; has written all about it &lt;/span&gt;&lt;a href="http://martinfowler.com/eaaDev/EventSourcing.html"&gt;&lt;span class="Apple-style-span"  style="font-family:verdana;"&gt;here&lt;/span&gt;&lt;/a&gt;&lt;span class="Apple-style-span"  style="font-family:verdana;"&gt;. Also, Patrik has written a couple of blog entries of how he has played with it and now also added support for it in Sculptor, see &lt;/span&gt;&lt;a href="http://fornax-sculptor.blogspot.com/2010/05/prototyping-event-sourcing.html"&gt;&lt;span class="Apple-style-span"  style="font-family:verdana;"&gt;here&lt;/span&gt;&lt;/a&gt;&lt;span class="Apple-style-span"  style="font-family:verdana;"&gt; and &lt;/span&gt;&lt;a href="http://fornax-sculptor.blogspot.com/2010/06/event-sourcing-snapshots.html"&gt;&lt;span class="Apple-style-span"  style="font-family:verdana;"&gt;here&lt;/span&gt;&lt;/a&gt;&lt;span class="Apple-style-span"  style="font-family:verdana;"&gt;.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:verdana;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;span class="Apple-style-span"  style="font-family:verdana;"&gt;CQRS&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:verdana;"&gt;Command and Query Responsibility Segregation is a very cool area. I will not try to explain it in detail, it is very well done &lt;/span&gt;&lt;a href="http://www.udidahan.com/2009/12/09/clarified-cqrs/"&gt;&lt;span class="Apple-style-span"  style="font-family:verdana;"&gt;here&lt;/span&gt;&lt;/a&gt;&lt;span class="Apple-style-span"  style="font-family:verdana;"&gt;.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="border-collapse: collapse; line-height: 16px; "&gt;&lt;span class="Apple-style-span"  style="font-size:medium;"&gt;&lt;span class="Apple-style-span"  style="font-family:verdana;"&gt;Simplified it is about separating commands (that change the data) from the queries (that read the data). Separate subsystems take care of answering queries (reporting) and the domain for processing and storing updates can stay focused. The result of the commands are published to query subsystems, each optimized for its purpose.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="  border-collapse: collapse; line-height: 16px; font-size:11px;"&gt;&lt;span class="Apple-style-span"  style="font-family:verdana;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:verdana;"&gt;Ok, that was probably a very short and incomplete list of EDA related topics, but it gave you a taste for it and a ground for further reading. We will use it as a foundation when we talk about how we think about and implements EDA. &lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:verdana;"&gt;If you think we have missed an important topic, tool, concept, etc, around EDA, please add a comment about it.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:verdana;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:verdana;"&gt;Next up, how we support EDA in Sculptor.&lt;/span&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3314601722322098765-8601669826783066199?l=fornax-sculptor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fornax-sculptor.blogspot.com/feeds/8601669826783066199/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fornax-sculptor.blogspot.com/2010/07/eda-overview.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/8601669826783066199'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/8601669826783066199'/><link rel='alternate' type='text/html' href='http://fornax-sculptor.blogspot.com/2010/07/eda-overview.html' title='EDA overview'/><author><name>Andreas</name><uri>http://www.blogger.com/profile/08618614943051927546</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3314601722322098765.post-2864636838706229878</id><published>2010-07-12T18:00:00.002+02:00</published><updated>2010-07-12T19:07:18.924+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Sculptor'/><category scheme='http://www.blogger.com/atom/ns#' term='EDA'/><category scheme='http://www.blogger.com/atom/ns#' term='scalability'/><title type='text'>EDA intro</title><content type='html'>Both Patrik and I have always been believers of Event Driven Architecture. EDA has been around for many years, but recently the attention towards it has increased. The reasons for that is probably many, but I guess one of the most important is that EDA will help you build more concurrent and scalable system.&lt;div&gt;Therefore, as we strive to support popular technologies, we take the opportunity to implement support for EDA in the newly &lt;a href="http://fornax.itemis.de/confluence/display/fornax/0.+What's+New+(CSC)#0.What%27sNew%28CSC%29-v19"&gt;released 1.9.0 version&lt;/a&gt; of Sculptor.&lt;br /&gt;&lt;br /&gt;Patrik has written a couple of interesting posts (&lt;a href="http://fornax-sculptor.blogspot.com/2010/05/prototyping-event-sourcing.html"&gt;here&lt;/a&gt; and &lt;a href="http://fornax-sculptor.blogspot.com/2010/06/event-sourcing-snapshots.html"&gt;here&lt;/a&gt;) about event driven architecture in a special implementation model, &lt;a href="http://martinfowler.com/eaaDev/EventSourcing.html"&gt;event sourcing&lt;/a&gt;.&lt;br /&gt;Event sourcing is a powerful tool in certain circumstances. Though, not always a perfect match.&lt;/div&gt;&lt;div&gt;Also, many people reads &lt;a href="http://en.wikipedia.org/wiki/Event-driven_SOA#SOA_2.0"&gt;SOA2.0&lt;/a&gt; or &lt;a href="http://en.wikipedia.org/wiki/Enterprise_service_bus"&gt;ESB&lt;/a&gt; when you say EDA. This is for sure true in many cases, but just a small set of the truth. &lt;/div&gt;&lt;div&gt;EDA is much more than event sourcing, SOA2.0 or ESB implementations.&lt;/div&gt;&lt;div&gt;EDA plays a big role in application architecture and design (or even module design) as well.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Our approach is that you as a developer should be able to choose how and where you implement EDA.&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;That is what we will talk about in a sequence of posts.&lt;/div&gt;&lt;div&gt;EDA in general. &lt;/div&gt;&lt;div&gt;EDA, how we see it. &lt;/div&gt;&lt;div&gt;EDA, how we support it.&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;So, stay tuned for more about EDA.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3314601722322098765-2864636838706229878?l=fornax-sculptor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fornax-sculptor.blogspot.com/feeds/2864636838706229878/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fornax-sculptor.blogspot.com/2010/07/eda-intro.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/2864636838706229878'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/2864636838706229878'/><link rel='alternate' type='text/html' href='http://fornax-sculptor.blogspot.com/2010/07/eda-intro.html' title='EDA intro'/><author><name>Andreas</name><uri>http://www.blogger.com/profile/08618614943051927546</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3314601722322098765.post-108261384347816922</id><published>2010-07-11T20:25:00.000+02:00</published><updated>2010-07-11T20:27:27.177+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Sculptor'/><category scheme='http://www.blogger.com/atom/ns#' term='Release'/><title type='text'>What's Next, after 1.9.0</title><content type='html'>Next release will mostly be a technical upgrade. &lt;a href="http://www.eclipse.org/Xtext/"&gt;Xtext&lt;/a&gt; and &lt;a href="http://wiki.eclipse.org/Xpand"&gt;Xpand&lt;/a&gt; version 1.0 were released together with Eclipse Helios. We will upgrade to that.&lt;br /&gt;&lt;br /&gt;Spring 3.0 and JPA 2.0 are also important upgrades.&lt;br /&gt;&lt;br /&gt;Oliver will contribute with more cool stuff. He has some almost finished things that he will share with us (hopefully) soon. &lt;br /&gt;&lt;br /&gt;The original design of Sculptor has worked fine and we will not change it, but 3 years of additions of different combinations of target implementations is starting to hurt some templates. Therefore we will remove some old (probably unused) target implementations. Those have been deprecated in 1.9.0 and in case you are using any of it you should start migration to some of the more modern alternatives:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;EJB/Spring combination =&gt; migrate to EJB3 or Spring &lt;/li&gt;&lt;br /&gt;&lt;li&gt;EJB 2 =&gt; migrate to EJB3 or Spring &lt;/li&gt;&lt;br /&gt;&lt;li&gt;Hibernate mapping with XML =&gt; migrate to JPA/Hibernate with annotations &lt;/li&gt;&lt;br /&gt;&lt;li&gt;Spring definitions in XML =&gt; migrate to Spring with annotations &lt;/li&gt;&lt;br /&gt;&lt;li&gt;Hibernate without JPA =&gt; migrate to JPA/Hibernate&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;This makes a good reason to use version number 2.0.0 for next release. Personally I get bad vibrations when Open Source project go to 2.0. That often means total redesign and no backwards compatibility. That will not be the case for Sculptor 2.0. It will be a cleanup, and some pieces are not supported any more, but most of it will stay intact and be compatible (with some migration steps, as usual).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3314601722322098765-108261384347816922?l=fornax-sculptor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fornax-sculptor.blogspot.com/feeds/108261384347816922/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fornax-sculptor.blogspot.com/2010/07/whats-next-after-190.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/108261384347816922'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/108261384347816922'/><link rel='alternate' type='text/html' href='http://fornax-sculptor.blogspot.com/2010/07/whats-next-after-190.html' title='What&apos;s Next, after 1.9.0'/><author><name>Patrik</name><uri>http://www.blogger.com/profile/11876565293495127382</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3314601722322098765.post-4701289128693253955</id><published>2010-07-11T20:15:00.000+02:00</published><updated>2010-07-11T20:25:07.528+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Sculptor'/><category scheme='http://www.blogger.com/atom/ns#' term='Release'/><title type='text'>Exploring new features in 1.9.0</title><content type='html'>This post is dedicated for those of you who are already using Sculptor and would like to have a quick overview of some selected features in the release. I will not bring up bug fixes and all improvements. Please refer to the complete list in &lt;a href="http://fornax.itemis.de/jira/secure/IssueNavigator.jspa?reset=true&amp;pid=10050&amp;fixfor=10320"&gt;issue tracker&lt;/a&gt; for that. &lt;br /&gt;&lt;br /&gt;I have written a separate &lt;a href="http://fornax-sculptor.blogspot.com/2010/07/sculptor-190-support-for-mongodb-and.html"&gt;teaser about the MongoDB and EDA&lt;/a&gt; features of the new release.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Generated Documentation&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;In the model you can write documentation as a quoted string in front of almost every element (attribute, reference, entity, operation, ...). From the model Sculptor generates HTML documentation of all domain objects including their attributes and associations. Documentation of Services are also included. The generated result is located in src/generated/resources/DomainModelDoc.html&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Improved Graphviz Visualization&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Several diagrams with different focus and level of detail are generated. They are also included in the generated documentation. &lt;a href="http://fornax-sculptor.blogspot.com/2010/03/improved-graphviz-visualization.html"&gt;Samples here&lt;/a&gt;. There is a new maven plugin fornax-graphviz-m2-plugin that generates images (.png) from the .dot files. &lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Syntax Diagrams&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;We have mainly focused on sample based documentation. Some users have asked for more formal syntax description of the DSL. We have created &lt;a href="http://fornax.itemis.de/confluence/x/OIBH"&gt;railroad syntax diagrams&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Aggregate Syntax&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Aggregates can now be defined with the new belongsTo keyword. It is more informative than the previous !aggregateRoot.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:text"&gt;&lt;br /&gt;        Entity Cargo {&lt;br /&gt;            - TrackingId trackingId key&lt;br /&gt;            - Location origin required&lt;br /&gt;            - Location destination required&lt;br /&gt;            - Itinerary itinerary nullable inverse opposite cargo&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        ValueObject Itinerary {&lt;br /&gt;            belongsTo Cargo&lt;br /&gt;            not optimisticLocking&lt;br /&gt;            not immutable&lt;br /&gt;            - Cargo cargo nullable opposite itinerary&lt;br /&gt;            - List&lt;Leg&gt; legs inverse&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        "An itinerary consists of one or more legs."&lt;br /&gt;        ValueObject Leg {&lt;br /&gt;            belongsTo Cargo &lt;br /&gt;            - CarrierMovement carrierMovement;&lt;br /&gt;            - Location from;&lt;br /&gt;            - Location ^to;&lt;br /&gt;        }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Nullable in key&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;It is now allowed to have some nullable fields as part a composite natural key.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:text"&gt;&lt;br /&gt;    ValueObject HandlingEvent {&lt;br /&gt;        - Type type key&lt;br /&gt;        - CarrierMovement carrierMovement nullable key&lt;br /&gt;        - Location location key&lt;br /&gt;        DateTime completionTime key&lt;br /&gt;        DateTime registrationTime&lt;br /&gt;        - Cargo cargo key opposite events&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;ToStringStyle&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;We are using commons-lang for toString of domain objects. It has a &lt;a href="http://commons.apache.org/lang/api-2.5/org/apache/commons/lang/builder/ToStringStyle.html"&gt;style&lt;/a&gt; parameter, which is now possible to define in sculptor-generator.properties. You can choose between several styles. I like this format:&lt;br /&gt;&lt;pre class="brush:text"&gt;&lt;br /&gt;toStringStyle=SHORT_PREFIX_STYLE&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;You can also override this for individual DomainObject with hint:&lt;br /&gt;&lt;pre class="brush:text"&gt;&lt;br /&gt;    ValueObject Foo { &lt;br /&gt;        hint="toStringStyle=MULTI_LINE_STYLE" &lt;br /&gt;        String aaa &lt;br /&gt;        String bbb &lt;br /&gt;    } &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;databaseJoinTable&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;It is possible to define many-to-many join table with databaseJoinTable and its columns with databaseColumn at both sides of the bidirectional association:&lt;br /&gt;&lt;pre class="brush:text"&gt;&lt;br /&gt;- Set&lt;@Media&gt; existsInMedia opposite mediaCharacters&lt;br /&gt;        databaseJoinTable="MED_CHR" databaseColumn="CHR"&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;BTW opposite doesn't have to be defined last any more.&lt;br /&gt;&lt;br /&gt;Those keywords are also useful for unidirectional to-many associations. Additionally databaseJoinColumn is used, since there is no opposite side to define the column on.&lt;br /&gt;&lt;pre class="brush:text"&gt;&lt;br /&gt;- Set&lt;@Person&gt; playedBy databaseJoinTable="CHR_PERS" &lt;br /&gt;        databaseColumn="PERS" databaseJoinColumn="CHR"&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Clob/Blob&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Clob and Blob are now supported with default java types String and byte[]. They are mapped with JPA @Lob.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;GUI with Not Persistent Value Objects&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The structure of the persistent domain model might not always match the presentation, for example you might want a dialog for editing several domain objects in one single screen. Then you can create a non-persistent ValueObject with a corresponding Service. The application service handles the transformation between the presentation object and the persistent domain objects. More documentation and sample is available &lt;a href="http://fornax.itemis.de/confluence/display/fornax/5.1+Web+CRUD+GUI+Tutorial+(CSC)#5.1WebCRUDGUITutorial%28CSC%29-NotPersistentValueObjects"&gt;here&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3314601722322098765-4701289128693253955?l=fornax-sculptor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fornax-sculptor.blogspot.com/feeds/4701289128693253955/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fornax-sculptor.blogspot.com/2010/07/exploring-new-features-in-190.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/4701289128693253955'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/4701289128693253955'/><link rel='alternate' type='text/html' href='http://fornax-sculptor.blogspot.com/2010/07/exploring-new-features-in-190.html' title='Exploring new features in 1.9.0'/><author><name>Patrik</name><uri>http://www.blogger.com/profile/11876565293495127382</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3314601722322098765.post-8267972306974833239</id><published>2010-07-11T20:00:00.000+02:00</published><updated>2010-07-11T20:21:20.708+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Sculptor'/><category scheme='http://www.blogger.com/atom/ns#' term='EDA'/><category scheme='http://www.blogger.com/atom/ns#' term='MongoDB'/><category scheme='http://www.blogger.com/atom/ns#' term='Release'/><title type='text'>Sculptor 1.9.0 - Support for MongoDB and Event-Driven Architecture</title><content type='html'>The Sculptor development team has a track record delivering around 3 releases per year. We have delivered 10 releases in total. That means that the core pieces are rock solid. I have personally used it successfully together with 15 other developers on daily basis for about a year now. It simply works very well.&lt;br /&gt;&lt;br /&gt;We care about bugfixing and making small improvements. At the same time we are excited about learning new technology and using emergent design. This release contains two new features in the area of scalability. Persistence backed with MongoDB and support for Event-Driven Architecture.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;MongoDB&lt;/span&gt; bridges the gap between key-value stores (which are fast and highly scalable) and traditional RDBMS systems (which provide rich queries and deep functionality). I think this makes MongoDB very interesting for applications that need high-performance and/or scalability, but also prefer using a rich persistent domain model with complex associations. The schema less structure is attractive from a developer productivity perspective, which is one of the two goals with Sculptor (quality is the other).&lt;br /&gt;&lt;br /&gt;Sculptor generates data mapper classes that converts domain objects to/from MongoDB data structures, DBObjects. This makes it easy to use a domain model à la DDD with automatic mapping to MongoDB data structures.&lt;br /&gt;&lt;br /&gt;Sculptor provides generic repository operations for use with MongoDB. This includes operations such as save, delete, findById, findByKey, findByCondition, and some more. You get CRUD operations, and GUI, for free.&lt;br /&gt;&lt;br /&gt;Queries can be expressed with a slick fluent api that support code completion and refactoring.&lt;br /&gt;&lt;br /&gt;Rich support for associations. Aggregates are stored as a embedded documents. Other associations are stored with referring ids. In the domain objects there are generated getters that lazily fetch associated objects from the ids. This means that you don't have to work with the ids yourself, you can follow associations as usual.&lt;br /&gt;&lt;br /&gt;Read more about how to use MongoDB with Sculptor &lt;a href="http://fornax.itemis.de/confluence/x/CIBE"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Event-Driven Architecture&lt;/span&gt; (EDA) is a good complement to Domain-Driven Design. We think EDA is an important ingredient for building scalable systems. It is also an enabler for designing loosely coupled modules and bounded contexts.&lt;br /&gt;&lt;br /&gt;For this Sculptor makes it possible to define Domain Events in the model in similar way as Entities and Value Objects. Sculptor also provide a mechanism to publish and subscribe through a simple event bus. This is done either in a declarative way in the model, or programatically with a simple API.&lt;br /&gt;&lt;br /&gt;Implementations of the event bus that integrates with Apache Camel and Spring Integration are available out-of-the-box. &lt;br /&gt;&lt;br /&gt;In addition to publish/subscribe Sculptor also has support for CQRS and EventSourcing. &lt;br /&gt;&lt;br /&gt;CQRS is about separating commands (that change the data) from the queries (that read the data). Separate subsystems take care of answering queries (reporting) and the domain for processing and storing updates can stay focused. &lt;br /&gt;&lt;br /&gt;Event Sourcing makes it possible to see how we got to the current state and query how the state looked liked in the past. Essentially it means that we have to capture all changes to an application state as a sequence of events. Sculptor provides a default implementation of EventSourcing, which can be extended and customized to fit specific needs. &lt;br /&gt;&lt;br /&gt;Read more about how to use the new event features of Sculptor &lt;a href="http://fornax.itemis.de/confluence/x/FgBL"&gt;here&lt;/a&gt;. We will blog more about this topic later, so stay tuned.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3314601722322098765-8267972306974833239?l=fornax-sculptor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fornax-sculptor.blogspot.com/feeds/8267972306974833239/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fornax-sculptor.blogspot.com/2010/07/sculptor-190-support-for-mongodb-and.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/8267972306974833239'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/8267972306974833239'/><link rel='alternate' type='text/html' href='http://fornax-sculptor.blogspot.com/2010/07/sculptor-190-support-for-mongodb-and.html' title='Sculptor 1.9.0 - Support for MongoDB and Event-Driven Architecture'/><author><name>Patrik</name><uri>http://www.blogger.com/profile/11876565293495127382</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3314601722322098765.post-6029135226294157770</id><published>2010-06-04T22:12:00.005+02:00</published><updated>2010-06-04T22:19:42.799+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Sculptor'/><title type='text'>New Home Page</title><content type='html'>New Sculptor home page is live: &lt;span style="font-weight:bold;"&gt;&lt;a href="http://sculptor.fornax-platform.org/"&gt;http://sculptor.fornax-platform.org/&lt;/a&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_qr2e_ywa0-I/TAle5Cc_LlI/AAAAAAAABTA/YjQfWDKHllc/s1600/SculptorLogo2.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 200px; height: 78px;" src="http://3.bp.blogspot.com/_qr2e_ywa0-I/TAle5Cc_LlI/AAAAAAAABTA/YjQfWDKHllc/s400/SculptorLogo2.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5479014755676925522" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;I have also updated the original TSS article: &lt;span style="font-weight:bold;"&gt;&lt;a href="http://sites.google.com/site/fornaxsculptor/improving-developer-productivity"&gt;Improving Developer Productivity with Sculptor&lt;/a&gt;&lt;/span&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3314601722322098765-6029135226294157770?l=fornax-sculptor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fornax-sculptor.blogspot.com/feeds/6029135226294157770/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fornax-sculptor.blogspot.com/2010/06/new-home-page.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/6029135226294157770'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/6029135226294157770'/><link rel='alternate' type='text/html' href='http://fornax-sculptor.blogspot.com/2010/06/new-home-page.html' title='New Home Page'/><author><name>Patrik</name><uri>http://www.blogger.com/profile/11876565293495127382</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_qr2e_ywa0-I/TAle5Cc_LlI/AAAAAAAABTA/YjQfWDKHllc/s72-c/SculptorLogo2.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3314601722322098765.post-2972683978693142433</id><published>2010-06-01T20:30:00.003+02:00</published><updated>2010-06-01T20:40:31.628+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='EDA'/><category scheme='http://www.blogger.com/atom/ns#' term='MongoDB'/><title type='text'>Event Sourcing Snapshots</title><content type='html'>I got a relevant &lt;a href="http://old.nabble.com/New-blog-entry-td28734037s17564.html"&gt;question&lt;/a&gt; on &lt;a href="http://fornax-sculptor.blogspot.com/2010/05/prototyping-event-sourcing.html"&gt;yesterdays post&lt;/a&gt;. How does the snapshot mechanism work?&lt;br /&gt;&lt;br /&gt;The easiest way to reconstruct a historical state is to start with a clean database, maybe populated with static reference data, and then apply all events in order up to the point you are interested in. This will take forever if your system has been running for a while, it contains a lot of events. &lt;br /&gt;&lt;br /&gt;The trick to make it work is to periodically take a snapshot of the complete database. When doing replay it can start with the preceding snapshot and only replay events after the snapshot time up to the time you are interested in.&lt;br /&gt;&lt;br /&gt;Taking a snapshot can be implemented in various ways. I have tried an approach that aims at being able to take a snapshot without stopping/locking normal operation, i.e. inflow of events and processing of them can continue.&lt;br /&gt;&lt;br /&gt;The steps to create snapshots are illustrated by the following figure. It also illustrates a query to replay events to a specific point in time.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_qr2e_ywa0-I/TAVT-fe0ydI/AAAAAAAABS4/OVO5HssOdC8/s1600/snapshots.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 400px; height: 332px;" src="http://1.bp.blogspot.com/_qr2e_ywa0-I/TAVT-fe0ydI/AAAAAAAABS4/OVO5HssOdC8/s400/snapshots.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5477876854833859026" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;How do I copy database? For MongoDB there is a simple copydb command, which can be executed from the application. Exactly how to do this with other databases is vendor specific, but I guess it is possible somehow. At least in command line admin mode. Since I always copy from a read-only (snapshot) database it is should be a consistency safe operation.&lt;br /&gt;&lt;br /&gt;These operations will of course not be super fast and temporary databases for historical queries must probably be prepared in advanced to have decent response times. If you need to do a lot of historical queries you need something more, such as a separate system responsible for answering such queries. It may be a data warehouse, a denormalized database, or whatever tailored for the purpose. This separate system is fed from the same original event stream. The design is called &lt;a href="http://codebetter.com/blogs/gregyoung/archive/2010/02/13/cqrs-and-event-sourcing.aspx"&gt;CQRS&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3314601722322098765-2972683978693142433?l=fornax-sculptor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fornax-sculptor.blogspot.com/feeds/2972683978693142433/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fornax-sculptor.blogspot.com/2010/06/event-sourcing-snapshots.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/2972683978693142433'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/2972683978693142433'/><link rel='alternate' type='text/html' href='http://fornax-sculptor.blogspot.com/2010/06/event-sourcing-snapshots.html' title='Event Sourcing Snapshots'/><author><name>Patrik</name><uri>http://www.blogger.com/profile/11876565293495127382</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_qr2e_ywa0-I/TAVT-fe0ydI/AAAAAAAABS4/OVO5HssOdC8/s72-c/snapshots.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3314601722322098765.post-5719489154060004911</id><published>2010-05-31T18:30:00.002+02:00</published><updated>2010-05-31T18:37:04.852+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Sculptor'/><category scheme='http://www.blogger.com/atom/ns#' term='EDA'/><category scheme='http://www.blogger.com/atom/ns#' term='MongoDB'/><title type='text'>Prototyping Event Sourcing</title><content type='html'>In Domain Language Newsletter March 2010 we could read Eric Evans writeup on Domain Events.&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;Over the last few years it has become clear that it is very useful to add a new pattern to the DDD "Building Blocks" (Entities, Value Objects, Services, etc.) -- Domain Events. This pattern has been around for a long time. Martin Fowler wrote &lt;a href="http://martinfowler.com/eaaDev/DomainEvent.html"&gt;a good description&lt;/a&gt;. &lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;Andreas and I have lately been prototyping on some mechanism to better support Event-Driven Architecture in Sculptor. In this article I will describe how to do &lt;a href="http://martinfowler.com/eaaDev/EventSourcing.html"&gt;Event Sourcing&lt;/a&gt;.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;A domain model typically holds current state of the world. Event Sourcing makes it possible to see how we got to this state and query how the state looked liked in the past. Essentially it means that we have to &lt;span style="font-style:italic;"&gt;capture all changes to an application state as a sequence of events.&lt;/span&gt; These events can be used to reconstruct current and past states.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;In the prototype we have used Martin Fowler's &lt;a href="http://martinfowler.com/eaaDev/EventSourcing.html"&gt;shipping system example&lt;/a&gt;. The full source code for the sample is available in &lt;a href="http://fornax.svn.sourceforge.net/svnroot/fornax/trunk/cartridges/sculptor/mongodb-sample/sculptor-shipping/"&gt;Subversion&lt;/a&gt;. I have developed Event Sourcing with existing Sculptor mechanisms. Next step will be to move the general pieces into the tool so that it can be easily used. I would like to share the ideas right away in this blog.&lt;/div&gt;&lt;div&gt;The domain model for the shipping system is very simple. Ships that carry cargo and move between ports. It looks like this in Sculptor DSL:&lt;/div&gt;&lt;pre class="brush:text"&gt;Entity Ship {&lt;br /&gt;   gap&lt;br /&gt;   - ShipId shipId key&lt;br /&gt;   String name&lt;br /&gt;   - Port port nullable&lt;br /&gt;   - Set&amp;lt;@Cargo&amp;gt; cargos&lt;br /&gt;&lt;br /&gt;   Repository ShipRepository {&lt;br /&gt;       gap&lt;br /&gt;       inject @CargoRepository&lt;br /&gt;       save;&lt;br /&gt;       findByKey;&lt;br /&gt;   }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;Entity Port {&lt;br /&gt;   - UnLocode unlocode key;&lt;br /&gt;   String city&lt;br /&gt;   - Country country&lt;br /&gt;&lt;br /&gt;   Repository PortRepository {&lt;br /&gt;       save;&lt;br /&gt;       findByKey;&lt;br /&gt;   }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;Entity Cargo {&lt;br /&gt;   gap&lt;br /&gt;   String cargoId key&lt;br /&gt;   boolean hasBeenInCanada&lt;br /&gt;&lt;br /&gt;   Repository CargoRepository {&lt;br /&gt;       save;&lt;br /&gt;       findByKey;&lt;br /&gt;   }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;BasicType ShipId {&lt;br /&gt;   String identifier key&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;"United nations location code."&lt;br /&gt;BasicType UnLocode {&lt;br /&gt;   String identifier key&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;enum Country {&lt;br /&gt;   US,&lt;br /&gt;   CANADA&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;div&gt;When a ship arrives at port it is registered in the TrackingService, recordArrival operation. Instead of taking that information and directly save it to current domain objects, the TrackingService creates a DomainEvent and pass it in to the processor for further execution.&lt;/div&gt;&lt;pre class="brush:java"&gt;public void recordArrival(DateTime occured, Ship ship, Port port) {&lt;br /&gt;   DateTime now = new DateTime();&lt;br /&gt;   ArrivalEvent event = new ArrivalEvent(occured, now, ship.getShipId(), port.getUnlocode());&lt;br /&gt;   getDomainEventProcessor().process(event);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span"   style=" white-space: normal;  font-family:Georgia, serif;font-size:16px;"&gt;&lt;a href="http://3.bp.blogspot.com/_qr2e_ywa0-I/TAJH-IdWBTI/AAAAAAAABSo/fFZH1y1eMOA/s1600/EventSourcingDesign.png"&gt;&lt;img src="http://3.bp.blogspot.com/_qr2e_ywa0-I/TAJH-IdWBTI/AAAAAAAABSo/fFZH1y1eMOA/s400/EventSourcingDesign.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5477019229583967538" style="cursor: pointer; width: 400px; height: 272px; " /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The DomainEventProcessor stores the event, which is important for audit and possibility to replay events to reconstruct past states. The DomainEventProcessor is generic, knows nothing about the shipping domain. It dispatches the event to an EventHandler that knows how to process the shipping specific events. As a start I use simple Spring dependency injection to select EventHandler, but we will probably use a slightly more sophisticated mechanism later. The important thing is that the generic DomainEventProcessor doesn't know how to process events, so it delegates to application specific event handlers.&lt;br /&gt;&lt;br /&gt;A useful utility for dispatching events to separate methods is available in commons beanutils. The ShippingEventHandler looks like this (error handling removed):&lt;br /&gt;&lt;pre class="brush:java"&gt;&lt;br /&gt;public void handleEvent(DomainEvent event) {&lt;br /&gt;   dispatch(event);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt;* Runtime dispatch to handle method with correct event parameter type&lt;br /&gt;*/&lt;br /&gt;protected void dispatch(DomainEvent event) {&lt;br /&gt;   MethodUtils.invokeMethod(this, "handle", new Object[] { event });&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public void handle(ArrivalEvent event) {&lt;br /&gt;   Ship ship = getShipRepository().findByKey(event.getShip());&lt;br /&gt;   Port port = getPortRepository().findByKey(event.getPort());&lt;br /&gt;   ship.arrival(port);&lt;br /&gt;   getShipRepository().save(ship);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public void handle(DepartureEvent event) {&lt;br /&gt;   Ship ship = getShipRepository().findByKey(event.getShip());&lt;br /&gt;   Port port = getPortRepository().findByKey(event.getPort());&lt;br /&gt;   ship.departure(port);&lt;br /&gt;   getShipRepository().save(ship);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;The events are also defined in Sculptor DSL. So far I have used ordinary ValueObjects for the events but we will add a special syntax for defining events like this:&lt;pre class="brush:text"&gt;DomainEvent ArrivalEvent {&lt;br /&gt;   - ShipId ship&lt;br /&gt;   - UnLocode port&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;DomainEvent DepartureEvent {&lt;br /&gt;   - ShipId ship&lt;br /&gt;   - UnLocode port&lt;br /&gt;}&lt;/pre&gt;What have we done? We have made the design more complicated by an intermediate event step, but we have also a foundation for all the exciting things that can be done with Event Sourcing.&lt;br /&gt;&lt;br /&gt;We can query the state of a ship for a specific point in time, and it is not only the state of the ship, the complete domain model can be used as usual at a specific point in time.&lt;br /&gt;&lt;pre class="brush:java"&gt;&lt;br /&gt;    DateTime to = travelStart.plusDays(15);&lt;br /&gt;    ReplaySpecification replaySpec = new ReplaySpecification().withTo(to).withTarget(tmpDb);&lt;br /&gt;    domainEventProcessor.replay(replaySpec);&lt;br /&gt;    DbManager.setThreadInstance(tmpDb);&lt;br /&gt;    Ship ship = referenceDataService.getShip(shipId);&lt;br /&gt;    Port port = ship.getPort();&lt;br /&gt;    Set&amp;lt;Cargo&amp;gt; cargos = ship.getCargos();&lt;/pre&gt;&lt;div&gt;When doing this prototype we have used MongoDB, which is excellent for these kind of features because:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;it is schema-less, so any event type can be stored without predefined schema&lt;/li&gt;&lt;li&gt;it has low latency, so replay of events are fast&lt;/li&gt;&lt;li&gt;it is easy to make new database instances and copy database, which make replay and snapshot mechanisms simple&lt;/li&gt;&lt;/ul&gt;Note that in the above example we replay the events into a temporary database (tmpDb), a &lt;a href="http://martinfowler.com/eaaDev/ParallelModel.html"&gt;Parallel Model&lt;/a&gt;.  This is a full featured MongoDB instance and therefore we have all the ordinary query capabilities, i.e. exactly the same domain model including repositories can be used with the temporary database as with the ordinary database.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The generic DomainEventProcessor provides several methods to operate on the events. For example to speed up replay of large data volumes it is possible to create snapshots. Latest preceding snapshot is used as base when replaying, i.e. only the events after the snapshot time need to be processed.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The event processor module is defined like this in the prototype. The idea is that it will be provided automatically by Sculptor. Of course with customization possibilities to support variations.&lt;pre class="brush:text"&gt;Module event {&lt;br /&gt;   Service DomainEventProcessor {&lt;br /&gt;       inject @DomainEventRepository&lt;br /&gt;       inject @SnapshotRepository&lt;br /&gt;&lt;br /&gt;       process(@DomainEvent event);&lt;br /&gt;       getAllEvents =&amp;gt; DomainEventRepository.findAll;&lt;br /&gt;       replayAll;&lt;br /&gt;       replayUpTo(DateTime timePoint);&lt;br /&gt;       replay(@ReplaySpecification spec);&lt;br /&gt;       save(List&amp;lt;@DomainEvent&amp;gt; events);&lt;br /&gt;       String createSnapshot(DateTime timePoint);&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   abstract ValueObject DomainEvent {&lt;br /&gt;       gap&lt;br /&gt;       DateTime occured index;&lt;br /&gt;       DateTime recorded;&lt;br /&gt;&lt;br /&gt;       Repository DomainEventRepository {&lt;br /&gt;           save;&lt;br /&gt;           save(List&amp;lt;@DomainEvent&amp;gt; entities);&lt;br /&gt;           findAll(PagingParameter pagingParameter);&lt;br /&gt;           findBetween(DateTime from, DateTime to, PagingParameter pagingParameter);&lt;br /&gt;           protected findByCondition(PagingParameter pagingParameter);&lt;br /&gt;       }&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   ValueObject ReplaySpecification {&lt;br /&gt;       not persistent&lt;br /&gt;       DateTime from nullable&lt;br /&gt;       DateTime to nullable&lt;br /&gt;       DB target nullable   &lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   ValueObject SnapshotInfo {&lt;br /&gt;       DateTime timePoint index&lt;br /&gt;       String name&lt;br /&gt;&lt;br /&gt;       Repository SnapshotRepository {&lt;br /&gt;           @SnapshotInfo findLatest(DateTime timePoint);&lt;br /&gt;           String snapshotName(DateTime timePoint);&lt;br /&gt;           @SnapshotInfo createSnapshot(DateTime timePoint);&lt;br /&gt;           copyDb(String fromDbName, String toDbName) =&amp;gt; CopyDbAccessObject;&lt;br /&gt;           protected findByCondition;&lt;br /&gt;           protected save;&lt;br /&gt;       }&lt;br /&gt;   }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;We are also prototyping how to add other event mechanisms, such as publish/subscribe and integration with various products, such as &lt;a href="http://www.springsource.org/spring-integration"&gt;Spring Integration&lt;/a&gt;, &lt;a href="http://camel.apache.org/"&gt;Apache Camel &lt;/a&gt;and &lt;a href="http://akkasource.org/"&gt;Akka&lt;/a&gt;. More about that later.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3314601722322098765-5719489154060004911?l=fornax-sculptor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fornax-sculptor.blogspot.com/feeds/5719489154060004911/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fornax-sculptor.blogspot.com/2010/05/prototyping-event-sourcing.html#comment-form' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/5719489154060004911'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/5719489154060004911'/><link rel='alternate' type='text/html' href='http://fornax-sculptor.blogspot.com/2010/05/prototyping-event-sourcing.html' title='Prototyping Event Sourcing'/><author><name>Patrik</name><uri>http://www.blogger.com/profile/11876565293495127382</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_qr2e_ywa0-I/TAJH-IdWBTI/AAAAAAAABSo/fFZH1y1eMOA/s72-c/EventSourcingDesign.png' height='72' width='72'/><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3314601722322098765.post-2723569469884947810</id><published>2010-05-04T16:18:00.025+02:00</published><updated>2010-05-06T15:52:40.631+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Sculptor'/><category scheme='http://www.blogger.com/atom/ns#' term='DSL'/><title type='text'>Generating Syntax/Railroad diagrams from Xtext</title><content type='html'>&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:arial;"&gt;&lt;span class="Apple-style-span"  style="font-size:large;"&gt;Problem&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;span class="Apple-style-span"  style="font-family:arial;"&gt;&lt;span class="Apple-style-span"  style="font-size:medium;"&gt;Some months ago we started to speak with Patrik about syntax (railroad) diagrams for Sculptor. Syntax of Sculptor DSL is very rich now and we have no formal reference documentation. In &lt;/span&gt;&lt;/span&gt;&lt;a href="http://fornax.itemis.de/confluence/x/dQQ"&gt;&lt;span class="Apple-style-span"  style="font-family:arial;"&gt;&lt;span class="Apple-style-span"  style="font-size:medium;"&gt;Advanced&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;span class="Apple-style-span"  style="font-family:arial;"&gt;&lt;span class="Apple-style-span"  style="font-size:medium;"&gt; and &lt;/span&gt;&lt;/span&gt;&lt;a href="http://fornax.itemis.de/confluence/x/dAQ"&gt;&lt;span class="Apple-style-span"  style="font-family:arial;"&gt;&lt;span class="Apple-style-span"  style="font-size:medium;"&gt;Developer doc&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;span class="Apple-style-span"  style="font-family:arial;"&gt;&lt;span class="Apple-style-span"  style="font-size:medium;"&gt; many details of language are described but pure syntax description would be very helpful.&lt;/span&gt;&lt;/span&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:arial;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:arial;"&gt;&lt;span class="Apple-style-span"  style="font-size:large;"&gt;Solution&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:arial;"&gt;I started looking for some tools. I found following generator for syntax diagrams:&lt;/span&gt;&lt;/div&gt;&lt;div&gt;   &lt;span class="Apple-style-span"  style="font-family:arial;"&gt;&lt;a href="http://www-cgi.uni-regensburg.de/~brf09510/syntax.html"&gt;http://www-cgi.uni-regensburg.de/~brf09510/syntax.html&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:arial;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:arial;"&gt;Little bit old but output looks good. Now we need only BNF definition for our language. I looked at Xtext definition and start to manually change it. For example this Xtext:&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;pre class="brush:text"&gt;DslValueObject :&lt;br /&gt;  (doc=STRING)?&lt;br /&gt;  (abstract?=&amp;quot;abstract&amp;quot;)? &amp;quot;ValueObject&amp;quot; name=ID (&amp;quot;extends&amp;quot; ((&amp;quot;@&amp;quot;extends=[DslValueObject]) | (extendsName=DslJavaIdentifier)))? &amp;quot;{&amp;quot;&lt;br /&gt;    (&amp;quot;package&amp;quot; &amp;quot;=&amp;quot; package=DslJavaIdentifier )?&lt;br /&gt;    (((notOptimisticLocking?=NOT &amp;quot;optimisticLocking&amp;quot;) | (&amp;quot;optimisticLocking&amp;quot;)) |&lt;br /&gt;     ((notImmutable?=NOT &amp;quot;immutable&amp;quot;) | (&amp;quot;immutable&amp;quot;)) |&lt;br /&gt;     ((cache?=&amp;quot;cache&amp;quot;) | (NOT &amp;quot;cache&amp;quot;)) |&lt;br /&gt;     ((gapClass?=&amp;quot;gap&amp;quot;) | (noGapClass?=&amp;quot;nogap&amp;quot;)) |&lt;br /&gt;     (scaffold?=&amp;quot;scaffold&amp;quot;) |&lt;br /&gt;     (&amp;quot;hint&amp;quot; &amp;quot;=&amp;quot; hint=STRING) |&lt;br /&gt;     (&amp;quot;databaseTable&amp;quot; &amp;quot;=&amp;quot; databaseTable=STRING) |&lt;br /&gt;     (&amp;quot;discriminatorValue&amp;quot; &amp;quot;=&amp;quot; discriminatorValue=STRING) |&lt;br /&gt;     (&amp;quot;discriminatorColumn&amp;quot; &amp;quot;=&amp;quot; discriminatorColumn=STRING) |&lt;br /&gt;     (&amp;quot;discriminatorType&amp;quot; &amp;quot;=&amp;quot; discriminatorType=DslDiscriminatorType) |&lt;br /&gt;     (&amp;quot;discriminatorLength&amp;quot; &amp;quot;=&amp;quot; discriminatorLength=STRING) |&lt;br /&gt;     (&amp;quot;inheritanceType&amp;quot; &amp;quot;=&amp;quot; inheritanceType=DslInheritanceType) |&lt;br /&gt;     (&amp;quot;validate&amp;quot; &amp;quot;=&amp;quot; validate=STRING) |&lt;br /&gt;     ((notPersistent?=NOT &amp;quot;persistent&amp;quot;) | (&amp;quot;persistent&amp;quot;)))*&lt;br /&gt;    ((attributes+=DslAttribute) |&lt;br /&gt;     (references+=DslReference))*&lt;br /&gt;    (repository=DslRepository)?&lt;br /&gt;  &amp;quot;}&amp;quot;;&lt;/pre&gt;&lt;br /&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:arial;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style=" ;font-family:arial;"&gt;have to be transformed to this BNF notation:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;pre class="brush:text"&gt;DslValueObject :&lt;br /&gt;  STRING?&lt;br /&gt;  &amp;quot;abstract&amp;quot;? &amp;quot;ValueObject&amp;quot; ID (&amp;quot;extends&amp;quot; ((&amp;quot;@&amp;quot;DslValueObject) | DslJavaIdentifier))? &amp;quot;{&amp;quot;&lt;br /&gt;    (&amp;quot;package&amp;quot; &amp;quot;=&amp;quot; DslJavaIdentifier )?&lt;br /&gt;    ((NOT? &amp;quot;optimisticLocking&amp;quot;) |&lt;br /&gt;     (NOT? &amp;quot;immutable&amp;quot;) |&lt;br /&gt;     (NOT? &amp;quot;cache&amp;quot;) |&lt;br /&gt;     (&amp;quot;gap&amp;quot; | &amp;quot;nogap&amp;quot;) |&lt;br /&gt;     &amp;quot;scaffold&amp;quot; |&lt;br /&gt;     (&amp;quot;hint&amp;quot; &amp;quot;=&amp;quot; STRING) |&lt;br /&gt;     (&amp;quot;databaseTable&amp;quot; &amp;quot;=&amp;quot; STRING) |&lt;br /&gt;     (&amp;quot;discriminatorValue&amp;quot; &amp;quot;=&amp;quot; STRING) |&lt;br /&gt;     (&amp;quot;discriminatorColumn&amp;quot; &amp;quot;=&amp;quot; STRING) |&lt;br /&gt;     (&amp;quot;discriminatorType&amp;quot; &amp;quot;=&amp;quot; DslDiscriminatorType) |&lt;br /&gt;     (&amp;quot;discriminatorLength&amp;quot; &amp;quot;=&amp;quot; STRING) |&lt;br /&gt;     (&amp;quot;inheritanceType&amp;quot; &amp;quot;=&amp;quot; DslInheritanceType) |&lt;br /&gt;     (&amp;quot;validate&amp;quot; &amp;quot;=&amp;quot; STRING) |&lt;br /&gt;     (NOT? &amp;quot;persistent&amp;quot;))*&lt;br /&gt;    (DslAttribute |&lt;br /&gt;     DslReference)*&lt;br /&gt;    DslRepository?&lt;br /&gt;  &amp;quot;}&amp;quot;;&lt;/pre&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style=" ;font-family:arial;"&gt;As you can see, it is not that different. In first round I did it manually but I found that it will be possible automatize with short sed script. Store following to convertToBNF.sed:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style=" ;font-family:arial;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;pre class="brush:text"&gt;s/[A-Za-z]\+.=//g&lt;br /&gt;s/\[//g&lt;br /&gt;s/\]//g&lt;br /&gt;s/""/" "/g&lt;br /&gt;s/(\([A-Za-z"]\+\))/\1/g&lt;br /&gt;s/^enum *//&lt;br /&gt;s/^terminal *//&lt;br /&gt;/^grammar /d&lt;br /&gt;/^generate /d&lt;/pre&gt;&lt;br /&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style=" ;font-family:arial;"&gt;You can apply it to Xtext file by:&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:arial;"&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;sed -f convertToBNF.sed Sculptordsl.xtext  &gt; Sculptordsl.bnf&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:arial;"&gt;Now you can copy and paste content of Sculptordsl.bnf to text area on previous mentioned site:&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;a href="http://www-cgi.uni-regensburg.de/~brf09510/syntax.html"&gt;&lt;span class="Apple-style-span"  style="font-size:medium;"&gt;&lt;span class="Apple-style-span"  style="font-family:arial;"&gt;http://www-cgi.uni-regensburg.de/~brf09510/syntax.html&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:arial;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:arial;"&gt;For making it nice we will use bigger font and than resize to 50% with anti-aliasing. Set font size to 22 and image size to double of expected result image. For me with our example it's width 2000 and height 2000. For big grammars more images are generated on page. Just save whole page to some directory. All new browsers always store html file and all other resources like images, styles, javascripts to separate directory. Go to this resource directory and run following command (this need ImageMagic convert tool):&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;for i in *.png; do convert $i -trim -resize 52% ${i%.png}-S.png; done&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:arial;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:arial;"&gt;Now you have small version of original syntax diagrams with nice anti-aliasing. Here is my result:&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_CBW1jk_oLkw/S-CVGejtLyI/AAAAAAAAAA4/1Agi28i-QIw/s1600/DslValueObject-S.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 400px; height: 359px;" src="http://1.bp.blogspot.com/_CBW1jk_oLkw/S-CVGejtLyI/AAAAAAAAAA4/1Agi28i-QIw/s400/DslValueObject-S.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5467533886142361378" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-size:large;"&gt;&lt;span class="Apple-style-span"  style="font-family:arial;"&gt;Conclusion&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:arial;"&gt;As you can see, generating nice looking syntax diagrams from Xtext grammar isn't that difficult. In next days we will make page with all Sculptor DSL syntax diagrams. Question is: "Can I apply it for my Xtext grammar too?". Sed tool is working with pure text. It's possible to confuse sed with strange declaration but for normal grammars it should work. It will be better to have special tool which will parse Xtext notation, maybe some transformation and simplification for nicer results and generate BNF notation directly, but this is bigger job and sed work nice with Sculptor Xtext. Here is full version of sed script which I'm using now for transforming Xtext grammar to BNF notation. You can see also some transformations at end of sed script which do beautification of BNF:&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;pre class="brush:text"&gt;s/[A-Za-z]\+.=//g&lt;br /&gt;s/\[//g&lt;br /&gt;s/\]//g&lt;br /&gt;s/&amp;quot;&amp;quot;/&amp;quot; &amp;quot;/g&lt;br /&gt;s/(\([A-Za-z&amp;quot;]\+\))/\1/g&lt;br /&gt;s/^enum *//&lt;br /&gt;s/^terminal *//&lt;br /&gt;/^grammar /d&lt;br /&gt;/^generate /d&lt;br /&gt;$i \&lt;br /&gt;ID :\&lt;br /&gt;   LETTER (LETTER | NUMBER)*\&lt;br /&gt;\&lt;br /&gt;STRING :\&lt;br /&gt;  &amp;quot;&amp;quot;&amp;quot;&amp;quot; CHAR+ &amp;quot;&amp;quot;&amp;quot;&amp;quot;;\&lt;br /&gt;\&lt;br /&gt;LETTER :\&lt;br /&gt;  &amp;quot;A-Z&amp;quot; | &amp;quot;a-z&amp;quot;;\&lt;br /&gt;\&lt;br /&gt;CHAR :\&lt;br /&gt;  LETTER | NUMBER | &amp;quot; ~!@#$%&amp;amp;*()_+-={}[]:|\&amp;#39;;/.,;&amp;lt;&amp;gt;?&amp;quot;;\&lt;br /&gt;\&lt;br /&gt;INTEGER :\&lt;br /&gt;  NUMBER+;\&lt;br /&gt;\&lt;br /&gt;NUMBER :\&lt;br /&gt;  &amp;#39;0-9&amp;#39;;\&lt;br /&gt;&lt;br /&gt;# Syntactic sugars&lt;br /&gt;# X | (Y X) =&amp;gt; Y? X&lt;br /&gt;s/\([A-Za-z&amp;quot;]\+\) *| *(\([A-Za-z&amp;quot;]\+\) *\1)/\2? \1/g&lt;br /&gt;&lt;br /&gt;# X | (X Y) =&amp;gt; X Y?&lt;br /&gt;s/\([A-Za-z&amp;quot;]\+\) *| *(\1 *\([A-Za-z&amp;quot;]\+\))/\1 \2?/g&lt;br /&gt;&lt;br /&gt;# (X Y) | Y =&amp;gt; X? Y&lt;br /&gt;s/(\([A-Za-z&amp;quot;]\+\) *\([A-Za-z&amp;quot;]\+\)) *| *\2/\1? \2/g&lt;br /&gt;&lt;br /&gt;# (X Y) | X =&amp;gt; X Y?&lt;br /&gt;s/(\([A-Za-z&amp;quot;]\+\) *\([A-Za-z&amp;quot;]\+\)) *| *\1/\1 \2?/g&lt;/pre&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3314601722322098765-2723569469884947810?l=fornax-sculptor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fornax-sculptor.blogspot.com/feeds/2723569469884947810/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fornax-sculptor.blogspot.com/2010/05/generating-syntaxrailroad-diagrams-from.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/2723569469884947810'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/2723569469884947810'/><link rel='alternate' type='text/html' href='http://fornax-sculptor.blogspot.com/2010/05/generating-syntaxrailroad-diagrams-from.html' title='Generating Syntax/Railroad diagrams from Xtext'/><author><name>Pavel Tavoda</name><uri>http://www.blogger.com/profile/09784303107477583252</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://1.bp.blogspot.com/_CBW1jk_oLkw/S-AltouYAhI/AAAAAAAAAAM/D0pr3K3b-UM/S220/face.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_CBW1jk_oLkw/S-CVGejtLyI/AAAAAAAAAA4/1Agi28i-QIw/s72-c/DslValueObject-S.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3314601722322098765.post-7445075985947921070</id><published>2010-04-28T21:20:00.002+02:00</published><updated>2010-04-28T21:35:53.709+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Sculptor'/><category scheme='http://www.blogger.com/atom/ns#' term='Internal DSL'/><category scheme='http://www.blogger.com/atom/ns#' term='MongoDB'/><category scheme='http://www.blogger.com/atom/ns#' term='NoSQL'/><title type='text'>MongoDB with Sculptor - Repository</title><content type='html'>This post is part of a &lt;a href="http://fornax-sculptor.blogspot.com/2010/04/mongodb-with-sculptor-introduction.html"&gt;series of articles&lt;/a&gt; describing the support for MongoDB in Sculptor. This post explains how Repositories with useful operations are defined and how queries can be expressed in terms of the domain.&lt;br /&gt;&lt;br /&gt;In the visualization of the &lt;a href="http://fornax-sculptor.blogspot.com/2010/04/mongodb-with-sculptor-introduction.html"&gt;blog sample&lt;/a&gt; you maybe noticed that there are Services with &lt;span style="font-style:italic;"&gt;findById&lt;/span&gt;, &lt;span style="font-style:italic;"&gt;findAll&lt;/span&gt;, &lt;span style="font-style:italic;"&gt;save&lt;/span&gt; and &lt;span style="font-style:italic;"&gt;delete&lt;/span&gt; even though they were not explicitly defined in the model. They come from that the domain objects are marked with with &lt;span style="font-style:italic;"&gt;scaffold&lt;/span&gt;. That automatically generates some predefined CRUD operations in the Repository and corresponding Service.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:text"&gt;&lt;br /&gt;    Entity BlogPost {&lt;br /&gt;            scaffold&lt;br /&gt;            - Blog inBlog&lt;br /&gt;            String slug key&lt;br /&gt;            String title&lt;br /&gt;            String body&lt;br /&gt;            DateTime published nullable&lt;br /&gt;            Set&amp;lt;String&amp;gt; tags&lt;br /&gt;            - Author writtenBy&lt;br /&gt;            - List&amp;lt;Comment&amp;gt; comments opposite forPost&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;You can also define the needed operations explicitly like this:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:text"&gt;&lt;br /&gt;    Entity BlogPost {&lt;br /&gt;            - Blog inBlog&lt;br /&gt;            String slug key&lt;br /&gt;            String title&lt;br /&gt;            String body&lt;br /&gt;            DateTime published nullable&lt;br /&gt;            Set&amp;lt;String&amp;gt; tags&lt;br /&gt;            - Author writtenBy&lt;br /&gt;            - List&amp;lt;Comment&amp;gt; comments opposite forPost&lt;br /&gt;&lt;br /&gt;            Repository BlogPostRepository { &lt;br /&gt;                save;&lt;br /&gt;                delete;&lt;br /&gt;                findAll(PagingParameter pagingParameter);&lt;br /&gt;                findById;&lt;br /&gt;                findByKey;&lt;br /&gt;             List&amp;lt;@BlogPost&amp;gt; findPostsInBlog(@Blog blog);&lt;br /&gt;             List&amp;lt;@BlogPost&amp;gt; findPostsWithComments =&amp;gt; AccessObject;&lt;br /&gt;             List&amp;lt;@BlogPost&amp;gt; findPostsWithTags(Set&amp;lt;String&amp;gt; tags);&lt;br /&gt;             protected findByCondition;&lt;br /&gt;            }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;You don't need to do any manual coding for the built in operations, such as save, delete, findAll, findById, findByKey. &lt;br /&gt;findByKey is for the natural key, i.e. the attributes marked with &lt;span style="font-style:italic;"&gt;key&lt;/span&gt; (slug above). &lt;br /&gt;&lt;br /&gt;There is good support for pagination and sorting.&lt;br /&gt;&lt;br /&gt;As you see in the above sample it is also possible to define your own repository operations, such as findPostsWithTags.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;findByCondition&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The most interesting finder is findByCondition. Queries can expressed with an internal DSL that support code completion and refactoring. It is best illustrated with a few samples.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:java"&gt;&lt;br /&gt;    public List&amp;lt;BlogPost&amp;gt; findPostsInBlog(Blog blog) {&lt;br /&gt;        List&amp;lt;ConditionalCriteria&amp;gt; condition = criteriaFor(BlogPost.class)&lt;br /&gt;            .withProperty(inBlog()).eq(blog)&lt;br /&gt;            .orderBy(published()).descending()&lt;br /&gt;            .build();&lt;br /&gt;        return findByCondition(condition);&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:java"&gt;&lt;br /&gt;    public List&amp;lt;BlogPost&amp;gt; findPostsWithGreatComments() {&lt;br /&gt;        List&amp;lt;ConditionalCriteria&amp;gt; condition = criteriaFor(BlogPost.class)&lt;br /&gt;            .withProperty(comments().title()).ignoreCaseLike(&amp;quot;.*great.*&amp;quot;)&lt;br /&gt;            .and().withProperty(published()).isNotNull()&lt;br /&gt;            .orderBy(published()).descending().build();&lt;br /&gt;        return findByCondition(condition);&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The ConditionalCriteriaBuilder has support for defining queries with eq, like, between, lessThan, greaterThan, in, and, not, orderBy, and some more.&lt;br /&gt;&lt;br /&gt;You can of course also work directly with the underlaying MongoDB DBCollection and DBObject for doing advanced queries that might not be supported by findByCondition.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3314601722322098765-7445075985947921070?l=fornax-sculptor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fornax-sculptor.blogspot.com/feeds/7445075985947921070/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fornax-sculptor.blogspot.com/2010/04/mongodb-with-sculptor-repository.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/7445075985947921070'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/7445075985947921070'/><link rel='alternate' type='text/html' href='http://fornax-sculptor.blogspot.com/2010/04/mongodb-with-sculptor-repository.html' title='MongoDB with Sculptor - Repository'/><author><name>Patrik</name><uri>http://www.blogger.com/profile/11876565293495127382</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3314601722322098765.post-3956408294894832761</id><published>2010-04-28T21:10:00.001+02:00</published><updated>2010-04-28T21:27:19.348+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Sculptor'/><category scheme='http://www.blogger.com/atom/ns#' term='MongoDB'/><category scheme='http://www.blogger.com/atom/ns#' term='DDD'/><category scheme='http://www.blogger.com/atom/ns#' term='NoSQL'/><title type='text'>MongoDB with Sculptor - Associations</title><content type='html'>This post is part of a &lt;a href="http://fornax-sculptor.blogspot.com/2010/04/mongodb-with-sculptor-introduction.html"&gt;series of articles&lt;/a&gt; describing the support for MongoDB in Sculptor. This post explains how associations and inheritance are managed.&lt;br /&gt;&lt;br /&gt;MongoDB is not a relational database and there is no such thing as joins. You can still use associations between domain objects. Associations can be of two main categories, either embedded or reference by id.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Aggregates&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Aggregates are one of the core building blocks in Domain-Driven Design (DDD). MongoDB has perfect support for aggregates, since an associated object can be stored as an embedded document, i.e. it belongs to parent object and cannot be shared between several objects. &lt;br /&gt;&lt;br /&gt;Let us repeat what DDD says about aggregates:&lt;br /&gt;&lt;blockquote&gt;An Aggregate is a group of associated objects which are considered as one unit with regard to data changes. The Aggregate is demarcated by a boundary which separates the objects inside from those outside. Each Aggregate has one root. The root is an Entity, and it is the only object accessible from outside. The root can hold references to any of the aggregate objects, and the other objects can hold references to each other, but an outside object can hold references only to the root object.&lt;br /&gt;    - from DDD Quickly&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;Sculptor will validate the reference constraints described in the quote above. Repositories are only available for aggregate roots. Aggregates are defined with &lt;span style="font-style:italic;"&gt;belongsTo&lt;/span&gt; or &lt;span style="font-style:italic;"&gt;not aggregateRoot&lt;/span&gt; in the owned DomainObjects.&lt;br /&gt;&lt;br /&gt;A typical aggregate in the blog sample is that Comment belongs to BlogPost&lt;br /&gt;&lt;pre class="brush:text"&gt;&lt;br /&gt;        Entity BlogPost {&lt;br /&gt;            String slug key&lt;br /&gt;            String title&lt;br /&gt;            String body&lt;br /&gt;            DateTime published nullable&lt;br /&gt;            - List&lt;Comment&gt; comments opposite forPost&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        ValueObject Comment {&lt;br /&gt;            not aggregateRoot&lt;br /&gt;            - BlogPost forPost opposite comments&lt;br /&gt;            String title&lt;br /&gt;            String body&lt;br /&gt;        }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;An aggregate can of course include several classes as in this sample:&lt;br /&gt;&lt;pre class="brush:text"&gt;&lt;br /&gt;        Entity Cargo {&lt;br /&gt;            - TrackingId trackingId key;&lt;br /&gt;            - Location origin required;&lt;br /&gt;            - Location destination required;&lt;br /&gt;            - Itinerary itinerary nullable opposite cargo;&lt;br /&gt;            - Set&amp;lt;HandlingEvent&amp;gt; events opposite cargo;&lt;br /&gt;&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        BasicType TrackingId {&lt;br /&gt;            String identifier key&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        ValueObject Itinerary {&lt;br /&gt;            belongsTo Cargo&lt;br /&gt;            - Cargo cargo nullable opposite itinerary&lt;br /&gt;            - List&amp;lt;Leg&amp;gt; legs&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        ValueObject Leg {&lt;br /&gt;            belongsTo Cargo&lt;br /&gt;            - CarrierMovement carrierMovement&lt;br /&gt;            - Location from &lt;br /&gt;            - Location to&lt;br /&gt;        }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;In above sample the TrackingId, Itinary and Leg are all stored toghether with the Cargo. &lt;a href="http://fornax-sculptor.blogspot.com/2009/08/introducing-type.html"&gt;BasicTypes&lt;/a&gt; are also stored as embedded documents.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Reference by Id&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The other alternative is to store ids of the referred objects. In the domain objects there are generated getters that lazily fetch associated objects from the ids. This means that you don't have to work with the ids yourself, you can follow associations as usual, but be aware that an invocation of such a getter might need to query the database.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:java"&gt;&lt;br /&gt;    Set&lt;Author&gt; writers = blog.getWriters();&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;In the same way you can modify unowned associations by working with objects rather than ids. &lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:java"&gt;&lt;br /&gt;        Author pn = new Author("Patrik");&lt;br /&gt;        pn = authorService.save(getServiceContext(), pn);&lt;br /&gt;        blog.addWriter(pn);&lt;br /&gt;        Author ak = new Author("Andreas");&lt;br /&gt;        ak = authorService.save(getServiceContext(), ak);&lt;br /&gt;        blog.addWriter(ak);&lt;br /&gt;        blogService.save(getServiceContext(), blog);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Referential integrity of the stored ids are not enforced. It must be handled by your program. Lazy getters of associations will not fail if referred to object is missing, they will return null for single value references and ignore missing objects for collection references. This means that you can cleanup dangling references by fetching objects, populate associations by invoking the getters and then save the object.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Inheritance&lt;/span&gt;&lt;br /&gt;The MongoDB feature of Sculptor has full support for inheritance. It is even possible to do polymorphic queries.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:text"&gt;&lt;br /&gt;        abstract Entity Media {&lt;br /&gt;            String title !changeable&lt;br /&gt;            &lt;br /&gt;            Repository MediaRepository {&lt;br /&gt;                List&amp;lt;@Media&amp;gt; findByTitle(String title);&lt;br /&gt;                protected findByCondition;&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;        &lt;br /&gt;        Entity Book extends @Media {&lt;br /&gt;            String isbn key length=&amp;quot;20&amp;quot;&lt;br /&gt;        }&lt;br /&gt;        &lt;br /&gt;        Entity Movie extends @Media {&lt;br /&gt;            String urlIMDB key&lt;br /&gt;            Integer playLength&lt;br /&gt;            - @Genre category nullable&lt;br /&gt;        }&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3314601722322098765-3956408294894832761?l=fornax-sculptor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fornax-sculptor.blogspot.com/feeds/3956408294894832761/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fornax-sculptor.blogspot.com/2010/04/mongodb-with-sculptor-associations.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/3956408294894832761'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/3956408294894832761'/><link rel='alternate' type='text/html' href='http://fornax-sculptor.blogspot.com/2010/04/mongodb-with-sculptor-associations.html' title='MongoDB with Sculptor - Associations'/><author><name>Patrik</name><uri>http://www.blogger.com/profile/11876565293495127382</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3314601722322098765.post-8082403564730322708</id><published>2010-04-28T21:00:00.001+02:00</published><updated>2010-04-28T21:20:57.935+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Sculptor'/><category scheme='http://www.blogger.com/atom/ns#' term='MongoDB'/><category scheme='http://www.blogger.com/atom/ns#' term='NoSQL'/><category scheme='http://www.blogger.com/atom/ns#' term='Code generator'/><title type='text'>MongoDB with Sculptor - Data Mapper</title><content type='html'>This post is part of a &lt;a href="http://fornax-sculptor.blogspot.com/2010/04/mongodb-with-sculptor-introduction.html"&gt;series of articles&lt;/a&gt; describing the support for MongoDB in Sculptor. This post explains how the mapping between domain objects and MongoDB data objects is performed.&lt;br /&gt;&lt;br /&gt;When working with mongoDB Java API the &lt;a href="http://api.mongodb.org/java/1.3/com/mongodb/DBObject.html"&gt;DBObject&lt;/a&gt; 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.&lt;br /&gt;&lt;br /&gt;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. &lt;br /&gt;&lt;br /&gt;An early definition of a BlogPost in the blog sample might look like this in Sculptor model:&lt;br /&gt;&lt;pre class="brush:text"&gt;&lt;br /&gt;        Entity BlogPost {&lt;br /&gt;            String slug key&lt;br /&gt;            String title&lt;br /&gt;            String body&lt;br /&gt;        }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;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:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:java"&gt;&lt;br /&gt;    public DBObject toData(BlogPost from) {&lt;br /&gt;        if (from == null) {&lt;br /&gt;            return null;&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        DBObject result = new BasicDBObject();&lt;br /&gt;&lt;br /&gt;        if (from.getId() != null) {&lt;br /&gt;            ObjectId objectId = ObjectId.massageToObjectId(from.getId());&lt;br /&gt;            result.put("_id", objectId);&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        result.put("slug", from.getSlug());&lt;br /&gt;        result.put("title", from.getTitle());&lt;br /&gt;        result.put("body", from.getBody());&lt;br /&gt;        &lt;br /&gt;        return result;&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;and in the other direction:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:java"&gt;&lt;br /&gt;    public BlogPost toDomain(DBObject from) {&lt;br /&gt;        if (from == null) {&lt;br /&gt;            return null;&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        String slug = (String) from.get("slug");&lt;br /&gt;&lt;br /&gt;        BlogPost result = new BlogPost(slug);&lt;br /&gt;&lt;br /&gt;        if (from.containsField("_id")) {&lt;br /&gt;            ObjectId objectId = (ObjectId) from.get("_id");&lt;br /&gt;            String idString = objectId.toStringMongod();&lt;br /&gt;            IdReflectionUtil.internalSetId(result, idString);&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        if (from.containsField("title")) {&lt;br /&gt;            result.setTitle((String) from.get("title"));&lt;br /&gt;        }&lt;br /&gt;        if (from.containsField("body")) {&lt;br /&gt;            result.setBody((String) from.get("body"));&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        return result;&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:text"&gt;&lt;br /&gt;        Entity BlogPost {&lt;br /&gt;            databaseTable="BlogEntries"&lt;br /&gt;            String slug key&lt;br /&gt;            String title&lt;br /&gt;            String body databaseColumn="content"&lt;br /&gt;        }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;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.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3314601722322098765-8082403564730322708?l=fornax-sculptor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fornax-sculptor.blogspot.com/feeds/8082403564730322708/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fornax-sculptor.blogspot.com/2010/04/mongodb-with-sculptor-data-mapper.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/8082403564730322708'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/8082403564730322708'/><link rel='alternate' type='text/html' href='http://fornax-sculptor.blogspot.com/2010/04/mongodb-with-sculptor-data-mapper.html' title='MongoDB with Sculptor - Data Mapper'/><author><name>Patrik</name><uri>http://www.blogger.com/profile/11876565293495127382</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3314601722322098765.post-242758945763161391</id><published>2010-04-27T11:30:00.004+02:00</published><updated>2010-04-28T21:42:29.045+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Sculptor'/><category scheme='http://www.blogger.com/atom/ns#' term='MongoDB'/><category scheme='http://www.blogger.com/atom/ns#' term='DDD'/><category scheme='http://www.blogger.com/atom/ns#' term='NoSQL'/><title type='text'>MongoDB with Sculptor - Introduction</title><content type='html'>&lt;a href="http://www.mongodb.org/"&gt;MongoDB&lt;/a&gt; bridges the gap between key-value stores (which are fast and highly scalable) and traditional RDBMS systems (which provide rich queries and deep functionality).&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Would you like to?&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;Use a rich persistent domain model à la DDD with automatic mapping to MongoDB data structures.&lt;/li&gt;&lt;li&gt;Use associations even though the underlaying data store is not relational.&lt;/li&gt;&lt;li&gt;Express queries in refactoring safe terms of the domain model.&lt;/li&gt;&lt;li&gt;Get CRUD operations, and GUI, for free. &lt;/li&gt;&lt;/ul&gt;&lt;div&gt;Then you should try Sculptor with the brand new MongoDB &lt;a href="http://fornax-sculptor.blogspot.com/2010/01/pick-n-choose-target-implementation.html"&gt;target implementation&lt;/a&gt;.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;In the next few days I will describe the features one-by-one, but first an brief overview of what Sculptor provides for efficient development of a typical enterprise or web application with MongoDB as persistent store.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Sculptor is a productivity tool that let you express your design intent in a textual DSL, from which Sculptor generates high quality Java code and configuration. Read more in &lt;a href="http://fornax-sculptor.blogspot.com/2009/06/what-is-sculptor.html"&gt;What is Sculptor&lt;/a&gt;?&lt;/div&gt;&lt;/div&gt;&lt;br /&gt;Sculptor generates &lt;b&gt;data mapper&lt;/b&gt; classes that converts domain objects to/from MongoDB data structures, &lt;a href="http://api.mongodb.org/java/1.3/com/mongodb/DBObject.html"&gt;DBObjects&lt;/a&gt;.&lt;div&gt;&lt;br /&gt;Sculptor provides generic &lt;b&gt;repository operations&lt;/b&gt; for use with MongoDB. This includes operations such as save, delete, findById, findByKey, findByCondition, and some more.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Queries&lt;/b&gt; can be expressed with a slick fluent api that support code completion and refactoring.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Rich support for associations&lt;/b&gt;. Aggregates are stored as a embedded documents. Other associations are stored with referring ids. In the domain objects there are generated getters that lazily fetch associated objects from the ids. This means that you don't have to work with the ids yourself, you can follow associations as usual.&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;A sample blog application may look like this when defined in Sculptor textual DSL:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;a href="http://4.bp.blogspot.com/_qr2e_ywa0-I/S9arXylkhjI/AAAAAAAABSQ/34EOPPx9V68/s1600/blog_model.png"&gt;&lt;img src="http://4.bp.blogspot.com/_qr2e_ywa0-I/S9arXylkhjI/AAAAAAAABSQ/34EOPPx9V68/s800/blog_model.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5464743623065372210" style="cursor: pointer; width: 564px; height: 754px; " /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Visualization, generated by Sculptor, of the above model looks like this:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;a href="http://4.bp.blogspot.com/_qr2e_ywa0-I/S9ar9vQhcJI/AAAAAAAABSY/RROhQY460Lc/s1600/blog_umlgraph.png"&gt;&lt;img src="http://4.bp.blogspot.com/_qr2e_ywa0-I/S9ar9vQhcJI/AAAAAAAABSY/RROhQY460Lc/s800/blog_umlgraph.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5464744275006812306" style="cursor: pointer; width: 509px; height: 697px; " /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Read the other posts that describes the features in more detail:&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://fornax-sculptor.blogspot.com/2010/04/mongodb-with-sculptor-data-mapper.html"&gt;Data Mapper&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://fornax-sculptor.blogspot.com/2010/04/mongodb-with-sculptor-associations.html"&gt;Associations&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://fornax-sculptor.blogspot.com/2010/04/mongodb-with-sculptor-repository.html"&gt;Repository&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;More hands on instructions of how to use Sculptor is available in the &lt;a href="http://fornax.itemis.de/confluence/x/CIBE"&gt;Sculptor MongoDB Tutorial&lt;/a&gt;.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3314601722322098765-242758945763161391?l=fornax-sculptor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fornax-sculptor.blogspot.com/feeds/242758945763161391/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fornax-sculptor.blogspot.com/2010/04/mongodb-with-sculptor-introduction.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/242758945763161391'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/242758945763161391'/><link rel='alternate' type='text/html' href='http://fornax-sculptor.blogspot.com/2010/04/mongodb-with-sculptor-introduction.html' title='MongoDB with Sculptor - Introduction'/><author><name>Patrik</name><uri>http://www.blogger.com/profile/11876565293495127382</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_qr2e_ywa0-I/S9arXylkhjI/AAAAAAAABSQ/34EOPPx9V68/s72-c/blog_model.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3314601722322098765.post-5130271592302342841</id><published>2010-03-31T21:30:00.004+02:00</published><updated>2010-03-31T22:00:19.159+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Sculptor'/><category scheme='http://www.blogger.com/atom/ns#' term='NoSQL'/><title type='text'>Support for mongoDB started</title><content type='html'>I think NoSQL datastores are a very interesting complement to relational databases. I think &lt;a href="http://www.mongodb.org/"&gt;mongoDB&lt;/a&gt; is a good choice for the rich persistent domain models that are typical for a Sculptor application. Today's &lt;a href="http://www.infoq.com/news/2010/03/mongodb"&gt;article&lt;/a&gt; at InfoQ about mongoDB mention the relation between document stores and DDD. &lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;We have started prototyping and it looks promising. The idea is that that Sculptor will provide:&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;Access objects (implementation of repository operations) that interact with mongoDB.&lt;/li&gt;&lt;li&gt;Good query support (e.g. &lt;a href="http://fornax.itemis.de/confluence/display/fornax/3.+Advanced+Tutorial+(CSC)#3.AdvancedTutorial%28CSC%29-findByCondition"&gt;findByCondition&lt;/a&gt;). &lt;/li&gt;&lt;li&gt;Automatic mapping between domain objects and mongoDB documents (DBObject) will be generated. &lt;/li&gt;&lt;li&gt;Different types of association will be supported. Aggregates are typically embedded documents and other associations are stored as id references. Maybe we will provide a mechanism to lazy load associated documents.&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3314601722322098765-5130271592302342841?l=fornax-sculptor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fornax-sculptor.blogspot.com/feeds/5130271592302342841/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fornax-sculptor.blogspot.com/2010/03/support-for-mongodb-started.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/5130271592302342841'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/5130271592302342841'/><link rel='alternate' type='text/html' href='http://fornax-sculptor.blogspot.com/2010/03/support-for-mongodb-started.html' title='Support for mongoDB started'/><author><name>Patrik</name><uri>http://www.blogger.com/profile/11876565293495127382</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3314601722322098765.post-6773964340260069853</id><published>2010-03-20T22:10:00.015+01:00</published><updated>2010-03-20T23:16:26.380+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Sculptor'/><category scheme='http://www.blogger.com/atom/ns#' term='Visualization'/><title type='text'>Improved Graphviz Visualization</title><content type='html'>I have improved the visualization with several new features. You can use this new diagram generator right away as described in the end.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Several diagrams with different focus and level of detail are generated. Below are samples of the different types of diagrams. You can click on the images below to see them in full scale.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Module dependencies:&lt;/div&gt;&lt;div&gt;&lt;img src="http://3.bp.blogspot.com/_qr2e_ywa0-I/S6U7pzJg12I/AAAAAAAABRk/1ByqwfKZXGw/s400/umlgraph-dependencies.dot.png" style="cursor:pointer; cursor:hand;width: 157px; height: 267px;" border="0" alt="" id="BLOGGER_PHOTO_ID_5450828513292638050" /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Overview:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;a href="http://2.bp.blogspot.com/_qr2e_ywa0-I/S6U8DKtaPRI/AAAAAAAABRs/1HBz-EGXdVA/s1600-h/umlgraph-overview.dot.png"&gt;&lt;img src="http://2.bp.blogspot.com/_qr2e_ywa0-I/S6U8DKtaPRI/AAAAAAAABRs/1HBz-EGXdVA/s400/umlgraph-overview.dot.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5450828949113945362" style="cursor: pointer; width: 400px; height: 171px; " /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;One separate diagram for each module:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;a href="http://1.bp.blogspot.com/_qr2e_ywa0-I/S6U8nOxlfQI/AAAAAAAABR0/lFBpxh18Ed4/s1600-h/umlgraph-person.dot.png"&gt;&lt;img src="http://1.bp.blogspot.com/_qr2e_ywa0-I/S6U8nOxlfQI/AAAAAAAABR0/lFBpxh18Ed4/s400/umlgraph-person.dot.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5450829568680492290" style="cursor: pointer; width: 400px; height: 190px; " /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Diagram with focus on elements marked as core domain (hint="umlgraph=core"). Note that core domain objects also have another font color (and background color) in all diagrams.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;a href="http://3.bp.blogspot.com/_qr2e_ywa0-I/S6U9iD1nt8I/AAAAAAAABR8/2IhBqF-oUPs/s1600-h/umlgraph-core-domain.dot.png"&gt;&lt;img src="http://3.bp.blogspot.com/_qr2e_ywa0-I/S6U9iD1nt8I/AAAAAAAABR8/2IhBqF-oUPs/s400/umlgraph-core-domain.dot.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5450830579356907458" style="cursor: pointer; width: 400px; height: 214px; " /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;And finally, a diagram with full detail of all objects.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;a href="http://2.bp.blogspot.com/_qr2e_ywa0-I/S6U99mIQoxI/AAAAAAAABSE/pVfT0bvq_94/s1600-h/umlgraph.dot.png"&gt;&lt;img src="http://2.bp.blogspot.com/_qr2e_ywa0-I/S6U99mIQoxI/AAAAAAAABSE/pVfT0bvq_94/s400/umlgraph.dot.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5450831052418360082" style="cursor: pointer; width: 400px; height: 268px; " /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;All colors can be configured using generator properties file and it is also possible to set the color for individual elements using a hint in model file (hint="umlgraph.bgcolor=FFCC99"). It is also possible to hide elements using hint="umlgraph=hide".&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;You can generate the new diagrams in your Sculptor project right away. Version 1.8.1-SNAPSHOT includes this, but even if you need to use an older version (not snaphot) you can drop in the new generator templates in your project. Download from trunk&lt;/div&gt;&lt;div&gt;&lt;a href="https://fornax.svn.sourceforge.net/svnroot/fornax/trunk/cartridges/sculptor/fornax-cartridges-sculptor-generator/src/main/resources/extensions/umlgraphhelper.ext"&gt;umlgraphhelper.ext&lt;/a&gt; to your src/main/resources/extensions/ and &lt;/div&gt;&lt;div&gt;&lt;a href="https://fornax.svn.sourceforge.net/svnroot/fornax/trunk/cartridges/sculptor/fornax-cartridges-sculptor-generator/src/main/resources/templates/UMLGraph.xpt"&gt;UMLGraph.xpt&lt;/a&gt; to your src/main/resources/templates/&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;To generate images (png) from the generated &lt;a href="http://www.graphviz.org/"&gt;Graphviz&lt;/a&gt; dot files you can use the new fornax-graphviz-m2-plugin. Add the following to your pom.xml:&lt;/div&gt;&lt;br /&gt;&lt;pre class="brush:xml"&gt;&lt;br /&gt;  &amp;lt;plugin&amp;gt;&lt;br /&gt;   &amp;lt;groupId&amp;gt;org.fornax.toolsupport&amp;lt;/groupId&amp;gt;&lt;br /&gt;   &amp;lt;artifactId&amp;gt;fornax-graphviz-m2-plugin&amp;lt;/artifactId&amp;gt;&lt;br /&gt;   &amp;lt;version&amp;gt;1.0.0&amp;lt;/version&amp;gt;&lt;br /&gt;   &amp;lt;executions&amp;gt;&lt;br /&gt;    &amp;lt;execution&amp;gt;&lt;br /&gt;     &amp;lt;phase&amp;gt;generate-resources&amp;lt;/phase&amp;gt;&lt;br /&gt;     &amp;lt;goals&amp;gt;&amp;lt;goal&amp;gt;run&amp;lt;/goal&amp;gt;&amp;lt;/goals&amp;gt;&lt;br /&gt;    &amp;lt;/execution&amp;gt;&lt;br /&gt;   &amp;lt;/executions&amp;gt;&lt;br /&gt;  &amp;lt;/plugin&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;div&gt;You also need to install the excellent &lt;a href="http://www.graphviz.org/"&gt;Graphviz&lt;/a&gt; tool.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3314601722322098765-6773964340260069853?l=fornax-sculptor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fornax-sculptor.blogspot.com/feeds/6773964340260069853/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fornax-sculptor.blogspot.com/2010/03/improved-graphviz-visualization.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/6773964340260069853'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/6773964340260069853'/><link rel='alternate' type='text/html' href='http://fornax-sculptor.blogspot.com/2010/03/improved-graphviz-visualization.html' title='Improved Graphviz Visualization'/><author><name>Patrik</name><uri>http://www.blogger.com/profile/11876565293495127382</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_qr2e_ywa0-I/S6U7pzJg12I/AAAAAAAABRk/1ByqwfKZXGw/s72-c/umlgraph-dependencies.dot.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3314601722322098765.post-2545286726854366362</id><published>2010-03-11T19:07:00.004+01:00</published><updated>2010-03-11T19:20:29.024+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Sculptor'/><category scheme='http://www.blogger.com/atom/ns#' term='Success Story'/><title type='text'>Success Story - Using my Own Tool</title><content type='html'>I’m the founder and lead developer of Sculptor. Why would I write a success story? They should be written by users. Right, I’m a user of my own tool. This is maybe the key to why the tool is working as good as it does.&lt;br /&gt;&lt;br /&gt;Since May 2009 I have been working with a customer in the logistics business. I’m involved in several projects but in my “main” project we develop a Track &amp;amp; Trace system. We receive events with parcel state information from several production systems in different countries. Volumes around 500 million tracking events during 6 months, which is the period we need to support track&amp;amp;trace searching.&lt;br /&gt;&lt;br /&gt;In this department JEE was greenfield development. They have been using Java before, but not with application servers and modern frameworks. We established guidelines and tools for the new platform.&lt;br /&gt;&lt;br /&gt;The introduction of Sculptor started with a technical prototype of the domain model. About 20 entities were developed in 2 days. Test driven. Of course this was only an initial draft, but it was important to quickly be able to try our ideas in practice. We have done a lot of &lt;a href="http://fornax-sculptor.blogspot.com/2009/08/refactoring.html"&gt;refactoring&lt;/a&gt; of the domain model since then and Sculptor doesn’t sit in the way for doing changes. It simplifies change.&lt;br /&gt;&lt;br /&gt;When evaluating Sculptor for the prototype my team developers stressed the importance of being able to remove the tool and continue without starting from scratch. It was good to see that the developers were able to quickly grasp the concepts and be able to use the tool immediately without any lengthy education.&lt;br /&gt;&lt;br /&gt;When I and the other developers in my team had used Sculptor for a while another team wanted to try it also. I helped with converting existing code. The conversion and initial learning was smooth.&lt;br /&gt;&lt;br /&gt;We listed the following benefits of using Sculptor for all new projects at the department:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Improved productivity. Generate code from high level description. Less manual mistakes. Relieved the developers from repeating and tedious programming tasks.&lt;/li&gt;&lt;li&gt;High quality of generated code. As if written manually, but better. We actually found several flaws in the hand written code when converting it to Sculptor.&lt;/li&gt;&lt;li&gt;Consistent design and structure. The department was running several projects in parallel, with different developers, and varying experience of JEE. Consistency between solutions was considered very important from a maintenance perspective.&lt;/li&gt;&lt;li&gt;Easy to learn. The developers that tried Sculptor found it intuitive to use. “It’s nothing magic”.&lt;/li&gt;&lt;li&gt;Great possibilities for customization. We did some adjustments to fit the &lt;a href="http://fornax-sculptor.blogspot.com/2009/10/even-weird-naming-conventions-are-good.html"&gt;database naming conventions&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;Good design. The design of the generated code match our guidelines, JEE, JPA, DDD.&lt;/li&gt;&lt;li&gt;Easy to &lt;a href="http://fornax-sculptor.blogspot.com/2010/01/how-to-remove-sculptor.html"&gt;remove the tool&lt;/a&gt;. No dynamic runtime magic.&lt;/li&gt;&lt;/ul&gt;We have continued to use Sculptor successfully. We use Sculptor for all new projects. Several new developers have joined the teams and quickly learnt how to use Sculptor. We are 11 developers in 4 projects right now.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3314601722322098765-2545286726854366362?l=fornax-sculptor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fornax-sculptor.blogspot.com/feeds/2545286726854366362/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fornax-sculptor.blogspot.com/2010/03/success-story-using-my-own-tool.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/2545286726854366362'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/2545286726854366362'/><link rel='alternate' type='text/html' href='http://fornax-sculptor.blogspot.com/2010/03/success-story-using-my-own-tool.html' title='Success Story - Using my Own Tool'/><author><name>Patrik</name><uri>http://www.blogger.com/profile/11876565293495127382</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3314601722322098765.post-2259425763586955252</id><published>2010-02-21T21:11:00.006+01:00</published><updated>2010-02-21T21:44:22.264+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Sculptor'/><category scheme='http://www.blogger.com/atom/ns#' term='Customization'/><title type='text'>Bonus Material</title><content type='html'>For my customer's project I created a small addition to Sculptor, that might be useful for you also. The domain model is important for communication, and we need to document it. The difficult thing with documentation is to keep it up to date with changes. Placing the descriptions close to the code makes it easier to do the changes to the documentation at the same time as when you change the code. Since we use Sculptor it was a natural choice to write the descriptions in the model file and generated the documentation.&lt;pre class="brush:java"&gt;&lt;br /&gt;     "A location is our model is stops on a journey, such as cargo&lt;br /&gt;     origin or destination, or carrier movement endpoints."&lt;br /&gt;     Entity Location {&lt;br /&gt;         not optimisticLocking&lt;br /&gt;         - @UnLocode unLocode key&lt;br /&gt;         'Actual name of this location, e.g. "Stockholm"'&lt;br /&gt;         String name not changeable&lt;br /&gt;      &lt;br /&gt;         Repository LocationRepository {&lt;br /&gt;             @Location find(@UnLocode unLocode) throws LocationNotFoundException;&lt;br /&gt;             findAll;&lt;br /&gt;             protected findByKeys;&lt;br /&gt;         }&lt;br /&gt;     }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;From the model we generate HTML documentation of all domain objects including their attributes and associations.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Try it if you like. It is easy to understand and adjust to fit your needs.&lt;br /&gt;&lt;br /&gt;&lt;a href="https://fornax.svn.sourceforge.net/svnroot/fornax/trunk/cartridges/sculptor/DDDSample/src/main/resources/templates/DomainModelDoc.xpt"&gt;Download this generation template&lt;/a&gt; and place it in src/main/resources/templates/&lt;br /&gt;In SpecialCases.xpt you add&lt;br /&gt;&lt;pre class="brush:text"&gt;&lt;br /&gt;«AROUND templates::Root::Root FOR Application»&lt;br /&gt;   «targetDef.proceed()»&lt;br /&gt;   «EXPAND DomainModelDoc::start»&lt;br /&gt;«ENDAROUND»&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The generated result is located in src/generated/resources/DomainModelDoc.html&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3314601722322098765-2259425763586955252?l=fornax-sculptor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fornax-sculptor.blogspot.com/feeds/2259425763586955252/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fornax-sculptor.blogspot.com/2010/02/bonus-material.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/2259425763586955252'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/2259425763586955252'/><link rel='alternate' type='text/html' href='http://fornax-sculptor.blogspot.com/2010/02/bonus-material.html' title='Bonus Material'/><author><name>Patrik</name><uri>http://www.blogger.com/profile/11876565293495127382</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3314601722322098765.post-392264695707184580</id><published>2010-02-16T20:45:00.001+01:00</published><updated>2010-02-16T20:59:36.435+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Sculptor'/><category scheme='http://www.blogger.com/atom/ns#' term='DSL'/><category scheme='http://www.blogger.com/atom/ns#' term='Release'/><title type='text'>Sculptor 1.8.0 Released</title><content type='html'>&lt;div&gt;Only two weeks after the &lt;a href="http://fornax-sculptor.blogspot.com/2010/02/sculptor-170-gae-smartclient.html"&gt;big 1.7.0&lt;/a&gt; we are happy to deliver another major release - 1.8.0. This time it is no new functionality, but the usability of DSL editor is much, much better. &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;We have migrated from openArchitectureWare 4.3.1 to Xtext/Xpand/MWE 0.7.2 at Eclipse.org. Many thanks to Todd Ferrell for doing most of this migration.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Now, content assist (ctrl+space) works as you would expect.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Another useful feature is formatting (pretty printing) of model files (ctrl+shift+F).&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Watch the DSL editor in action in the demo below, or even better &lt;a href="http://fornax-platform.org/cp/x/aAQ"&gt;try it yourself&lt;/a&gt;.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;br /&gt;&lt;embed src="http://blip.tv/play/AYHGkiwA" type="application/x-shockwave-flash" width="852" height="510" allowscriptaccess="always" allowfullscreen="true"&gt;&lt;/embed&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3314601722322098765-392264695707184580?l=fornax-sculptor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fornax-sculptor.blogspot.com/feeds/392264695707184580/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fornax-sculptor.blogspot.com/2010/02/sculptor-180-released.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/392264695707184580'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/392264695707184580'/><link rel='alternate' type='text/html' href='http://fornax-sculptor.blogspot.com/2010/02/sculptor-180-released.html' title='Sculptor 1.8.0 Released'/><author><name>Patrik</name><uri>http://www.blogger.com/profile/11876565293495127382</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3314601722322098765.post-5221104541325225870</id><published>2010-02-09T07:42:00.000+01:00</published><updated>2010-02-09T11:41:54.434+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='springframework'/><category scheme='http://www.blogger.com/atom/ns#' term='Sculptor'/><category scheme='http://www.blogger.com/atom/ns#' term='Customization'/><category scheme='http://www.blogger.com/atom/ns#' term='Webflow'/><category scheme='http://www.blogger.com/atom/ns#' term='DSL'/><category scheme='http://www.blogger.com/atom/ns#' term='Spring'/><category scheme='http://www.blogger.com/atom/ns#' term='Code generator'/><title type='text'>Customization of webflows</title><content type='html'>With the new 1.7 release of sculptor the possibilities to customize flows for the web client is much better.&lt;br /&gt;With our Library example here is what you can do.&lt;br /&gt;For example, lets say you want to implement filter functionality for the library list feature. Here is the steps to do that:&lt;br /&gt;&lt;br /&gt;1) Specify that you want &lt;a href="http://martinfowler.com/dslwip/GenerationGap.html"&gt;gap&lt;/a&gt;-files for the list library feature, so in your model.guidesign:&lt;br /&gt;&lt;pre class="brush:java"&gt;&lt;br /&gt;gui Library for Library {&lt;br /&gt;Module for media {&lt;br /&gt;    ListTask for Library {&lt;br /&gt;        gap&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now you have a bunch of files for the feature:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;src/generated/java/org...library/ListLibraryActionBase.java&lt;/li&gt;&lt;li&gt;src/main/java/org...library/ListLibraryAction.java -&gt; gap&lt;/li&gt;&lt;li&gt;src/generated/java/org...library/ListLibraryForm.java&lt;/li&gt;&lt;li&gt;src/WEB-INF/generated/flows/media/listLibrary/listLibrary-base.xml&lt;/li&gt;&lt;li&gt;src/WEB-INF/generated/flows/media/listLibrary/list_include.xhtml&lt;/li&gt;&lt;li&gt;src/WEB-INF/flows/media/listLibrary/listLibrary-flow.xml -&gt; gap&lt;/li&gt;&lt;li&gt;src/WEB-INF/flows/media/listLibrary/list.xhtml -&gt; gap&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;2) Edit the media/listLibrary flow:&lt;br /&gt;&lt;pre class="brush:xml"&gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;?xml version="1.0" encoding="UTF-8"?&amp;gt;&lt;br /&gt;&amp;lt;flow xmlns="http://www.springframework.org/schema/webflow"&lt;br /&gt;     xmlns:ns0="http://www.w3.org/2001/XMLSchema-instance"&lt;br /&gt;     ns0:schemaLocation="http://www.springframework.org/schema/webflow&lt;br /&gt;     http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd"&lt;br /&gt;     parent="media/listLibraryBase"&amp;gt;&lt;br /&gt; &amp;lt;view-state id="list"&amp;gt;&lt;br /&gt;     &amp;lt;transition on="filterLibrary" to="listByFilter" /&amp;gt;&lt;br /&gt; &amp;lt;/view-state&amp;gt;&lt;br /&gt; &amp;lt;view-state id="listByFilter" model="listLibraryForm"&lt;br /&gt;     view="/WEB-INF/flows/media/listLibrary/list.xhtml" parent="media/listLibraryBase#list"&amp;gt;&lt;br /&gt;     &amp;lt;on-render&amp;gt;&lt;br /&gt;         &amp;lt;evaluate&lt;br /&gt;             expression="listLibraryAction.findByFilter(flowRequestContext)" /&amp;gt;&lt;br /&gt;     &amp;lt;/on-render&amp;gt;&lt;br /&gt; &amp;lt;/view-state&amp;gt;&lt;br /&gt;&amp;lt;/flow&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;3) Edit the ListLibraryAction, add the method:&lt;br /&gt;&lt;pre class="brush:java"&gt;&lt;br /&gt;&lt;br /&gt;public String findByFilter(RequestContext ctx) {&lt;br /&gt;    getRepository().clear();&lt;br /&gt;&lt;br /&gt;    List&amp;lt;library&amp;gt; allLibraries = getLibraryService().findAll(ServiceContextStore.get());&lt;br /&gt;    List&amp;lt;library&amp;gt; filtered = new ArrayList&amp;lt;library&amp;gt;();&lt;br /&gt;    String filter = ctx.getRequestParameters().get("libraryFilter");&lt;br /&gt;    for (Library library : allLibraries) {&lt;br /&gt;        if (library.getName().startsWith(filter)) {&lt;br /&gt;            filtered.add(library);&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;    formObject(ctx).setAllLibraries(filtered);&lt;br /&gt;    return "success";&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;4) Add a simple form with a text field and a button to the media/listLibrary/list.xhtml-file:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:xml"&gt;&lt;br /&gt;&amp;lt;html xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets"&lt;br /&gt; xmlns:f="http://java.sun.com/jsf/core" xmlns:t="http://myfaces.apache.org/tomahawk"&lt;br /&gt; xmlns:h="http://java.sun.com/jsf/html" xmlns:c="http://java.sun.com/jstl/core"&lt;br /&gt; xmlns:a="ApplicationTaglib"&amp;gt;&lt;br /&gt; &amp;lt;body&amp;gt;&lt;br /&gt;     &amp;lt;ui:composition template="/WEB-INF/common/template.xhtml"&amp;gt;&lt;br /&gt;         &amp;lt;ui:define name="content"&amp;gt;&lt;br /&gt;             &amp;lt;h1&amp;gt;&lt;br /&gt;                 &amp;lt;h:outputFormat value="#{msg['list.header']}"&amp;gt;&lt;br /&gt;                     &amp;lt;f:param&lt;br /&gt;                         value="#{msgMedia['model.DomainObject.Library.plural']}" /&amp;gt;&lt;br /&gt;                 &amp;lt;/h:outputFormat&amp;gt;&lt;br /&gt;             &amp;lt;/h1&amp;gt;&lt;br /&gt;             &amp;lt;h:form xmlns="http://www.w3.org/1999/xhtml"&lt;br /&gt;                 xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:f="http://java.sun.com/jsf/core"&lt;br /&gt;                 xmlns:t="http://myfaces.apache.org/tomahawk"&lt;br /&gt;                 xmlns:h="http://java.sun.com/jsf/html" xmlns:c="http://java.sun.com/jstl/core"&lt;br /&gt;                 xmlns:a="ApplicationTaglib"&amp;gt;&lt;br /&gt;             &amp;lt;div&amp;gt;&lt;br /&gt;                 &amp;lt;label for="_libraryFilter"&amp;gt;#{msgMedia['model.DomainObject.Library.filter']}: &amp;lt;/label&amp;gt;&lt;br /&gt;                 &amp;lt;input type="text" value="#{requestParameters.libraryFilter}" name="libraryFilter" id="_libraryFilter"/&amp;gt;&lt;br /&gt;                 &amp;lt;h:commandButton value="#{msgMedia['model.DomainObject.Library.filterButton']}" action="filterLibrary" /&amp;gt;&lt;br /&gt;             &amp;lt;/div&amp;gt;&lt;br /&gt;             &amp;lt;/h:form&amp;gt;&lt;br /&gt;             &amp;lt;ui:include&lt;br /&gt;                 src="/WEB-INF/generated/flows/media/listLibrary/list_include.html" /&amp;gt;&lt;br /&gt;         &amp;lt;/ui:define&amp;gt;&lt;br /&gt;     &amp;lt;/ui:composition&amp;gt;&lt;br /&gt; &amp;lt;/body&amp;gt;&lt;br /&gt;&amp;lt;/html&amp;gt;&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;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&lt;br /&gt;&lt;br /&gt;That's it.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3314601722322098765-5221104541325225870?l=fornax-sculptor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fornax-sculptor.blogspot.com/feeds/5221104541325225870/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fornax-sculptor.blogspot.com/2010/02/customization-of-webflows.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/5221104541325225870'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/5221104541325225870'/><link rel='alternate' type='text/html' href='http://fornax-sculptor.blogspot.com/2010/02/customization-of-webflows.html' title='Customization of webflows'/><author><name>Andreas</name><uri>http://www.blogger.com/profile/08618614943051927546</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3314601722322098765.post-3376671946008926123</id><published>2010-02-02T20:20:00.001+01:00</published><updated>2010-02-02T20:22:50.355+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Sculptor'/><category scheme='http://www.blogger.com/atom/ns#' term='Success Story'/><title type='text'>Two Success Stories from Factory4Solutions</title><content type='html'>&lt;b&gt;Introduction&lt;/b&gt;&lt;br /&gt;I work as architect at company Factory4Solutions (www.f4s.sk). Our&lt;br /&gt;primary customers are banks, insurance companies, telecommunication&lt;br /&gt;sector. We was using MDA approach long before. We started around 1997&lt;br /&gt;with our own home made solution based on RationalRose, XML and custom&lt;br /&gt;generator. Than we switched to AndroMDA. Tool was nice but stalled in&lt;br /&gt;development around 2007. Then I start investigate another MDA tools.&lt;br /&gt;We were looking for tool based on open technologies, universal and able&lt;br /&gt;to generate JEE code. After playing little bit with proof of concept&lt;br /&gt;projects we got first real project in which we could apply it.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Insurance company&lt;/b&gt;&lt;br /&gt;Decision was not easy. We have to migrate existing portal with huge&lt;br /&gt;amount of existing data to new portal. Persistent layer was hand&lt;br /&gt;written in Oracle TopLink and front-end was in JSPs. I expected many&lt;br /&gt;problems because hand written persistent layer is usually full of&lt;br /&gt;nonstandard approaches which are hard to achieve in generic MDA tool.&lt;br /&gt;&lt;br /&gt;&lt;div&gt;However for me as an architect it was important to achieve&lt;br /&gt;standardization because we had complicated deployment mode. Because of&lt;br /&gt;security, portal was split to two. First was 3 tier extranet deployed&lt;br /&gt;on WebLogic cluster and internal part which had similar functionality&lt;br /&gt;but was running only on single server. Both portals were using same&lt;br /&gt;database. For easier development I decided go with only one code base.&lt;br /&gt;At build time we split the application and created more WAR files (Full&lt;br /&gt;intranet, Extranet service tier, Extranet web tier).&lt;br /&gt;&lt;br /&gt;&lt;div&gt;I was really afraid about MDA approach. I started with SQL DDL and&lt;br /&gt;created first version of model design file for Sculptor. Then I started&lt;br /&gt;to tweak model file to fit existing database. After 2 days I was ready &lt;/div&gt;&lt;div&gt;with our model and first version of important services.&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Thanks to strict Domain Driven Design which Sculptor follow I moved&lt;br /&gt;code to developers to finish services and develop web frontend. Thanks&lt;br /&gt;to Spring Remoting it took just 1 day to develop build procedure for 3&lt;br /&gt;different WAR files. Except some small problems when developer used&lt;br /&gt;some utility methods from frontend on backend. The architecture is stable,&lt;br /&gt;system is maintainable and extendable. Today we still use one code&lt;br /&gt;base also for demo system, batch processing system and Web Service&lt;br /&gt;layer. Flexibility of Sculptor is endless. When you reach the border&lt;br /&gt;with what Sculptor allow, you are still free to override parts of&lt;br /&gt;generated code thanks to Xpand language.&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Project facts:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;6 developers + 0.5 architect + 1 project manager + 0.5 auality assurance + 0.5 analyst for 9 month&lt;/li&gt;&lt;li&gt;108.000 lines of java code, 34.000 lines of JSP code&lt;/li&gt;&lt;li&gt;Processing big reports from customers take 5-6 minutes (10 MB XML)&lt;/li&gt;&lt;li&gt;20.000 reports a day are send in peaks&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;b&gt;Investment company&lt;/b&gt;&lt;br /&gt;We had advantage that we should start from scratch. Now project&lt;br /&gt;consist of 40 entities and 26 services. Very complex security and&lt;br /&gt;entity processing by complicated business processes involving bank,&lt;br /&gt;customers, internal users, stock exchange, e-mail processing and&lt;br /&gt;advisor networks. It was developed fully with Sculptor for entity and&lt;br /&gt;service tier.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;This is our first project where we used Sculptor also for generating&lt;br /&gt;web frontend. We used smartclient (Sculptor allow to generate different&lt;br /&gt;frontends). We are satisfied with speed of development and nice GUI&lt;br /&gt;which users appreciate. &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;We also used very unique concept of waving rule engine - Drools &lt;/div&gt;&lt;div&gt;in front of service tier. This way we validate objects, sending &lt;/div&gt;&lt;div&gt;announcement, sending business alerts and also for complex price &lt;/div&gt;&lt;div&gt;list processing which consist of 8 different payment tables applied &lt;/div&gt;&lt;div&gt;at complex conditions. Result is very robust system which allow at &lt;/div&gt;&lt;div&gt;one side dynamic change but on other side very stable. &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;We are now running User Acceptance Tests at client side.&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Project facts:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;2 developers + 0.5 architect + 0.5 quality assurance during 6 month&lt;/li&gt;&lt;li&gt;9.000 lines of hand written code, 3.600 lines of test code, 49.000 lines of generated code&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;b&gt;Conclusion&lt;/b&gt;&lt;br /&gt;Today we are using Sculptor for all our projects. We master it to nice&lt;br /&gt;extend and we now participate in development of new versions. I'm very&lt;br /&gt;happy with new 1.7 release. This brings some nice features.&lt;br /&gt;Specifically it's Smartclient frontend, hints for custom generation of&lt;br /&gt;code and condition criteria builder with nice Java DSL (type and name&lt;br /&gt;checking and IDE autocompletion).&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;This story was written by Pavel Tavoda, &lt;a href="http://www.f4s.sk/"&gt;Factory4Solutions&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3314601722322098765-3376671946008926123?l=fornax-sculptor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fornax-sculptor.blogspot.com/feeds/3376671946008926123/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fornax-sculptor.blogspot.com/2010/02/two-success-stories-from.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/3376671946008926123'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/3376671946008926123'/><link rel='alternate' type='text/html' href='http://fornax-sculptor.blogspot.com/2010/02/two-success-stories-from.html' title='Two Success Stories from Factory4Solutions'/><author><name>Sculptor</name><uri>http://www.blogger.com/profile/15660077990398682094</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://4.bp.blogspot.com/_0g7ky1dgnS4/SkKTtArD_yI/AAAAAAAAAAM/ruLNzCTSrYk/S220/Sculptor.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3314601722322098765.post-737742547170945684</id><published>2010-02-02T20:10:00.001+01:00</published><updated>2010-02-02T20:18:22.182+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Sculptor'/><category scheme='http://www.blogger.com/atom/ns#' term='Success Story'/><title type='text'>An Old Success Story</title><content type='html'>&lt;div&gt;It was a completely new project started from scratch. The main goal of a project was processing of a big files and then generates reports. Despite the fact that installation of a product includes configuration for personal and network use, we choose choose architecture of app that includes Tomcat and Hibernate. After some investigation I found Sculptor. It uses well known (by me) technologies and best practices for that moment. When main functionality was implemented in a draft state Sculptor helped to start with a simple domain model. It was not really big model. This model includes about 50 entities and services (Users, Reports, Settings, Schedule, Directory objects, ...). It was implemented by a team with a different experience in JEE technologies. And almost all of them  started to use Sculptor model very quickly. &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Our team used SCRUM as a development process. So, it was really exciting "Agile" process implementation. Even Domain Model was agile! There were several serious refactorings that includes changes in Domain Model and Sculptor framework supported this in several ways. First, It requires tests implementation, so our changes was tested just after applying. Second, the way Sculptor uses to separate generated and hand-written code doesn't prevent refactorings. Sure, there were some minor problems, e.g. with using "lazy" initialization. But they were successfully solved and now product is on the market. &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Regards, &lt;/div&gt;&lt;div&gt;Vladimir &lt;/div&gt;&lt;div&gt;December 2008&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3314601722322098765-737742547170945684?l=fornax-sculptor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fornax-sculptor.blogspot.com/feeds/737742547170945684/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fornax-sculptor.blogspot.com/2010/02/old-success-story.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/737742547170945684'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/737742547170945684'/><link rel='alternate' type='text/html' href='http://fornax-sculptor.blogspot.com/2010/02/old-success-story.html' title='An Old Success Story'/><author><name>Sculptor</name><uri>http://www.blogger.com/profile/15660077990398682094</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://4.bp.blogspot.com/_0g7ky1dgnS4/SkKTtArD_yI/AAAAAAAAAAM/ruLNzCTSrYk/S220/Sculptor.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3314601722322098765.post-4002242352257200513</id><published>2010-02-02T20:00:00.001+01:00</published><updated>2010-02-02T20:13:22.979+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Sculptor'/><category scheme='http://www.blogger.com/atom/ns#' term='Customization'/><category scheme='http://www.blogger.com/atom/ns#' term='Release'/><title type='text'>Sculptor 1.7.0 - GAE, Smartclient, EclipseLink, DataNucleus, JEE</title><content type='html'>Sculptor 1.7.0 has been released. In this version the list of supported technologies has grown with several popular alternatives.&lt;br /&gt;&lt;br /&gt;Sculptor has many built in customization options to&lt;a href="http://fornax-sculptor.blogspot.com/2010/01/pick-n-choose-target-implementation.html"&gt; pick and choose from&lt;/a&gt;. The major new target implementation options in this release:&lt;div&gt;&lt;ul&gt;&lt;li&gt;Google App Engine&lt;/li&gt;&lt;li&gt;EclipseLink and DataNucleus JPA Provider&lt;/li&gt;&lt;li&gt;Smartclient GWT&lt;/li&gt;&lt;li&gt;Pure EJB3 (without Spring)&lt;/li&gt;&lt;li&gt;Web Services with JAX-WS&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;Sculptor also has good possibilities for extensibility if you prefer a design or framework that is not supported out-of-the-box. Sculptor is using openArchitectureWare code generation platform, which makes it possible to change the code generation templates using Aspect-Oriented Programming (AOP) features.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The Pure EJB3 implementation, one of the new features in 1.7.0, has already been successfully used in several projects, i.e. it is well tested and production ready. This means that not only Spring users can gain the productivity and quality benefits of Sculptor, but also those who develop on the standard JEE stack.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3314601722322098765-4002242352257200513?l=fornax-sculptor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fornax-sculptor.blogspot.com/feeds/4002242352257200513/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fornax-sculptor.blogspot.com/2010/02/sculptor-170-gae-smartclient.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/4002242352257200513'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/4002242352257200513'/><link rel='alternate' type='text/html' href='http://fornax-sculptor.blogspot.com/2010/02/sculptor-170-gae-smartclient.html' title='Sculptor 1.7.0 - GAE, Smartclient, EclipseLink, DataNucleus, JEE'/><author><name>Patrik</name><uri>http://www.blogger.com/profile/11876565293495127382</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3314601722322098765.post-5208737084168541301</id><published>2010-01-28T22:00:00.001+01:00</published><updated>2010-01-28T22:47:27.577+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Scala'/><category scheme='http://www.blogger.com/atom/ns#' term='Sculptor'/><category scheme='http://www.blogger.com/atom/ns#' term='GAE'/><category scheme='http://www.blogger.com/atom/ns#' term='Customization'/><category scheme='http://www.blogger.com/atom/ns#' term='Productivity'/><category scheme='http://www.blogger.com/atom/ns#' term='Maven'/><title type='text'>Enable scala in your appengine/sculptor project</title><content type='html'>I've recently started to interest myself in the &lt;a href="http://www.scala-lang.org/"&gt;Scala&lt;/a&gt; language. It is a language that runs on the jvm. It's very rich in its syntax and it has a very pleasant mix of OO- and functional programming. &lt;div&gt;But since I always starts my explorations with the solid foundation of maven the first problem I encountered was how to integrate scala in my build process in a project that already mixes GAE and Sculptor. I.e. I start off from a project that has been created with our &lt;a href="http://fornax.itemis.de/confluence/display/fornax/10.+App+Engine+%28CSC%29"&gt;maven appengine archetype&lt;/a&gt;. So this is a log of my steps for making this happen.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;1) In your pom,  add a property for the version of Scala:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;pre class="brush:xml"&gt;&lt;br /&gt;&amp;lt;scala.version&amp;gt;2.7.7&amp;lt;/scala.version&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;2) In your pom, add repository for scala:&lt;/div&gt;&lt;br /&gt;&lt;pre class="brush:xml"&gt;&lt;br /&gt;&amp;lt;repository&amp;gt;&lt;br /&gt;&amp;lt;id&amp;gt;scala-tools.org&amp;lt;/id&amp;gt;&lt;br /&gt;&amp;lt;name&amp;gt;Scala-Tools Maven2 Repository&amp;lt;/name&amp;gt;&lt;br /&gt;&amp;lt;url&amp;gt;http://scala-tools.org/repo-releases&amp;lt;/url&amp;gt;&lt;br /&gt;&amp;lt;/repository&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;div&gt;3) In your pom , add pluginRepository:&lt;/div&gt;&lt;br /&gt;&lt;pre class="brush:xml"&gt;&lt;br /&gt;&amp;lt;pluginRepository&amp;gt;&lt;br /&gt;&amp;lt;id&amp;gt;scala-tools.org&amp;lt;/id&amp;gt;&lt;br /&gt;&amp;lt;name&amp;gt;Scala-Tools Maven2 Repository&amp;lt;/name&amp;gt;&lt;br /&gt;&amp;lt;url&amp;gt;http://scala-tools.org/repo-releases&amp;lt;/url&amp;gt;&lt;br /&gt;&amp;lt;/pluginRepository&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;div&gt;4) In your pom, add dependency:&lt;/div&gt;&lt;br /&gt;&lt;pre class="brush:xml"&gt;&lt;br /&gt;&amp;lt;dependency&amp;gt;&lt;br /&gt;&amp;lt;groupId&amp;gt;org.scala-lang&amp;lt;/groupId&amp;gt;&lt;br /&gt;&amp;lt;artifactId&amp;gt;scala-library&amp;lt;/artifactId&amp;gt;&lt;br /&gt;&amp;lt;version&amp;gt;${scala.version}&amp;lt;/version&amp;gt;&lt;br /&gt;&amp;lt;/dependency&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;div&gt;5) In your pom, add entry in maven-dependency-plugin in the build section:&lt;/div&gt;&lt;br /&gt;&lt;pre class="brush:xml"&gt;&lt;br /&gt;&amp;lt;artifactItem&amp;gt;&lt;br /&gt;&amp;lt;groupId&amp;gt;org.scala-lang&amp;lt;/groupId&amp;gt;&lt;br /&gt;&amp;lt;artifactId&amp;gt;scala-library&amp;lt;/artifactId&amp;gt;&lt;br /&gt;&amp;lt;version&amp;gt;${scala.version}&amp;lt;/version&amp;gt;&lt;br /&gt;&amp;lt;outputDirectory&amp;gt;war/WEB-INF/lib&amp;lt;/outputDirectory&amp;gt;&lt;br /&gt;&amp;lt;/artifactItem&amp;gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;div&gt;6) In your pom, add dependency to specs-library (if you want it):&lt;/div&gt;&lt;br /&gt;&lt;pre class="brush:xml"&gt;&lt;br /&gt;&amp;lt;dependency&amp;gt;&lt;br /&gt;&amp;lt;groupId&amp;gt;org.specs&amp;lt;/groupId&amp;gt;&lt;br /&gt;&amp;lt;artifactId&amp;gt;specs&amp;lt;/artifactId&amp;gt;&lt;br /&gt;&amp;lt;version&amp;gt;1.4.3&amp;lt;/version&amp;gt;&lt;br /&gt;&amp;lt;scope&amp;gt;test&amp;lt;/scope&amp;gt;&lt;br /&gt;&amp;lt;/dependency&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;div&gt;7) In your pom, add the scala-plugin to the build section:&lt;/div&gt;&lt;br /&gt;&lt;pre class="brush:xml"&gt;&lt;br /&gt;&amp;lt;plugin&amp;gt;&lt;br /&gt;&amp;lt;groupId&amp;gt;org.scala-tools&amp;lt;/groupId&amp;gt;&lt;br /&gt;&amp;lt;artifactId&amp;gt;maven-scala-plugin&amp;lt;/artifactId&amp;gt;&lt;br /&gt;&amp;lt;executions&amp;gt;&lt;br /&gt;&amp;lt;execution&amp;gt;&lt;br /&gt; &amp;lt;id&amp;gt;scala-compile-first&amp;lt;/id&amp;gt;&lt;br /&gt; &amp;lt;phase&amp;gt;process-resources&amp;lt;/phase&amp;gt;&lt;br /&gt; &amp;lt;goals&amp;gt;&lt;br /&gt;   &amp;lt;goal&amp;gt;add-source&amp;lt;/goal&amp;gt;&lt;br /&gt;   &amp;lt;goal&amp;gt;compile&amp;lt;/goal&amp;gt;&lt;br /&gt; &amp;lt;/goals&amp;gt;&lt;br /&gt;&amp;lt;/execution&amp;gt;&lt;br /&gt;&amp;lt;execution&amp;gt;&lt;br /&gt; &amp;lt;id&amp;gt;scala-test-compile&amp;lt;/id&amp;gt;&lt;br /&gt; &amp;lt;phase&amp;gt;process-test-resources&amp;lt;/phase&amp;gt;&lt;br /&gt; &amp;lt;goals&amp;gt;&lt;br /&gt;   &amp;lt;goal&amp;gt;testCompile&amp;lt;/goal&amp;gt;&lt;br /&gt; &amp;lt;/goals&amp;gt;&lt;br /&gt;&amp;lt;/execution&amp;gt;&lt;br /&gt;&amp;lt;/executions&amp;gt;&lt;br /&gt;&amp;lt;configuration&amp;gt;&lt;br /&gt;&amp;lt;scalaVersion&amp;gt;${scala.version}&amp;lt;/scalaVersion&amp;gt;&lt;br /&gt;&amp;lt;args&amp;gt;&lt;br /&gt; &amp;lt;arg&amp;gt;-target:jvm-1.5&amp;lt;/arg&amp;gt;&lt;br /&gt;&amp;lt;/args&amp;gt;&lt;br /&gt;&amp;lt;/configuration&amp;gt;&lt;br /&gt;&amp;lt;/plugin&amp;gt;&lt;br /&gt;&lt;/pre&gt; &lt;br /&gt;&lt;br /&gt;&lt;div&gt;8) In your project file structure, add a simple scala file: src/main/scala/org/foo/App.scala&lt;/div&gt;&lt;br /&gt;&lt;pre class="brush:java"&gt;&lt;br /&gt;package org.foo&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt;* Hello world!&lt;br /&gt;*&lt;br /&gt;*/&lt;br /&gt;object App extends Application {&lt;br /&gt;println( "Hello World!" )&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;div&gt;9) In your project file structure, add a simple scala test: src/test/scala/org/foo/AppTest.scala&lt;/div&gt;&lt;br /&gt;&lt;pre class="brush:java"&gt;&lt;br /&gt;package org.foo&lt;br /&gt;&lt;br /&gt;import org.junit._&lt;br /&gt;import Assert._&lt;br /&gt;&lt;br /&gt;@Test&lt;br /&gt;class AppTest {&lt;br /&gt;&lt;br /&gt; @Test&lt;br /&gt; def testOK() = assertTrue(true)&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;10) In the root of your project file structure, run mvn install to see that everything builds ok&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3314601722322098765-5208737084168541301?l=fornax-sculptor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fornax-sculptor.blogspot.com/feeds/5208737084168541301/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fornax-sculptor.blogspot.com/2010/01/enable-scala-in-your-appenginesculptor.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/5208737084168541301'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/5208737084168541301'/><link rel='alternate' type='text/html' href='http://fornax-sculptor.blogspot.com/2010/01/enable-scala-in-your-appenginesculptor.html' title='Enable scala in your appengine/sculptor project'/><author><name>Andreas</name><uri>http://www.blogger.com/profile/08618614943051927546</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3314601722322098765.post-4867843869692732688</id><published>2010-01-26T23:00:00.001+01:00</published><updated>2010-01-26T23:02:21.409+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Sculptor'/><title type='text'>How to Remove Sculptor</title><content type='html'>When taking the decision to try a tool, such as Sculptor, it is important to know what it takes to stop using it if doesn't turn out as expected. Therefore I will in this post explain how to remove Sculptor. You don't have to do all steps and you don't have to do everything at once.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Step 1&lt;/b&gt;&lt;/div&gt;&lt;div&gt;The first step is to remove the code generation. This will take you less than 10 minutes.&lt;/div&gt;&lt;br /&gt;a) In the maven pom.xml you remove the following:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;plugin fornax-oaw-m2-plugin&lt;/li&gt;&lt;li&gt;plugin maven-clean-plugin&lt;/li&gt;&lt;li&gt;plugin fornax-checksum-m2-plugin&lt;/li&gt;&lt;li&gt;dependency fornax-cartridges-sculptor-generator&lt;/li&gt;&lt;/ul&gt;b) Remove these files in src/main/resources:&lt;div&gt;&lt;ul&gt;&lt;li&gt;model files, .btdesign, .guidesign&lt;/li&gt;&lt;li&gt;sculptor-generator.properties&lt;/li&gt;&lt;li&gt;templates/SpecialCases.xpt&lt;/li&gt;&lt;li&gt;extensions/SpecialCases.ext&lt;/li&gt;&lt;/ul&gt;c) Move generated files&lt;br /&gt;&lt;ul&gt;&lt;li&gt;src/generated/java -&gt; src/main/java&lt;/li&gt;&lt;li&gt;src/generated/resources -&gt; src/main/resources&lt;/li&gt;&lt;li&gt;src/test/generated/java -&gt; src/test/java&lt;/li&gt;&lt;li&gt;src/test/generated/resources -&gt; src/test/resources&lt;/li&gt;&lt;/ul&gt;d) Run mvn eclipse:eclipse to generate eclipse project without generated source directories&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Step 2&lt;/b&gt;&lt;br /&gt;You don't need to do more right away, but later you would probably like to refactor the gap classes. Separation of generated and manually written code is done by a generated base class and manually written subclass. E.g. Person and PersonBase. You would probably like to collapse those into one class. To do this you can use the 'Push Down' refactoring in Eclipse, or simply copy paste the code from the base class to the subclass, and remove the extension.&lt;br /&gt;&lt;br /&gt;This takes a few minutes per class, so you should be able to do a whole project within a few hours. It is a trivial and safe refactoring.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Step 3&lt;/b&gt;&lt;/div&gt;&lt;div&gt;Sculptor is a development tool, but it also has a small runtime library, with classes of utility and general character. This &lt;a href="http://fornax-sculptor.blogspot.com/2010/01/why-we-combine-code-generator-with.html"&gt;blog post&lt;/a&gt; explains why we have a runtime library. You can continue to use those classes as is. If you feel uncomfortable with that you can copy the source and remove classes that you don't use. The runtime library is not advanced at all, and you will have no problems to understan, maintain and change it as you need.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3314601722322098765-4867843869692732688?l=fornax-sculptor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fornax-sculptor.blogspot.com/feeds/4867843869692732688/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fornax-sculptor.blogspot.com/2010/01/how-to-remove-sculptor.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/4867843869692732688'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/4867843869692732688'/><link rel='alternate' type='text/html' href='http://fornax-sculptor.blogspot.com/2010/01/how-to-remove-sculptor.html' title='How to Remove Sculptor'/><author><name>Patrik</name><uri>http://www.blogger.com/profile/11876565293495127382</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3314601722322098765.post-8009948512710530904</id><published>2010-01-21T20:30:00.003+01:00</published><updated>2010-01-21T20:37:36.524+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Sculptor'/><category scheme='http://www.blogger.com/atom/ns#' term='Roo'/><category scheme='http://www.blogger.com/atom/ns#' term='Spring'/><title type='text'>Why we Combine Code Generator with Runtime Library</title><content type='html'>&lt;div&gt;Sculptor is a development tool, but it also has a small runtime library. Some people find it scaring to bring in yet another library in their software stack. &lt;/div&gt;&lt;br /&gt;&lt;div&gt;It contains classes of utility and general character. It is not advanced at all. We could have generated those also and have no runtime dependency at all. Instead, we try to only generate things that must be variable, such as application specific structure and glue code. Static content should not be generated.&lt;/div&gt;&lt;br /&gt;&lt;div&gt;If we would have decided to not use a runtime library I think it would be a high risk that we would have generated code with bad design, such as repeated, duplicated code. We think in terms of DRY for the generated code. We always try to produce code as if we would have written it by hand, without a code generator. There are some exceptions to this rule, for example separation of generated and hand written code in separate classes.&lt;/div&gt;&lt;br /&gt;&lt;div&gt;It is interesting to compare Spring Roo and Sculptor. I see a lot of similarities, but also differences. One difference related to this topic is that Roo doesn't have a runtime library (except ordinary Spring Framework). They generate everything. They are using this as a selling point, but I think they have fallen into the trap of generating duplicated code. Take a look at the persistence methods in the ITD for an Entity.&lt;/div&gt;&lt;pre class="brush:java"&gt;   @Transactional &lt;br /&gt; public void Planet.remove() { &lt;br /&gt;     if (this.entityManager == null) this.entityManager = entityManager();     &lt;br /&gt;     if (this.entityManager.contains(this)) {     &lt;br /&gt;         this.entityManager.remove(this);         &lt;br /&gt;     } else {     &lt;br /&gt;         Planet attached = this.entityManager.find(Planet.class, this.id);         &lt;br /&gt;         this.entityManager.remove(attached);         &lt;br /&gt;     }     &lt;br /&gt; }&lt;/pre&gt;What happens if you decide to remove Roo? Then you will have to maintain a lot of duplicated code, that was generated initially. I think it illustrates how easy it is to forget or not bother about ordinary design principles when you solve everything with code generation. DRY should of course be used even when you have a code generator in your hands.&lt;br /&gt;&lt;br /&gt;In Sculptor we have placed the general persistence operations in parameterized access objects, in the runtime library.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3314601722322098765-8009948512710530904?l=fornax-sculptor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fornax-sculptor.blogspot.com/feeds/8009948512710530904/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fornax-sculptor.blogspot.com/2010/01/why-we-combine-code-generator-with.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/8009948512710530904'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/8009948512710530904'/><link rel='alternate' type='text/html' href='http://fornax-sculptor.blogspot.com/2010/01/why-we-combine-code-generator-with.html' title='Why we Combine Code Generator with Runtime Library'/><author><name>Patrik</name><uri>http://www.blogger.com/profile/11876565293495127382</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3314601722322098765.post-4190501159900037039</id><published>2010-01-16T15:00:00.001+01:00</published><updated>2010-01-16T15:37:56.442+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Sculptor'/><category scheme='http://www.blogger.com/atom/ns#' term='Customization'/><title type='text'>Pick 'n' Choose Target Implementation</title><content type='html'>This article gives an overview of the options you have regarding the generated target implementation. We add new stuff all the time so this describes what is available at the time of this writing, i.e. in Sculptor 1.7.0.&lt;br /&gt;&lt;br /&gt;The default target implementation is using:&lt;br /&gt;&lt;div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;JPA with Hibernate as provider&lt;/li&gt;&lt;li&gt;Spring with annotations, no EJBs&lt;/li&gt;&lt;li&gt;Web CRUD GUI client with JSF, Facelets, and Spring Web Flow&lt;/li&gt;&lt;li&gt;In-memory Hsqldb as database&lt;/li&gt;&lt;li&gt;Deployment as war in Jetty&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;The default choices gives a good starting environment with minimal requirements of installation of external software, such as database and application server. &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;For each of these areas there are alternative implementations that are supported by Sculptor out of the box. The features are selected with simple configuration.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;img src="http://3.bp.blogspot.com/_qr2e_ywa0-I/S1HMEDlLCII/AAAAAAAABRA/tdkOfkOJ-c4/s800/SculptorTargetImplementation.png" style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 800px; height: 250px;" border="0" alt="" id="BLOGGER_PHOTO_ID_5427343396010920066" /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;If you have a different reference architecture than what is provided by Sculptor out of the box you can rather easily customize the generator to fit your needs. It is possible to add your own code generator templates or redefine sections of existing templates. All that is described in the &lt;a href="http://fornax.itemis.de/confluence/x/dAQ"&gt;Developer's Guide&lt;/a&gt;.&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3314601722322098765-4190501159900037039?l=fornax-sculptor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fornax-sculptor.blogspot.com/feeds/4190501159900037039/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fornax-sculptor.blogspot.com/2010/01/pick-n-choose-target-implementation.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/4190501159900037039'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/4190501159900037039'/><link rel='alternate' type='text/html' href='http://fornax-sculptor.blogspot.com/2010/01/pick-n-choose-target-implementation.html' title='Pick &apos;n&apos; Choose Target Implementation'/><author><name>Patrik</name><uri>http://www.blogger.com/profile/11876565293495127382</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_qr2e_ywa0-I/S1HMEDlLCII/AAAAAAAABRA/tdkOfkOJ-c4/s72-c/SculptorTargetImplementation.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3314601722322098765.post-8874559806695539314</id><published>2010-01-16T13:39:00.004+01:00</published><updated>2010-01-16T13:53:06.721+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Sculptor'/><category scheme='http://www.blogger.com/atom/ns#' term='test'/><title type='text'>Test Scaffold</title><content type='html'>I must admit that when I use Sculptor I often skip completing the JUnit tests for the ordinary CRUD operations. To my defense I would say that they are trivial and completely generated, but anyway I think that is a bad habit that should be changed. Therefore I wrote a small Eclipse template for them. Maybe useful for you also:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:java"&gt;&lt;br /&gt;    @${testType:newType(org.junit.Test)}&lt;br /&gt;    public void testFindById() throws Exception {&lt;br /&gt;  ${DomainObject:localVar(org.fornax.cartridges.sculptor.framework.domain.AbstractDomainObject)} obj = ${service:field}.findById(getServiceContext(), 1L);&lt;br /&gt;        assertNotNull(obj);&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt;    @${testType:newType(org.junit.Test)}(expected=${DomainObject}NotFoundException.class)&lt;br /&gt; public void testFindByIdWithNotFoundException() throws Exception {&lt;br /&gt;  ${service}.findById(getServiceContext(), -1L);&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt;    @${testType:newType(org.junit.Test)}&lt;br /&gt;    public void testFindAll() throws Exception {&lt;br /&gt;        ${listType:newType(java.util.List)}&amp;lt;${DomainObject}&amp;gt; found = ${service}.findAll(getServiceContext());&lt;br /&gt;        assertEquals(1, found.size());&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    @${testType:newType(org.junit.Test)}&lt;br /&gt;    public void testSave() throws Exception {&lt;br /&gt;        int countBefore = countRowsInTable(${DomainObject}.class);&lt;br /&gt;        ${DomainObject} obj = new ${DomainObject}();&lt;br /&gt;        // TODO complete...&lt;br /&gt;        // obj.set${cursor}&lt;br /&gt;        ${service}.save(getServiceContext(), obj);&lt;br /&gt;        assertEquals(countBefore + 1, countRowsInTable(${DomainObject}.class));&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    @${testType:newType(org.junit.Test)}&lt;br /&gt;    public void testDelete() throws Exception {&lt;br /&gt;        int countBefore = countRowsInTable(${DomainObject}.class);&lt;br /&gt;        ${DomainObject} obj = ${service}.findById(getServiceContext(), 1L);&lt;br /&gt;        ${service}.delete(getServiceContext(), obj);&lt;br /&gt;        assertEquals(countBefore - 1, countRowsInTable(${DomainObject}.class));&lt;br /&gt;  }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Add it in Eclipse &gt; Preferences &gt; Java &gt; Editor &gt; Templates&lt;br /&gt;Name: testScaffold&lt;br /&gt;Context: Java type members&lt;br /&gt;Description: JUnit test methods for Sculptor CRUD operations&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3314601722322098765-8874559806695539314?l=fornax-sculptor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fornax-sculptor.blogspot.com/feeds/8874559806695539314/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fornax-sculptor.blogspot.com/2010/01/test-scaffold.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/8874559806695539314'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/8874559806695539314'/><link rel='alternate' type='text/html' href='http://fornax-sculptor.blogspot.com/2010/01/test-scaffold.html' title='Test Scaffold'/><author><name>Patrik</name><uri>http://www.blogger.com/profile/11876565293495127382</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3314601722322098765.post-1958786980158582325</id><published>2009-12-01T21:00:00.001+01:00</published><updated>2009-12-01T21:01:00.081+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Sculptor'/><category scheme='http://www.blogger.com/atom/ns#' term='Internal DSL'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><title type='text'>Internal DSL for Criteria - part 3:3</title><content type='html'>In &lt;a href="http://fornax-sculptor.blogspot.com/2009/11/internal-dsl-for-criteria-part-23.html"&gt;previous 2 articles&lt;/a&gt; I have described the basics of the new internal DSL for JPA/Hibernate criteria. Let us dive in to an advanced topic. We would like to be able to specify conditional expressions with logical operators, and with grouping of nested conditions.&lt;br /&gt;&lt;pre class="brush:java"&gt;&lt;br /&gt;  // A and (B or C)&lt;br /&gt;  criteriaFor(Person.class).withProperty(aaa()).eq("A").and().lbrace()&lt;br /&gt;      .withProperty(bbb()).eq("B").or().withProperty(ccc()).eq("C").rbrace()&lt;br /&gt;      .build();&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;That should build a syntax tree like:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;         and&lt;br /&gt;       /    \&lt;br /&gt;      A      or&lt;br /&gt;             / \&lt;br /&gt;            B   C&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This means that I need to hold state of current and previous not completed operators in the builder. I hold the current operator in a stack and give braces a special meaning. The expression builder looks like this:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:java"&gt;&lt;br /&gt;public class ConditionalCriteriaBuilder&amp;lt;T&amp;gt; {&lt;br /&gt;&lt;br /&gt;   private enum ExpressionOperator {&lt;br /&gt;       Not, Or, And, LBrace&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   private ConditionalCriteriaBuilder() {&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   public static &amp;lt;T&amp;gt; ConditionRoot&amp;lt;T&amp;gt; criteriaFor(Class&amp;lt;T&amp;gt; clazz) {&lt;br /&gt;       return new ConditionalCriteriaBuilder&amp;lt;T&amp;gt;().new RootBuilderImpl();&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   public interface ConditionRoot&amp;lt;T&amp;gt; {&lt;br /&gt;       List&amp;lt;ConditionalCriteria&amp;gt; build();&lt;br /&gt;       CondditionProperty&amp;lt;T&amp;gt; withProperty(Property&amp;lt;T&amp;gt; property);&lt;br /&gt;       ConditionRoot&amp;lt;T&amp;gt; and();&lt;br /&gt;       ConditionRoot&amp;lt;T&amp;gt; or();&lt;br /&gt;       ConditionRoot&amp;lt;T&amp;gt; not();&lt;br /&gt;       ConditionRoot&amp;lt;T&amp;gt; lbrace();&lt;br /&gt;       ConditionRoot&amp;lt;T&amp;gt; rbrace();&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   public interface CondditionProperty&amp;lt;T&amp;gt; {&lt;br /&gt;       ConditionRoot&amp;lt;T&amp;gt; eq(Object value);&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   public class RootBuilderImpl implements ConditionRoot&amp;lt;T&amp;gt; {&lt;br /&gt;       private final SimpleStack&amp;lt;ConditionalCriteria&amp;gt; criteriaStack = new SimpleStack&amp;lt;ConditionalCriteria&amp;gt;();&lt;br /&gt;       private final SimpleStack&amp;lt;ExpressionOperator&amp;gt; operatorStack = new SimpleStack&amp;lt;ExpressionOperator&amp;gt;();&lt;br /&gt;&lt;br /&gt;       /**&lt;br /&gt;        * End the expression with this build&lt;br /&gt;        */&lt;br /&gt;       public List&amp;lt;ConditionalCriteria&amp;gt; build() {&lt;br /&gt;           return criteriaStack.asList();&lt;br /&gt;       }&lt;br /&gt;&lt;br /&gt;       public CondditionProperty&amp;lt;T&amp;gt; withProperty(Property&amp;lt;T&amp;gt; property) {&lt;br /&gt;           return new PropBuilderImpl(property.getName());&lt;br /&gt;       }&lt;br /&gt;&lt;br /&gt;       public ConditionRoot&amp;lt;T&amp;gt; and() {&lt;br /&gt;           operatorStack.push(ExpressionOperator.And);&lt;br /&gt;           return this;&lt;br /&gt;       }&lt;br /&gt;&lt;br /&gt;       public ConditionRoot&amp;lt;T&amp;gt; or() {&lt;br /&gt;           operatorStack.push(ExpressionOperator.Or);&lt;br /&gt;           return this;&lt;br /&gt;       }&lt;br /&gt;&lt;br /&gt;       public ConditionRoot&amp;lt;T&amp;gt; not() {&lt;br /&gt;           operatorStack.push(ExpressionOperator.Not);&lt;br /&gt;           return this;&lt;br /&gt;       }&lt;br /&gt;&lt;br /&gt;       public ConditionRoot&amp;lt;T&amp;gt; lbrace() {&lt;br /&gt;           operatorStack.push(ExpressionOperator.LBrace);&lt;br /&gt;           return this;&lt;br /&gt;       }&lt;br /&gt;&lt;br /&gt;       public ConditionRoot&amp;lt;T&amp;gt; rbrace() {&lt;br /&gt;           operatorStack.pop();&lt;br /&gt;           if (criteriaStack.isEmpty()) {&lt;br /&gt;               return this;&lt;br /&gt;           }&lt;br /&gt;           ConditionalCriteria lastCriteria = popCriteria();&lt;br /&gt;           pushCriteria(lastCriteria);&lt;br /&gt;           return this;&lt;br /&gt;       }&lt;br /&gt;&lt;br /&gt;       private ConditionalCriteria popCriteria() {&lt;br /&gt;           return criteriaStack.pop();&lt;br /&gt;       }&lt;br /&gt;&lt;br /&gt;       private void pushCriteria(ConditionalCriteria criteria) {&lt;br /&gt;           ExpressionOperator currentOperator = operatorStack.peek();&lt;br /&gt;           if (currentOperator == ExpressionOperator.Or || currentOperator == ExpressionOperator.And) {&lt;br /&gt;               ConditionalCriteria compositeCriteria;&lt;br /&gt;               if (currentOperator == ExpressionOperator.Or) {&lt;br /&gt;                   compositeCriteria = ConditionalCriteria.or(popCriteria(), criteria);&lt;br /&gt;               } else {&lt;br /&gt;                   compositeCriteria = ConditionalCriteria.and(popCriteria(), criteria);&lt;br /&gt;               }&lt;br /&gt;               criteriaStack.push(compositeCriteria);&lt;br /&gt;               operatorStack.pop();&lt;br /&gt;           } else if (currentOperator == ExpressionOperator.Not) {&lt;br /&gt;               ConditionalCriteria notCriteria = ConditionalCriteria.not(criteria);&lt;br /&gt;               criteriaStack.push(notCriteria);&lt;br /&gt;               operatorStack.pop();&lt;br /&gt;               if (!operatorStack.isEmpty() &amp;amp;&amp;amp; !criteriaStack.isEmpty()) {&lt;br /&gt;                   pushCriteria(criteriaStack.pop());&lt;br /&gt;               }&lt;br /&gt;           } else if (currentOperator == ExpressionOperator.LBrace) {&lt;br /&gt;               criteriaStack.push(criteria);&lt;br /&gt;           } else {&lt;br /&gt;               criteriaStack.push(criteria);&lt;br /&gt;           }&lt;br /&gt;       }&lt;br /&gt;&lt;br /&gt;       private class PropBuilderImpl implements CondditionProperty&amp;lt;T&amp;gt; {&lt;br /&gt;           String propertyName;&lt;br /&gt;&lt;br /&gt;           PropBuilderImpl(String name) {&lt;br /&gt;               this.propertyName = name;&lt;br /&gt;           }&lt;br /&gt;&lt;br /&gt;           public ConditionRoot&amp;lt;T&amp;gt; eq(Object value) {&lt;br /&gt;               pushCriteria(ConditionalCriteria.equal(propertyName, value));&lt;br /&gt;               return RootBuilderImpl.this;&lt;br /&gt;           }&lt;br /&gt;       }&lt;br /&gt;&lt;br /&gt;   }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The magic is in pushCriteria, which is invoked at the end of a condition, such as &lt;code&gt;eq()&lt;/code&gt;, and also when right brace is reached. It is not a trivial problem, but thanks to the stack metaphor the solution is not that complicated.&lt;br /&gt;&lt;br /&gt;A final thought about the syntax. Look at the expression again:&lt;br /&gt;&lt;pre class="brush:java"&gt;&lt;br /&gt;  // A and (B or C)&lt;br /&gt;  criteriaFor(Person.class).withProperty(aaa()).eq("A").and().lbrace()&lt;br /&gt;      .withProperty(bbb()).eq("B").or().withProperty(ccc()).eq("C").rbrace()&lt;br /&gt;      .build();&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;I use special keywords for left brace and right brace. Why not use grouping with an single method instead and write the nested expression as method parameter?&lt;br /&gt;&lt;pre class="brush:java"&gt;&lt;br /&gt;  // A and (B or C)&lt;br /&gt;  criteriaFor(Person.class).withProperty(aaa()).eq("A").and().group(&lt;br /&gt;      criteriaFor(Person.class).withProperty(bbb()).eq("B").or().withProperty(ccc()).eq("C"))&lt;br /&gt;      .buildSingle();&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;That would be possible, and probably easier to implement, but I see a few problems from end user perspective:&lt;br /&gt;&lt;div&gt;&lt;ol&gt;&lt;li&gt;There are already much clutter of braces due to the methods and it is hard to see and write the end brace of the group operation.&lt;/li&gt;&lt;li&gt;You loose context and have to start the nested expression from scratch, i.e. with static criteriaFor method.&lt;/li&gt;&lt;/ol&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;That was all of this exercise. I hope you find it useful as a practical example of how to develop a small Internal DSL. You find the full &lt;a href="https://fornax.svn.sourceforge.net/svnroot/fornax/trunk/cartridges/sculptor/fornax-cartridges-sculptor-framework/src/main/java/org/fornax/cartridges/sculptor/framework/accessapi/ConditionalCriteriaBuilder.java"&gt;source code&lt;/a&gt; in subversion, including &lt;a href="https://fornax.svn.sourceforge.net/svnroot/fornax/trunk/cartridges/sculptor/fornax-cartridges-sculptor-framework/src/test/java/org/fornax/cartridges/sculptor/framework/accessapi/ConditionalCriteriaBuilderTest.java"&gt;JUnit test&lt;/a&gt;.&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3314601722322098765-1958786980158582325?l=fornax-sculptor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fornax-sculptor.blogspot.com/feeds/1958786980158582325/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fornax-sculptor.blogspot.com/2009/12/internal-dsl-for-criteria-part-33.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/1958786980158582325'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/1958786980158582325'/><link rel='alternate' type='text/html' href='http://fornax-sculptor.blogspot.com/2009/12/internal-dsl-for-criteria-part-33.html' title='Internal DSL for Criteria - part 3:3'/><author><name>Patrik</name><uri>http://www.blogger.com/profile/11876565293495127382</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3314601722322098765.post-8234343261388349709</id><published>2009-11-30T22:00:00.001+01:00</published><updated>2009-12-01T21:03:00.377+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Sculptor'/><category scheme='http://www.blogger.com/atom/ns#' term='Internal DSL'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><title type='text'>Internal DSL for Criteria - part 2:3</title><content type='html'>In &lt;a href="http://fornax-sculptor.blogspot.com/2009/11/internal-dsl-for-criteria-part-13.html"&gt;previous article&lt;/a&gt; I introduced the new internal DSL for JPA/Hibernate criteria. Let us look at how to handle the property names.&lt;pre class="brush:java"&gt;&lt;br /&gt;criteria().prop("lastName").eq("Svensson").build();&lt;br /&gt;&lt;/pre&gt;The problem with the above expression is that the property lastName is a String. Refactoring of Person.lastName will not be automatically detected. Another issue is that you have to remember/lookup that the property name is "lastName" when writing this expression, the IDE will not help you.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;We, who are using Sculptor, define the domain object in the design model like this:&lt;/div&gt;&lt;pre class="brush:text"&gt;&lt;br /&gt;Entity Person {&lt;br /&gt;   String firstName&lt;br /&gt;   String lastName&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;From this Sculptor generates the Person class. We can also easily generate meta data of the properties of Person, which makes it possible to write expressions like this:&lt;br /&gt;&lt;pre class="brush:java"&gt;&lt;br /&gt;List&amp;lt;ConditionalCriteria&amp;gt; conditionalCriteria = ConditionalCriteriaBuilder.criteriaFor(Person.class)&lt;br /&gt;    .withProperty(PersonProperties.lastName()).eq("Svensson").build();&lt;br /&gt;&lt;/pre&gt;&lt;code&gt;PersonProperties&lt;/code&gt; is generated and it contains static methods for each property, returning a &lt;code&gt;Property&amp;lt;Person&amp;gt;&lt;/code&gt; object, which internally defines the strings. The builder is also parameterized, using the  class defined in the &lt;code&gt;criteriaFor&lt;/code&gt; factory method. This means that the &lt;code&gt;withProperty&lt;/code&gt; method only accepts properties of correct type, i.e. &lt;code&gt;Property&amp;lt;Person&amp;gt;&lt;/code&gt;. &lt;br /&gt;&lt;br /&gt;Static imports can be used to make the expression more compact and readable:&lt;br /&gt;&lt;pre class="brush:java"&gt;&lt;br /&gt;import static org.fornax.cartridges.sculptor.framework.accessapi.ConditionalCriteriaBuilder.criteriaFor;&lt;br /&gt;import static org.library.person.domain.PersonProperties.lastName;&lt;br /&gt;&lt;br /&gt;List&amp;lt;ConditionalCriteria&amp;gt; conditionalCriteria = criteriaFor(Person.class)&lt;br /&gt;  .withProperty(lastName()).eq("Svensson").build();&lt;br /&gt;&lt;/pre&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Note that the eclipse keyboard shortcut for static import is &amp;lt;ctrl+shift+m&amp;gt; (mac: &amp;lt;cmd+shift+m&amp;gt;).&lt;br /&gt;&lt;br /&gt;The expression builder looks like this:&lt;br /&gt;&lt;pre class="brush:java"&gt;&lt;br /&gt;public class ConditionalCriteriaBuilder&amp;lt;T&amp;gt; {&lt;br /&gt;&lt;br /&gt;    private final List&amp;lt;ConditionalCriteria&amp;gt; criteriaList = new ArrayList&amp;lt;ConditionalCriteria&amp;gt;();&lt;br /&gt;&lt;br /&gt;    private ConditionalCriteriaBuilder() {&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public static &amp;lt;T&amp;gt; ConditionalCriteriaBuilder&amp;lt;T&amp;gt; criteriaFor(Class&amp;lt;T&amp;gt; clazz) {&lt;br /&gt;        return new ConditionalCriteriaBuilder&amp;lt;T&amp;gt;();&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public List&amp;lt;ConditionalCriteria&amp;gt; build() {&lt;br /&gt;        return criteriaList;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    private void addCriteria(ConditionalCriteria criteria) {&lt;br /&gt;        criteriaList.add(criteria);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public CondditionProperty&amp;lt;T&amp;gt; withProperty(Property&amp;lt;T&amp;gt; property) {&lt;br /&gt;        return new PropBuilderImpl(property.getName());&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public interface CondditionProperty&amp;lt;T&amp;gt; {&lt;br /&gt;        ConditionalCriteriaBuilder&amp;lt;T&amp;gt; eq(Object value);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    private class PropBuilderImpl implements CondditionProperty&amp;lt;T&amp;gt; {&lt;br /&gt;        String propertyName;&lt;br /&gt;&lt;br /&gt;        PropBuilderImpl(String name) {&lt;br /&gt;            this.propertyName = name;&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        public ConditionalCriteriaBuilder&amp;lt;T&amp;gt; eq(Object value) {&lt;br /&gt;            addCriteria(ConditionalCriteria.equal(propertyName, value));&lt;br /&gt;            return ConditionalCriteriaBuilder.this;&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;Alright, let us refactor the model, and introduce a BasicType for the first and last name of the Person:&lt;/div&gt;&lt;br /&gt;&lt;pre class="brush:text"&gt;&lt;br /&gt;Entity Person {&lt;br /&gt;  - @PersonName name&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;BasicType PersonName {&lt;br /&gt;    String first&lt;br /&gt;    String last&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Generate, and you will immediately detect compilation error in the criteria expression. Fix it and it looks like this:&lt;br /&gt;&lt;pre class="brush:java"&gt;&lt;br /&gt;List&amp;lt;ConditionalCriteria&amp;gt; conditionalCriteria = criteriaFor(Person.class)&lt;br /&gt;  .withProperty(name().last()).eq("Svensson").build();&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The generated Properties classes defines refererences also, so it easy to navigate associations with full code completion support. Great, our goals of code completion and refactoring are fulfilled.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;That is not the end of this series of articles. In &lt;a href="http://fornax-sculptor.blogspot.com/2009/12/internal-dsl-for-criteria-part-33.html"&gt;next article&lt;/a&gt; I will illustrate some more advanced operators that requires some intellectual thoughts. See you tomorrow.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3314601722322098765-8234343261388349709?l=fornax-sculptor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fornax-sculptor.blogspot.com/feeds/8234343261388349709/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fornax-sculptor.blogspot.com/2009/11/internal-dsl-for-criteria-part-23.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/8234343261388349709'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/8234343261388349709'/><link rel='alternate' type='text/html' href='http://fornax-sculptor.blogspot.com/2009/11/internal-dsl-for-criteria-part-23.html' title='Internal DSL for Criteria - part 2:3'/><author><name>Patrik</name><uri>http://www.blogger.com/profile/11876565293495127382</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3314601722322098765.post-1331961069591179923</id><published>2009-11-29T20:30:00.003+01:00</published><updated>2009-12-01T21:04:30.065+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Sculptor'/><category scheme='http://www.blogger.com/atom/ns#' term='Internal DSL'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><title type='text'>Internal DSL for Criteria - part 1:3</title><content type='html'>Hibernate has support for criteria queries and that is an awaited feature in JPA 2.0. The criteria API is rather technical and doesn't read to well with your domain terms, your DDD &lt;a href="http://domaindrivendesign.org/node/132"&gt;Ubiquitous Language&lt;/a&gt;. Therefore I have developed a small &lt;a href="http://martinfowler.com/dslwip/InternalOverview.html"&gt;internal DSL&lt;/a&gt; in Java to be able to express conditional criteria in human readable format. It is not intended to handle everything in the criteria API.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I use this to illustrate how to implement an internal DSL. It is not hard once you have learned the tools to play with. Java is not optimal for DSLs, but let us use it as good as possible.&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The goal was to create a fluent api that supported code completion and refactoring in a good way. A simple criteria may look like:&lt;/div&gt;&lt;br /&gt;&lt;pre class="brush:java"&gt;criteriaFor(Person.class).withProperty(sex()).eq(Gender.FEMALE).build();&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;and a more advanced criteria:&lt;br /&gt;&lt;pre class="brush:java"&gt;&lt;br /&gt;criteriaFor(Person.class)&lt;br /&gt;    .withProperty(sex()).eq(Gender.FEMALE).and()&lt;br /&gt;    .withProperty(ssn().country()).eq(SWEDEN).and()&lt;br /&gt;    .lbrace().withProperty(name().first()).like("A%")&lt;br /&gt;        .or().withProperty(name().last()).like("A%").rbrace()&lt;br /&gt;    .orderBy(name().last())&lt;br /&gt;    .build();&lt;br /&gt;&lt;/pre&gt;I will show how a few pieces of the DSL was developed. I have mostly used &lt;a href="http://martinfowler.com/dslwip/ExpressionBuilder.html"&gt;Expression Builder&lt;/a&gt; and &lt;a href="http://martinfowler.com/dslwip/MethodChaining.html"&gt;Method Chaining&lt;/a&gt;, but also a few other tricks.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The expression builder doesn't create the JPA/Hibernate criteria directly. It creates an intermediate structure of objects , which can be translated to criteria API. The main reason for that is decoupling and possibility to have vendor specific implementations.&lt;/div&gt;&lt;br /&gt;&lt;div&gt;First, let me state the obvious: When developing an internal DSL it is perfect to do it the TDD way. You will add more and more features and do a lot of refactoring until you are satisfied with the language.&lt;/div&gt;&lt;br /&gt;&lt;div&gt;Enough of the introductory talking, let us go hands on to a simple equals expression:&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;pre  class="brush:java"&gt;List&amp;lt;ConditionalCriteria&amp;gt; conditionalCriteria =   &lt;br /&gt;    ConditionalCriteriaBuilder.criteria().prop("name").eq("Svensson").build();&lt;br /&gt;&lt;/pre&gt;This can be implemented in the builder as:&lt;br /&gt;&lt;pre class="brush:java"&gt;&lt;br /&gt;public class ConditionalCriteriaBuilder {&lt;br /&gt;    private final List&amp;lt;ConditionalCriteria&amp;gt; criteriaList = new ArrayList&amp;lt;ConditionalCriteria&amp;gt;();&lt;br /&gt;    private String propertyName;&lt;br /&gt;&lt;br /&gt;    private ConditionalCriteriaBuilder() {&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public static ConditionalCriteriaBuilder criteria() {&lt;br /&gt;        return new ConditionalCriteriaBuilder();&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public List&amp;lt;ConditionalCriteria&amp;gt; build() {&lt;br /&gt;        return criteriaList;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public ConditionalCriteriaBuilder prop(String name) {&lt;br /&gt;        propertyName = name;&lt;br /&gt;        return this;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public ConditionalCriteriaBuilder eq(Object value) {&lt;br /&gt;        addCriteria(ConditionalCriteria.equal(propertyName, value));&lt;br /&gt;        return this;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    private void addCriteria(ConditionalCriteria criteria) {&lt;br /&gt;        criteriaList.add(criteria);&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;But that is not very good because it allows invalid expressions, such &lt;code&gt;criteria().eq("A").prop("aaa").eq("B").&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;div&gt;Therefore I introduce an interface for the property level:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;pre class="brush:java"&gt;&lt;br /&gt;public PropBuilder prop(String name) {&lt;br /&gt;    return new PropBuilderImpl(name);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public interface PropBuilder {&lt;br /&gt;    ConditionalCriteriaBuilder eq(Object value);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;private class PropBuilderImpl implements PropBuilder {&lt;br /&gt;    String propertyName;&lt;br /&gt;&lt;br /&gt;    PropBuilderImpl(String name) {&lt;br /&gt;        this.propertyName = name;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public ConditionalCriteriaBuilder eq(Object value) {&lt;br /&gt;        addCriteria(ConditionalCriteria.equal(propertyName, value));&lt;br /&gt;        return ConditionalCriteriaBuilder.this;&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This also facilitates code completion (ctrl+space) in a good way, which was one of our goals.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;In &lt;a href="http://fornax-sculptor.blogspot.com/2009/11/internal-dsl-for-criteria-part-23.html"&gt;next post&lt;/a&gt;, tomorrow, I will take care of the string property names that are not very refactoring friendly.&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3314601722322098765-1331961069591179923?l=fornax-sculptor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fornax-sculptor.blogspot.com/feeds/1331961069591179923/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fornax-sculptor.blogspot.com/2009/11/internal-dsl-for-criteria-part-13.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/1331961069591179923'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/1331961069591179923'/><link rel='alternate' type='text/html' href='http://fornax-sculptor.blogspot.com/2009/11/internal-dsl-for-criteria-part-13.html' title='Internal DSL for Criteria - part 1:3'/><author><name>Patrik</name><uri>http://www.blogger.com/profile/11876565293495127382</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3314601722322098765.post-2788936553709600981</id><published>2009-11-19T13:41:00.000+01:00</published><updated>2009-11-19T18:57:50.431+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Sculptor'/><category scheme='http://www.blogger.com/atom/ns#' term='Productivity'/><category scheme='http://www.blogger.com/atom/ns#' term='automation'/><category scheme='http://www.blogger.com/atom/ns#' term='Agile'/><category scheme='http://www.blogger.com/atom/ns#' term='test'/><category scheme='http://www.blogger.com/atom/ns#' term='Quality'/><title type='text'>How we do automated regression testing with Selenium and Hudson</title><content type='html'>When developing a piece of software that has a lifecycle that spans over several years and periodically is released you have to do regression testing, i.e. making sure that previous features doesn't break because of new stuff.&lt;div&gt;And after a while when your software grows and you add new features, the amount of regression tests increases. To avoid drowning yourself with testing you need to automate as much as possible.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The Sculptor team is a bunch of guys that are driven by interest and are developing the software on there spare time during late nights. We doesn't have the time to do deep manual testing, hence automation is very attractive for us. And since we are geographically distributed and don't have a central CI environment we have to solve some practical problems locally.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;What we do (amongst other things), involves having a local &lt;a href="https://hudson.dev.java.net/"&gt;Hudson&lt;/a&gt; server running on our developing environment (i.e. my iMac) that (of course)  builds all projects and runs unit tests.&lt;/div&gt;&lt;div&gt;But we also use &lt;a href="http://seleniumhq.org/"&gt;Selenium&lt;/a&gt; to run automated functional tests to make sure that our example application works. I though I should show some more details about how we use Selenium.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;As I said, we have an example application called &lt;a href="http://fornax.itemis.de/confluence/display/fornax/3.+Advanced+Tutorial+%28CSC%29"&gt;Library&lt;/a&gt;. If you look in the source code for the &lt;a href="https://fornax.svn.sourceforge.net/svnroot/fornax/trunk/cartridges/sculptor/fornax-cartridges-sculptor-examples-library-web"&gt;library-web&lt;/a&gt; module you will find the directory:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;src/main/webapp/selenium&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-family:Georgia, serif;"&gt;In there you will find a bunch of tests. The root is the suite file:&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;___test-suite.xhtml&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;Selenium test can be written in various programming languages. We have chosen to keep it simple and implement the tests in html. &lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;&lt;br /&gt;&lt;/i&gt;&lt;/div&gt;&lt;div&gt;Having these tests enables us to run them as soon as the code base changes thanks to Hudson and Maven. In Hudson we just creates a new job that is triggered to run as soon as the Library projects is compiled and the unit tests passes. In the pom-file for the Library web project we have a profile that we can use to start a local instance of the &lt;a href="http://www.mortbay.org/jetty/"&gt;Jetty&lt;/a&gt; server. Deploy the the application. Run our selenium tests. And finally stop the server. The Maven configuration for this is:&lt;/div&gt;&lt;pre class="brush:xml"&gt;&amp;lt;profile&amp;gt;&lt;br /&gt;&amp;lt;id&amp;gt;regression&amp;lt;/id&amp;gt;&lt;br /&gt;&amp;lt;build&amp;gt;&lt;br /&gt;&amp;lt;plugins&amp;gt;&lt;br /&gt; &amp;lt;plugin&amp;gt;&lt;br /&gt;   &amp;lt;groupId&amp;gt;org.mortbay.jetty&amp;lt;/groupId&amp;gt;&lt;br /&gt;   &amp;lt;artifactId&amp;gt;maven-jetty-plugin&amp;lt;/artifactId&amp;gt;&lt;br /&gt;   &amp;lt;version&amp;gt;6.1.11&amp;lt;/version&amp;gt;&lt;br /&gt;   &amp;lt;configuration&amp;gt;&lt;br /&gt;     &amp;lt;scanIntervalSeconds&amp;gt;10&amp;lt;/scanIntervalSeconds&amp;gt;&lt;br /&gt;     &amp;lt;stopKey&amp;gt;foo&amp;lt;/stopKey&amp;gt;&lt;br /&gt;     &amp;lt;stopPort&amp;gt;9999&amp;lt;/stopPort&amp;gt;&lt;br /&gt;   &amp;lt;/configuration&amp;gt;&lt;br /&gt;   &amp;lt;executions&amp;gt;&lt;br /&gt;     &amp;lt;execution&amp;gt;&lt;br /&gt;       &amp;lt;id&amp;gt;start-jetty&amp;lt;/id&amp;gt;&lt;br /&gt;       &amp;lt;phase&amp;gt;pre-integration-test&amp;lt;/phase&amp;gt;&lt;br /&gt;       &amp;lt;goals&amp;gt;&lt;br /&gt;         &amp;lt;goal&amp;gt;run&amp;lt;/goal&amp;gt;&lt;br /&gt;       &amp;lt;/goals&amp;gt;&lt;br /&gt;       &amp;lt;configuration&amp;gt;&lt;br /&gt;         &amp;lt;scanIntervalSeconds&amp;gt;0&amp;lt;/scanIntervalSeconds&amp;gt;&lt;br /&gt;         &amp;lt;daemon&amp;gt;true&amp;lt;/daemon&amp;gt;&lt;br /&gt;       &amp;lt;/configuration&amp;gt;&lt;br /&gt;     &amp;lt;/execution&amp;gt;&lt;br /&gt;     &amp;lt;execution&amp;gt;&lt;br /&gt;       &amp;lt;id&amp;gt;stop-jetty&amp;lt;/id&amp;gt;&lt;br /&gt;       &amp;lt;phase&amp;gt;post-integration-test&amp;lt;/phase&amp;gt;&lt;br /&gt;       &amp;lt;goals&amp;gt;&lt;br /&gt;         &amp;lt;goal&amp;gt;stop&amp;lt;/goal&amp;gt;&lt;br /&gt;       &amp;lt;/goals&amp;gt;&lt;br /&gt;     &amp;lt;/execution&amp;gt;&lt;br /&gt;   &amp;lt;/executions&amp;gt;&lt;br /&gt; &amp;lt;/plugin&amp;gt;&lt;br /&gt; &amp;lt;plugin&amp;gt;&lt;br /&gt;   &amp;lt;groupId&amp;gt;org.codehaus.mojo&amp;lt;/groupId&amp;gt;&lt;br /&gt;   &amp;lt;artifactId&amp;gt;selenium-maven-plugin&amp;lt;/artifactId&amp;gt;&lt;br /&gt;   &amp;lt;version&amp;gt;1.0&amp;lt;/version&amp;gt;&lt;br /&gt;   &amp;lt;configuration&amp;gt;&lt;br /&gt;     &amp;lt;suite&amp;gt;src/main/webapp/selenium/___test-suite.xhtml&amp;lt;/suite&amp;gt;&lt;br /&gt;     &amp;lt;browser&amp;gt;*firefox&amp;lt;/browser&amp;gt;&lt;br /&gt;     &amp;lt;results&amp;gt;${project.build.directory}/target/selenium.html&amp;lt;/results&amp;gt;&lt;br /&gt;     &amp;lt;startURL&amp;gt;http://localhost:8080/${artifactId}&amp;lt;/startURL&amp;gt;&lt;br /&gt;   &amp;lt;/configuration&amp;gt;&lt;br /&gt;   &amp;lt;executions&amp;gt;&lt;br /&gt;     &amp;lt;execution&amp;gt;&lt;br /&gt;       &amp;lt;id&amp;gt;run-tests&amp;lt;/id&amp;gt;&lt;br /&gt;       &amp;lt;phase&amp;gt;integration-test&amp;lt;/phase&amp;gt;&lt;br /&gt;       &amp;lt;goals&amp;gt;&lt;br /&gt;         &amp;lt;goal&amp;gt;selenese&amp;lt;/goal&amp;gt;&lt;br /&gt;       &amp;lt;/goals&amp;gt;&lt;br /&gt;     &amp;lt;/execution&amp;gt;&lt;br /&gt;   &amp;lt;/executions&amp;gt;&lt;br /&gt; &amp;lt;/plugin&amp;gt;&lt;br /&gt;&amp;lt;/plugins&amp;gt;&lt;br /&gt;&amp;lt;/build&amp;gt;&lt;br /&gt;&amp;lt;/profile&amp;gt;&lt;br /&gt;&lt;/pre&gt;But, since the amount of tests keeps growing and the time it takes to run them all also grows, there is a need to be able to run just a single test or a few of them. For instance, it is very convenient to have that possibility when developing new features, fixing bugs, or just doing a refactoring. To enable this we use the very competent &lt;a href="http://www.blogger.com/www.mozilla.com/firefox/"&gt;Firefox&lt;/a&gt; plugin called &lt;a href="http://seleniumhq.org/projects/ide/"&gt;SeleniumIDE&lt;/a&gt;. Beside recording test case, you can also load already defined test cases (or suites) and run them.&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;embed src="http://blip.tv/play/AYGvywkA" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="852" height="510"&gt;&lt;/embed&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3314601722322098765-2788936553709600981?l=fornax-sculptor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fornax-sculptor.blogspot.com/feeds/2788936553709600981/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fornax-sculptor.blogspot.com/2009/11/how-we-do-automated-regression-testing.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/2788936553709600981'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/2788936553709600981'/><link rel='alternate' type='text/html' href='http://fornax-sculptor.blogspot.com/2009/11/how-we-do-automated-regression-testing.html' title='How we do automated regression testing with Selenium and Hudson'/><author><name>Andreas</name><uri>http://www.blogger.com/profile/08618614943051927546</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3314601722322098765.post-4738914580864674228</id><published>2009-11-15T21:00:00.001+01:00</published><updated>2009-11-15T21:31:27.604+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Sculptor'/><category scheme='http://www.blogger.com/atom/ns#' term='Quality'/><title type='text'>Promote Quality with Sculptor</title><content type='html'>&lt;style type="text/css"&gt;.nobr br { display: none }&lt;/style&gt;We have written an article that has been published in the paper magazine &lt;a href="http://www.jayway.se/jayview.html"&gt;JayView&lt;/a&gt; Issue 20.&lt;br /&gt;&lt;br /&gt;Without a vision of how to design applications within an organization the development can be compared to lawless Wild West. Development guidelines are often used, but seldom successful over the long haul. We suggest taking the architectural decisions one step further by automating them using a tool such as Sculptor.&lt;br /&gt;&lt;br /&gt;When using a general purpose language, such as Java, and its big toolbox of APIs and frameworks there is a huge freedom of choice. This is a double-edged sword. We meet a lot of companies that have realized that they must narrow down the choices so that each new project doesn't invent its own unique system architecture and product suite. The benefits of a homogeneous architecture is obvious when looking at the big picture.&lt;br /&gt;&lt;br /&gt;The reference architecture is often accomplished by writing guidelines and maybe a sample reference application. There are several problems with a reference architecture that is only promoted by documentation. We suggest automating some pieces of the development by using a code generator tool, such as Sculptor, to enforce consistency in the architecture.&lt;br /&gt;&lt;br /&gt;Read more in the &lt;a href="http://www.jayway.se/jayview/sculptor.pdf"&gt;full article&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3314601722322098765-4738914580864674228?l=fornax-sculptor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fornax-sculptor.blogspot.com/feeds/4738914580864674228/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fornax-sculptor.blogspot.com/2009/11/promote-quality-with-sculptor.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/4738914580864674228'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/4738914580864674228'/><link rel='alternate' type='text/html' href='http://fornax-sculptor.blogspot.com/2009/11/promote-quality-with-sculptor.html' title='Promote Quality with Sculptor'/><author><name>Patrik</name><uri>http://www.blogger.com/profile/11876565293495127382</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3314601722322098765.post-2706778669631558493</id><published>2009-11-03T21:00:00.001+01:00</published><updated>2009-11-03T21:07:32.837+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Sculptor'/><category scheme='http://www.blogger.com/atom/ns#' term='GAE'/><category scheme='http://www.blogger.com/atom/ns#' term='Spring'/><category scheme='http://www.blogger.com/atom/ns#' term='TDD'/><title type='text'>Mocking with App Engine and Spring</title><content type='html'>In &lt;a href="http://fornax-sculptor.blogspot.com/2009/11/unit-testing-with-app-engine-and-spring.html"&gt;previous article&lt;/a&gt; I illustrated how easy it is to get started with unit testing with the local App Engine environment. In this article I will go in to more advanced interaction based testing, i.e. mocking.&lt;br /&gt;&lt;br /&gt;The App Engine APIs are simulated in the local environment. Some local implementations are  designed with testing in mind, such as the email API. It is possible to verify the emails that were sent.&lt;br /&gt;&lt;pre class="brush:java"&gt;&lt;br /&gt;LocalMailService localMailService = AppEngineTestHelper.getLocalMailService();&lt;br /&gt;List&amp;lt;MailMessage&amp;gt; sentMessages = localMailService.getSentMessages();&lt;br /&gt;assertEquals(2, sentMessages.size());&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Some other local implementations are not suitable for unit testing, such as the URL fetch service, which executes a real remote request. To solve this you need to encapsulate usage of external communication and make it possible to replace it when unit testing.&lt;br /&gt;&lt;br /&gt;Since we are using Spring for dependency injection it is possible to replace any Spring bean for testing purpose. In our &lt;a href="http://avega-customer.appspot.com/"&gt;customer-supplier sample&lt;/a&gt; the InquiryRepository in the customer application sends inquiries to the customer application with a REST post.&lt;br /&gt;&lt;br /&gt;This can be replaced when testing by defining a stub implementation that overrides the method that sends then inquiries. This is done in spring xml configuration (more-test.xml):&lt;br /&gt;&lt;pre class="brush:xml"&gt;&lt;br /&gt;&amp;lt;bean id="inquiryRepository"&lt;br /&gt;  class="org.customer.inquiry.repositoryimpl.InquiryRepositoryStub"/&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="brush:java"&gt;&lt;br /&gt;public class InquiryRepositoryStub extends InquiryRepositoryImpl {&lt;br /&gt;    @Override&lt;br /&gt;    protected boolean sendInquiryToSupplier(Inquiry inquiry, Supplier supplier) {&lt;br /&gt;        return true;&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Next step is to use a mocking framework instead. This makes it possible to verify the interaction, i.e. that the &lt;font face="courier new"&gt;sendInquiryToSupplier&lt;/font&gt; method was invoked.&lt;br /&gt;&lt;br /&gt;Then it is motivated to extract the sending to a separate class and interface. It is this interface that we want to mock.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:java"&gt;&lt;br /&gt;public interface InquirySender {&lt;br /&gt;    boolean sendInquiryToSupplier(Inquiry inquiry, Supplier supplier);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The real implementation is an ordinary Spring @Component, that is @Autowired in &lt;font face="courier new"&gt;InquiryRepositoryImpl&lt;/font&gt;. It is this implementation we want to replace with a mock when testing.&lt;br /&gt;&lt;pre class="brush:java"&gt;&lt;br /&gt;@Component&lt;br /&gt;public class InquirySenderImpl implements InquirySender {&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;We use the approach described in the first part of &lt;a href="http://javadevelopmentforthemasses.blogspot.com/2008/07/mocking-spring-tests.html"&gt;Mocking &amp;amp; Spring tests&lt;/a&gt;. The FactoryBean is included in Sculptor so we only need to add the xml definition (more-test.xml):&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:xml"&gt;&lt;br /&gt;  &amp;lt;bean id="inquirySenderMockFactory"&lt;br /&gt;    class="org.fornax.cartridges.sculptor.framework.test.MockitoFactory"&lt;br /&gt;    primary="true" &amp;gt;&lt;br /&gt;      &amp;lt;property name="type" value="org.customer.inquiry.repositoryimpl.InquirySender"/&amp;gt;&lt;br /&gt;  &amp;lt;/bean&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The junit test looks like this:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:java; highlight:[11,12,37,38]"&gt;&lt;br /&gt;public class InquiryServiceTest extends AbstractAppEngineJpaTests&lt;br /&gt;  implements InquiryServiceTestBase {&lt;br /&gt;&lt;br /&gt;  @Autowired&lt;br /&gt;  private InquiryService inquiryService;&lt;br /&gt;  @Autowired&lt;br /&gt;  private InquirySender inquirySenderMock;&lt;br /&gt;&lt;br /&gt;  @Before&lt;br /&gt;  public void initMock() {&lt;br /&gt;      when(inquirySenderMock.sendInquiryToSupplier(any(Inquiry.class), any(Supplier.class)))&lt;br /&gt;          .thenReturn(true);&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  @Before&lt;br /&gt;  public void populateDatastore() {&lt;br /&gt;      Inquiry inquiry1 = new Inquiry();&lt;br /&gt;      inquiry1.setMessage("M1");&lt;br /&gt;      inquiry1.setOwnerEmail("foo@gmail.com2");&lt;br /&gt;      getEntityManager().persist(inquiry1);&lt;br /&gt;&lt;br /&gt;      Supplier supplier1 = new Supplier("S1");&lt;br /&gt;      supplier1.setUrl("http://localhost:8081/rest/inquiry");&lt;br /&gt;      getEntityManager().persist(supplier1);&lt;br /&gt;&lt;br /&gt;      Supplier supplier2 = new Supplier("S2");&lt;br /&gt;      supplier2.setUrl("http://localhost:8081/rest/inquiry");&lt;br /&gt;      getEntityManager().persist(supplier2);&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  @Test&lt;br /&gt;  public void testSendInquiry() throws Exception {&lt;br /&gt;      Key key = KeyFactory.createKey(Inquiry.class.getSimpleName(), 1L);&lt;br /&gt;      boolean ok = inquiryService.sendInquiry(getServiceContext(), key);&lt;br /&gt;      assertTrue(ok);&lt;br /&gt;      // there are 2 suppliers&lt;br /&gt;      verify(inquirySenderMock, times(2)).sendInquiryToSupplier(&lt;br /&gt;          any(Inquiry.class), any(Supplier.class));&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Note that the mock is initialized in the @Before method and then verified last in the test method. In this case two messages should be sent, one for each supplier.&lt;br /&gt;&lt;br /&gt;Maybe you have noticed that this approach is not at all specific for App Engine, it can be used for any Spring application. We need to learn a lot of new things when using App Engine, but some old knowledge still applies. :-)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3314601722322098765-2706778669631558493?l=fornax-sculptor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fornax-sculptor.blogspot.com/feeds/2706778669631558493/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fornax-sculptor.blogspot.com/2009/11/mocking-with-app-engine-and-spring.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/2706778669631558493'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/2706778669631558493'/><link rel='alternate' type='text/html' href='http://fornax-sculptor.blogspot.com/2009/11/mocking-with-app-engine-and-spring.html' title='Mocking with App Engine and Spring'/><author><name>Patrik</name><uri>http://www.blogger.com/profile/11876565293495127382</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3314601722322098765.post-5271259963091575746</id><published>2009-11-01T17:30:00.000+01:00</published><updated>2009-11-01T17:53:37.176+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Sculptor'/><category scheme='http://www.blogger.com/atom/ns#' term='GAE'/><category scheme='http://www.blogger.com/atom/ns#' term='Spring'/><category scheme='http://www.blogger.com/atom/ns#' term='TDD'/><title type='text'>Unit Testing with App Engine and Spring</title><content type='html'>Sculptor makes it easy to write JUnit tests for Google App Engine. A test case looks like this:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:java"&gt;&lt;br /&gt;public class SupplierServiceTest extends AbstractAppEngineJpaTests {&lt;br /&gt;&lt;br /&gt;    @Autowired&lt;br /&gt;    private SupplierService supplierService;&lt;br /&gt;&lt;br /&gt;    @Before&lt;br /&gt;    public void populateDatastore() {&lt;br /&gt;        Supplier supplier1 = new Supplier("S1");&lt;br /&gt;        getEntityManager().persist(supplier1);&lt;br /&gt;&lt;br /&gt;        Supplier supplier2 = new Supplier("S2");&lt;br /&gt;        getEntityManager().persist(supplier2);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    @Test&lt;br /&gt;    public void testFindAll() throws Exception {&lt;br /&gt;        List&amp;lt;Supplier&amp;gt; all = supplierService.findAll(getServiceContext());&lt;br /&gt;        assertEquals(2, all.size());&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    @Test&lt;br /&gt;    public void testFindByName() throws Exception {&lt;br /&gt;        Supplier found = supplierService.findByName(getServiceContext(), "S2");&lt;br /&gt;        assertNotNull(found);&lt;br /&gt;        assertEquals("S2", found.getName());&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Very natural!&lt;br /&gt;&lt;br /&gt;It is interesting to take a look at the base class. It defines a few annotations and extends AbstractJUnit4SpringContextTests to initialize the Spring environment. This enables usage of  ordinary @Autowire dependency injection directly in the test class.&lt;br /&gt;&lt;pre class="brush:java"&gt;&lt;br /&gt;@RunWith(SpringJUnit4ClassRunner.class)&lt;br /&gt;@ContextConfiguration(locations =  {"classpath:applicationContext-test.xml"})&lt;br /&gt;public abstract class AbstractAppEngineJpaTests&lt;br /&gt;    extends AbstractJUnit4SpringContextTests {&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The embedded App Engine environment is initialized from a method annotated with @Before, i.e. invoked before each test method.&lt;br /&gt;&lt;pre class="brush:java"&gt;&lt;br /&gt;public static void setUpAppEngine(ApiProxy.Environment testEnvironment) {&lt;br /&gt;    ApiProxy.setEnvironmentForCurrentThread(testEnvironment);&lt;br /&gt;&lt;br /&gt;    ApiProxy.setDelegate(new ApiProxyLocalImpl(new File(".")) {&lt;br /&gt;    });&lt;br /&gt;&lt;br /&gt;    ApiProxyLocalImpl proxy = (ApiProxyLocalImpl) ApiProxy.getDelegate();&lt;br /&gt;    proxy.setProperty(LocalDatastoreService.NO_STORAGE_PROPERTY, Boolean.TRUE.toString());&lt;br /&gt;    clearSentEmailMessages();&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public static void tearDownAppEngine() {&lt;br /&gt;    ApiProxyLocalImpl proxy = (ApiProxyLocalImpl) ApiProxy.getDelegate();&lt;br /&gt;    LocalDatastoreService datastoreService = (LocalDatastoreService) proxy.getService("datastore_v3");&lt;br /&gt;    datastoreService.clearProfiles();&lt;br /&gt;    clearSentEmailMessages();&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;It is initialized with in memory data store, i.e. it is empty before each test method. You may populate it with initial data in your subclass in a @Before method, see populateDataStore in the sample above.&lt;br /&gt;&lt;br /&gt;I learned one thing when doing junit testing in the app engine environment. When working with ordinary databases I have found the Spring transactional test support useful, i.e. Spring executes each test method in a transaction, which is rolled back after the test mehtod. That is achieved with the following annotations and usage of the annotation @BeforeTransaction instead of the ordinary @Before.&lt;br /&gt;&lt;pre class="brush:java"&gt;&lt;br /&gt;@RunWith(SpringJUnit4ClassRunner.class)&lt;br /&gt;@ContextConfiguration(locations =  {"classpath:applicationContext-test.xml"})&lt;br /&gt;@TestExecutionListeners(TransactionalTestExecutionListener.class)&lt;br /&gt;@TransactionConfiguration(transactionManager = "txManager", defaultRollback = true)&lt;br /&gt;@Transactional&lt;br /&gt;public abstract class AbstractAppEngineJpaTests&lt;br /&gt;    extends AbstractJUnit4SpringContextTests {&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;That was my initial approach also with app engine, but I realized that it was not good. Look at the following test. It will fail on the last assert when using the above transactional support.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:java; highlight:[6,7]"&gt;&lt;br /&gt;   @Test&lt;br /&gt;   public void testSave() throws Exception {&lt;br /&gt;       int countBefore = countRowsInTable(Supplier.class);&lt;br /&gt;       Supplier supplier3 = new Supplier("S3");&lt;br /&gt;       supplierService.save(getServiceContext(), supplier3);&lt;br /&gt;       int countAfter = countRowsInTable(Supplier.class);&lt;br /&gt;       assertEquals(countBefore + 1, countAfter);&lt;br /&gt;   }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The reason is that queries see a snapshot of the datastore as of the beginning of the transaction.&lt;br /&gt;&lt;br /&gt;Data isolation between test methods is no problem, since the datastore is initialized (empty) before each test method.&lt;br /&gt;&lt;br /&gt;That's all!  Try it yourself by running the &lt;a href="http://fornax-sculptor.blogspot.com/2009/10/maven-archetype-for-app-engine.html"&gt;Maven Archetype for App Engine&lt;/a&gt; and fill in the details in the generated PlanetServiceTest.&lt;br /&gt;&lt;ol&gt;&lt;li&gt;&lt;code&gt;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/&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;mvn clean eclipse:eclipse&lt;/code&gt;&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;Stay tuned, in next post I will describe how to mock.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3314601722322098765-5271259963091575746?l=fornax-sculptor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fornax-sculptor.blogspot.com/feeds/5271259963091575746/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fornax-sculptor.blogspot.com/2009/11/unit-testing-with-app-engine-and-spring.html#comment-form' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/5271259963091575746'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/5271259963091575746'/><link rel='alternate' type='text/html' href='http://fornax-sculptor.blogspot.com/2009/11/unit-testing-with-app-engine-and-spring.html' title='Unit Testing with App Engine and Spring'/><author><name>Patrik</name><uri>http://www.blogger.com/profile/11876565293495127382</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3314601722322098765.post-3620556309774125151</id><published>2009-10-24T18:54:00.002+02:00</published><updated>2009-10-26T15:15:03.073+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='springframework'/><category scheme='http://www.blogger.com/atom/ns#' term='Sculptor'/><category scheme='http://www.blogger.com/atom/ns#' term='GAE'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='REST'/><category scheme='http://www.blogger.com/atom/ns#' term='Spring'/><category scheme='http://www.blogger.com/atom/ns#' term='Quality'/><category scheme='http://www.blogger.com/atom/ns#' term='System design'/><title type='text'>Decouple modules with asynchronous event dispatching using Spring and task queues in GAE</title><content type='html'>To build applications that are maintainable and robust you should strive for decoupling between modules. To build applications that scale you will always benefit from asynchronism and parallellism.&lt;br /&gt;Here we will look how to accomplish the above in GoogleAppEngine and with some help from springframework.&lt;br /&gt;Lets say we have an application where users can register them self. When they do, the application creates a persistence instance of a User-object. But, we will also keep track of how many users we have registered on the site. Now, being in GAE with BigTable luring in the back, doing queries and calculations (as we are used to with a traditional database) isn't a good idea. So as an alternative we choose to have a separate Counter-object that we updates when ever a new user registers. Ok, nothing strange here. But, there are a couple of flaws here:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;The User module needs to know about the Counter module.&lt;/li&gt;&lt;li&gt;The User module has to wait for the Counter module to finish when updating the counting.  &lt;/li&gt;&lt;/ol&gt;Ok, lets solve the first by using spring's mechanism for ApplicationEvent's. First, let us put some aop magic to work to intercept the call to UserService.createUser and when it returns (and we have the transaction boundaries on service methods, so no exception, all went well) fire off an event. Spring config for the aop stuff:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:xml"&gt;&lt;br /&gt;&amp;lt;bean id="userListener" class="org.fornax.sculptor.UserListener"/&amp;gt;&lt;br /&gt;&amp;lt;bean id="userAdvice" class="org.fornax.sculptor.UserAdvice"/&amp;gt;&lt;br /&gt;&amp;lt;aop:config&amp;gt;&lt;br /&gt;&amp;lt;aop:pointcut id="userCreationPointcut" expression="execution(public * org..UserService.createUser(..))"/&amp;gt;&lt;br /&gt;&amp;lt;aop:advisor pointcut-ref="userCreationPointcut" ref="userCreationPointcut"/&amp;gt;&lt;br /&gt;&amp;lt;/aop:config&amp;gt;&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Next, here is the advice:&lt;br /&gt;&lt;pre class="brush:java"&gt;&lt;br /&gt;public class UserAdvice implements MethodInterceptor, ApplicationContextAware {&lt;br /&gt;&lt;br /&gt;private ApplicationContext ctx;&lt;br /&gt;&lt;br /&gt;public Object invoke(MethodInvocation invocation) throws Throwable {&lt;br /&gt;  User user = (User) invocation.proceed();&lt;br /&gt;  fireNewUserEvent(user);&lt;br /&gt;  return user;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;private void fireNewUserEvent(User user) {&lt;br /&gt;  ctx.publishEvent(new UserCreatedEvent(user));&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {&lt;br /&gt;  this.ctx = applicationContext;&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The listener that is being notified:&lt;br /&gt;&lt;pre class="brush:java"&gt;&lt;br /&gt;public class UserListener implements ApplicationListener&amp;lt;UserCreatedEvent&amp;gt; {&lt;br /&gt;@Autowired private CounterService counterService;&lt;br /&gt;public void onApplicationEvent(UserCreatedEvent event) {&lt;br /&gt;  counterService.increment();&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And the event being passed:&lt;br /&gt;&lt;pre class="brush:java"&gt;&lt;br /&gt;public class UserCreatedEvent extends ApplicationEvent {&lt;br /&gt;public UserCreatedEvent(User user) {&lt;br /&gt;  super(user);&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Ok, so now we are half way. We have the Observer pattern in place. But we still does everything synchronous.&lt;br /&gt;Enter GAE's task queue's. Let us modify our UserListener:&lt;br /&gt;&lt;pre class="brush:java"&gt;&lt;br /&gt;public class UserListener implements ApplicationListener&amp;lt;UserCreatedEvent&amp;gt; {&lt;br /&gt;&lt;br /&gt;public void onApplicationEvent(UserCreatedEvent event) {&lt;br /&gt;  TaskOptions task = url("/rest/admin/counter/user").method(POST);&lt;br /&gt;  Queue queue = QueueFactory.getDefaultQueue();&lt;br /&gt;  queue.add(task);&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And by the wonders of task queue's, we now put a task on the queue and by that we do the counting job asynchronous. And of course, we dropped the reference to the CounterService. But we miss one piece here, right? What does the url in the task point at. Well, nothing strange here, it is just a spring mvc controller:&lt;br /&gt;&lt;pre class="brush:java"&gt;&lt;br /&gt;@Controller&lt;br /&gt;public class CounterCountroller {&lt;br /&gt;@Autowired private CounterService counterService;&lt;br /&gt;@RequestMapping(value = "/admin/counter/user", method = RequestMethod.POST)&lt;br /&gt;public void incrementCounter() throws IOException {&lt;br /&gt;  try {&lt;br /&gt;    counterService.increment();&lt;br /&gt;  } catch (Exception ignore) {&lt;br /&gt;    // doesn't matter if we get an exception here, just log it&lt;br /&gt;    log.error("Failed to increment counter!", ignore);&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;And now we have a more loosely coupled system that scales better. And with a little effort, the code can be generalized so more features are easy to add with the same pattern.&lt;br /&gt;Of course, the downside of this kind of design is that error handling gets more complicated and you can't always trust it to be 'right'. But that is system design, you have to decide what's best for each situation.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3314601722322098765-3620556309774125151?l=fornax-sculptor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fornax-sculptor.blogspot.com/feeds/3620556309774125151/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fornax-sculptor.blogspot.com/2009/10/decouple-modules-with-asynchronous.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/3620556309774125151'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/3620556309774125151'/><link rel='alternate' type='text/html' href='http://fornax-sculptor.blogspot.com/2009/10/decouple-modules-with-asynchronous.html' title='Decouple modules with asynchronous event dispatching using Spring and task queues in GAE'/><author><name>Andreas</name><uri>http://www.blogger.com/profile/08618614943051927546</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3314601722322098765.post-2392416861756279187</id><published>2009-10-21T22:00:00.001+02:00</published><updated>2009-10-21T22:03:40.657+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Customization'/><category scheme='http://www.blogger.com/atom/ns#' term='Quality'/><category scheme='http://www.blogger.com/atom/ns#' term='Code generator'/><title type='text'>Even Weird Naming Conventions are Good</title><content type='html'>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 :-)&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Table names should be prefixed with application/component identifier.&lt;/li&gt;&lt;li&gt;Primary key id column should be prefixed with table name (without application prefix) and followed by _GID.&lt;/li&gt;&lt;li&gt;Underscore to separate words.&lt;/li&gt;&lt;li&gt;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.&lt;/li&gt;&lt;/ul&gt;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.&lt;br /&gt;&lt;br /&gt;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.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3314601722322098765-2392416861756279187?l=fornax-sculptor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fornax-sculptor.blogspot.com/feeds/2392416861756279187/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fornax-sculptor.blogspot.com/2009/10/even-weird-naming-conventions-are-good.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/2392416861756279187'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/2392416861756279187'/><link rel='alternate' type='text/html' href='http://fornax-sculptor.blogspot.com/2009/10/even-weird-naming-conventions-are-good.html' title='Even Weird Naming Conventions are Good'/><author><name>Patrik</name><uri>http://www.blogger.com/profile/11876565293495127382</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3314601722322098765.post-2393644508453522118</id><published>2009-10-19T14:22:00.000+02:00</published><updated>2009-10-19T19:42:54.258+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Comparison'/><category scheme='http://www.blogger.com/atom/ns#' term='Sculptor'/><category scheme='http://www.blogger.com/atom/ns#' term='Refactoring'/><category scheme='http://www.blogger.com/atom/ns#' term='Agile'/><category scheme='http://www.blogger.com/atom/ns#' term='Code generator'/><title type='text'>Sculptor and Agile</title><content type='html'>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 :-)&lt;div&gt;Sculptor will help you if you want to work agile.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Things change&lt;/b&gt;&lt;/div&gt;&lt;div&gt;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.&lt;br /&gt;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.&lt;br /&gt;So, what are the changes  that needs to be done?&lt;br /&gt;From the bottom up:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Database schema&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Persistence layer (JPA, Hibernate, etc) :&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Domain object&lt;/li&gt;&lt;li&gt;Service layer (if we have any logic tied to the attribute)&lt;/li&gt;&lt;li&gt;Back office Client (for creating a customer object)&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Public Client (for viewing a customer object)&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;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:&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;style type="text/css"&gt;#foo tr {margin-bottom: 1px} #foo td {text-align:right;background-color:#EEEEEE;margin-bottom:1px}&lt;/style&gt;&lt;br /&gt;&lt;table id="foo" style="" width="80%"&gt;&lt;tbody style="text-align: center;"&gt;&lt;tr&gt;&lt;th&gt;&lt;br /&gt;&lt;/th&gt;&lt;th&gt;Without Sculptor&lt;/th&gt;&lt;th&gt;With Sculptor&lt;/th&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;model:&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;db script:&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;db migration script:&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;property in domain object:&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;jpa annotation för property:&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;logic in service or domain object:&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;back office client (create/update/list/view):&lt;/td&gt;&lt;td&gt;7&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;public client (view):&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;td&gt;junit test (save, find):&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;td&gt;documentation (class diagrams):&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;td&gt;sum:&lt;/td&gt;&lt;td&gt;16&lt;/td&gt;&lt;td&gt;6&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;/tbody&gt;&lt;tbody&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;&lt;div&gt;So, being simple, lets say that each point is equally valued. By this assumption we reduce risk and time by 150%&lt;/div&gt;&lt;div&gt;&lt;br /&gt;This can be very valuable when change comes along. And it does, doesn't it...?&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3314601722322098765-2393644508453522118?l=fornax-sculptor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fornax-sculptor.blogspot.com/feeds/2393644508453522118/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fornax-sculptor.blogspot.com/2009/08/sculptor-and-agile.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/2393644508453522118'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/2393644508453522118'/><link rel='alternate' type='text/html' href='http://fornax-sculptor.blogspot.com/2009/08/sculptor-and-agile.html' title='Sculptor and Agile'/><author><name>Andreas</name><uri>http://www.blogger.com/profile/08618614943051927546</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3314601722322098765.post-5629084319217374733</id><published>2009-10-04T22:00:00.003+02:00</published><updated>2009-10-04T22:24:26.305+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Sculptor'/><category scheme='http://www.blogger.com/atom/ns#' term='GAE'/><category scheme='http://www.blogger.com/atom/ns#' term='Productivity'/><category scheme='http://www.blogger.com/atom/ns#' term='Spring'/><category scheme='http://www.blogger.com/atom/ns#' term='Code generator'/><title type='text'>Sculptor in the Cloud</title><content type='html'>Now you can use &lt;a href="http://fornax-sculptor.blogspot.com/2009/06/what-is-sculptor.html"&gt;Sculptor&lt;/a&gt; to speed up and simplify development of applications running in the &lt;a href="http://code.google.com/appengine/"&gt;Google App Engine&lt;/a&gt; cloud.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_qr2e_ywa0-I/SsiFn4s_WWI/AAAAAAAABQk/NGQzl1EemA0/s1600-h/sculptor-logo.jpg"&gt;&lt;img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer; width: 218px; height: 66px;" src="http://1.bp.blogspot.com/_qr2e_ywa0-I/SsiFn4s_WWI/AAAAAAAABQk/NGQzl1EemA0/s400/sculptor-logo.jpg" alt="" id="BLOGGER_PHOTO_ID_5388703874431277410" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;a href="http://code.google.com/appengine/"&gt;&lt;img src="http://code.google.com/appengine/images/appengine-silver-120x30.gif" alt="Powered by App Engine" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Let's start with a demo of how easy it is to create a new application and deploy it.&lt;br /&gt;&lt;br /&gt;&lt;embed src="http://blip.tv/play/AYGk2kIA" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" height="510" width="852"&gt;&lt;/embed&gt;&lt;br /&gt;&lt;br /&gt;For this we are using Sculptor &lt;a href="http://fornax-sculptor.blogspot.com/2009/10/maven-archetype-for-app-engine.html"&gt;maven archetype for App Engine&lt;/a&gt;. Try it yourself:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;&lt;code&gt;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/&lt;/code&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;code&gt;cd&lt;/code&gt; to the new directory&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;code&gt;mvn clean&lt;/code&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;code&gt;mvn generate-sources&lt;/code&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;code&gt;mvn eclipse:eclipse&lt;/code&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Import the project in Eclipse&lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;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: &lt;a href="http://sculptor-helloworld.appspot.com/"&gt;http://sculptor-helloworld.appspot.com&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_qr2e_ywa0-I/SscjIJWtndI/AAAAAAAABP0/ipRgEHhH2c4/s1600-h/image2.png"&gt;&lt;img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer; width: 328px; height: 335px;" src="http://3.bp.blogspot.com/_qr2e_ywa0-I/SscjIJWtndI/AAAAAAAABP0/ipRgEHhH2c4/s800/image2.png" alt="" id="BLOGGER_PHOTO_ID_5388314102029196754" border="0" /&gt;&lt;/a&gt;The archetype creates a sample of of a RESTful Spring 3.0 Controller and JSP pages for the CRUD operations.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;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).&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_qr2e_ywa0-I/SsckufrpCkI/AAAAAAAABP8/J-n0YHIEvj0/s1600-h/image1.png"&gt;&lt;img style="cursor: pointer; width: 698px; height: 344px;" src="http://4.bp.blogspot.com/_qr2e_ywa0-I/SsckufrpCkI/AAAAAAAABP8/J-n0YHIEvj0/s800/image1.png" alt="" id="BLOGGER_PHOTO_ID_5388315860369214018" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;Owned and embedded associations are supported and mapped as ordinary JPA associations. They are specified with &lt;code&gt;aggregate&lt;/code&gt; and &lt;code&gt;BasicType&lt;/code&gt; in the Sculptor model.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_qr2e_ywa0-I/SsclRek6l3I/AAAAAAAABQE/gzmXcp8mOiY/s1600-h/image3.png"&gt;&lt;img style="cursor: pointer; width: 705px; height: 424px;" src="http://1.bp.blogspot.com/_qr2e_ywa0-I/SsclRek6l3I/AAAAAAAABQE/gzmXcp8mOiY/s800/image3.png" alt="" id="BLOGGER_PHOTO_ID_5388316461367990130" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Unowned associations are handled with &lt;code&gt;id&lt;/code&gt; references and you must lookup the objects with &lt;code&gt;findById&lt;/code&gt; when needed.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_qr2e_ywa0-I/SsclbQb1YNI/AAAAAAAABQM/sOZaoOgylrY/s1600-h/image4.png"&gt;&lt;img style="cursor: pointer; width: 744px; height: 407px;" src="http://4.bp.blogspot.com/_qr2e_ywa0-I/SsclbQb1YNI/AAAAAAAABQM/sOZaoOgylrY/s800/image4.png" alt="" id="BLOGGER_PHOTO_ID_5388316629370495186" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Services and Repositories are implemented as Spring components with @Autowired dependency injection. Spring AOP is used for error handling and transaction management.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_qr2e_ywa0-I/SshHAxHWAtI/AAAAAAAABQc/FV4xSKARa18/s1600-h/image5.png"&gt;&lt;img style="cursor: pointer; width: 746px; height: 499px;" src="http://4.bp.blogspot.com/_qr2e_ywa0-I/SshHAxHWAtI/AAAAAAAABQc/FV4xSKARa18/s800/image5.png" alt="" id="BLOGGER_PHOTO_ID_5388635032658510546" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Behavior is implemented with hand written code in subclass, separated from re-generated code in base class.  In the above example the &lt;code&gt;sayHello&lt;/code&gt; method is typically implemented in the Service by first using the generated &lt;code&gt;findByKey&lt;/code&gt; method in the Repository. Note that the &lt;code&gt;name&lt;/code&gt; attribute of the &lt;code&gt;Planet&lt;/code&gt; is marked as key.&lt;br /&gt;&lt;br /&gt;Sculptor also provides support for JUnit testing with the local App Engine environment. I will cover that in another article some day soon.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3314601722322098765-5629084319217374733?l=fornax-sculptor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fornax-sculptor.blogspot.com/feeds/5629084319217374733/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fornax-sculptor.blogspot.com/2009/10/sculptor-in-cloud.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/5629084319217374733'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/5629084319217374733'/><link rel='alternate' type='text/html' href='http://fornax-sculptor.blogspot.com/2009/10/sculptor-in-cloud.html' title='Sculptor in the Cloud'/><author><name>Patrik</name><uri>http://www.blogger.com/profile/11876565293495127382</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_qr2e_ywa0-I/SsiFn4s_WWI/AAAAAAAABQk/NGQzl1EemA0/s72-c/sculptor-logo.jpg' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3314601722322098765.post-1974386804948754274</id><published>2009-10-01T21:00:00.005+02:00</published><updated>2009-10-01T21:24:25.720+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Sculptor'/><category scheme='http://www.blogger.com/atom/ns#' term='GAE'/><category scheme='http://www.blogger.com/atom/ns#' term='Productivity'/><category scheme='http://www.blogger.com/atom/ns#' term='Code generator'/><title type='text'>Maven Archetype for App Engine</title><content type='html'>I have developed a maven archetype for &lt;a href="http://code.google.com/appengine/"&gt;Google App Engine&lt;/a&gt; projects. The generated project supports:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;All dependency jar files are downloaded from maven repositories and copied to lib directory as required by &lt;a href="http://code.google.com/appengine/docs/java/tools/eclipse.html"&gt;App Engine Eclipse plugin&lt;/a&gt;, and local development server.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Eclipse project is created with &lt;code&gt;mvn eclipse:eclipse&lt;/code&gt;. The resulting Eclipse project has the necessary settings for App Engine Eclipse plugin.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Entity classes are processed by DataNucleus enhancer in the build lifecycle.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;JUnit tests with local App Engine environment can be run from maven.&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;Setting up all of this is not trivial and therefore I would like to share the solution and I hope you find it useful.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Eclipse Project&lt;/span&gt;&lt;br /&gt;The maven eclipse plugin need a lot of configuration.&lt;br /&gt;&lt;pre style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 100%;"&gt;&lt;code&gt;&amp;lt;build&amp;gt;&lt;br /&gt;&amp;lt;outputDirectory&amp;gt;war/WEB-INF/classes&amp;lt;/outputDirectory&amp;gt;&lt;br /&gt;&amp;lt;plugins&amp;gt;&lt;br /&gt;    &amp;lt;plugin&amp;gt;&lt;br /&gt;        &amp;lt;artifactId&amp;gt;maven-eclipse-plugin&amp;lt;/artifactId&amp;gt;&lt;br /&gt;        &amp;lt;version&amp;gt;2.5.1&amp;lt;/version&amp;gt;&lt;br /&gt;        &amp;lt;configuration&amp;gt;&lt;br /&gt;            &amp;lt;!--&lt;br /&gt;                buildOutputDirectory doesn't work due to&lt;br /&gt;                http://jira.codehaus.org/browse/MECLIPSE-422 An workaround is the&lt;br /&gt;                outputDirectory at project/build level&lt;br /&gt;                &amp;lt;buildOutputDirectory&amp;gt;war/WEB-INF/classes&amp;lt;/buildOutputDirectory&amp;gt;&lt;br /&gt;            --&amp;gt;&lt;br /&gt;            &amp;lt;testOutputDirectory&amp;gt;target/test-classes&amp;lt;/testOutputDirectory&amp;gt;&lt;br /&gt;            &amp;lt;classpathContainers&amp;gt;&lt;br /&gt;                &amp;lt;classpathContainer&amp;gt;com.google.appengine.eclipse.core.GAE_CONTAINER&amp;lt;/classpathContainer&amp;gt;&lt;br /&gt;            &amp;lt;/classpathContainers&amp;gt;&lt;br /&gt;            &amp;lt;buildcommands&amp;gt;&lt;br /&gt;                &amp;lt;buildcommand&amp;gt;org.eclipse.jdt.core.javabuilder&amp;lt;/buildcommand&amp;gt;&lt;br /&gt;                &amp;lt;buildcommand&amp;gt;com.google.gdt.eclipse.core.webAppProjectValidator&amp;lt;/buildcommand&amp;gt;&lt;br /&gt;                &amp;lt;buildcommand&amp;gt;com.google.appengine.eclipse.core.enhancerbuilder&amp;lt;/buildcommand&amp;gt;&lt;br /&gt;                &amp;lt;buildcommand&amp;gt;com.google.appengine.eclipse.core.projectValidator&amp;lt;/buildcommand&amp;gt;&lt;br /&gt;            &amp;lt;/buildcommands&amp;gt;&lt;br /&gt;            &amp;lt;additionalProjectnatures&amp;gt;&lt;br /&gt;                &amp;lt;projectnature&amp;gt;org.eclipse.jdt.core.javanature&amp;lt;/projectnature&amp;gt;&lt;br /&gt;                &amp;lt;projectnature&amp;gt;com.google.appengine.eclipse.core.gaeNature&amp;lt;/projectnature&amp;gt;&lt;br /&gt;                &amp;lt;projectnature&amp;gt;com.google.gdt.eclipse.core.webAppNature&amp;lt;/projectnature&amp;gt;&lt;br /&gt;            &amp;lt;/additionalProjectnatures&amp;gt;&lt;br /&gt;            &amp;lt;excludes&amp;gt;&lt;br /&gt;                &amp;lt;!-- Included in GAE_CONTAINER --&amp;gt;&lt;br /&gt;                &amp;lt;exclude&amp;gt;com.google.appengine:appengine-api-1.0-sdk&amp;lt;/exclude&amp;gt;&lt;br /&gt;                &amp;lt;exclude&amp;gt;com.google.appengine:appengine-api-1.0-labs&amp;lt;/exclude&amp;gt;&lt;br /&gt;                &amp;lt;exclude&amp;gt;com.google.appengine.orm:datanucleus-appengine&amp;lt;/exclude&amp;gt;&lt;br /&gt;                &amp;lt;exclude&amp;gt;org.datanucleus:datanucleus-jpa&amp;lt;/exclude&amp;gt;&lt;br /&gt;                &amp;lt;exclude&amp;gt;org.datanucleus:datanucleus-core&amp;lt;/exclude&amp;gt;&lt;br /&gt;                &amp;lt;exclude&amp;gt;org.apache.geronimo.specs:geronimo-jpa_3.0_spec&amp;lt;/exclude&amp;gt;&lt;br /&gt;                &amp;lt;exclude&amp;gt;org.apache.geronimo.specs:geronimo-jta_1.1_spec&amp;lt;/exclude&amp;gt;&lt;br /&gt;                &amp;lt;exclude&amp;gt;javax.jdo:jdo2-api&amp;lt;/exclude&amp;gt;&lt;br /&gt;            &amp;lt;/excludes&amp;gt;&lt;br /&gt;        &amp;lt;/configuration&amp;gt;&lt;br /&gt;    &amp;lt;/plugin&amp;gt;&lt;br /&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;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 (&lt;a href="http://jira.codehaus.org/browse/MECLIPSE-422"&gt;MECLIPSE-422&lt;/a&gt;) which cause the test classes to not be separated if &lt;code&gt;buildOutputDirectory&lt;/code&gt; 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 &lt;code&gt;testOutputDirectory&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Copy Dependencies&lt;/span&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;&lt;pre style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 100%;"&gt;&lt;code&gt;&amp;lt;plugin&amp;gt;&lt;br /&gt;&amp;lt;groupId&amp;gt;org.apache.maven.plugins&amp;lt;/groupId&amp;gt;&lt;br /&gt;&amp;lt;artifactId&amp;gt;maven-dependency-plugin&amp;lt;/artifactId&amp;gt;&lt;br /&gt;&amp;lt;executions&amp;gt;&lt;br /&gt;    &amp;lt;execution&amp;gt;&lt;br /&gt;        &amp;lt;id&amp;gt;copy&amp;lt;/id&amp;gt;&lt;br /&gt;        &amp;lt;phase&amp;gt;clean&amp;lt;/phase&amp;gt;&lt;br /&gt;        &amp;lt;goals&amp;gt;&lt;br /&gt;            &amp;lt;goal&amp;gt;copy&amp;lt;/goal&amp;gt;&lt;br /&gt;        &amp;lt;/goals&amp;gt;&lt;br /&gt;        &amp;lt;configuration&amp;gt;&lt;br /&gt;            &amp;lt;artifactItems&amp;gt;&lt;br /&gt;                &amp;lt;artifactItem&amp;gt;&lt;br /&gt;                    &amp;lt;groupId&amp;gt;org.springframework&amp;lt;/groupId&amp;gt;&lt;br /&gt;                    &amp;lt;artifactId&amp;gt;spring-core&amp;lt;/artifactId&amp;gt;&lt;br /&gt;                    &amp;lt;version&amp;gt;${spring.version}&amp;lt;/version&amp;gt;&lt;br /&gt;                    &amp;lt;outputDirectory&amp;gt;war/WEB-INF/lib&amp;lt;/outputDirectory&amp;gt;&lt;br /&gt;                &amp;lt;/artifactItem&amp;gt;&lt;br /&gt;                &amp;lt;!-- more ... --&amp;gt;&lt;br /&gt;                &amp;lt;artifactItem&amp;gt;&lt;br /&gt;                    &amp;lt;groupId&amp;gt;com.google.appengine&amp;lt;/groupId&amp;gt;&lt;br /&gt;                    &amp;lt;artifactId&amp;gt;appengine-api-1.0-sdk&amp;lt;/artifactId&amp;gt;&lt;br /&gt;                    &amp;lt;version&amp;gt;${appengine.version}&amp;lt;/version&amp;gt;&lt;br /&gt;                    &amp;lt;outputDirectory&amp;gt;war/WEB-INF/lib&amp;lt;/outputDirectory&amp;gt;&lt;br /&gt;                &amp;lt;/artifactItem&amp;gt;&lt;br /&gt;                &amp;lt;artifactItem&amp;gt;&lt;br /&gt;                    &amp;lt;groupId&amp;gt;com.google.appengine&amp;lt;/groupId&amp;gt;&lt;br /&gt;                    &amp;lt;artifactId&amp;gt;appengine-api-1.0-labs&amp;lt;/artifactId&amp;gt;&lt;br /&gt;                    &amp;lt;version&amp;gt;${appengine.version}&amp;lt;/version&amp;gt;&lt;br /&gt;                    &amp;lt;outputDirectory&amp;gt;war/WEB-INF/lib&amp;lt;/outputDirectory&amp;gt;&lt;br /&gt;                &amp;lt;/artifactItem&amp;gt;&lt;br /&gt;                &amp;lt;artifactItem&amp;gt;&lt;br /&gt;                    &amp;lt;groupId&amp;gt;com.google.appengine.orm&amp;lt;/groupId&amp;gt;&lt;br /&gt;                    &amp;lt;artifactId&amp;gt;datanucleus-appengine&amp;lt;/artifactId&amp;gt;&lt;br /&gt;                    &amp;lt;version&amp;gt;1.0.3&amp;lt;/version&amp;gt;&lt;br /&gt;                    &amp;lt;outputDirectory&amp;gt;war/WEB-INF/lib&amp;lt;/outputDirectory&amp;gt;&lt;br /&gt;                &amp;lt;/artifactItem&amp;gt;&lt;br /&gt;                &amp;lt;artifactItem&amp;gt;&lt;br /&gt;                    &amp;lt;groupId&amp;gt;org.datanucleus&amp;lt;/groupId&amp;gt;&lt;br /&gt;                    &amp;lt;artifactId&amp;gt;datanucleus-jpa&amp;lt;/artifactId&amp;gt;&lt;br /&gt;                    &amp;lt;version&amp;gt;1.1.5&amp;lt;/version&amp;gt;&lt;br /&gt;                    &amp;lt;outputDirectory&amp;gt;war/WEB-INF/lib&amp;lt;/outputDirectory&amp;gt;&lt;br /&gt;                &amp;lt;/artifactItem&amp;gt;&lt;br /&gt;                &amp;lt;artifactItem&amp;gt;&lt;br /&gt;                    &amp;lt;groupId&amp;gt;org.datanucleus&amp;lt;/groupId&amp;gt;&lt;br /&gt;                    &amp;lt;artifactId&amp;gt;datanucleus-core&amp;lt;/artifactId&amp;gt;&lt;br /&gt;                    &amp;lt;version&amp;gt;1.1.5&amp;lt;/version&amp;gt;&lt;br /&gt;                    &amp;lt;outputDirectory&amp;gt;war/WEB-INF/lib&amp;lt;/outputDirectory&amp;gt;&lt;br /&gt;                &amp;lt;/artifactItem&amp;gt;&lt;br /&gt;                &amp;lt;artifactItem&amp;gt;&lt;br /&gt;                    &amp;lt;groupId&amp;gt;org.apache.geronimo.specs&amp;lt;/groupId&amp;gt;&lt;br /&gt;                    &amp;lt;artifactId&amp;gt;geronimo-jpa_3.0_spec&amp;lt;/artifactId&amp;gt;&lt;br /&gt;                    &amp;lt;version&amp;gt;1.1.1&amp;lt;/version&amp;gt;&lt;br /&gt;                    &amp;lt;outputDirectory&amp;gt;war/WEB-INF/lib&amp;lt;/outputDirectory&amp;gt;&lt;br /&gt;                &amp;lt;/artifactItem&amp;gt;&lt;br /&gt;                &amp;lt;artifactItem&amp;gt;&lt;br /&gt;                    &amp;lt;groupId&amp;gt;org.apache.geronimo.specs&amp;lt;/groupId&amp;gt;&lt;br /&gt;                    &amp;lt;artifactId&amp;gt;geronimo-jta_1.1_spec&amp;lt;/artifactId&amp;gt;&lt;br /&gt;                    &amp;lt;version&amp;gt;1.1.1&amp;lt;/version&amp;gt;&lt;br /&gt;                    &amp;lt;outputDirectory&amp;gt;war/WEB-INF/lib&amp;lt;/outputDirectory&amp;gt;&lt;br /&gt;                &amp;lt;/artifactItem&amp;gt;&lt;br /&gt;                &amp;lt;artifactItem&amp;gt;&lt;br /&gt;                    &amp;lt;groupId&amp;gt;javax.jdo&amp;lt;/groupId&amp;gt;&lt;br /&gt;                    &amp;lt;artifactId&amp;gt;jdo2-api&amp;lt;/artifactId&amp;gt;&lt;br /&gt;                    &amp;lt;version&amp;gt;2.3-eb&amp;lt;/version&amp;gt;&lt;br /&gt;                    &amp;lt;outputDirectory&amp;gt;war/WEB-INF/lib&amp;lt;/outputDirectory&amp;gt;&lt;br /&gt;                &amp;lt;/artifactItem&amp;gt;&lt;br /&gt;            &amp;lt;/artifactItems&amp;gt;&lt;br /&gt;            &amp;lt;!-- other configurations here --&amp;gt;&lt;br /&gt;        &amp;lt;/configuration&amp;gt;&lt;br /&gt;    &amp;lt;/execution&amp;gt;&lt;br /&gt;&amp;lt;/executions&amp;gt;&lt;br /&gt;&amp;lt;/plugin&amp;gt;&lt;br /&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;DataNucleus Enhancer&lt;/span&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;&lt;pre style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 100%;"&gt;&lt;code&gt;&amp;lt;plugin&amp;gt;&lt;br /&gt;&amp;lt;groupId&amp;gt;org.datanucleus&amp;lt;/groupId&amp;gt;&lt;br /&gt;&amp;lt;artifactId&amp;gt;maven-datanucleus-plugin&amp;lt;/artifactId&amp;gt;&lt;br /&gt;&amp;lt;version&amp;gt;1.1.4&amp;lt;/version&amp;gt;&lt;br /&gt;&amp;lt;configuration&amp;gt;&lt;br /&gt;   &amp;lt;api&amp;gt;JPA&amp;lt;/api&amp;gt;&lt;br /&gt;   &amp;lt;mappingIncludes&amp;gt;**/*.class&amp;lt;/mappingIncludes&amp;gt;&lt;br /&gt;   &amp;lt;log4jConfiguration&amp;gt;${basedir}/src/main/resources/log4j.properties&amp;lt;/log4jConfiguration&amp;gt;&lt;br /&gt;   &amp;lt;verbose&amp;gt;false&amp;lt;/verbose&amp;gt;&lt;br /&gt;&amp;lt;/configuration&amp;gt;&lt;br /&gt;&amp;lt;executions&amp;gt;&lt;br /&gt;   &amp;lt;execution&amp;gt;&lt;br /&gt;       &amp;lt;phase&amp;gt;process-classes&amp;lt;/phase&amp;gt;&lt;br /&gt;       &amp;lt;goals&amp;gt;&lt;br /&gt;           &amp;lt;goal&amp;gt;enhance&amp;lt;/goal&amp;gt;&lt;br /&gt;       &amp;lt;/goals&amp;gt;&lt;br /&gt;   &amp;lt;/execution&amp;gt;&lt;br /&gt;&amp;lt;/executions&amp;gt;&lt;br /&gt;&amp;lt;/plugin&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Archetype&lt;/span&gt;&lt;br /&gt;All of this is packaged in a maven archetype. Try it like this.&lt;br /&gt;&lt;ol&gt;&lt;li&gt;&lt;code&gt;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/&lt;/code&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;code&gt;cd&lt;/code&gt; to the new directory&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;code&gt;mvn clean&lt;/code&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;code&gt;mvn eclipse:eclipse&lt;/code&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Import the project in Eclipse&lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;As an extra bonus your new project is configured for Spring 3.0 with a sample of a RESTful controller.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://fornax-platform.org/cp/x/aAQ"&gt;Sculptor&lt;/a&gt; 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.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3314601722322098765-1974386804948754274?l=fornax-sculptor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fornax-sculptor.blogspot.com/feeds/1974386804948754274/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fornax-sculptor.blogspot.com/2009/10/maven-archetype-for-app-engine.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/1974386804948754274'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/1974386804948754274'/><link rel='alternate' type='text/html' href='http://fornax-sculptor.blogspot.com/2009/10/maven-archetype-for-app-engine.html' title='Maven Archetype for App Engine'/><author><name>Patrik</name><uri>http://www.blogger.com/profile/11876565293495127382</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3314601722322098765.post-6916907799462701376</id><published>2009-09-16T21:00:00.001+02:00</published><updated>2009-09-16T21:16:12.814+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Sculptor'/><category scheme='http://www.blogger.com/atom/ns#' term='Customization'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='Code generator'/><category scheme='http://www.blogger.com/atom/ns#' term='OO'/><title type='text'>Customer Specific Addon: Deep Merge</title><content type='html'>This article illustrates the possibility to add your own features to the Sculptor code generator. &lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;We designed this as a first step that converts the production messages to new transient domain object instances.&lt;br /&gt;&lt;br /&gt;Next step is to merge that object graph with present persistent objects.&lt;br /&gt;&lt;br /&gt;This feels like a tedious and repetitive programming task. If done manually it will require some maintenance when we do changes.&lt;br /&gt;&lt;br /&gt;At first I took a look at &lt;a href="http://dozer.sourceforge.net/"&gt;Dozer&lt;/a&gt;, but pretty soon things got complicated and required a lot of XML mapping files. So we gave up that idea.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;Next morning I implemented it like this...&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;&lt;pre style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 100%;"&gt;&lt;code&gt;  public void deepMerge(Item other) {&lt;br /&gt;    Set&amp;lt;Object&amp;gt; processed = new HashSet&amp;lt;Object&amp;gt;();&lt;br /&gt;    deepMerge(other, processed);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public void deepMerge(Item other, Set&amp;lt;Object&amp;gt; processed) {&lt;br /&gt;    if (processed.contains(this)) {&lt;br /&gt;        return;&lt;br /&gt;    }&lt;br /&gt;    processed.add(this);&lt;br /&gt;&lt;br /&gt;    if (other.getEstimatedTimeOfArrival() != null) {&lt;br /&gt;        setEstimatedTimeOfArrival(other.getEstimatedTimeOfArrival());&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    deepMergeShipment(other, processed);&lt;br /&gt;&lt;br /&gt;    deepMergeEvents(other, processed);&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public void deepMergeShipment(Item other, Set&amp;lt;Object&amp;gt; processed) {&lt;br /&gt;    Shipment currentValue = getShipment();&lt;br /&gt;    if (other.getShipment() != null) {&lt;br /&gt;        if (currentValue == null) {&lt;br /&gt;            setShipment(other.getShipment());&lt;br /&gt;        } else {&lt;br /&gt;            currentValue.deepMerge(other.getShipment(), processed);&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public void deepMergeEvents(Item other, Set&amp;lt;Object&amp;gt; processed) {&lt;br /&gt;    for (TrackingEvent each : other.getEvents()) {&lt;br /&gt;        if (getEvents().contains(each)) {&lt;br /&gt;            TrackingEvent currentValue = eventForKey(other.getKey());&lt;br /&gt;            currentValue.deepMerge(each, processed);&lt;br /&gt;        } else {&lt;br /&gt;            addEvent(each);&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;protected TrackingEvent eventForKey(Object key) {&lt;br /&gt;    for (TrackingEvent each : getEvents()) {&lt;br /&gt;        if (each.getKey().equals(key)) {&lt;br /&gt;            return each;&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;    return null;&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;I developed this as a project specific addon, i.e. I invoked a code generation template from SpecialCases.xpt:&lt;br /&gt;&lt;pre style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 100%;"&gt;&lt;code&gt;«AROUND templates::DomainObject::keyGetter FOR DomainObject»&lt;br /&gt;«targetDef.proceed()»&lt;br /&gt;&lt;br /&gt;«EXPAND templates::DeepMerge::deepMerge»&lt;br /&gt;«ENDAROUND»&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;I started with the simple attributes.&lt;br /&gt;&lt;pre style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 100%;"&gt;&lt;code&gt;«DEFINE deepMerge FOR DomainObject»&lt;br /&gt;&lt;br /&gt;«EXPAND deepMergeMethod»&lt;br /&gt;&lt;br /&gt;«ENDDEFINE»&lt;br /&gt;&lt;br /&gt;«DEFINE deepMergeMethod FOR DomainObject»&lt;br /&gt;public void deepMerge(«getDomainPackage()».«name» other) {&lt;br /&gt;«EXPAND deepMergeAttribute FOREACH attributes.reject(e | !e.changeable)»&lt;br /&gt;«ENDDEFINE»&lt;br /&gt;&lt;br /&gt;«DEFINE deepMergeAttribute FOR Attribute»&lt;br /&gt;if (other.«getGetAccessor()»() != null) {&lt;br /&gt;set«name.toFirstUpper()»(other.«getGetAccessor()»());&lt;br /&gt;}&lt;br /&gt;«ENDDEFINE»&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;I generated and looked at the result.&lt;br /&gt;&lt;br /&gt;I noticed that the auditable fields were included. Ok, then I can use the helper function isSystemAttribute() to skip those.&lt;br /&gt;&lt;pre style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 100%;"&gt;&lt;code&gt;«EXPAND deepMergeAttribute FOREACH attributes&lt;br /&gt;.reject(e | !e.changeable || e.isSystemAttribute())»&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;I added the templates for references. Starting with the to-one references:&lt;br /&gt;&lt;br /&gt;&lt;pre style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 100%;"&gt;&lt;code&gt;«DEFINE deepMergeOneReference FOR Reference»&lt;br /&gt;  public void deepMerge«name.toFirstUpper()»(«from.getDomainPackage()».«from.name» other) {&lt;br /&gt;      «to.getDomainPackage()».«to.name» currentValue = get«name.toFirstUpper()»();&lt;br /&gt;      if (other.get«name.toFirstUpper()»() != null) {&lt;br /&gt;          if (currentValue == null) {&lt;br /&gt;              set«name.toFirstUpper()»(other.get«name.toFirstUpper()»());&lt;br /&gt;          } else {&lt;br /&gt;              currentValue.deepMerge(other.get«name.toFirstUpper()»());&lt;br /&gt;          }&lt;br /&gt;      }&lt;br /&gt;  }&lt;br /&gt;«ENDDEFINE»&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;pre style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 100%;"&gt;&lt;code&gt;«DEFINE deepMergeManyReference FOR Reference»&lt;br /&gt;  public void deepMerge«name.toFirstUpper()»(«from.getDomainPackage()».«from.name» other) {&lt;br /&gt;      for («getTypeName()» each : other.get«name.toFirstUpper()»()) {&lt;br /&gt;          if (get«name.toFirstUpper()»().contains(each)) {&lt;br /&gt;              «to.getDomainPackage()».«to.name» currentValue = «name.singular()»ForKey(other.getKey());&lt;br /&gt;              currentValue.deepMerge(each);&lt;br /&gt;          } else {&lt;br /&gt;              add«name.toFirstUpper().singular()»(each);&lt;br /&gt;          }&lt;br /&gt;      }&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  protected «to.getDomainPackage()».«to.name» «name.singular()»ForKey(Object key) {&lt;br /&gt;      for («to.getDomainPackage()».«to.name» each : get«name.toFirstUpper()»()) {&lt;br /&gt;          if (each.getKey().equals(key)) {&lt;br /&gt;              return each;&lt;br /&gt;          }&lt;br /&gt;      }&lt;br /&gt;      return null;&lt;br /&gt;  }&lt;br /&gt;«ENDDEFINE»&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;All this took me 2 hours to implement, probably much less than implementing it manually in all domain objects. &lt;font style="font-weight: bold;"&gt;The big benefit is that it is much less risk of manual faults and requires zero maintenance when making changes to the domain objects.&lt;/font&gt;&lt;br /&gt;&lt;br /&gt;The final template file below, in case you are interested in implementing something similar:&lt;br /&gt;&lt;br /&gt;&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;&amp;#171;IMPORT sculptormetamodel&amp;#187;&lt;br /&gt;&amp;#171;EXTENSION extensions::helper&amp;#187;&lt;br /&gt;&amp;#171;EXTENSION extensions::dbhelper&amp;#187;&lt;br /&gt;&amp;#171;EXTENSION extensions::properties&amp;#187;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&amp;#171;DEFINE deepMerge FOR DomainObject&amp;#187;&lt;br /&gt;    &amp;#171;IF !isImmutable()&amp;#187;&lt;br /&gt;        &amp;#171;EXPAND deepMergeMethod&amp;#187;&lt;br /&gt;&lt;br /&gt;        &amp;#171;EXPAND deepMergeOneReference FOREACH references.select(r &amp;#124; !r.many).reject(e &amp;#124; !e.changeable)&amp;#187;&lt;br /&gt;        &amp;#171;EXPAND deepMergeManyReference FOREACH references.select(r &amp;#124; r.many)&amp;#187;&lt;br /&gt;    &amp;#171;ENDIF&amp;#187;&lt;br /&gt;&amp;#171;ENDDEFINE&amp;#187;&lt;br /&gt;&lt;br /&gt;&amp;#171;DEFINE deepMergeMethod FOR DomainObject&amp;#187;&lt;br /&gt;    public void deepMerge(&amp;#171;getDomainPackage()&amp;#187;.&amp;#171;name&amp;#187; other) {&lt;br /&gt;        java.util.Set&amp;lt;Object&amp;gt; processed = new java.util.HashSet&amp;lt;Object&amp;gt;();&lt;br /&gt;        deepMerge(other, processed);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public void deepMerge(&amp;#171;getDomainPackage()&amp;#187;.&amp;#171;name&amp;#187; other, java.util.Set&amp;lt;Object&amp;gt; processed) {&lt;br /&gt;        if (processed.contains(this)) {&lt;br /&gt;            return;&lt;br /&gt;        }&lt;br /&gt;        processed.add(this);&lt;br /&gt;        &lt;br /&gt;        &amp;#171;EXPAND deepMergeAttribute FOREACH attributes.reject(e &amp;#124; !e.changeable &amp;#124;&amp;#124; e.isSystemAttribute())&amp;#187;&lt;br /&gt;        &lt;br /&gt;        &amp;#171;FOREACH references.reject(e &amp;#124; !e.changeable) AS ref&amp;#187;&lt;br /&gt;        deepMerge&amp;#171;ref.name.toFirstUpper()&amp;#187;(other, processed);&lt;br /&gt;        &amp;#171;ENDFOREACH&amp;#187;&lt;br /&gt;    }&lt;br /&gt;&amp;#171;ENDDEFINE&amp;#187;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&amp;#171;DEFINE deepMergeAttribute FOR Attribute&amp;#187;&lt;br /&gt;    &amp;#171;IF isPrimitive() -&amp;#187;&lt;br /&gt;    set&amp;#171;name.toFirstUpper()&amp;#187;(other.&amp;#171;getGetAccessor()&amp;#187;());&lt;br /&gt;    &amp;#171;ELSE-&amp;#187;&lt;br /&gt;    if (other.&amp;#171;getGetAccessor()&amp;#187;() != null) {&lt;br /&gt;        set&amp;#171;name.toFirstUpper()&amp;#187;(other.&amp;#171;getGetAccessor()&amp;#187;());&lt;br /&gt;    }&lt;br /&gt;    &amp;#171;ENDIF-&amp;#187;&lt;br /&gt;&amp;#171;ENDDEFINE&amp;#187;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&amp;#171;DEFINE deepMergeOneReference FOR Reference&amp;#187;&lt;br /&gt;    public void deepMerge&amp;#171;name.toFirstUpper()&amp;#187;(&amp;#171;from.getDomainPackage()&amp;#187;.&amp;#171;from.name&amp;#187; other, java.util.Set&amp;lt;Object&amp;gt; processed) {&lt;br /&gt;        if (other.get&amp;#171;name.toFirstUpper()&amp;#187;() != null) {&lt;br /&gt;            &amp;#171;IF to.isImmutable()&amp;#187;&lt;br /&gt;                if (!other.get&amp;#171;name.toFirstUpper()&amp;#187;().equals(get&amp;#171;name.toFirstUpper()&amp;#187;())) {&lt;br /&gt;                    set&amp;#171;name.toFirstUpper()&amp;#187;(other.get&amp;#171;name.toFirstUpper()&amp;#187;());&lt;br /&gt;                }&lt;br /&gt;            &amp;#171;ELSE&amp;#187;&lt;br /&gt;            &amp;#171;to.getDomainPackage()&amp;#187;.&amp;#171;to.name&amp;#187; currentValue = get&amp;#171;name.toFirstUpper()&amp;#187;();&lt;br /&gt;            if (currentValue == null) {&lt;br /&gt;                set&amp;#171;name.toFirstUpper()&amp;#187;(other.get&amp;#171;name.toFirstUpper()&amp;#187;());&lt;br /&gt;            } else {&lt;br /&gt;                currentValue.deepMerge(other.get&amp;#171;name.toFirstUpper()&amp;#187;(), processed);&lt;br /&gt;            }&lt;br /&gt;            &amp;#171;ENDIF&amp;#187;&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&amp;#171;ENDDEFINE&amp;#187;&lt;br /&gt;&lt;br /&gt;&amp;#171;DEFINE deepMergeManyReference FOR Reference&amp;#187;&lt;br /&gt;    public void deepMerge&amp;#171;name.toFirstUpper()&amp;#187;(&amp;#171;from.getDomainPackage()&amp;#187;.&amp;#171;from.name&amp;#187; other, java.util.Set&amp;lt;Object&amp;gt; processed) {&lt;br /&gt;        for (&amp;#171;getTypeName()&amp;#187; each : other.get&amp;#171;name.toFirstUpper()&amp;#187;()) {&lt;br /&gt;            if (get&amp;#171;name.toFirstUpper()&amp;#187;().contains(each)) {&lt;br /&gt;            &amp;#171;IF to.isImmutable()&amp;#187;&lt;br /&gt;                // replace&lt;br /&gt;                remove&amp;#171;name.toFirstUpper().singular()&amp;#187;(each);&lt;br /&gt;                add&amp;#171;name.toFirstUpper().singular()&amp;#187;(each);&lt;br /&gt;            &amp;#171;ELSE&amp;#187;&lt;br /&gt;                &amp;#171;to.getDomainPackage()&amp;#187;.&amp;#171;to.name&amp;#187; currentValue = &amp;#171;name.singular()&amp;#187;ForKey(each.getKey());&lt;br /&gt;                currentValue.deepMerge(each, processed);&lt;br /&gt;            &amp;#171;ENDIF&amp;#187;&lt;br /&gt;            } else {&lt;br /&gt;                add&amp;#171;name.toFirstUpper().singular()&amp;#187;(each);&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;    &lt;br /&gt;    protected &amp;#171;to.getDomainPackage()&amp;#187;.&amp;#171;to.name&amp;#187; &amp;#171;name.singular()&amp;#187;ForKey(Object key) {&lt;br /&gt;        for (&amp;#171;to.getDomainPackage()&amp;#187;.&amp;#171;to.name&amp;#187; each : get&amp;#171;name.toFirstUpper()&amp;#187;()) {&lt;br /&gt;            if (each.getKey().equals(key)) {&lt;br /&gt;                return each;&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;        return null;&lt;br /&gt;    }&lt;br /&gt;&amp;#171;ENDDEFINE&amp;#187;&lt;br /&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3314601722322098765-6916907799462701376?l=fornax-sculptor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fornax-sculptor.blogspot.com/feeds/6916907799462701376/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fornax-sculptor.blogspot.com/2009/09/customer-specific-addon-deep-merge.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/6916907799462701376'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/6916907799462701376'/><link rel='alternate' type='text/html' href='http://fornax-sculptor.blogspot.com/2009/09/customer-specific-addon-deep-merge.html' title='Customer Specific Addon: Deep Merge'/><author><name>Patrik</name><uri>http://www.blogger.com/profile/11876565293495127382</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3314601722322098765.post-5792542571678451111</id><published>2009-08-27T07:00:00.002+02:00</published><updated>2009-08-27T09:07:32.852+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Refactoring'/><category scheme='http://www.blogger.com/atom/ns#' term='Productivity'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='DDD'/><category scheme='http://www.blogger.com/atom/ns#' term='DSL'/><category scheme='http://www.blogger.com/atom/ns#' term='Code generator'/><title type='text'>Screencast: Introduction to Sculptor</title><content type='html'>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. &lt;br /&gt;&lt;br /&gt;If you are totally new to Sculptor you might need to read &lt;a href="http://fornax-sculptor.blogspot.com/2009/06/what-is-sculptor.html"&gt;What is Sculptor?&lt;/a&gt; before looking at the practical example.&lt;br /&gt;&lt;br /&gt;The series illustrates the following, step-by-step:&lt;br /&gt;&lt;ol&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://fornax-sculptor.blogspot.com/2009/06/jump-start.html"&gt;Jump Start&lt;/a&gt; - Initial creation of maven and eclipse projects. Persistent entity and CRUD GUI are created in a few minutes.&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://fornax-sculptor.blogspot.com/2009/07/world-is-changing.html"&gt;The World is Changing&lt;/a&gt; - Adding more to the application. Quick development round trip, short feedback loop, it is not a one time shot.&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://fornax-sculptor.blogspot.com/2009/08/testing-is-simple.html"&gt;Testing is Simple&lt;/a&gt; - Testability is crucial and is of course supported.&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://fornax-sculptor.blogspot.com/2009/08/adding-behaviour.html"&gt;Adding Behaviour&lt;/a&gt; - Generated code is well separated from hand written code.&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://fornax-sculptor.blogspot.com/2009/08/say-hello.html"&gt;Say Hello&lt;/a&gt; - Entity, Repository and Service are some of the available building blocks. Yes, it is real DDD-style.&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://fornax-sculptor.blogspot.com/2009/08/introducing-type.html"&gt;Introducing a Type&lt;/a&gt; - Developing a high quality domain model is the core of Sculptor. Small type objects are typically part of a good domain model.&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://fornax-sculptor.blogspot.com/2009/08/refactoring.html"&gt;Refactoring&lt;/a&gt; - How is refactoring done when having a mix of hand written and generated code?&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;/ol&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3314601722322098765-5792542571678451111?l=fornax-sculptor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fornax-sculptor.blogspot.com/feeds/5792542571678451111/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fornax-sculptor.blogspot.com/2009/08/introduction-to-sculptor.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/5792542571678451111'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/5792542571678451111'/><link rel='alternate' type='text/html' href='http://fornax-sculptor.blogspot.com/2009/08/introduction-to-sculptor.html' title='Screencast: Introduction to Sculptor'/><author><name>Patrik</name><uri>http://www.blogger.com/profile/11876565293495127382</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3314601722322098765.post-3498383502848640802</id><published>2009-08-25T08:45:00.000+02:00</published><updated>2009-08-25T00:18:15.446+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Sculptor'/><category scheme='http://www.blogger.com/atom/ns#' term='GAE'/><category scheme='http://www.blogger.com/atom/ns#' term='Spring'/><category scheme='http://www.blogger.com/atom/ns#' term='JPA'/><title type='text'>GAE Transactions</title><content type='html'>I'm trying to understand what we should do to make Sculptor compatible with Google App Engine (GAE).&lt;br /&gt;&lt;br /&gt;I feel a bit sad when looking back to what I have just experienced, but I guess I should be happy, since I have learned a lot. In this post I will share my mistakes and insights to GAE transactions and Entity Groups.&lt;br /&gt;&lt;br /&gt;Together with Andreas I'm developing a little sample that consists of 3 interacting applications. Customer, Supplier and Profile apps. User stories for the initial sprint:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;As a customer I want to specify a request for consultants so that I can allocate resources to my project.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;As a salesman (supplier) I want to be notified when a customer enters a request for consultants so that I quickly can create an offer to that request.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;As a salesman I want to offer consultants to a customer so that I can sell our services.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;As a customer I want to see up to date information in the profiles so that I know that it is not obsolete.&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;I was developing the form enter of the inquiry in the customer app. I saved the form data in an Inquiry object and sent the request to the supplier app using &lt;a href="http://blog.springsource.com/2009/03/27/rest-in-spring-3-resttemplate/"&gt;RestTemplate&lt;/a&gt;. No problems so far.&lt;br /&gt;&lt;br /&gt;We are using the new &lt;a href="http://blog.springsource.com/2009/03/08/rest-in-spring-3-mvc/"&gt;REST features in Spring 3.0&lt;/a&gt; and have done some adjustments to Sculptor to make it generate JPA code that is compliant with GAE datastore.&lt;br /&gt;&lt;br /&gt;Since one inquiry should be sent to many suppliers it didn't feel very scalable to send them all in the form entry request. Therefore I separated the sending to a separate job, which would be invoked by the cron service (later, better with task queue). This is not only more scalable, it is also more fault tolerant, since supplier apps may not be available all the time. By separating it we can easily retry later.&lt;br /&gt;&lt;br /&gt;I created a Supplier entity also. In the sendToSuppliers job I got the first problem: &lt;br /&gt;&lt;br /&gt;&lt;i&gt;IllegalArgumentException: can't operate on multiple entity groups in a single transaction&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;Since I had two entities, Inquiry and Supplier and I was using both in the transaction I assumed that it was not allowed to query the Suppliers and update the Inquiries in the same transaction. I based that on the GAE &lt;a href="http://code.google.com/appengine/docs/java/datastore/transactions.html"&gt;documentation&lt;/a&gt;:&lt;br /&gt;&lt;i&gt;All datastore operations in a transaction must operate on entities in the same entity group. This includes querying for entities by ancestor, retrieving entities by key, updating entities, and deleting entities.&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;That assumption was a fatal mistake that got me on the wrong track. I started to separate the the retrieval of Suppliers and update of Inquiries in separate transactions.&lt;br /&gt;&lt;br /&gt;I learned from the documentation that it was possible to disable transactions, but that it was a temporary workaround.&lt;br /&gt;&lt;br /&gt;After removing all code except the update of the Inquiries I realized that the Inquiry instances themselves belonged to separate entity groups. I was looping over all Inquiries that had not been sent to suppliers, i.e. I was updating several instances. Of course, they belong to separate entity groups, otherwise it would not scale when the number of objects increase.&lt;br /&gt;&lt;br /&gt;Then I redesigned the sending job so that it would only send and update one Inquiry instance. The job will have to be run many times to send all Inquiries.&lt;br /&gt;&lt;br /&gt;On the way I learned some more things about GAE datastore:&lt;br /&gt;* A transaction is necessary for some operations, such  flush, otherwise; "&lt;i&gt;This operation requires a transaction yet it is not active&lt;/i&gt;"&lt;br /&gt;* Queries also require a transaction, otherwise when iterating over the result;&lt;br /&gt;"&lt;i&gt;Object Manager has been closed&lt;/i&gt;"&lt;br /&gt;* Modification several times; "&lt;i&gt;can't update the same entity twice in a transaction or operation&lt;/i&gt;"&lt;br /&gt;&lt;br /&gt;In the end I think the defaults for transactions in Sculptor are alright. Normally we define transaction boundary at the service layer. This is ok for many cases when using GAE also, but one have to design the operations so that they only update one instance (entity group). &lt;br /&gt;&lt;br /&gt;There is probably a need for more fine grained transaction control at the repository level. E.g. starting a new transaction for some repository operations. I think we should implement this with @Transactional annotations. Is it possible to mix txAdvice (defaults) with @Transactional (deviations from default)?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3314601722322098765-3498383502848640802?l=fornax-sculptor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fornax-sculptor.blogspot.com/feeds/3498383502848640802/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fornax-sculptor.blogspot.com/2009/08/gae-transactions.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/3498383502848640802'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/3498383502848640802'/><link rel='alternate' type='text/html' href='http://fornax-sculptor.blogspot.com/2009/08/gae-transactions.html' title='GAE Transactions'/><author><name>Patrik</name><uri>http://www.blogger.com/profile/11876565293495127382</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3314601722322098765.post-6773413814107957087</id><published>2009-08-25T07:30:00.000+02:00</published><updated>2009-08-24T22:34:03.287+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Sculptor'/><category scheme='http://www.blogger.com/atom/ns#' term='Refactoring'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='Code generator'/><title type='text'>Refactoring</title><content type='html'>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.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;Eventual mistakes will normally be caught by the compiler and JUnit tests.&lt;br /&gt;&lt;br /&gt;The following screencast illustrates how to rename Planet to Planet2.&lt;br /&gt;&lt;br /&gt;&lt;embed src="http://blip.tv/play/AYGNkyoA" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" height="510" width="852"&gt;&lt;br /&gt;&lt;/embed&gt;&lt;br /&gt;&lt;br /&gt;&lt;a rel="enclosure" href="http://blip.tv/file/get/Fornaxsculptor-RenameRefactoring926.mov" onclick="window.popup_player_2312518 = window.open('http://blip.tv/file/2296302/?skin=popup&amp;amp;file_type=mpeg2','post_2312518','toolbar=no,scrollbars=no,directories=no,resizable=yes,width=360,height=305,top=20,left=20,location=no,menubar=no,status=yes,'); return false;"&gt;Alternative video format (mpg)&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Sculptor doesn't make it more difficult to do refactoring. Sometimes it makes refactoring easier, when the change only affects generated code.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3314601722322098765-6773413814107957087?l=fornax-sculptor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fornax-sculptor.blogspot.com/feeds/6773413814107957087/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fornax-sculptor.blogspot.com/2009/08/refactoring.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/6773413814107957087'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/6773413814107957087'/><link rel='alternate' type='text/html' href='http://fornax-sculptor.blogspot.com/2009/08/refactoring.html' title='Refactoring'/><author><name>Patrik</name><uri>http://www.blogger.com/profile/11876565293495127382</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3314601722322098765.post-676344637403510716</id><published>2009-08-20T07:45:00.000+02:00</published><updated>2009-08-19T22:47:42.358+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Sculptor'/><category scheme='http://www.blogger.com/atom/ns#' term='Productivity'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='DDD'/><category scheme='http://www.blogger.com/atom/ns#' term='DSL'/><category scheme='http://www.blogger.com/atom/ns#' term='Code generator'/><category scheme='http://www.blogger.com/atom/ns#' term='OO'/><category scheme='http://www.blogger.com/atom/ns#' term='JPA'/><title type='text'>Introducing a Type</title><content type='html'>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.&lt;br /&gt;&lt;br /&gt;&lt;embed src="http://blip.tv/play/AYGNiX0A" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" height="510" width="852"&gt;&lt;/embed&gt;&lt;br /&gt;&lt;br /&gt;&lt;a rel="enclosure" href="http://blip.tv/file/get/Fornaxsculptor-IntroducingBasicType562.mov" onclick="window.popup_player_2311321 = window.open('http://blip.tv/file/2295173/?skin=popup&amp;amp;file_type=mpeg2','post_2311321','toolbar=no,scrollbars=no,directories=no,resizable=yes,width=360,height=305,top=20,left=20,location=no,menubar=no,status=yes,'); return false;"&gt;Alternative video format (mpg)&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Length is a typical &lt;a href="http://martinfowler.com/ap2/quantity.html"&gt;Quantity&lt;/a&gt; with a value and unit, e.g. meter, kilometer.&lt;br /&gt;&lt;br /&gt;In the design model it looks like this:&lt;br /&gt;&lt;pre style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 100%;"&gt;&lt;code&gt;BasicType Length {&lt;br /&gt; BigDecimal value min="0"&lt;br /&gt; -@LengthUnit unit&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;enum LengthUnit {&lt;br /&gt; cm, m, km&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;Entity Planet {&lt;br /&gt; gap&lt;br /&gt; scaffold&lt;br /&gt; String name key&lt;br /&gt; Long population min="0"&lt;br /&gt; -@Length diameter nullable&lt;br /&gt; -Set&amp;lt;@Moon&amp;gt; moons opposite planet&lt;br /&gt;&lt;br /&gt; Repository PlanetRepository {&lt;br /&gt;     findByKey;&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;We also need to convert between different units. The behaviour expressed as a JUnit test:&lt;br /&gt;&lt;pre style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 100%;"&gt;&lt;code&gt;public class LengthTest {&lt;br /&gt;&lt;br /&gt;  @Test&lt;br /&gt;  public void shouldConvertFromMeterToKilometer() {&lt;br /&gt;      Length length = new Length(new BigDecimal("31000"), m);&lt;br /&gt;      Length lengthInKilometer = length.to(km);&lt;br /&gt;      assertEquals(new Length(new BigDecimal("31"), km),&lt;br /&gt;          lengthInKilometer);&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  @Test&lt;br /&gt;  public void shouldConvertFromKilometerToMeter() {&lt;br /&gt;      Length length = new Length(new BigDecimal("44"), km);&lt;br /&gt;      Length lengthInMeter = length.to(m);&lt;br /&gt;      assertEquals(new Length(new BigDecimal("44000"), m),&lt;br /&gt;          lengthInMeter);&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  @Test&lt;br /&gt;  public void shouldNotConvertSameUnit() {&lt;br /&gt;      Length length = new Length(new BigDecimal("17"), km);&lt;br /&gt;      Length length2 = length.to(km);&lt;br /&gt;      assertSame(length, length2);&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;BasicType objects may contain business logic in the same way as other domain objects. The following screencast illustrates how to implement the conversion.&lt;br /&gt;&lt;br /&gt;&lt;embed src="http://blip.tv/play/AYGNjSwA" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" height="510" width="852"&gt;&lt;/embed&gt;&lt;br /&gt;&lt;br /&gt;&lt;a rel="enclosure" href="http://blip.tv/file/get/Fornaxsculptor-LengthConversion255.mov" onclick="window.popup_player_2311752 = window.open('http://blip.tv/file/2295577/?skin=popup&amp;amp;file_type=mpeg2','post_2311752','toolbar=no,scrollbars=no,directories=no,resizable=yes,width=360,height=305,top=20,left=20,location=no,menubar=no,status=yes,'); return false;"&gt;Alternative video format (mpg)&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;BasicType is a stored in the same table as the Domain Object referencing it. It corresponds to JPA @Embeddable.&lt;br /&gt;&lt;br /&gt;There are a lot of cases when it is a good idea to introduce types.&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Identifers, natural business keys. It is more readable to pass around an identifier type instead of a plain String or Integer&lt;/li&gt;&lt;li&gt;&lt;a href="http://martinfowler.com/eaaCatalog/money.html"&gt;Money&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://martinfowler.com/ap2/range.html"&gt;Range&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://martinfowler.com/ap2/quantity.html"&gt;Quantity&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;I can recommend reading &lt;a href="http://www.martinfowler.com/ieeeSoftware/whenType.pdf"&gt;When to Make a Type&lt;/a&gt;, Martin Fowler.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3314601722322098765-676344637403510716?l=fornax-sculptor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fornax-sculptor.blogspot.com/feeds/676344637403510716/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fornax-sculptor.blogspot.com/2009/08/introducing-type.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/676344637403510716'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/676344637403510716'/><link rel='alternate' type='text/html' href='http://fornax-sculptor.blogspot.com/2009/08/introducing-type.html' title='Introducing a Type'/><author><name>Patrik</name><uri>http://www.blogger.com/profile/11876565293495127382</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3314601722322098765.post-7636625336646529152</id><published>2009-08-15T06:30:00.000+02:00</published><updated>2009-08-14T21:39:31.748+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JEE'/><category scheme='http://www.blogger.com/atom/ns#' term='Sculptor'/><category scheme='http://www.blogger.com/atom/ns#' term='Productivity'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='DDD'/><category scheme='http://www.blogger.com/atom/ns#' term='DSL'/><category scheme='http://www.blogger.com/atom/ns#' term='Code generator'/><title type='text'>Say Hello</title><content type='html'>In &lt;a href="http://fornax-sculptor.blogspot.com/2009/08/adding-behaviour.html"&gt;previous article&lt;/a&gt; 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.&lt;br /&gt;&lt;br /&gt;&lt;embed src="http://blip.tv/play/AYGNiDUA" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" height="510" width="852" &gt;&lt;/embed&gt;&lt;br /&gt;&lt;br /&gt;&lt;a rel="enclosure" href="http://blip.tv/file/get/Fornaxsculptor-SayHello334.mov" onclick="window.popup_player_2311121 = window.open('http://blip.tv/file/2295014/?skin=popup&amp;amp;file_type=mpeg2','post_2311121','toolbar=no,scrollbars=no,directories=no,resizable=yes,width=360,height=305,top=20,left=20,location=no,menubar=no,status=yes,'); return false;"&gt;Alternative video format (mpg)&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;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:&lt;br /&gt;&lt;pre style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 100%;"&gt;&lt;code&gt;      Service PlanetService {&lt;br /&gt;         String sayHello(String planetName) throws PlanetNotFoundException;&lt;br /&gt;     }&lt;br /&gt;&lt;br /&gt;     Entity Planet {&lt;br /&gt;         gap&lt;br /&gt;         scaffold&lt;br /&gt;         String name key&lt;br /&gt;         Long population min="0"&lt;br /&gt;         Long diameter min="0" nullable&lt;br /&gt;         -Set&amp;lt;@Moon&amp;gt; moons opposite planet&lt;br /&gt;      &lt;br /&gt;         Repository PlanetRepository {&lt;br /&gt;             findByKey;&lt;br /&gt;         }&lt;br /&gt;     }&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;All hand written java code we need to add is for testing and two trivial lines in PlanetServiceImpl:&lt;br /&gt;&lt;pre style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 100%;"&gt;&lt;code&gt;    public String sayHello(ServiceContext ctx, String planetName)&lt;br /&gt;       throws PlanetNotFoundException {&lt;br /&gt;&lt;br /&gt;       Planet planet = getPlanetRepository().findByKey(planetName);&lt;br /&gt;       return planet.greeting();&lt;br /&gt;   }&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3314601722322098765-7636625336646529152?l=fornax-sculptor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fornax-sculptor.blogspot.com/feeds/7636625336646529152/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fornax-sculptor.blogspot.com/2009/08/say-hello.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/7636625336646529152'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/7636625336646529152'/><link rel='alternate' type='text/html' href='http://fornax-sculptor.blogspot.com/2009/08/say-hello.html' title='Say Hello'/><author><name>Patrik</name><uri>http://www.blogger.com/profile/11876565293495127382</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3314601722322098765.post-6036480919355608375</id><published>2009-08-11T08:00:00.000+02:00</published><updated>2009-08-10T23:15:01.763+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Sculptor'/><category scheme='http://www.blogger.com/atom/ns#' term='BDD'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='DDD'/><category scheme='http://www.blogger.com/atom/ns#' term='DSL'/><category scheme='http://www.blogger.com/atom/ns#' term='Code generator'/><title type='text'>Adding Behaviour</title><content type='html'>In &lt;a href="http://fornax-sculptor.blogspot.com/2009/08/testing-is-simple.html"&gt;previous articles&lt;/a&gt; we created a simple helloworld application without any hand written code. This article shows how to add some hand written business logic.&lt;br /&gt;&lt;br /&gt;&lt;embed src="http://blip.tv/play/AYGNhxEA" type="application/x-shockwave-flash" width="852" height="510" allowscriptaccess="always" allowfullscreen="true"&gt;&lt;/embed&gt;&lt;br /&gt;&lt;br /&gt;&lt;a rel="enclosure" href="http://blip.tv/file/get/Fornaxsculptor-AddingBehaviour831.mov" onclick="window.popup_player_2310957 = window.open('http://blip.tv/file/2294899/?skin=popup&amp;file_type=mpeg2','post_2310957','toolbar=no,scrollbars=no,directories=no,resizable=yes,width=360,height=305,top=20,left=20,location=no,menubar=no,status=yes,'); return false;"&gt;Alternative video format (mpg)&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;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:&lt;br /&gt;&lt;pre style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 100%;"&gt;&lt;code&gt;public class PlanetTest {&lt;br /&gt;&lt;br /&gt;  @Test&lt;br /&gt;  public void shouldSayHelloWhenHasPopulation() {&lt;br /&gt;      Planet earth = new Planet("Earth");&lt;br /&gt;      earth.setPopulation(7000000000L);&lt;br /&gt;      String message = earth.greeting();&lt;br /&gt;      assertEquals("Hello from Earth", message);&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  @Test&lt;br /&gt;  public void shouldBeQuietWhenNoPopulation() {&lt;br /&gt;      Planet pluto = new Planet("Pluto");&lt;br /&gt;      String message = pluto.greeting();&lt;br /&gt;      assertEquals("", message);&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;The video illustrates how to implement this.&lt;br /&gt;&lt;br /&gt;As you see nothing special, you add the business logic in Java as usual.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;The gap class is not generated initially. When you need a gap class you specify that in the DSL with gap keyword.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3314601722322098765-6036480919355608375?l=fornax-sculptor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fornax-sculptor.blogspot.com/feeds/6036480919355608375/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fornax-sculptor.blogspot.com/2009/08/adding-behaviour.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/6036480919355608375'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/6036480919355608375'/><link rel='alternate' type='text/html' href='http://fornax-sculptor.blogspot.com/2009/08/adding-behaviour.html' title='Adding Behaviour'/><author><name>Patrik</name><uri>http://www.blogger.com/profile/11876565293495127382</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3314601722322098765.post-6239780193911969532</id><published>2009-08-08T06:30:00.000+02:00</published><updated>2009-08-10T23:07:58.069+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JEE'/><category scheme='http://www.blogger.com/atom/ns#' term='Sculptor'/><category scheme='http://www.blogger.com/atom/ns#' term='Webflow'/><category scheme='http://www.blogger.com/atom/ns#' term='Productivity'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='DDD'/><category scheme='http://www.blogger.com/atom/ns#' term='DSL'/><category scheme='http://www.blogger.com/atom/ns#' term='Spring'/><category scheme='http://www.blogger.com/atom/ns#' term='Code generator'/><category scheme='http://www.blogger.com/atom/ns#' term='JPA'/><title type='text'>Testing is Simple</title><content type='html'>In &lt;a href="http://fornax-sculptor.blogspot.com/2009/07/world-is-changing.html"&gt;previous articles&lt;/a&gt; we created a simple helloworld application without any tests. This article shows how to do integration testing of services with Sculptor.&lt;br /&gt;&lt;br /&gt;&lt;embed src="http://blip.tv/play/AYGNhFkA" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" height="510" width="852"&gt;&lt;/embed&gt;&lt;br /&gt;&lt;br /&gt;&lt;a rel="enclosure" href="http://blip.tv/file/get/Fornaxsculptor-TestingIsSimple489.mov" onclick="window.popup_player_2310645 = window.open('http://blip.tv/file/2294785/?skin=popup&amp;amp;file_type=mpeg2','post_2310645','toolbar=no,scrollbars=no,directories=no,resizable=yes,width=360,height=305,top=20,left=20,location=no,menubar=no,status=yes,'); return false;"&gt;Alternative video format (mpg)&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;We turn on generation of junit test and complete the failing tests from &lt;a href="http://fornax-sculptor.blogspot.com/2009/07/world-is-changing.html"&gt;previous articles&lt;/a&gt;. For each Service there is a generated JUnit test class that we are encouraged to implement. It uses &lt;a href="http://static.springframework.org/spring/docs/2.5.x/reference/testing.html"&gt;Spring transactional test&lt;/a&gt; fixtures and &lt;a href="http://www.dbunit.org/"&gt;DbUnit&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Spring beans are injected in the test with ordinary @Autowired annotations.&lt;br /&gt;&lt;br /&gt;&lt;pre style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 100%;"&gt;&lt;code&gt;public class PlanetServiceTest extends AbstractDbUnitJpaTests&lt;br /&gt;    implements PlanetServiceTestBase {&lt;br /&gt;  private PlanetService planetService;&lt;br /&gt;&lt;br /&gt;  @Autowired&lt;br /&gt;  public void setPlanetService(PlanetService planetService) {&lt;br /&gt;    this.planetService = planetService;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  @Test&lt;br /&gt;  public void testFindById() throws Exception {&lt;br /&gt;    Planet found = planetService.findById(getServiceContext(), 1L);&lt;br /&gt;    assertEquals("Earth", found.getName());&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  @Test&lt;br /&gt;  public void testFindAll() throws Exception {&lt;br /&gt;    List&amp;lt;Planet&amp;gt; found = planetService.findAll(getServiceContext());&lt;br /&gt;    assertEquals(2, found.size());&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  @Test&lt;br /&gt;  public void testSave() throws Exception {&lt;br /&gt;    int countBefore = countRowsInTable("PLANET");&lt;br /&gt;    Planet planet = new Planet("Pluto");&lt;br /&gt;    planet.setPopulation(0L);&lt;br /&gt;    planetService.save(getServiceContext(), planet);&lt;br /&gt;    assertEquals(countBefore + 1, countRowsInTable("PLANET"));&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  @Test&lt;br /&gt;  public void testDelete() throws Exception {&lt;br /&gt;    int countBefore = countRowsInTable("PLANET");&lt;br /&gt;    Planet planet = planetService.findById(getServiceContext(), 2L);&lt;br /&gt;    planetService.delete(getServiceContext(), planet);&lt;br /&gt;    assertEquals(countBefore - 1, countRowsInTable("PLANET"));&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;The initial test data is defined in DbUnit xml file. The database is refreshed for each test method.&lt;br /&gt;&lt;pre style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 100%;"&gt;&lt;code&gt;&amp;lt;?xml version="1.0" encoding="UTF-8"?&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;dataset&amp;gt;&lt;br /&gt;  &amp;lt;PLANET ID="1" NAME="Earth" POPULATION="7000000000" VERSION="0"/&amp;gt;&lt;br /&gt;  &amp;lt;PLANET ID="2" NAME="Jupiter" POPULATION="0" VERSION="0"/&amp;gt;&lt;br /&gt;  &amp;lt;MOON/&amp;gt;&lt;br /&gt;&amp;lt;/dataset&amp;gt;&lt;br /&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;The tests illustrated here are kind of integration tests and you should do ordinary unit tests for domain objects and other classes of importance.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3314601722322098765-6239780193911969532?l=fornax-sculptor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fornax-sculptor.blogspot.com/feeds/6239780193911969532/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fornax-sculptor.blogspot.com/2009/08/testing-is-simple.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/6239780193911969532'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/6239780193911969532'/><link rel='alternate' type='text/html' href='http://fornax-sculptor.blogspot.com/2009/08/testing-is-simple.html' title='Testing is Simple'/><author><name>Patrik</name><uri>http://www.blogger.com/profile/11876565293495127382</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3314601722322098765.post-7614295002429807042</id><published>2009-07-28T06:00:00.000+02:00</published><updated>2009-07-27T21:19:12.883+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JEE'/><category scheme='http://www.blogger.com/atom/ns#' term='Sculptor'/><category scheme='http://www.blogger.com/atom/ns#' term='Webflow'/><category scheme='http://www.blogger.com/atom/ns#' term='Productivity'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='DDD'/><category scheme='http://www.blogger.com/atom/ns#' term='JSF'/><category scheme='http://www.blogger.com/atom/ns#' term='DSL'/><category scheme='http://www.blogger.com/atom/ns#' term='Spring'/><category scheme='http://www.blogger.com/atom/ns#' term='Code generator'/><category scheme='http://www.blogger.com/atom/ns#' term='Hibernate'/><category scheme='http://www.blogger.com/atom/ns#' term='JPA'/><title type='text'>The World is Changing</title><content type='html'>In the &lt;a href="http://fornax-sculptor.blogspot.com/2009/06/jump-start.html"&gt;previous article&lt;/a&gt; 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.&lt;br /&gt;&lt;br /&gt;&lt;embed src="http://blip.tv/play/AYGM+RYA" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" height="510" width="852"&gt;&lt;/embed&gt;&lt;br /&gt;&lt;br /&gt;&lt;a rel="enclosure" href="http://blip.tv/file/get/Fornaxsculptor-SculptorWorldIsChanging396.mov" onclick="window.popup_player_2309170 = window.open('http://blip.tv/file/2293532/?skin=popup&amp;amp;file_type=mpeg2','post_2309170','toolbar=no,scrollbars=no,directories=no,resizable=yes,width=360,height=305,top=20,left=20,location=no,menubar=no,status=yes,'); return false;"&gt;Alternative video format (mpg)&lt;/a&gt;                                           &lt;br /&gt;&lt;br /&gt;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".&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_qr2e_ywa0-I/SkaWVdyc2yI/AAAAAAAABPc/pJPymtCWqPc/s1600-h/helloworld_model3.png"&gt;&lt;img style="cursor: pointer; width: 343px; height: 229px;" src="http://2.bp.blogspot.com/_qr2e_ywa0-I/SkaWVdyc2yI/AAAAAAAABPc/pJPymtCWqPc/s400/helloworld_model3.png" alt="" id="BLOGGER_PHOTO_ID_5352130502694066978" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;We add the Moon Entity and its association to Planet.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_qr2e_ywa0-I/SkaWfVv3YvI/AAAAAAAABPk/ha_XtO5Ev_w/s1600-h/helloworld_model4.png"&gt;&lt;img style="cursor: pointer; width: 363px; height: 319px;" src="http://1.bp.blogspot.com/_qr2e_ywa0-I/SkaWfVv3YvI/AAAAAAAABPk/ha_XtO5Ev_w/s400/helloworld_model4.png" alt="" id="BLOGGER_PHOTO_ID_5352130672334430962" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Build and start Jetty. The changes are immediately reflected in the generated CRUD GUI.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_qr2e_ywa0-I/SkaJX-ZT9CI/AAAAAAAABPU/koNEPrmOXcg/s1600-h/helloworld_webgui2.png"&gt;&lt;img style="cursor: pointer; width: 400px; height: 217px;" src="http://3.bp.blogspot.com/_qr2e_ywa0-I/SkaJX-ZT9CI/AAAAAAAABPU/koNEPrmOXcg/s400/helloworld_webgui2.png" alt="" id="BLOGGER_PHOTO_ID_5352116252155573282" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;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.&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3314601722322098765-7614295002429807042?l=fornax-sculptor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fornax-sculptor.blogspot.com/feeds/7614295002429807042/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fornax-sculptor.blogspot.com/2009/07/world-is-changing.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/7614295002429807042'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/7614295002429807042'/><link rel='alternate' type='text/html' href='http://fornax-sculptor.blogspot.com/2009/07/world-is-changing.html' title='The World is Changing'/><author><name>Patrik</name><uri>http://www.blogger.com/profile/11876565293495127382</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_qr2e_ywa0-I/SkaWVdyc2yI/AAAAAAAABPc/pJPymtCWqPc/s72-c/helloworld_model3.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3314601722322098765.post-5094016673093198708</id><published>2009-07-22T08:15:00.000+02:00</published><updated>2009-07-21T23:17:40.300+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JEE'/><category scheme='http://www.blogger.com/atom/ns#' term='Sculptor'/><category scheme='http://www.blogger.com/atom/ns#' term='Webflow'/><category scheme='http://www.blogger.com/atom/ns#' term='Productivity'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='DDD'/><category scheme='http://www.blogger.com/atom/ns#' term='JSF'/><category scheme='http://www.blogger.com/atom/ns#' term='DSL'/><category scheme='http://www.blogger.com/atom/ns#' term='Spring'/><category scheme='http://www.blogger.com/atom/ns#' term='Code generator'/><category scheme='http://www.blogger.com/atom/ns#' term='Hibernate'/><category scheme='http://www.blogger.com/atom/ns#' term='JPA'/><title type='text'>Jump Start</title><content type='html'>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.&lt;br /&gt;&lt;br /&gt;&lt;embed src="http://blip.tv/play/AYGM9gkA" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" height="510" width="852"&gt;&lt;/embed&gt;&lt;br /&gt;&lt;br /&gt;&lt;a rel="enclosure" href="http://blip.tv/file/get/Fornaxsculptor-JumpStart881.mov" onclick="window.popup_player_2308773 = window.open('http://blip.tv/file/2293250/?skin=popup&amp;amp;file_type=mpeg2','post_2308773','toolbar=no,scrollbars=no,directories=no,resizable=yes,width=360,height=305,top=20,left=20,location=no,menubar=no,status=yes,'); return false;"&gt;Alternative video format (mpg)&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_qr2e_ywa0-I/SkZmIrMdApI/AAAAAAAABOc/KPcWBb35Jk8/s1600-h/helloworld_projects.png"&gt;&lt;img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer; width: 296px; height: 167px;" src="http://4.bp.blogspot.com/_qr2e_ywa0-I/SkZmIrMdApI/AAAAAAAABOc/KPcWBb35Jk8/s320/helloworld_projects.png" alt="" id="BLOGGER_PHOTO_ID_5352077506396357266" border="0" /&gt;&lt;/a&gt;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 &lt;a href="http://fornax.itemis.de/confluence/x/AwU"&gt;wiki&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_qr2e_ywa0-I/SkZooKGevKI/AAAAAAAABOs/HSVvUFkW-jY/s1600-h/helloworld_model1.png"&gt;&lt;img style="cursor: pointer; width: 400px; height: 203px;" src="http://3.bp.blogspot.com/_qr2e_ywa0-I/SkZooKGevKI/AAAAAAAABOs/HSVvUFkW-jY/s400/helloworld_model1.png" alt="" id="BLOGGER_PHOTO_ID_5352080246292004002" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;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).&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_qr2e_ywa0-I/SkZo68WjwsI/AAAAAAAABO0/rnSxpGNQqfs/s1600-h/helloworld_model2.png"&gt;&lt;img style="cursor: pointer; width: 400px; height: 241px;" src="http://3.bp.blogspot.com/_qr2e_ywa0-I/SkZo68WjwsI/AAAAAAAABO0/rnSxpGNQqfs/s400/helloworld_model2.png" alt="" id="BLOGGER_PHOTO_ID_5352080569018860226" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;pre style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 100%;"&gt;&lt;code&gt;generate.test=false&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;We build the application by executing mvn install from the -parent project. The code generation is part of the maven build cycle.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;The generated CRUD GUI is available at http://localhost:8080/helloworld-web/&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_qr2e_ywa0-I/SkZsUAG1T2I/AAAAAAAABO8/o7-3Xo2FEkU/s1600-h/helloworld_webgui1.png"&gt;&lt;img style="cursor: pointer; width: 400px; height: 314px;" src="http://2.bp.blogspot.com/_qr2e_ywa0-I/SkZsUAG1T2I/AAAAAAAABO8/o7-3Xo2FEkU/s400/helloworld_webgui1.png" alt="" id="BLOGGER_PHOTO_ID_5352084298058256226" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;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.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3314601722322098765-5094016673093198708?l=fornax-sculptor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fornax-sculptor.blogspot.com/feeds/5094016673093198708/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fornax-sculptor.blogspot.com/2009/06/jump-start.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/5094016673093198708'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/5094016673093198708'/><link rel='alternate' type='text/html' href='http://fornax-sculptor.blogspot.com/2009/06/jump-start.html' title='Jump Start'/><author><name>Patrik</name><uri>http://www.blogger.com/profile/11876565293495127382</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_qr2e_ywa0-I/SkZmIrMdApI/AAAAAAAABOc/KPcWBb35Jk8/s72-c/helloworld_projects.png' height='72' width='72'/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3314601722322098765.post-7331695729376262633</id><published>2009-06-27T09:16:00.000+02:00</published><updated>2009-06-28T00:22:40.673+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JEE'/><category scheme='http://www.blogger.com/atom/ns#' term='Sculptor'/><category scheme='http://www.blogger.com/atom/ns#' term='Webflow'/><category scheme='http://www.blogger.com/atom/ns#' term='Productivity'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='DDD'/><category scheme='http://www.blogger.com/atom/ns#' term='JSF'/><category scheme='http://www.blogger.com/atom/ns#' term='DSL'/><category scheme='http://www.blogger.com/atom/ns#' term='Spring'/><category scheme='http://www.blogger.com/atom/ns#' term='Code generator'/><category scheme='http://www.blogger.com/atom/ns#' term='Hibernate'/><category scheme='http://www.blogger.com/atom/ns#' term='JPA'/><title type='text'>What is Sculptor?</title><content type='html'>Sculptor is an open source productivity tool that applies the concepts from Domain-Driven Design and Domain Specific Languages.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;The generated code is based on well-known frameworks, such as JPA, Hibernate, Spring Framework, Spring Web Flow, JSF, RCP, and Java EE.&lt;br /&gt;&lt;br /&gt;Product characteristics:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Easy to learn, intuitive syntax of the textual DSL, editor with error highlight, code completion, and outline&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;Quick development round trip, it is not a one time generation&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;Existing IDE tools, such as refactoring, code assist and debugger will continue to be of service to you&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;High quality of generated code, based on well known frameworks, best practices, and a lot of experience&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;Great extensibility and customization options&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;Easy to remove the tool, no runtime magic&lt;/li&gt;&lt;/ul&gt;Visit the &lt;a href="http://fornax-platform.org/cp/x/aAQ"&gt;Fornax Sculptor web site&lt;/a&gt; to learn more and try it.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3314601722322098765-7331695729376262633?l=fornax-sculptor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fornax-sculptor.blogspot.com/feeds/7331695729376262633/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://fornax-sculptor.blogspot.com/2009/06/what-is-sculptor.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/7331695729376262633'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3314601722322098765/posts/default/7331695729376262633'/><link rel='alternate' type='text/html' href='http://fornax-sculptor.blogspot.com/2009/06/what-is-sculptor.html' title='What is Sculptor?'/><author><name>Patrik</name><uri>http://www.blogger.com/profile/11876565293495127382</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry></feed>
