Fornax-Platform View a printable version of the current page. Export Page as PDF
  7. Developer's Guide (CSC)
 
 Browse Space
General


Projects


Latest News
Latest News
(The 15 most recent blogposts in space Fornax-Platform.)


Global Reports
Find all pages that arent linked from anywhere.
Find all undefined pages.
Feed for new pages.
Added by Patrik Nordwall, last edited by Patrik Nordwall on Feb 16, 2010  (view change)

Labels:

Enter labels to add to this page:
Wait Image 
Looking for a label? Just start typing.

Sculptor Developer's Guide

Sculptor is not an one-size-fits-all product. Even though it is a good start for many systems, sooner or later customization is always needed. This guide will give you an understanding of the internal design of Sculptor and explains how to do changes for some typical scenarios. Some things can easily be changed with properties or AOP, while other things requires more effort, since you need to setup the development environment. Some changes are staightforward and some requires more in depth understanding of Eclipse Xpand and Xtext (previously known as oAW).

Table of Contents:

Sculptor Internal Design

Figure 1. Internal Design of Sculptor

1. The developer is using the DSL Editor plugin to edit the application specific model.btdesign, i.e. the source for the concrete model that is the input to the code generation process. Constraints of the DSL is validated while editing.

2. When generating code the application specific workflow.mwe is executed. It doesn't contain much.

3. It invokes the sculptorworkflow.mwe which defines the flow of the code generation process.

4. It starts with parsing the model.btdesign file using the XText parser. Constraints of the DSL are checked.

5. The DSL model is transformed into a model of the type defined by the sculptormetamodel.ecore meta model.

6. Constraint validation is performed.

7. The model is transformed again. Now it is actually modified to add some default values.

8. Constraint validation again.

9. Now the actual generation of Java code and configuration files begin. It is done with code generation templates written in XPand language. The templates extract values from the model and uses Extensions and Java helper classes.

10. Properties of technical nature, which doesn't belong in the DSL or meta model, are used by the templates and the helpers.

Generator Properties

There are a many things that can be easily customized with properties. The default properties are defined in default-sculptor-generator.properties in fornax-cartridges-sculptor-generator. You can override these properties by defining them in sculptor-generator.properties and sculptor-gui-generator.properties. You only have to define the ones that you need to change.

Sculptor will look for properties in the following order:

  1. System.properties (only intended for temporary tests)
  2. sculptor-gui-generator.properties
  3. sculptor-generator.properties
  4. default-sculptor-generator.properties

Project Nature

The main capabilities and responsibilities of the project is defined by the project.nature property. It is typically used to assign default values of more fine grained properties. E.g. the generated artifact of a presentation tier project is different from a business tier project.

The default project nature is business-tier. For a web project you specify:

project.nature=presentation-tier
From version 1.6, the jsp version of the presentation-tier have been removed, in favor of jsf.

A project may have several natures and the value of the property can be a comma separated list. E.g. to generate business and presentation tier in the same project you can specify:

project.nature=presentation-tier, business-tier

Framework Classes

You can replace the runtime framework with your own implementations by defining the class names in sculptor-generator.properties. The naming convention is that the property key is the last part, starting with framework, of the default Sculptor framework class.
E.g. to use your own base class for all domain objects:

framework.domain.AbstractDomainObject=org.myown.framework.MyAbstractDomainObject

Generic Access Objects

Sculptor provides generic access objects for operations such as findById, findAll, save, delete, etc. The implementation of these operations are located in the runtime framework. It is easy to replace those with your own implementation.

To replace all of them you define the following properties:

framework.accessimpl.package=org.mypkg.accessimpl
framework.accessimpl.prefix=
framework.accessapi.package=org.mypkg.accessapi

To replace a single implementation you define the class name like this:

framework.accessimpl.SaveAccessImpl=org.mypkg.accessimpl.SaveAccessImpl

If you need to replace the interface also:

framework.accessapi.SaveAccess=org.mypkg.accessapi.SaveAccess
framework.accessimpl.SaveAccessImpl=org.mypkg.accessimpl.SaveAccessImpl

When using Hibernate as JPA provider, which is the default, Hibernate specific implementations are used. For example findByCriteria is Hibernate specific. In case you want to use Hibernate as JPA provider, but stick to pure JPA implementations then you should add the following properties:

framework.accessimpl.prefix=Jpa
framework.accessimpl.package=org.fornax.cartridges.sculptor.framework.accessimpl.jpa

Database Product

MySQL, Oracle, PostgreSQL and HSQLDB-InMemory are supported out-of-the-box. You can select database with

db.product=mysql
#db.product=oracle
#db.product=postgresql
#db.product=hsqldb-inmemory

Contribution of support for Apache Derby has been posted in the
Forum

The database properties are only used for generating the DDL and some Hibernate settings.
You can also change the default type mapping:

db.mysql.type.Boolean=CHAR(1)
db.mysql.type.boolean=CHAR(1)
db.mysql.type.Integer=INTEGER
db.mysql.type.int=INTEGER
db.mysql.type.Long=BIGINT
db.mysql.type.long=BIGINT
db.mysql.type.Date=DATE
db.mysql.type.java.util.Date=TIMESTAMP
db.mysql.type.DateTime=DATETIME
db.mysql.type.Timestamp=TIMESTAMP
db.mysql.type.BigDecimal=DOUBLE
db.mysql.type.Double=DOUBLE
db.mysql.type.double=DOUBLE
db.mysql.type.String=VARCHAR
db.mysql.length.String=100
db.mysql.length.Enum=40
db.mysql.type.discriminatorType.STRING=VARCHAR
db.mysql.length.discriminatorType.STRING=31
db.mysql.type.discriminatorType.CHAR=CHAR(1)
db.mysql.type.discriminatorType.INTEGER=INTEGER

For parent child (one-to-many) relations the database can do cascaded delete of children when deleting parent. This is the default setting. It is possible to disable that and let Hibernate do the delete by setting the following property to false.

db.mysql.onDeleteCascade=false

Cascade

Default cascade values can be defined for different types of associations. References between different modules have no default cascade value. 'all' is default for references within the same module. You might want to change that to 'persist,merge' if you don't want delete to be propagated.

# default cascade values for aggregate references
cascade.aggregate=all
# bidirectional one-to-many for aggregate reference
cascade.aggregate.oneToMany=all-delete-orphan
# default cascade values for references within same module
# bidirectional many-to-many
cascade.manyToMany=all
# bidirectional one-to-many
cascade.oneToMany=all
# unidirectional to-many
cascade.toMany=all
# unidirectional to-one
cascade.toOne=all

Types

Mapping from simple types used in the DSL to generated types are defined with these properties:

javaType.Date=java.util.Date
javaType.DateTime=java.util.Date
javaType.Timestamp=java.util.Date
javaType.Map=java.util.Map
javaType.List=java.util.List
javaType.Set=java.util.Set
javaType.Collection=java.util.Collection

Note that the java.lang types, such as String and Integer are not defined, since they have the same name in the DSL and the Java code.

You can add your own types also, e.g.

javaType.AccountNumber=org.foobar.type.AccountNumber
javaType.Currency=String
javaType.Amount=BigDecimal

When you use your own classes, such as AccountNumber, you need to define the corresponding Hibernate user type.

hibernateType.AccountNumber=org.foobar.type.PersistentAccountNumber

If you define the above types you can use them directly in the DSL.

Entity Account {
  AccountNumber accountNumber
  - @Money balance
}
BasicType Money {
  Amount amount
  Currency currency
}

Another example, ShortString, which combines the database and java type properties.

db.mysql.type.ShortString=VARCHAR
db.mysql.length.ShortString=10
javaType.ShortString=String

The type of the automatically generated id attribute is defined as

id.type=Long

Joda Date and Time API

It is possible to use Joda Time instead of the Java date and time classes.

datetime.library=joda

Package Names

You can define the names of the generated packages.

package.serviceInterface=serviceapi
package.serviceImplementation=serviceimpl
package.serviceProxy=serviceproxy
package.consumer=consumer
package.xmlmapper=consumer
package.domain=domain
package.repositoryInterface=domain
package.exception=exception
package.repositoryImplementation=repositoryimpl
package.accessInterface=repositoryimpl
package.accessImplementation=accessimpl
package.dto=serviceapi
package.web=web
package.richClient=richclient
package.common=common

You can also use subpackages.

package.domain=domain
package.repositoryInterface=domain.repository
package.exception=domain.exception

Database Naming

There are a few configuration options for different naming strategies of database elements.

Convert camel case to underscore in database names:

db.useUnderscoreNaming=true

Foreign key column names ending with _ID:

db.useIdSuffixInForeigKey=true

ID column prefixed with table name:

db.useTablePrefixedIdColumn=true

File Header

You can define a header to be generated in java files. The java comments are added automatically, but you need to include the newline character.

javaHeader=Copyright line 1 \n\
 line 2 \n\
 line 3

Removing the generation of the ServiceContext

For some reason it might be required to switch off the generation of the ServiceContext - for example if you want to create a client without the created/updated functionality.
In this case it can be done easily with setting a property value in the sculptor-generator.properties file.

# specifies if ServiceContext is to be generated
generate.serviceContext=false

This turns off the generation of the ServiceContext. Please note that in this case the auditable feature does not work or must be implemented manually.

You can skip generation of auditable with:

generate.auditable=false

Influencing the generation of the UMLGraph feature

By default Sculptor generates a dot file for Graphviz for documentation purposes. This might not be needed for all projects. Please set the following property to drive the generation of this feature.

# specifies if umlgraph for Graphviz is to be generated
generate.umlgraph=false

XML configuration for Spring

If you prefer Spring bean definitions in xml files instead of annotations you can use this property:

generate.spring.annotation=false

XML mapping for Hibernate

If you prefer hibernate mapping in xml files (hbm) instead of JPA annotations you can use this property:

generate.jpa.annotation=false

Cache Provider

Cache provider settings are generated based on the property cache.provider.

cache.provider=EhCache
#cache.provider=JbossTreeCache
#cache.provider=DeployedTreeCache
#cache.provider=TreeCache

There are some built in cache providers.

  1. EhCache - settings are defined in ehcache.xml file, skeleton is generated
  2. JbossTreeCache - settings are defined as a JBoss mbean, EJB3EntityTreeCache
  3. DeployedTreeCache - settings are defined as a JBoss mbean.
  4. TreeCache - settings are defined in treecache.xml file

The choice also affects the cache usage attribute in the Hibernate mapping.

EhCache is the default cache provider, since it doesn't require any additional configuration. JBoss JbossTreeCache is recommended for (clustered) applications that require a transactional cache. Refer to JBoss documentation for information about how to configure the TreeCache.

Validation

In case you don't want annotations for Hibernate validator you can omit them with:

generate.validation.annotation=false

You can add your own validations or replace the implementation of the built in validators:

validation.annotation.AssertFalse=org.hibernate.validator.AssertFalse
validation.annotation.AssertTrue=org.hibernate.validator.AssertTrue
validation.annotation.CreditCardNumber=org.hibernate.validator.CreditCardNumber
validation.annotation.Digits=org.hibernate.validator.Digits
validation.annotation.EAN=org.hibernate.validator.EAN
validation.annotation.Email=org.hibernate.validator.Email
validation.annotation.Future=org.fornax.cartridges.sculptor.framework.validation.validator.Future
validation.annotation.Length=org.hibernate.validator.Length
validation.annotation.Max=org.hibernate.validator.Max
validation.annotation.Min=org.hibernate.validator.Min
validation.annotation.NotEmpty=org.hibernate.validator.NotEmpty
validation.annotation.NotNull=org.hibernate.validator.NotNull
validation.annotation.Past=org.fornax.cartridges.sculptor.framework.validation.validator.Past
validation.annotation.Pattern=org.hibernate.validator.Pattern
validation.annotation.Patterns=org.hibernate.validator.Patterns
validation.annotation.Range=org.hibernate.validator.Range
validation.annotation.Size=org.hibernate.validator.Size
validation.annotation.Valid=org.hibernate.validator.Valid

JPA annotation settings

If you prefer JPA annotations on getters instead of fields:

generate.jpa.annotation.onField=false

To generate annotations for database definition of column type:

generate.jpa.annotation.columnDefinition=true

JPA provider settings

If you prefer to use another JPA provider instead of Hibernate you can choose between EclipseLink and DataNucleus. With EclipseLink or DataNucleus you won't be able to use all features of sculptor, e.g. there is no support for Joda Date and Time API, Criteria API and limited support for enums.

To use EclipseLink add

jpa.provider=eclipselink

Replace the hibernate dependencies from pom.xml with

<dependency>
  <groupId>org.eclipse.persistence</groupId>
  <artifactId>eclipselink</artifactId>
  <version>1.2.0</version>
</dependency>

add the plugin to enhance the domain classes and the repository to resolve the dependencies

<plugin>
     <artifactId>maven-antrun-plugin</artifactId>
     <executions>
       <execution>
         <phase>process-classes</phase>
         <configuration>
           <tasks>
             <java classname="org.eclipse.persistence.tools.weaving.jpa.StaticWeave"
                   classpathref="maven.compile.classpath" fork="true">
               <arg line="-loglevel FINE target/classes target/classes"/>
             </java>
           </tasks>
         </configuration>
         <goals>
           <goal>run</goal>
         </goals>
       </execution>
     </executions>
</plugin>

<repository>
    <id>EclipseLink Repo</id>
    <url>http://www.eclipse.org/downloads/download.php?r=1&amp;nf=1&amp;file=/rt/eclipselink/maven.repo</url>
</repository>

If you want to use DataNucleus add

jpa.provider=datanucleus

Replace the hibernate dependencies from pom.xml with

<dependency>
    <groupId>org.datanucleus</groupId>
    <artifactId>datanucleus-core</artifactId>
    <version>1.1.6</version>
</dependency>
<dependency>
    <groupId>org.datanucleus</groupId>
    <artifactId>datanucleus-rdbms</artifactId>
    <version>1.1.6</version>
</dependency>
<dependency>
    <groupId>org.datanucleus</groupId>
    <artifactId>datanucleus-jpa</artifactId>
    <version>1.1.5</version>
</dependency>
<dependency>
    <groupId>javax.jdo</groupId>
    <artifactId>jdo2-api</artifactId>
    <version>2.3-eb</version>
</dependency>

add the plugin to enhance the domain classes and the repository to resolve the dependencies

<plugin>
    <groupId>org.datanucleus</groupId>
    <artifactId>maven-datanucleus-plugin</artifactId>
    <version>${datanucleus.plugin.version}</version>
    <configuration>
	<mappingIncludes>**/domain/*.class</mappingIncludes>
	<log4jConfiguration>${basedir}/src/test/resources/log4j-test.properties</log4jConfiguration>
	<api>JPA</api>
	<verbose>false</verbose>
	<outputFile>${basedir}/target/datanucleus_ddl.sql</outputFile>
	<completeDdl>true</completeDdl>
    </configuration>
    <dependencies>
	<dependency>
 	    <groupId>org.hibernate</groupId>
	    <artifactId>hibernate-validator</artifactId>
	    <version>${hibernate.validator.version}</version>
	</dependency>
	<dependency>
	    <groupId>hsqldb</groupId>
	    <artifactId>hsqldb</artifactId>
	    <version>1.8.0.4</version>
	</dependency>
   </dependencies>
   <executions>
	<execution>
	    <id>enhance</id>
	    <phase>process-classes</phase>
	    <goals>
	 	<goal>enhance</goal>
	    </goals>
	</execution>
	<execution>
	    <id>test-schema-create</id>
	    <phase>process-test-classes</phase>
	    <goals>
		<goal>schema-create</goal>
	    </goals>
	    <configuration>
	        <props>${basedir}/src/test/generated/resources/datanucleus-test.properties</props>
	    </configuration>
	</execution>
    </executions>
</plugin>

<repository>
    <id>DataNucleus</id>
    <name>DataNucleus Repository</name>
    <url>http://www.datanucleus.org/downloads/maven2/</url>
</repository>
<pluginRepository>
    <id>DataNucleus</id>
    <url>http://www.datanucleus.org/downloads/maven2/</url>
    </pluginRepository>
</pluginRepositories

Hibernate Repository

The default implementation of a Repository consists of an implementation class and Access Objects. The intention is a separation of concerns between the domain and the data layer. Repository is close to the business domain and Access Objects are close to the data layer. The Hibernate specific code is located in the Access Object, and not in the Repository.

For some systems this separation might be overkill and you might prefer to implement the data access directly in the Repository. Sculptor supports this design out-of-the-box. You only have to specify a property in sculptor-generator.properties to have JPA/Hibernate support directly in the Repository implementation.

generate.repository.jpaSupport=true

or if you don't use JPA:

generate.repository.hibernateSupport=true

This will result in that the Repository implementation will extend Spring JpaDaoSupport/HibernateDaoSupport and you can implement JPA/Hibernate specific code directly in the Repository.

When using Pure EJB3 this will result in enity manager injection in repository.

It is possible to define another class that all repositories extend with property:

repository.extends=com.foo.bar.MyRepositorySupport

Deployment in Tomcat

By default Jetty is used to run the application. Instead of Jetty you can use Tomcat.
Deployment in Tomcat requires that you do the following.

1. Define the following property in sculptor-generator.properties in the business tier project.

deployment.applicationServer=Tomcat

2. The Tomcat definition of the datasource is located in META-INF/context.xml in the WAR. This file is generated once, but you might need to adjust the settings.

3. Copy the jdbc driver jar to tomcat lib directory.

4. Rebuild with mvn clean install in the parent directory.

5. The WAR located in the target directory of the web project can be deployed in Tomcat.

Deployment as EAR or WAR

Sculptor supports deployment as EAR or WAR. WAR is default when creating new projects with the archetypes. The design differences are:

  • Services are exposed as EJBs when deployed as EAR, POJOs when deployed as WAR.
  • Transaction management is done with JTA by the application server when deployed as EAR, by Spring when deployed as WAR.
  • Consumers are not supported when deployed as WAR.

The Archetype Tutorial describes the steps how to convert WAR to EAR deployment. It also describes how to deploy in JBoss.

Scaffold

Scaffold operations are defined as a comma separated list.

scaffold.operations=findById,findAll,save,delete

Visibility of setters for not changeable attributes and references

By default the visibility is private for setter methods of not changeable attributes. Some tools might need visible setter methods and you can change the visibility with the following property. When you make these setters visible they will check that the value is not changed, once it has been set.

notChangeablePropertySetter.visibility=private
#notChangeablePropertySetter.visibility=protected

The visibility of setters for not changeable references are public. These setters will also check that the reference value is not changed, once it has been set. The reason for having the public setters is that sometimes the referred object is not available at construction time. Then it is possible to pass in null in the constructor and then afterwards assign the reference. If you would like to hide these setters you can use the following property.

notChangeableReferenceSetter.visibility=private

EJB Version

EJB 3 is default, but Sculptor also supports EJB 2.1. Activate EJB 2.1 with

ejb.version=2.1

Spring Configuration

You can change the settings in spring.properties. It is runtime configuration of Spring.

Spring makes it possible to override bean definitions. The last definition wins. It is applicationContext.xml that is loaded first and it imports other configuration files. more.xml is imported last, which means that you can override generated bean definitions by specifying them in more.xml. This is also true for beans defined with annotations (@Repository, @Service, @Component). When running JUnit tests the corresponding file is more-test.xml.

This approach is not recommended, since it might be confusing to have multiple bean definitions. It is also easy to forget to change when something is renamed and that can have dangerous side effects. For testing or temporary modifications it might be an easy solution, but for permanent customization it is better to use one of the other alternatives presented in this article.

Change Generation Templates

You can change the code generation templates using the Aspect-Oriented Programming features in Xpand. It is described in Aspect-Oriented Programming in Xtend .

To use AOP you add a section in workflow.mwe in your application project.

	<component adviceTarget="generator" id="reflectionAdvice"
			class="org.eclipse.xpand2.GeneratorAdvice">
		<advices value="templates::SpecialCases" />
		<fileEncoding value="iso-8859-1" />
	</component>

Define your advice in src/main/resources/templates/SpecialCases.xpt. For example if you need to replace the UUID generation:

SpecialCases.xpt
«IMPORT sculptormetamodel»
«EXTENSION extensions::helper»
«EXTENSION extensions::properties»

«AROUND templates::DomainObject::uuidAccessor FOR DomainObject»
    public String getUuid() {
        // lazy init of UUID
        if (uuid == null) {
            uuid = org.myorg.MyUUIDGenerator.generate().toString();
        }
        return uuid;
    }

    private void setUuid(String uuid) {
        this.uuid = uuid;
    }
«ENDAROUND»

You find the default templates in fornaxcartridgessculptorgenerator/src/main/resources/templats.
Everything starts in Root.xpt, which you also can intercept to add more templates or exclude some of the existing templates.

Another alternative is to setup the development environment according to the next section and change the original templates and build a new version of fornax-cartridges-sculptor-generator.

Sometimes you need to override the extension functions used by the templates. This can also be done with the AOP features of Xtend.

Add extensionAdvices in the generator advice in workflow.mwe in your application project.

	<component adviceTarget="generator" id="reflectionAdvice"
			class="org.eclipse.xpand2.GeneratorAdvice">
		<advices value="templates::SpecialCases" />
		<extensionAdvices value="extensions::SpecialCases" />
		<fileEncoding value="iso-8859-1" />
	</component>

Define your advice in src/main/resources/extensions/SpecialCases.ext. For example if would like to change the location of spring resource files:

SpecialCases.ext
import sculptormetamodel;

extension extensions::helper;

around extensions::properties::getResourceDir(Application application, String name) :
  name == "spring" ?
    "spring/" :
    ctx.proceed();

around extensions::properties::getResourceDir(Module module, String name) :
  name == "spring" ?
    "spring-" + module.name + "/" :
    ctx.proceed();

hint

A very useful extension mechanism is available via the hint keyword in the model. It is possible to use a hint on almost any element in the model. It may contain any key/values, which can be used in SpecialCases to customize the code generation. For example, if you need to use a special database sequence for the ids of some entities. In the model you can use the hint:

    Entity Person {
        hint="idSequence=SEQ2"

In SpecialCases.xpt:

«AROUND templates::DomainObject::idAnnotations FOR Attribute»
    «IF getDomainObject().hasHint("idSequence")»
        @javax.persistence.Id
        @javax.persistence.GeneratedValue(strategy=javax.persistence.GenerationType.SEQUENCE, 
            generator="«getDomainObject().getHint('idSequence')»")
        @javax.persistence.Column(name="«getDatabaseName()»")
    «ELSE»
    	«targetDef.proceed()»
    «ENDIF» 
«ENDAROUND»

Several hints can be defined, separated with comma, and the hint may have a value or not.

        String someAttribute hint="aaa=A,bbb,ccc"

Setup Development Environment

To be able to do more adjustments you need to setup Eclipse for development of Sculptor.

Checkout from Subversion

Install a Subversion plugin, such as Subversive

Checkout the following projects from https://fornax.svn.sourceforge.net/svnroot/fornax/trunk/cartridges/sculptor/

  • fornax-cartridges-sculptor-framework
  • fornax-cartridges-sculptor-framework-web
  • fornax-cartridges-sculptor-generator
  • fornax-cartridges-sculptor-metamodel
  • fornax-cartridges-sculptor-parent
  • org.fornax.cartridges.sculptor.dsl
  • org.fornax.cartridges.sculptor.dsl.ui
  • org.fornax.cartridges.sculptor.gui.dsl
  • org.fornax.cartridges.sculptor.gui.dsl.ui
  • fornax-cartridges-sculptor-archetype
  • fornax-cartridges-sculptor-archetype-ear
  • fornax-cartridges-sculptor-archetype-jsf
  • fornax-cartridges-sculptor-archetype-parent
  • fornax-cartridges-sculptor-archetype-standalone
  • org.fornax.cartridges.sculptor.framework.richclient
  • org.fornax.cartridges.sculptor.richclient.lib
  • org.fornax.cartridges.sculptor.wizard

Fornax Maven Launcher

Checkout Fornax maven-launcher as described in the Installation Guide (if you haven't already done that).

When you checkout/import the Fornax Maven Launcher into the workspace you can run maven inside Eclipse. The menu items for this are available in the external tools menu.

Try mvn

Verify that your environment is correct by running Fornax [mvn-install] for fornax-cartridges-sculptor-parent. Refresh all projects in Eclipse. Thereafter you should not have any red crosses (problems) in Eclipse. Sometimes, validation errors in code generation files (.xpt and .ext) must be cleaned. This is can be done with clean of the Eclipse project.

Checksum plugin

This maven plugin (fornax-checksum-m2-plugin) is responsible for deleting unchanged files from one-time-generate directories. Before when you changed for example Service name or method in your design file or SpecialCases.xpt you have to remove or change one-time-generated files manualy even if you don't touch them. This plugin care about this untouched files and they are removed before generation. New checksums are generated after generation. Everything is stored in 2 files. One is '.checksum.txt' which hold checksums and second is '.ignore-checksum.txt' which hold file names of changed (touched) files. Plugin is by default checking following directories: 'src/main/java,src/main/resources,src/test/java,src/test/resources'.

Some important notices:

  • DO NOT put '.checksum.txt' and '.ignore-checksum.txt' to version system. It's not tragical mistake but you will have many problems with merging this files when updating. Plugin works nice without this. (For SVN add this files to svn:ignore)
  • DO NOT change one-time-generated files after unsuccesful generation. This change will not be noticed by plugin and in next round, file will be deleted and regenerated. How to detect this? Check 'mvn' output. If you see line "ChecksumValidator: GENERATE CHECKSUMS" you are on safe side, you can change whatever you want. Maybe compilation crashed but generation went fine. If you don't see this line fix your model file or SpecialCases.xpt and restart generation 'mvn clean install'.
  • DO NOT put all files under version control. Only changed files should go to version control system (files from src/generated should never go into version control system). How to achieve this, read following HOW-TO section.

HOW-TO:

  • HOW TO START REGENERATION OF CHANGED FILE - remove file from filesystem and also from .ignore-checksum.txt
  • HOW TO CHECK IF ALL CHANGED FILES ARE UNDER VERSION CONTROL - just to be 100% sure run 'mvn -Dmaven.test.skip=true clean install clean'. After this you can start your favorit version synchronisation tool. If you want to double check your tool. All files in .ignore-checksum.txt should be under your version control (don't forget to run 'clean install clean' before this check).

Reference Application

When you customize Sculptor it is important that you have a representative reference application. It is used to verify the changes to the code generator. It must be big enough to span most of the design concepts you use, but still small enough to be supple.

When you do refactoring of the code generator it is recommended that you first do manual refactoring of the reference application. Keep that code base as a baseline. When you have changed the code generation the generated code should correspond to the manual baseline. Use a diff tool to compare the results.

The library example can act as a reference application if you haven't developed one of your own.
Checkout from Subversion:
https://fornax.svn.sourceforge.net/svnroot/fornax/trunk/cartridges/sculptor/fornax-cartridges-sculptor-examples-library

Run the JUnit tests to verify that it works before you start changing.

Eclipse project dependencies
It is convenient to use Eclipse project dependencies from the reference application to fornax-cartridges-sculptor-generator. Then you can do changes in the templates and try them directly, without having to build with maven. You can run the code generation directly inside Eclipse. In your reference application project, right click on workflow.mwe and select 'Run as MWE workflow'.

Constraint Validation

Check Language is used for validation of model constraints.

DSL Constraints

DSL constraints are validated directly when you edit the DSL file with the DSL editor (error highlight). DSL constraints are defined in SculptordslJavaValidator.java and SculptordslChecks.chk in org.fornax.cartridges.sculptor.dsl. If you change these you have to rebuild the plugins, but no other changes are needed.

Note that the constraints are both a help when editing the DSL and also a way to enforce certain design decisions.

Meta Model Constraints

Constraints of the ecore meta model is located in constraints.chk in fornax-cartridges-sculptor-generator.

Transformations

XTend is used for model transformations.

DSL Model Transformation

Two meta models are used. One for the DSL, which is generated from the XText grammar. Another meta model is used by the code generation templates. They are rather similar, but still different. They serve different purposes, which motivates why two meta models are needed.

It would also be possible to replace the XText DSL meta model with some other, e.g. a graphical DSL implemented with GMF.

A transformation converts the DSL model to the code generation model. This transformation is implemented in DslTransformation.ext in fornax-cartridges-sculptor-generator.

You can do a lot of powerful things in this transformation, without having to change code generation templates or its meta model. For example, the scaffold feature is implemented in the transformation. There is a property scaffold for DslEntity. It is like an ordinary Entity with attributes and references, but the DslTransformation automatically add Repository and Service with generic CRUD operations.

When you change the DSL, except for simple syntactic sugar, you often have to change the DslTransformation also.

Model Enrichment

There is one transformation that enrich the model with some useful default values. It is implemented in Transformation.ext. For example the id attribute of each DomainObject is added by this transformation.

You can add more features to this transformation when needed. If it is not specific to the concrete syntax of the DSL, it is better to add features to this transformation than to the DslTransformation, since they will be available even if the concrete syntax is replaced by some other DSL.

GUI Transformation

A separate model is used for generation of the CRUD GUI. A transformation takes the domain model as input and and creates a model that is better suited for generation of the user interface. This transformation is located in GuiTransformation.ext

Customize the Transformations

Extension advices can be used to customize the transformations.
Add the following section in workflow.mwe in your application project.

	<component adviceTarget="modelTransformation"
				class="org.eclipse.internal.xtend.xtend.XtendAdvice">
		<extensionAdvice value="extensions::SpecialCases" />
	</component>

	<component adviceTarget="dslTransformation"
				class="org.eclipse.internal.xtend.xtend.XtendAdvice">
		<extensionAdvice value="extensions::SpecialCases" />
	</component>

Define your advice in src/main/resources/extensions/SpecialCases.ext.
For example, to skip automatic addition of uuid property:

SpecialCases.ext
import sculptormetamodel;

around transformation::Transformation::modifyUuid(DomainObject domainObject) :
    null;

Meta Model

Input to the code generation is the model, which is structured according to the meta models sculptormetamodel.ecore and sculptorguimetamodel.ecore. The meta models are defined with Ecore.

Business Tier Model (domain model)


Figure 2. Meta Model for the code generation model (Click to view)

If you have installed Eclipse Graphical Modeling Framework SDK (Ganymede update site) you will be able to edit the ecore file with a graphical editor, see Figure 2. Open the sculptormetamodel.ecore_diagram with the editor.

MaxPermSize
To prevent OutOfMemoryError when you use the graphical ecore editor you can add -XX:MaxPermSize=128m in eclipse.ini, which is located in the Eclipse installation directory.
When you add a totally new concept you have to add it to this meta model.
When you change the meta model it is not enough to build with maven. First you must generate EMF model code.

  1. An EMF genmodel is created from the ecore model. Normally you don't have to create a new genmodel, but when you do major changes you can remove the sculptormetamodel.genmodel and create a new from the ecore model using File > New > Eclipse Modeling Framework > EMF Model.
  2. Open the genmodel and right click on the top node. Select Generate Model. It generates Java code to src/main/java. It is safe to remove that code and generate it again if needed.

Now you can package the meta model with mvn install.

GUI Model

Sculptor doesn't aim at implementing a general purpose GUI model that can be used for any type of GUI. We are focusing on the kind of CRUD GUI illustrated in the CRUD GUI Tutorial.


Figure 2. GUI Meta Model for the GUI parts of the code generation model (Click to view)

User Task
The model is arranged around User Tasks, The term UserTask is inspired by the presentation: From User Story to User Interface. In this presentation it is described that:

  • Tasks require intentional action on behalf of a tool's user
  • Tasks have an objective that can be completed
  • Tasks decompose into smaller tasks

Sculptor's implementation with Spring WebFlow is that a UserTask corresponds to Flow. A conversation corresponds to a top level UserTask and its subtasks.

Code Generation Templates

Xpand Language is used for the templates.

The code generation starts in Root.xpt, which selects some course grained elements from the model and invokes other templates. In the templates you can access simple properties of the model objects and extension methods.

«DEFINE attributeTypeAndName FOR Attribute»
«getTypeName()» «name»
«ENDDEFINE»

The extension methods are defined with Xtend Lanuage in helper.ext. Some methods are implemented inside this file with the Expression Language and some delegate to Java helper classes.

Expression Language is also used in the templates to for example select elements from the model.

references.select(r | !r.many && (r.referenceType.metaType == BasicType))

How to

Some examples are implemented
Some of the examples presented here are already implemented in Sculptor. It was convenient to add documentation and using real examples while implementing new features.

How to exclude generation

For some special cases the default generation might not be appropriate and it is desirable to handle the special case with manual code instead. For example, assume we have a complex domain object and we need to do the JPA/Hibernate mapping manually.

This can be nicely solved with the Aspect-Oriented Programming feautures in Xpand. It is described in Aspect-Oriented Programming in Xtend .

To use AOP you add a section in workflow.mwe in your application project.

	<component adviceTarget="generator" id="reflectionAdvice"
			class="org.eclipse.xpand2.GeneratorAdvice">
		<advices value="templates::SpecialCases" />
		<fileEncoding value="iso-8859-1" />
	</component>

Define your advice in src/main/resources/templates/SpecialCases.xpt.

SpecialCases.xpt
«IMPORT sculptormetamodel»
«EXTENSION extensions::helper»
«EXTENSION extensions::properties»

«REM»Skip createTable for Person«ENDREM»
«AROUND templates::OracleDDL::createTable FOR DomainObject»
    «IF name != "Person" -»
    	«targetDef.proceed() -»
    «ENDIF -»
«ENDAROUND»

«REM»Skip dropTable for Person«ENDREM»
«AROUND templates::OracleDDL::dropTable FOR DomainObject»
    «IF name != "Person" -»
    	«targetDef.proceed() -»
    «ENDIF -»
«ENDAROUND»

«REM»Skip DomainObject for Person«ENDREM»
«AROUND templates::DomainObject::domainObjectBase FOR DomainObject»
    «IF name != "Person" -»
    	«targetDef.proceed() -»
    «ENDIF -»
«ENDAROUND»

«AROUND templates::DomainObject::domainObjectSubclass FOR DomainObject»
    «IF name != "Person" -»
    	«targetDef.proceed() -»
    «ENDIF -»
«ENDAROUND»

Some of the generated artifacts can be controlled with properties in sculptor-generator.properties. Look in default-sculptor-generator.properties for properties named generate.xxx. For example, to skip generation of all junit tests:

generate.test=false

How to change syntactic sugar

It is rather easy to change the concrete syntax of the DSL. It is defined in the XText grammar sculptordsl.xtxt.

For example, as an alternative to ! we would like to be able to use not.

Define a rule.

terminal NOT :
  ('!'|'not');

Use this rule instead of the "!" literal.

((notChangeable?=NOT "changeable") | ("changeable")) |

If you change the core elements of the language, such as DslService, DslEntity, DslAttribute, you have to change the transformation also, which is located in DslTransformation.xpt in the fornax-cartridges-sculptor-generator project.

Try DSL changes
When working with the DSL you generate the parser and editor by running the GenerateSculptords.mwe in the
org.fornax.cartridges.sculptor.dsl project (Run as MWE Workflow). Thereafter you can try the DSL editor by launching a runtime workbench as an Eclipse Application, which includes the plugins in the workspace automatically.

How to change package names in the generated code

In the DSL you can define the root package for the Application. You can define the package of a Module, if the default is not satisfactory. The package name of individual Domain Objects can be specified, if the default is not appropriate.

Application Library {
    basePackage = com.mycorp.library

    Module common {
        basePackage = com.mycorp.common

        BasicType Address {
            package=types
            String street
            String city
        }
    }
}

You can change the names of the subpackages to the module by defining properties in sculptor-generator.properties as described in packageNames.

How to add support for another database

Generation of database schema (DDL) is specific for different database vendors. To add support for your database you can do like this.

1. In the application specific sculptor-generator.properties you define the database properties for your database. Note that the db.product value must be custom.

sculptor-generator.properties
db.product=custom
db.custom.maxNameLength=27
db.custom.hibernate.dialect=org.hibernate.dialect.OracleDialect
db.custom.onDeleteCascade=true
db.custom.type.Boolean=CHAR(1)
db.custom.type.boolean=CHAR(1)
db.custom.type.Integer=NUMBER
db.custom.length.Integer=10
db.custom.type.int=NUMBER
db.custom.length.int=10
db.custom.type.Long=NUMBER
db.custom.length.Long=20
db.custom.type.long=NUMBER
db.custom.length.long=20
db.custom.type.Date=DATE
db.custom.type.java.util.Date=DATE
db.custom.type.DateTime=DATE
db.custom.type.Timestamp=DATE
db.custom.type.BigDecimal=NUMBER
db.custom.type.Double=NUMBER
db.custom.type.double=NUMBER
db.custom.type.String=VARCHAR2
db.custom.length.String=100
db.custom.length.Enum=40
db.custom.type.discriminatorType.STRING=VARCHAR
db.custom.length.discriminatorType.STRING=31
db.custom.type.discriminatorType.CHAR=CHAR(1)
db.custom.type.discriminatorType.INTEGER=INTEGER

2. Create a template for the DDL generation and locate it in templates/CustomDDL.xpt in the classpath, before the fornax-cartridges-sculptor-generator jar.

OracleDDL.xpt is a template you can mimic.

Contribution of support for Apache Derby has been posted in the
Forum

How to remove the circular dependency check

The dependency check is done in constraints.chk in fornax-cartridges-sculptor-generator project.
Remove this rule:

context Module ERROR "Cyclic depencencies detected for module " + name:
    this.checkCyclicDependencies();

This can also be done by overriding the extension function checkCyclicDependencies located in helper.ext. You find description of how to override extension functions here.

How to add custom generic access object

The Sculptor runtime application framework provides some useful generic AccessObjects, such as findById, findByQuery, save. It is likely that you need to add other generic AccessObjects.

To add a new generic AccessObject you can do like this. Assume you need an AccessObject that can evict from second level cache.

Evict AccessObject Interface
/**
 * This AccessObject evicts objects from second level cache.
 */
public interface EvictAccess<T> {

    /**
     * Evict persistent objects. If {@link #setId(Long) id} is
     * not specified all objects of this class
     * be evicted.
     * @param persistentClass class of the persistent object
     */
    public void setPersistentClass(Class persistentClass);

    /**
     * Evict a specific object. Set
     * {@link #setPersistentClass(Class) persistentClass}
     * also.
     * @param id the id of the object to evict
     */
    public void setId(Long id);

    /**
     * Evict query cache with specified cache region
     */
    public void setQueryCacheRegion(String queryCacheRegion);

    public void execute();

}
Evict AccessObject Implementation
public class EvictAccessImpl<T> extends JpaAccessBase implements EvictAccess<T> {

    private Class persistentClass;
    private Long id;
    private String queryCacheRegion;

    public void setPersistentClass(Class persistentClass) {
        this.persistentClass = persistentClass;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public void setQueryCacheRegion(String queryCacheRegion) {
        this.queryCacheRegion = queryCacheRegion;
    }

    public void performExecute() throws PersistenceException {
        if (queryCacheRegion != null) {
            getHibernateSession().getSessionFactory().evictQueries(queryCacheRegion);
        }
        if (persistentClass != null) {
            if (id == null) {
                getHibernateSession().getSessionFactory().evict(persistentClass);
            } else {
                getHibernateSession().getSessionFactory().evict(persistentClass, id);
            }
        }
    }

}

There are a few conventions for the AccessObjects that the templates are based on:

  • There must be an execute method. In above example it is implemented in JpaAccessBase, which invokes performExecute.
  • There must be a setEntityManager method in the implementation. In above example it is implemented in JpaAccessBase.
  • Input parameters are defined in the interface as setter methods. These corresponds to the parameters of the repository method.
  • The result of the AccessObject must be available with getResult method, defined in the interface. This corresponds to the return type of the repository method. If void then no getResult is used.

Define the interface and implementation in sculptor-generator.properties:

sculptor-generator.properties
framework.accessapi.EvictAccess=org.helloworld.common.accessapi.EvictAccess
framework.accessimpl.EvictAccessImpl=org.helloworld.common.accessimpl.EvictAccessImpl

The above addition of properties and classes means that you can use the evict AccessObject in your DSL model without any further changes of Sculptor.
E.g.

model.btdesign
Repository PlanetRepository {
    findByExample;
    evict(String queryCacheRegion);
    evict(Class persistentClass);
    evict(Class persistentClass, Long id);

}

This would result in the following generated code in the Repository:

PlanetRepositoryBase
public void evict(String queryCacheRegion) {
    EvictAccess<Planet> ao = planetAccessFactory.createEvictAccess();
    ao.setQueryCacheRegion(queryCacheRegion);
    ao.execute();
}

public void evict(Class persistentClass) {
    EvictAccess<Planet> ao = planetAccessFactory.createEvictAccess();
    ao.setPersistentClass(persistentClass);
    ao.execute();
}

public void evict(Class persistentClass, Long id) {
    EvictAccess<Planet> ao = planetAccessFactory.createEvictAccess();
    ao.setPersistentClass(persistentClass);
    ao.setId(id);
    ao.execute();
}

The above is good enough in most cases, but it is also possible to define default parameters and return values for the RepositoryOperation, which means that you can skip those when using it in the DSL model. By this you can better support "convention over configuration".

For example assume that evict(Class persistentClass) is the most used operation and you would like that a simple evict declaration corresponds to this. Then you need to implement a GenericAccessObjectStrategy and register it in sculptor-generator.properties

public class EvictAccessObjectStrategy
        extends AbstractGenericAccessObjectStrategy
        implements GenericAccessObjectStrategy {

    public void addDefaultValues(RepositoryOperation operation) {
        if (operation.getParameters().isEmpty()) {
            addParameter(operation, "Class", "persistentClass");
        }
    }

    public String getGenericType(RepositoryOperation operation) {
        DomainObject aggregateRoot = operation.getRepository().getAggregateRoot();
        String aggregateRootName = GenerationHelper.getPackage(aggregateRoot) + "." +
            aggregateRoot.getName();
        return "<" + aggregateRootName + ">";
    }

    public boolean isPersistentClassConstructor() {
        return false;
    }

}
sculptor-generator.properties
framework.accessapi.EvictAccess=org.helloworld.common.accessapi.EvictAccess
framework.accessimpl.EvictAccessImpl=org.helloworld.common.accessimpl.EvictAccessImpl
genericAccessObjectStrategy.evict=org.helloworld.common.accessimpl.EvictAccessObjectStrategy

To understand how to implement the strategy class you can have a look in GenericAccessObjectManager and the strategies implemented for the ordinary generic AccessObjects. It is located in fornax-cartridges-sculptor-generator. These strategies are only used by the code generator. They are not used in runtime.

The above means that you can use evict without any parameters in the DSL model.

model.btdesign
Repository PlanetRepository {
    findByExample;
    evict;
}

and the generated Repository method would look like:

PlanetRepositoryBase
public void evict(Class persistentClass) {
    EvictAccess<Planet> ao = planetAccessFactory.createEvictAccess();
    ao.setPersistentClass(persistentClass);
    ao.execute();
}

For some rare special cases you might need to adjust the code generation template for the Repository, genericBaseRepositoryMethod in Repository.xpt.

You must place the genericAccessObjectStrategy class (EvictAccessObjectStrategy) in another library than your application project. It is used (only) by the code generator, and that is run before ordinary compilation, i.e. the class is not available in generate-sources phase if a clean has been done before.

How to add a transformation feature

Scaffold is a feature to be able to mark a Domain Object as scaffold and then automatically add some predefined operations (typically CRUD) to the corresponding Repository and Service. This is implemented in the transformation.

We add a boolean scaffold property to the DSL grammar for DslEntity. This is straightforward.

In the transformation DslTransformation.ext we add a Repository and Service if they doesn't already exist. Operations are added to these, if they don't exist.

scaffold(sculptormetamodel::DomainObject domainObject) :
    (domainObject.repository == null ?
        domainObject.addRepository() :
        null) ->
    domainObject.repository.addScaffoldOperations() ->
    (domainObject.module.services.exists(s | s.name == (domainObject.name + "Service")) ?
        null :
        domainObject.module.addService(domainObject.name + "Service")) ->
    domainObject.module.services.select(s | s.name == (domainObject.name + "Service")).
            addScaffoldOperations(domainObject.repository);

It is easiest to implement the actual creation and addition of model elements in Java. We implement the methods addRepository, addService and addScaffoldOperations in a Java helper class.

public static DomainObject addRepository(DomainObject domainObject) {
    SculptormetamodelFactory factory = SculptormetamodelFactoryImpl.eINSTANCE;

    Repository repository = factory.createRepository();
    repository.setName(domainObject.getName() + "Repository");
    domainObject.setRepository(repository);

    return domainObject;
}

How to change auditable column names

The auditable properties are added by a model transformation. You can modify that transformation in this way.

Add the following advice in workflow.mwe in your application project.

workflow.mwe
	<component adviceTarget="modelTransformation"
				class="org.eclipse.internal.xtend.xtend.XtendAdvice">
		<extensionAdvice value="extensions::TransformationSpecialCases" />
	</component>

Define your advice in src/main/resources/extensions/TransformationSpecialCases.ext.

TransformationSpecialCases.ext
import sculptormetamodel;

around transformation::Transformation::addAuditable(Entity entity) :
         addAuditDateAttribute(entity, "createdDate", "created_date") ->
         addAuditByAttribute(entity, "createdBy", "created_by") ->
         addAuditDateAttribute(entity, "lastUpdated", "updated_date") ->
         addAuditByAttribute(entity, "lastUpdatedBy", "updated_by");

create Attribute addAuditDateAttribute(Entity entity, String propertyName, String databaseName) :
setName(propertyName) ->
setDatabaseColumn(databaseName) ->
setType("java.util.Date") ->
setNullable(true) ->
         entity.attributes.add(this);

create Attribute addAuditByAttribute(Entity entity, String propertyName, String databaseName) :
setName(propertyName) ->
setDatabaseColumn(databaseName) ->
setType("String") ->
setLength("50") ->
setNullable(true) ->
         entity.attributes.add(this);

How to add a new feature in the meta model

We need to be able to define an additional feature for the attributes of the Domain Objects. It should be possible to define that an attribute is included in the constructor, but still have setter method. A complement to the changeable feature. Let us call this new feature required.

1. Open the sculptormetamodel.ecore_diagram with the graphical editor.
Add a new EAttribute to the Attribute "class". Set the name to required in the Properties view. Select EBoolean in the EAttribute Type.

2. Open the sculptormetamodel.genmodel and right click on the top node. Select Generate Model.

3. Open sculptordsl.xtxt and add required in the same way as nullable in the DslAttribute section. Note that required should by default be treated as false when not specified.

(required?"required")?

4. Run GenerateSculptordsl.mwe in org.fornax.cartridges.sculptor.dsl project. This can be done by right clicking GenerateSculptordsl.mwe and selecting "Run as MWE Workflow".

5. Open DslTransformation.ext and add the "copy" of the required property in the same way as the nullable property in the create Attribute method.

setRequired(attribute.required) ->

6. Modify the code generation templates. In this case it was only needed to adjust the logic for the constructorAttributes method, which is located in helper.ext.

7. Build fornax-cartridges-sculptor-parent with mvn clean install.

8. Install the DSL editor plugins and test the new feature with the reference application.

How to Add a New Core Concept

This section describes all steps of how to add a completely new concept, Consumer in this case. Consumer is implemented in Sculptor and since this description is very brief you have to look at the details in the source code to make any sense out of this.

1. Start with the Ecore meta model. Open the sculptormetamodel.ecore_diagram with the graphical editor.

  • Add Consumer class.
  • Add generalization link from Consumer to NamedElement, a Consumer has a name.
  • Add bi-directional aggregate association between Module and Consumer, one Module can contain many Consumers, a Consumer belongs to one Module.
  • Add association from Consumer to Service, serviceDependencies, used for dependency injection of Services into the Consumer.
  • Add association from Consumer to Repository, repositoryDependencies, used for dependency injection of Repositories into the Consumer.

2. DSL grammar, located in sculptordsl.xtxt

  • Define DslConsumer
    DslConsumer :
          (doc=STRING)?
          "Consumer" name=ID "{"
            (dependencies+=DslDependency)*
            ("unmarshall" "to" (("@")?messageRoot=ID))?
            ("queueName" "=" queue=DslQueueIdentifier )?
          "}";
    
  • Add (consumers+=DslConsumer)* to DslModule
  • Add DslDependency in the same way as the existing DslRepositoryDependency
  • Run GenerateSculptordsl.mwe in org.fornax.cartridges.sculptor.dsl to generate DSL meta model and DSL editor

3. DSL constraints checks, located in SculptordslJavaValidator.java and SculptordslChecks.chk.

  • Add DslServiceDependency in the same way as the existing DslRepositoryDependency, findService is already implemented in sculptordsl.ext

4. Transformation of DSL model to Ecore meta model, located in DslTransformation.ext in fornax-cartridges-sculptor-generator.

  • Add consumers.addAll(module.consumers.transform()) -> to the Module transformation.
  • Add create sculptormetamodel::Consumer this transform(DslConsumer consumer)...
  • Add module.consumers.transformDependencies() -> to the Module transformation.
  • Add transformDependencies(DslConsumer consumer)...
  • Add sculptormetamodel::Service transformServiceDependency(DslDependency dependency)...

5. Meta model constraints, located in constraints.chk in fornax-cartridges-sculptor-generator.

  • Add context Consumer ERROR "Not allowed to delegate to repository...
  • Add check of cyclic dependencies in DependencyConstraints class

6. Code generation template

  • Add Consumer.xpt and everything to generate for the Consumers.
  • Add properties for package and framework classes. This is done in default-sculptor-generator.properties, properties.ext and helper.ext.
  • Add EXPAND Consumer in Root.xpt, you need allConsumers() in helper.ext, which can be implemented in the same way as allServices.
  • Add Spring stuff for the Consumers in Spring.xpt.
    Article Rating: 9.0 (1 voters)
    2 3 4 5 6 7 8 9

Grate docu, thanks Patrik.

Denis 

Posted by Anonymous at Mar 12, 2008 17:39 | Reply To This

Hi, excellent documentation.

when I try add new parameter to DslReference (something like fetch parameter, with name indexColumn):

syntax: 

 - List<@Item> items indexColumn="id" <-> item

I add necessary parts to: sculptormetamodel.ecore_diagram, then generate model (generate.oaw and selecting "Run as oAW Workflow"), edit sculptordsl.xtxt

DslReference :
  (doc=STRING)?
  REF (visibility=DslVisibility)? (collectionType=DslCollectionType"<")? (("@")?domainObjectType=[DslSimpleDomainObject]) (">")? name=ID
    ((key?="key") |
     ((notChangeable?=NOT "changeable") | ("changeable")) |
     ((required?="required") | (NOT "required")) |
     ((nullable?="nullable") | (NOT "nullable")) |
     ("cascade" "=" cascade=STRING) |
     ("fetch" "=" fetch=STRING) |
     ((cache?="cache") | (NOT "cache")) |
     ((inverse?="inverse") | (NOT "inverse")) |
     ("databaseColumn" "=" databaseColumn=STRING) |
     ("indexColumn" "=" indexColumn=STRING) |
     ("orderBy" "=" orderBy=ID))*
    (OPPOSITE oppositeName=ID)? (";")?;

DslTransformation.ext

 create sculptormetamodel::Reference this transform(DslReference reference) :
    setDoc(reference.doc) ->
    setName(reference.name) ->
    setCollectionType(reference.collectionType) ->
    setMany(reference.collectionType != null) ->
    setNaturalKey(reference.key) ->
    setChangeable(!reference.notChangeable) ->
    setRequired(reference.required) ->
    setNullable(reference.nullable) ->
    setCache(reference.cache) ->
    setInverse(reference.inverse) ->
    setCascade(reference.cascade) ->
    setFetch(reference.fetch) ->
    setOrderBy(reference.orderBy) ->
    setTo(reference.domainObjectType.transform()) ->
    setDatabaseColumn(reference.databaseColumn) ->
    setIndexColumn(reference.indexColumn) ->
    (reference.oppositeName != null ?
      setOpposite(reference.findOppositeReference(reference.oppositeName).transform()):
      null);

 and build new jar file with maven in fornax-cartridges-sculptor-parent directory.

and build new jar with maven in org.fornax.cartridges.sculptor.dsl directory, then, i copy new org.fornax.cartridges.sculptor.dsl-1.4.0-SNAPSHOT.jar into eclipse plugin directory (in eclipse editor everything is OK, model.design syntax is without errors).

but, i try use it in my reference application , after mvn clean install a have this error message:

908  INFO  CompositeComponent - ParserComponent(sculptordsl-parser)
1270 INFO  CompositeComponent - IfComponent: executing if org/fornax/cartridges/
sculptor/dsl/parser/Parser.oaw in org/fornax/cartridges/sculptor/dsl/parser/Pars
er.oaw:9
1270 INFO  ConditionalComponent - CheckComponent(sculptordsl-checker): expressio
n dslModel.eAllContents.union(

Unknown macro: {dslModel}

) check file(s): org::fornax::cartridges
::sculptor::dsl::GenChecks org::fornax::cartridges::sculptor::dsl::Checks
1672 ERROR WorkflowRunner     - Workflow interrupted. Reason: Errors during vali
dation.
1673 ERROR WorkflowRunner     - mismatched input '=' expecting RULE_ID on line 2
87 in model.design
1673 ERROR WorkflowRunner     - mismatched input '<->' expecting 'Repository' on
 line 287 in model.design
thanks!

Ivan {{}}

Posted by Anonymous at May 29, 2008 00:20 | Reply To This

It looks like it is using the old version, without your modifications, when you generate with maven.
In your reference application, make sure that you are using the 1.4.0-SNAPSHOT version of sculptor in your pom.xml.

/Patrik

yes, i was using the old version of jar file. Thank you very much Patrik.

Ivan 

Posted by Anonymous at May 29, 2008 09:17 | Reply To This

Hi!, is there any chance to use two (or more) levels of extension, because i need two leves of extension and i have this error message:

2641 ERROR WorkflowRunner     - Workflow interrupted. Reason: Errors during vali
dation.
2643 ERROR WorkflowRunner     - Only one level of extension is supported Object3 ex
tends Object2 extends Object1 [sculptormetamodel.impl.EntityImpl@15b2043 (name: S
ZCO, doc: null) (abstract: false, optimisticLocking: true, cache: true, package:
 null, databaseTable: cr_rola, discriminatorValue: SZC) (aggregateRoot: true, au
ditable: true)]

my source code:

abstract Entity Object1

abstract  Entity Object2 extends Object1

Enitty Object3 extends Object2

please, have You any idea, how can I add this feature ?

thanks!
Ivan

Posted by Anonymous at May 30, 2008 16:11 | Reply To This

Hi,

in the section "Deployment as WAR" for Tomcat stay:
"3. The Tomcat definition of the datasource is located in META-INF/context.xml in the WAR. This file is generated once, but you might need to adjust the settings."

So, I do not find any generated context.xml file in any directory.
Is any configuration needed?
I do not want to inject this file after war-creation.

...which settings are mentioned in this quote?

Thanks for any advice!

Haf

Posted by Anonymous at Sep 27, 2008 11:16 | Reply To This

Are you using JSF? I noticed that it is not generated for the JSF implementation, which I think is a bug.

It looks like this, src/main/webapp/META-INF/context.xml:

<?xml version="1.0" encoding="iso-8859-1"?>
<Context crossContext="true" debug="5" docBase="LibraryDS"
	path="/LibraryDS" reloadable="true">
	<Resource auth="Container" driverClassName="com.mysql.jdbc.Driver"
		maxActive="100" maxIdle="30" maxWait="10000" name="jdbc/LibraryDS"
		password="root" type="javax.sql.DataSource"
		url="jdbc:mysql://localhost/library?autoReconnect=true"
		username="root" />
</Context>

Hi Patrik,

thanks for your answer!
yes I am using JSF.

Can I writes this file in any directory or is it necessary to inject this context.xml after WAR creation?
And the next question: You use in the example the name "LibraryDS", where is this name defined, to get the relation between this datasource and the application?

Thanks

Haf

Posted by Anonymous at Sep 29, 2008 00:11 | Reply To This

...to precise my last question:
In spring is it possible to define the JNDI for the datasource-bean.
In which file is it necessary, to set this setting. The applicationContext.xml will be generated every time?
The spring.properties in the business tier project has no effects?

Haf

Posted by Anonymous at Sep 29, 2008 00:23 | Reply To This

Hi Patrik,

unfortunately was the adding of context.xml not so successful.
Hibernate could not found the datasource.
Further I test following steps (...all unsuccessful):
1. adding context.xml in META-INF and jndi definition in applicationContext after war creation
2. adding datasource definition in applicationContext.xml after war creation
3. adding spring.properties with some hibernate.connection.

Unknown macro: {driver_class, url, user, ...}
properties
4. adding hibernate.properties
=> This file will be used. Unfortunetly expect DatasourceConnectionProvider a JNDI name.

atm, I do not want extends the templates....cause I hope to solve this problem on the "normal" way

Is this only a bug in 1.5.0-SNAPSHOT?
Maybe i try the JSP-version?!

Regards

Haf

Posted by Anonymous at Sep 29, 2008 16:35 | Reply To This

I would like to modify a few lines in the generated web.xml file. I was trying to use the AOP feautre of oAW, but couldn't figure out the way to created the AROUND statement. Can someone provide an example?
 
Thanks!

Posted by Anonymous at Apr 01, 2009 14:53 | Reply To This

The webXml template is unfortunately not broken up in smaller pieces so it is a all or nothing modification. However web.xml is rather static and it is probably alright for you to skip the generation and add your own modification directly in web.xml file.

Add the following in WebSpecialCases.xpt to skip generation of web.xml

«AROUND templates::web::JSFCrudGuiConfig::webXml FOR GuiApplication»
«ENDAROUND»

In 1.6.0 we will actually not re-generate web.xml.

I have two concrete entities deriving from an abstract one. Let's say B & C derive from A. B and C then have one-to-many relationship. If I use scaffold in A,B,C the Create/Update*ActionBase for B and C classes don't compile. The getBItems() in C and getCItems() in B have compile error. It defines Collection<B/C> instead of Collection<A> when seraching for items. Is there a way to configure the code generation to fix this problem?
 
Thanks

Posted by Anonymous at Apr 06, 2009 16:43 | Reply To This

I have created a jira issue for this bug. Thanks for reporting. http://www.fornax-platform.org/tracker/browse/CSC-327

In the topic "Customize the Transformations" the first adviceTarget should be "modelTransformation" instead of "transformation" if you want to add extension advices for Transformation.ext.
Perhaps, you could mention in this document the use of debugTrace present in helper.ext in conjunction with advices. It's really helpful when you want to know what's going on during the transformation phase.

Posted by Anonymous at May 14, 2009 15:50 | Reply To This

Hey Guys,

I am just starting to explore sculptor.I want to ask a basic question.I created a simple project and ran the sculptor generator.I see my access objects base classes extending JPAAccessBase.

public abstract class FindUserByMailIdAccessImplBase extends JpaAccessBase

My question is ...is the generator proving a native hibernate or JPA-Hibernate implemention? I just want to use Native hibernate .Is there any way I can get rid of JPA stuff.

Thanks,

Deepak.

Posted by Anonymous at May 18, 2009 04:29 | Reply To This

Hi,
Yes, it is possible. JPA, Spring annotations and JUnit annotations are new default features in Sculptor 1.6.0. If you still want to use the old style you need to add these properties:

generate.jpa.annotation=false
generate.spring.annotation=false
generate.test.annotation=false

This is described in What's New. I will describe this in the Developer's Guide also.

It says that DataNucleus has no support for JodaTime types. Not true. There's a plugin to do that, available from the DN downloads page, with DN 2.x
It also says that there is "limited support for enums" in DataNucleus. I've no idea what that means since DataNucleus passes the JDO and JPA TCKs and so has complete enum persistence capability. If you have something in mind then please do state what it is, preferably by mentioning on the DN forum so we would be aware of what it is, rather than having to stumble over this Wiki.

--Andy (DataNucleus)

Posted by Anonymous at Dec 27, 2009 16:43 | Reply To This