Wednesday, August 28, 2013

performance magnitude ft. sql java xml

This page is NOT a benchmark but the performance feeling that I experienced working with those 3 technologies.

While processing data 

SQL runs 100+ faster than JAVA which runs 100+ faster than XML


Is this impression true?
Do you have some magnitude order?

Do feed-back your impressions and results!

working with updatable code in mp4ox

Minuteproject promotes updatable code as one integration technic.
What it means is that you can change the generated code AND your modifications will be kept if you generate again (ex when your model change).

So this as the interesting effect, that minuteproject is not only for bootstrap time (the early age of your project) BUT throughout the lifecycle of your project.

Things change: code and model but it's OK you can still keep the power of reverse-engineering without losing your code.

Concretely, what does it mean when working with Minuteproject 4 Openxava?


The early age of your project - bootstrap time

Model

You have a idea, model it! the DB way (do not lose the power of your RDBMS)!
  • smart reverse-engineering (your model value more that just storing information, hunt the hidden concepts, enrich it!)
  • virtualization (use views as alternate models or graphs of views)
  • SDD (jdbc statements are sometimes enough)
  • Transient definition (DTO are welcome back for any purpose UC linked or not or partially to some persisted entities)

Generation

By default when you pick-up the target with catalog-entry="OpenXava" the resulting code will go to ../output//OpenXava

Create your OpenXava project and test your idea

If you open a prompt on your generated code directory and set/export your 2 environment variables MP_HOME and OX_HOME then you can call build-.
This will create an OX project; build it, deploy it and open your browser to a menu to access your model.

This is fine and described to create OX project.

Maturity age

Now your OpenXava project is up and running, you can extend your model, and sometimes you want to alter the generated code

Enabling updatable code

Enable your minuteproject to work and merge updated code.
You have to add
<generator-config>
	<configuration>
        <conventions>
            <target-convention type="enable-updatable-code-feature" />
        </conventions>

Working with updatable code

You can check your OX classes and xml, they contain comments.
Some comments are for extension MP-MANAGED-ADDED-AREA-BEGINNING
Some comments are for modification MP-MANAGED-UPDATABLE-BEGINNING-DISABLE to change to MP-MANAGED-UPDATABLE-BEGINNING-ENABLE when modifying the area.
If your artifact (java class, xml file) is final and you do not want to generate it again add MP-MANAGED-STOP-GENERATING

3 possibilities to alter your code are discribed here


Now you just have point Minuteproject 4 Openxava generation towards you Openxava workspace project.

Add the outputdir-root of the node targets and set the directory of your Openxava workspace project

And that's it!

Conclusion

This feature allows you to goes for continuous generation and continuous modification.
Both approach are not longer exclusive BUT complementary.
You got a development methodology to use the power of both approach:

Tuesday, August 27, 2013

Transient definition as a productivity weapon 4 Openxava



Under construction (need 0.8.5 for generation) but can be used in the meantime as a OX tutorial to work with DTO


Productivity challenge

Alongside with (1) smart reverse-engineering, (2) virtualization, (3) Statement-driven-development, Transient definition is a productivity tool and technic that fasten your development.

This article presents you transient definition and how it will help developing ad-hoc UCs in the case of Openxava. Ad-hoc means that there is no direct binding necessary between your screen info and your database entities.


Transient definition

Your model persistence is not enough to define all your data or you want to model the data differently than in the current model perspective. In short you have a UC that is not purely data centric?


Mission statement

UC general statement

You know the Input of a UC and a function to be performed.
The input does not have to match to field or entities in your DB. It may but it is not a requirement.
You want:
  • Input screen
  • Validation/affectation UC
  • Stub to actions
  • Partial or empty implementation of the function
  • to be able to change your requirement and your implementation without losing your modifications 

Concrete UC



















Model description

You have a model that store Release information of an application.
The release is linked to an application and environment.
It has a releaser (the person issuing the release) and a validator (validating the release).
Release status can be ACTIVE, DONE, REJECTED.
A creation date is automatically created when inserting a release.

UC description

Provide releaser user access to create a release
He can:

  • enter name, notes, time to start, time to end the release.
  • pick-up application and environment, releaser (a bit redundant it should be granted implicitly but it need a security wrapping to guess the id of the releaser - which is not part of this demo)
  • enter notification email (that is not persisted)


By clicking a release button

  • Status will be set to ACTIVE
  • Creation date populated by current date
  • Store persistence data.
Extend
  • input field notify available for any manipulation the user want (send mail for example) 


Provide validator user access to view all the releases, select one fill extra info and validate or reject it.
He can view the release


Openxava Code

Create Release

@Entity (name="CreateRelease")
@Table (name="bus_release")
@Views({
//MP-MANAGED-UPDATABLE-BEGINNING-DISABLE @view-base-bus_release@
 @View(
  name="base",
  members=
        ""  
        + "name  ; "
        + "notes  ; "
        + "timeToStart  ; "
        + "timeToEnd  ; "
        + "application  ; "
        + "environment  ; "
        + "releaser  ; "
        + "notify  ; "
  )
    )
})

public class CreateRelease {

    @Hidden  @Id @Column(name="ID" )
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id; 

    @Column(name="NAME",  length=45, nullable=false,  unique=false)
    @Required
    private String name;

    @Column(name="NOTES",    nullable=true,  unique=false)
    @Stereotype ("HTML_TEXT")
    private String notes;

    @Column(name="STATUS",  length=45,  nullable=true,  unique=false)
    private String status;

    @Column(name="CREATION_DATE",    nullable=true,  unique=false)
    @Temporal(TemporalType.TIMESTAMP)
    @ReadOnly(forViews="base,Create,Update,DEFAULT,createReleaseDEFAULT_VIEW")
    private java.util.Date creationDate;

    @Column(name="TIME_TO_START",    nullable=true,  unique=false)
    @Temporal(TemporalType.TIMESTAMP)
    private java.util.Date timeToStart;

    @Column(name="TIME_TO_END",    nullable=true,  unique=false)
    @Temporal(TemporalType.TIMESTAMP)
    private java.util.Date timeToEnd;

 @Transient
    private String validationReason;

 @Transient
    @Required
    @Stereotype ("EMAIL")
    private String notify;

    @ManyToOne (fetch=FetchType.LAZY ) 
    @JoinColumn(name="application_id", referencedColumnName = "ID",  nullable=true,  unique=false  )
    @ReferenceView ("reference") 
    private Application application;
    
    @ManyToOne (fetch=FetchType.LAZY ) 
    @JoinColumn(name="environment_id", referencedColumnName = "ID",  nullable=true,  unique=false  )
    @ReferenceView ("reference") 
    @DescriptionsList(
       descriptionProperties=
       "name "
       ,order=
       "displayOrder ASC "
//       "name asc // "
    )
    @NoCreate
    @NoModify  
    private Environment environment;
    

    @ManyToOne (fetch=FetchType.LAZY ) //remove optional=false to aggragate but leads to a side effect when going directly to the entity: required check is not performed=> if no set DB check constraint is raised...
    @JoinColumn(name="releaser_id", referencedColumnName = "ID",  nullable=true,  unique=false  )
    @ReferenceView ("reference") 
    private User releaser;
 
 ...
 
}
Standard OX entity - mapping
Contains transient field, stereotype and validation

Release Action

public class ReleaseAction extends ViewBaseAction {

 public void execute() throws Exception {
//MP-MANAGED-UPDATABLE-BEGINNING-DISABLE @execute-porphyry@
        //super.execute();
        //TODO
        Messages errors =
            MapFacade.validate("CreateRelease", getView().getValues());
        if (errors.contains()) throw new ValidationException(errors);
        EntityManager em = XPersistence.getManager();
        try {
         CreateRelease e = new CreateRelease();
         if (errors.contains()) throw new ValidationException(errors);
   // set init condition 
   // copy field from form
      e.setName((String)getView().getValue("name"));
      e.setNotes((String)getView().getValue("notes"));
      e.setTimeToStart((Date)getView().getValue("timeToStart"));
      e.setTimeToEnd((Date)getView().getValue("timeToEnd"));
      e.setNotify((String)getView().getValue("notify"));
         // parent

            Map applicationMap = (Map)getView().getValue("application");
            if (applicationMap!=null) {
                Integer applicationId = (Integer)applicationMap.get("id");
                if (applicationId != null) {
        Application application = new Application();
           application.setId(applicationId);
              e.setApplication(application);
             }
            }

            Map environmentMap = (Map)getView().getValue("environment");
            if (environmentMap!=null) {
                Integer environmentId = (Integer)environmentMap.get("id");
                if (environmentId != null) {
        Environment environment = new Environment();
           environment.setId(environmentId);
              e.setEnvironment(environment);
             }
            }

            Map releaserMap = (Map)getView().getValue("releaser");
            if (releaserMap!=null) {
                Integer releaserId = (Integer)releaserMap.get("id");
                if (releaserId != null) {
        User releaser = new User();
           releaser.setId(releaserId);
              e.setReleaser(releaser);
             }
            }
   // assign field
      e.setStatus(new String("ACTIVE"));
   XPersistence.getManager().persist(e); 
   getView().reset();
   resetDescriptionsCache();
        } catch (Exception e) {
            errors = new Messages();
            errors.add(e.getMessage());
            throw new ValidationException(errors);
        }
        //TODO return list
        addInfo("call ReleaseAction done!");
//MP-MANAGED-UPDATABLE-ENDING

 }
}

  • Extends ViewBaseAction
  • Performs validation
  • Retrieves fields and object from getView()
  • Adds status value to ACTIVE
  • Stores value via XPersistence API

Validate release

@Entity (name="ValidateRelease")
@Table (name="bus_release")
@Views({
//MP-MANAGED-UPDATABLE-BEGINNING-DISABLE @view-base-bus_release@
 @View(
  name="base",
  members=
        ""  
        + "name  ; "
        + "notes  ; "
        + "status  ; "
        + "creationDate  ; "
        + "timeToStart  ; "
        + "timeToEnd  ; "
        + "application  ; "
        + "environment  ; "
        + "releaser  ; "
        + "validator  ; "
        + "validationReason  ; "
        + "inform  ; "
  ),
  ...
})

@Tabs({
@Tab(
properties=
     " name "
    +",  notes "
    +",  status "
    +",  creationDate "
    +",  timeToStart "
    +",  timeToEnd "
    +",  environment.name "
    +",  releaser.identifier "
    +",  validator.identifier "
)
...
})

public class ValidateRelease {

    @Hidden  @Id @Column(name="ID" )
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id; 

    @Column(name="NAME",  length=45, nullable=false,  unique=false)
    @Required
 @ReadOnly
    private String name;

    @Column(name="NOTES",    nullable=true,  unique=false)
    @Stereotype ("HTML_TEXT")
 @ReadOnly
    private String notes;

    @Column(name="STATUS",  length=45,  nullable=true,  unique=false)
 @ReadOnly
    private String status;

    @Column(name="CREATION_DATE",    nullable=true,  unique=false)
    @Temporal(TemporalType.TIMESTAMP)
    @ReadOnly(forViews="base,Create,Update,DEFAULT,validateReleaseDEFAULT_VIEW")
    private java.util.Date creationDate;

    @Column(name="TIME_TO_START",    nullable=true,  unique=false)
    @Temporal(TemporalType.TIMESTAMP)
 @ReadOnly
    private java.util.Date timeToStart;

    @Column(name="TIME_TO_END",    nullable=true,  unique=false)
    @Temporal(TemporalType.TIMESTAMP)
 @ReadOnly
    private java.util.Date timeToEnd;

 @Transient
    private String validationReason;

 @Transient
    @Required
    @Stereotype ("EMAIL")
    private String inform;

    @ManyToOne (fetch=FetchType.LAZY ) 
    @JoinColumn(name="application_id", referencedColumnName = "ID",  nullable=true,  unique=false  )
    @ReferenceView ("reference") 
    @NoCreate
    @NoModify  
    private Application application;
    
    @ManyToOne (fetch=FetchType.LAZY ) 
    @JoinColumn(name="environment_id", referencedColumnName = "ID",  nullable=true,  unique=false  )
    @ReferenceView ("reference") 
    @DescriptionsList(
       descriptionProperties=
       "name "
       ,order=
       "displayOrder ASC "
//       "name asc // "
    )
    @NoCreate
    @NoModify  
    private Environment environment;
    
    @ManyToOne (fetch=FetchType.LAZY ) 
    @JoinColumn(name="releaser_id", referencedColumnName = "ID",  nullable=true,  unique=false  )
    @ReferenceView ("reference") 
    @NoCreate
    @NoModify  
    private User releaser;

    @ManyToOne (fetch=FetchType.LAZY ) 
    @JoinColumn(name="validator_id", referencedColumnName = "ID",  nullable=true,  unique=false  )
    @ReferenceView ("reference") 
    private User validator;
    

}

Validate Action

public class ValidateAction extends ViewBaseAction {

 public void execute() throws Exception {
//MP-MANAGED-UPDATABLE-BEGINNING-DISABLE @execute-porphyry@
        //super.execute();
        //TODO
        Messages errors =
            MapFacade.validate("ValidateRelease", getView().getValues());
        if (errors.contains()) throw new ValidationException(errors);
        EntityManager em = XPersistence.getManager();
        try {
         ValidateRelease e = new ValidateRelease();
   e.setId(Integer.valueOf(getView().getValueString("id")));
   e = XPersistence.getManager().find(ValidateRelease.class, e.getId());
            //check display condition
      if (!new String("ACTIVE").equals(e.getStatus()))
    errors.add ("Condition statusActive not met!");
         if (errors.contains()) throw new ValidationException(errors);
   // set init condition 
   // copy field from form
      e.setValidationReason((String)getView().getValue("validationReason"));
      e.setInform((String)getView().getValue("inform"));
         // parent

            Map validatorMap = (Map)getView().getValue("validator");
            if (validatorMap!=null) {
                Integer validatorId = (Integer)validatorMap.get("id");
                if (validatorId != null) {
        User validator = new User();
           validator.setId(validatorId);
              e.setValidator(validator);
             }
            }
   // assign field
      e.setStatus(new String("DONE"));
   XPersistence.getManager().persist(e); 
   getView().reset();
   resetDescriptionsCache();
        } catch (Exception e) {
            errors = new Messages();
            errors.add(e.getMessage());
            throw new ValidationException(errors);
        }
        //TODO return list
        addInfo("call ValidateAction done!");
//MP-MANAGED-UPDATABLE-ENDING

 }


  • Implement little business logic
  • Stores value
  • Updates view display info

Application

<application name="porphyry"> 
 <!-- transfer entities -->
 <module name="CreateRelease" >
     <model name="CreateRelease"/>
     <view name="base"/> 
     <controller name="CreateReleaseController"/>
     <mode-controller name="DetailOnly"/>
 </module>
 <module name="ValidateRelease" >
     <model name="ValidateRelease"/>
     <view name="base"/> 
     <controller name="ValidateReleaseController"/>
     <mode-controller name="SplitOnly"/>
 </module>
 ...
CreateRelease is DetailOnly while ValidateRelease is SplitOnly mode (list+detail)

Controllers

<controllers> 
    <!-- transfer entities -->
  <!-- bus_release -->
 <controller name="CreateReleaseController">
  <action name="release" mode="detail" class="net.sf.mp.demo.porphyry.action.business.busrelease.ReleaseAction" >
   <use-object name="xava_view" />
  </action>     
 </controller>
  <!-- bus_release -->
 <controller name="ValidateReleaseController">
  <action name="validate" mode="detail" class="net.sf.mp.demo.porphyry.action.business.busrelease.ValidateAction" >
   <use-object name="xava_view" />
  </action>     
  <action name="reject" mode="detail" class="net.sf.mp.demo.porphyry.action.business.busrelease.RejectAction" >
   <use-object name="xava_view" />
  </action>     
 </controller>

CreateReleaseController has one action 'Create' while ValidateReleaseController has 2 validate/reject

Minuteproject Configuration

Minuteproject abstract this effort by providing tooling to the analyst to describe those screens and actions that ultimately and implicitly become a flow.

<enrichment>
 <!-- transient objects -->
 <entity name="create release" is-transfer-entity="true" is-searchable="false"
  main-entity="bus_release">
  <field name="id" hidden="true"></field>
  <field name="name"></field>
  <field name="notes"></field>
  <field name="application_id"></field>
  <field name="environment_id"></field>
  <field name="releaser_id" ></field>
  <field name="status" value="ACTIVE" hidden="true"></field>
  <field name="creation_date" hidden="true"></field>
  <!-- <field name="validator_id"></field>  -->
  <field name="time_to_start"></field>
  <field name="time_to_end"></field>
  <field name="validation_reason" hidden="true"></field>
  <!-- other fields -->
  <field name="notify" type="string" mandatory="true">
   <stereotype stereotype="EMAIL" />
  </field>
  <actions>
   <action name="release" default-implementation="insert" >
    <field name="status" value="ACTIVE"></field>
   </action>
  </actions>
 </entity>
 <!-- transient objects -->
 <entity name="validate release" is-transfer-entity="true"
  main-entity="bus_release" is-editable="false" is-searchable="true">
  <field name="id" hidden="true"></field>
  <field name="name"></field>
  <field name="notes"></field>
  <field name="application_id"></field>
  <field name="environment_id"></field>
  <field name="releaser_id"></field>
  <field name="status"></field>
  <field name="creation_date"></field>
  <field name="validator_id" is-editable="true"/><!-- default-value="profile.user.id"></field> -->
  <field name="time_to_start"></field>
  <field name="time_to_end"></field>
  <field name="validation_reason" is-editable="true"></field>
  <!-- other fields -->
  <field name="inform" type="string" mandatory="true"
   is-editable="true">
   <stereotype stereotype="EMAIL" />
  </field>
  <actions>
   <action name="validate" default-implementation="update">
    <action-condition name="statusActive">
     <field name="status" value="ACTIVE"/>
    </action-condition>
    <field name="status" value="DONE"></field>
   </action>
   <action name="reject" default-implementation="update">
    <action-condition name="statusActive">
     <field name="status" value="ACTIVE"/>
    </action-condition>
    <field name="status" value="REJECT"></field>
   </action>      
  </actions>
 </entity>
</enrichment>

Screens

Create release




















Validation






































Releasing

















Validator view


Select one entry




Validate


Try to reprocess a status-DONE-release



Conclusion

Minuteproject provides transient definition facilities & generation capabilities.

Although the screens and actions are not very complex, minuteproject can induce a flow, and without a workflow engine!

Minuteproject is also generating portlet.xml in order to deploy 'Transient-definition portlet' on portal such as Liferay.

Monday, August 26, 2013

reviewing liferfay service builder for minuteproject integration


This article carries a study on Liferay service builder tooling and check the matching in minuteproject ecosystem.

The input from this article comes from:

Service builder principles

Service builder relies on forward engineering and a descriptive format of the model that complies with a dtd. So the user needs to create a valid xml file called service.xml.
Service xml main nodes are 'entity' node.
Entity node describe a persistence entity (i.e. table) for a RDBMS.
The entity description covers columns and relationships (parents and children via collection) as well as primary key strategy.
This file serves as input to an ant task that carries on the generation process. Some 20 different artifacts are generated. All of those artifacts are service-builder 'entity' node centric.

Service builder from a generation perspective

Forward-engineering

Is a approach domain centric as some advantages:
  • density of the information (a simple file to describe)

Service builder from minuteproject perspective

Minuteproject is evolving from smart reverse-engineering to a development methodology platform focusing on 4 Weapons of Mass Productivity:
  • smart reverse-engineering
  • virtualization
  • statement driven development (SDD)
  • transient definition

Service builder / Minuteproject matching

Although the philosophies (forward-engineering vs. reverse-engineering) are different there is a mapping between the both approach.

The question is are all the information in service builder available by minuteproject reverse-engineering?

YES!

Let's have a element review of service builder nodes

Service builder 

<
ATTLIST service-builder
    package-path CDATA #REQUIRED
    auto-namespace-tables CDATA #IMPLIED
>
<!ELEMENT author (#PCDATA)>
<!ELEMENT namespace (#PCDATA)
>

package-path, author, namespace can be deduced by minuteproject model node

Entity

<
ATTLIST entity
    name CDATA #REQUIRED
    human-name CDATA #IMPLIED
    table CDATA #IMPLIED
    uuid CDATA #IMPLIED
    uuid-accessor CDATA #IMPLIED
    local-service CDATA #IMPLIED
    remote-service CDATA #IMPLIED
    persistence-class CDATA #IMPLIED
    data-source CDATA #IMPLIED
    session-factory CDATA #IMPLIED
    tx-manager CDATA #IMPLIED
    cache-enabled CDATA #IMPLIED
    json-enabled CDATA #IMPLIED
>

Entity name can be deduced from table name (or view name)
Human name can be deduce by table alias name
All the other boolean field can be deduce by so convention or some parameters to set on the mp4liferay track
Datasource can be deduced by convention (related to the namespace) (and also generated by MP)

Column

<!ATTLIST column
    name CDATA #REQUIRED
    db-name CDATA #IMPLIED
    type CDATA #REQUIRED
    primary CDATA #IMPLIED
    accessor CDATA #IMPLIED
    filter-primary CDATA #IMPLIED
    entity CDATA #IMPLIED
    mapping-key CDATA #IMPLIED
    mapping-table CDATA #IMPLIED
    id-type CDATA #IMPLIED
    id-param CDATA #IMPLIED
    convert-null CDATA #IMPLIED
    lazy CDATA #IMPLIED
    localized CDATA #IMPLIED
    json-enabled CDATA #IMPLIED
>
Column info can be found by reverse-engineering name (via alias), db-name, type, primary, entity, mapping-key, mapping-table.
Id-type and id-param can be deduced from the PK strategy associated to the database type.
MP covers those described in service.xml (uuid, sequence, autoincrement, identity)

Finder and finder-column
can be deduce from defaulting (pk, fk) or with the semantic reference of the table.

References correspond to DB relationships.

Review conclusion

This means that the main information can be deduced from the smart reverse-engineering.
What is missing can be added by convention a liferay configuration for defaulting (such as companyId, groupId, finder extra info) and parametrization of the liferay track.
In all the case the configuration of service.xml (generated by MP) can be manually changed and at the next generation from the DB your alterations are kept!! Thank to updatable code feature of MP.

Can minuteproject even extend service-builder facility?

YES AGAIN!

Virtualization

This is the strong point of reverse-engineering: you can work with Views. In forward-engineering it is impossible since you cannot guess the implementation whereas in reverse-engineering, you take it as is!
So your view can be set as entity in service.xml (with readonly access)
But what is the identity field of this view. Minuteproject enrichment or convention enable to grant one to a field acting as a unique key.
Even better, minuteproject provides you the ability to grant relationships between view like parent-child foreign key and this can also be used in service.xml

Enrichment

Example: detect relationship between entity when no foreign key present.

Can minuteproject do more for service builder?

YES!

Validation - mandatory and type are not enough

You should have stereotype such as email, url, password. A stereotype is an abstraction between presentation and validation aspects.

Constraints

Limit the column value to a enumeration.

Updatable code

The code you generate can be updated via some extension points. The next time you generate your modification are kept!

Datasource

Minuteproject can generate the datasource code

Semantic reference

Sometimes you do not want to display the entire entity fields but just the more important. This is the semantic reference.

Work with Statement only

Sometimes you can go back to the fundamentals of your UCs (I/O + function).
Ex a jdbc call to a stored procedure.

DTO oriented service

The input is not mapped to a persistence entity.


Conclusion


After this feasability review, minuteproject can have the capabilities to generate service.xml of liferay from the DB. But also to extend it.
A sample generation is foreseen for MP release 0.8.5.

The first approach is to generate the SB input configuration. But nearly all generated artifacts SB generate can also be generated by MP.


Friday, August 23, 2013

SDD as a productivity weapon 4 openxava

Under construction (need 0.8.5 for generation) but can be used in the meantime as a OX tutorial to work with any jdbc sql statement (here stored-procedure)

Productivity challenge

With SDD (Statement driven development) what is important is I/O and functionality.
The model is secondary.
To experience it, let's have a store procedure that perform a required operation and get a web application out of it with Minuteproject 4 Openxava.

Mission statement

UC general statement

You have information to pass to a DB via a web application to perform a function.
You would like the input to be validated base on type, presence, stereotype, membership.

UC specific

You need a functionality to ask for role in the application.
As input you pass 3 params:
  • username
  • email
  • requested role
 UserName is:
  • mandatory
  • string
Email is:
  • mandatory
  • stereotype (format)
 Request role is:
  • mandatory
  • should be one of the application specific role

The function shall also:
  • register the time of creation
  • status=TO_TREAT
The function is provided in format of a stored procedure.

Dev environment constraints

A schema containing
  • a table SEC_ROLE containing roles
  • a table BUS_USER_ROLE_REQUEST
  • a store procedure ASK_FOR_ROLE
  • CREATE PROCEDURE ask_for_role(
      IN username VARCHAR(255),
      IN email VARCHAR(255),
       IN role VARCHAR(255)
    )
    BEGIN
    Insert into BUS_USER_ROLE_REQUEST (USERNAME, EMAIL, ROLE_REQUESTED, STATUS, REQUEST_DATE)
    values (username, email, role, 'TO_TREAT', NOW());
    
    END
    
  • SEC_ROLE and  BUS_USER_ROLE_REQUEST are not linked by any relationships (no FK nor m2m).

Configuration

This configuration focus on the SDD part
<!DOCTYPE root>
<generator-config>
 <configuration>
        <conventions>
            <target-convention type="enable-updatable-code-feature" />
        </conventions>

<!-- other configuration, data-model where sec_role and bus_user_role_request are present... -->

  <model name="porphyry" version="1.0" package-root="net.sf.mp.demo">
   <statement-model>
    <queries>
            <query name="ask_for_role" id="ask_for_role" >
                         <query-body>
                         <value>
<![CDATA[call ask_for_role (?,?,?)]]>
                            </value>
                         </query-body>
                         <query-params>
                             <query-param name="username" is-mandatory="true" type="string" sample="'a'" is-id="true"></query-param>
                             <query-param name="email" is-mandatory="true" type="string" sample="'b'">
                              <stereotype stereotype="EMAIL" />
                             </query-param>
                             <query-param name="role" is-mandatory="true" type="string" sample="'c'">
                                <query-param-link entity-name="sec_role" field-name="role"/>
                             </query-param>
                         </query-params>
                     </query>
                </queries>
            </statement-model>
         </model>
  <targets catalog-entry="OpenXava" >
  </targets>
 </configuration>
</generator-config> 
The main points are:
  • call ask_for_role with 3 parameters
  • description of the parameters (name, type, presence, stereotype)
  • restrict to a set of value coming from a table and field (query-param-link)

Openxava design flow

The input is in format of an Openxava/JPA2 entity to enable binding and link to other entity.
But this entity will never be persisted and never lookup.
The input screen will be accessed directly, and button match the action. After the action is performed a message is display.
To enable this flow OX controllers.xml and application.xml nodes are generated.
The action binding the input data from the form; validating and calling the store procedure call are also generated.

Generated code

Input/Output bean generated


import javax.persistence.*;
import org.openxava.annotations.*;

import net.sf.mp.demo.porphyry.domain.security.Role;

@Entity (name="AskForRoleIn")
@Table (name="ask_for_role")
@Views({
//MP-MANAGED-UPDATABLE-BEGINNING-DISABLE @view-base-ask_for_role@
 @View(
  name="base",
  members=
        ""  
        + "username  ; "
        + "email  ; "
        + "role  ; "
  ),
//MP-MANAGED-UPDATABLE-ENDING
 @View(
  name="Create", 
  extendsView="base"
 ),
 @View(
  name="Update", 
  extendsView="base",
        members=
          ""  
 ),
 @View(extendsView="base",
        members=
          ""  
 ),
    @View(name="askForRoleDEFAULT_VIEW", 
    members=
          " username ;"  
        + "email  ; "
        + "roleTransient  ; "
 ),
//MP-MANAGED-UPDATABLE-BEGINNING-DISABLE @view-reference-ask_for_role@
    @View(name="reference", 
       extendsView="askForRoleDEFAULT_VIEW"
//MP-MANAGED-UPDATABLE-ENDING
    )
})

//MP-MANAGED-ADDED-AREA-BEGINNING @class-annotation@
//MP-MANAGED-ADDED-AREA-ENDING @class-annotation@
public class AskForRoleIn {

     @Id @Column(name="username" ,length=255)
    private String username; 

//MP-MANAGED-ADDED-AREA-BEGINNING @email-field-annotation@
//MP-MANAGED-ADDED-AREA-ENDING @email-field-annotation@

//MP-MANAGED-UPDATABLE-BEGINNING-DISABLE @ATTRIBUTE-email@
    @Column(name="email",  length=255, nullable=false,  unique=false)
    @Required
    @Stereotype ("EMAIL")
    private String email;
//MP-MANAGED-UPDATABLE-ENDING

//MP-MANAGED-ADDED-AREA-BEGINNING @role_TRANSIENT-field-annotation@
//MP-MANAGED-ADDED-AREA-ENDING @role_TRANSIENT-field-annotation@

//MP-MANAGED-UPDATABLE-BEGINNING-DISABLE @ATTRIBUTE-role_TRANSIENT@
 @Transient
 @ReadOnly
    private String roleTransient;
//MP-MANAGED-UPDATABLE-ENDING


//MP-MANAGED-UPDATABLE-BEGINNING-DISABLE @parent-Role-ask_for_role@
    @ManyToOne (fetch=FetchType.LAZY ,optional=false) 
    @JoinColumn(name="role", referencedColumnName = "ID", nullable=false,  unique=false  )
    @ReferenceView ("reference") 
    private Role role;

...
} 
Although not persisted and never looked up AskForRoleIn can be used by Openxava:
  •  to pass by information as a DTO
  • perform validation
  • perform assignment (it is linked to table Role)
  • it contains a transient field roleTransient that will be used by the Openxava action to copy the 'role' field of the 'role' table (not the pk)
  • offers a view with
    • simple input field
    • associated entities
  • @Id is associated to one field (otherwise JPA/Hibernate complains)

Action


/**
 * template reference : 
 * - name      : ActionOX.SDD.query
 * - file name : ActionOX.SDD.query.vm
 * - time      : 2013/08/22 AD at 12:29:41 CEST
*/
package net.sf.mp.demo.porphyry.sdd.action.statement;

//MP-MANAGED-ADDED-AREA-BEGINNING @import@
//MP-MANAGED-ADDED-AREA-ENDING @import@

import org.openxava.jpa.*;
import org.openxava.model.*;
import org.openxava.util.*;
import org.openxava.validators.*;
import org.openxava.actions.*;
import java.util.*;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

import org.hibernate.HibernateException;
import org.hibernate.Session;

import net.sf.mp.demo.porphyry.sdd.out.statement.AskForRoleOutList;
import net.sf.mp.demo.porphyry.sdd.out.statement.AskForRoleOut;
import net.sf.mp.demo.porphyry.sdd.in.statement.AskForRoleIn;

public class AskForRoleAction extends ViewBaseAction {

    public static final String QUERY_NATIVE = "call ask_for_role (?,?,?)";

//MP-MANAGED-UPDATABLE-BEGINNING-DISABLE @SDD_EXECUTE_GET-ask_for_role@
    public AskForRoleOutList execute (AskForRoleIn askForRoleIn) {
        AskForRoleOutList askForRoleOutList = new AskForRoleOutList();
        List list = executeJDBC (askForRoleIn);
        askForRoleOutList.setAskForRoleOuts (list);
        return askForRoleOutList;
    }
//MP-MANAGED-UPDATABLE-ENDING

//MP-MANAGED-UPDATABLE-BEGINNING-DISABLE @SDD_EXECUTE_JDBC-ask_for_role@
 public List<askforroleout> executeJDBC(AskForRoleIn askForRoleIn) {
  if (askForRoleIn==null)
   askForRoleIn = new AskForRoleIn();
  List<askforroleout> list = new ArrayList<askforroleout>();
  PreparedStatement pstmt = null;
  ResultSet rs = null;
  Connection conn = null;
  try {
   conn = getConnection();
   pstmt = conn.prepareStatement(QUERY_NATIVE);
            if (askForRoleIn.getUsername()==null) {
               pstmt.setNull(1, java.sql.Types.VARCHAR);
            } else {
               pstmt.setString(1, askForRoleIn.getUsername()); 
            }
            if (askForRoleIn.getEmail()==null) {
               pstmt.setNull(2, java.sql.Types.VARCHAR);
            } else {
               pstmt.setString(2, askForRoleIn.getEmail()); 
            }
            if (askForRoleIn.getRoleTransient()==null) {
               pstmt.setNull(3, java.sql.Types.VARCHAR);
            } else {
               pstmt.setString(3, askForRoleIn.getRoleTransient()); 
            }
   rs = pstmt.executeQuery();
  } catch (Exception e) {
        e.printStackTrace();
     } finally {
       try {
         rs.close();
         pstmt.close();
         conn.close();
       } catch (Exception e) {
         e.printStackTrace();
       }
     }
  return list;
 }
//MP-MANAGED-UPDATABLE-ENDING

//if JPA2 implementation is hibernate
    @SuppressWarnings("deprecation")   
    public Connection getConnection() throws HibernateException {  
        Session session = getSession();  
        Connection connection = session.connection();  
        return connection;  
    } 
    
    private Session getSession() {  
        Session session = (Session) XPersistence.getManager().getDelegate();  
        return session;  
    }

 public void execute() throws Exception {
//MP-MANAGED-UPDATABLE-BEGINNING-DISABLE @execute-porphyry@
        //super.execute();
        //TODO
        Messages errors = 
            MapFacade.validate("AskForRoleIn", getView().getValues());
        if (errors.contains()) throw new ValidationException(errors);
        AskForRoleIn e = new AskForRoleIn();
 e.setUsername((String)getView().getValue("username"));
 e.setEmail((String)getView().getValue("email"));

 // parent to copy to transient field
        Map roleMap = (Map)getView().getValue("role");
        if (roleMap!=null) {
  e.setRoleTransient ((String)roleMap.get("role"));
        }
  
        try {
            execute(e);
        } catch (Exception ex) {
            errors = new Messages();
            errors.add(ex.getMessage());
            throw new ValidationException(errors);
        }
        //TODO return list
        addInfo("call AskForRoleAction done!");
//MP-MANAGED-UPDATABLE-ENDING

 }

//MP-MANAGED-ADDED-AREA-BEGINNING @implementation@
//MP-MANAGED-ADDED-AREA-ENDING @implementation@

} 
  • perform validation
  • retrieve simple type as well as complex (Role object type)
  • copy values a input of the store proc call
    • here the store proc does not return anything so there is no parsing of the resultset.
Here one can argue that we do not need the transient field 'roleTransient' in AskForRoleIn.
This is true. It is present because it was easier from a generation point of view to keep the parameter order of the stored procedure call.

Controller.xml


<controllers> 
    <!-- statement driven development SDD -->
  <!-- $table.name -->
 <controller name="AskForRoleController">
  <action name="askForRole" mode="detail" class="net.sf.mp.demo.porphyry.sdd.action.statement.AskForRoleAction" >
   <use-object name="xava_view" />
  </action>     
 </controller>
</controllers> 
Provides controller and action

Application.xml


<application name="porphyry"> 

    <!-- statement driven development SDD -->
 <module name="AskForRoleIn" >
     <model name="AskForRoleIn"/>
     <view name="base"/> 
     <controller name="AskForRoleController"/>
     <mode-controller name="DetailOnly"/>
 </module>
 
</application>

Wiring between Model/View/Controller and mode
Detail mode selected (ie no lookup)

Screens

Input screen 

Available at ${yourcontext}/xava/home.jsp?application=porphyry&module=AskForRoleIn
Is also available as a menu entry under statement

 Sub select Use case


 

 

Performing the action

Viewing the result


Since it is store in table USER_ROLE_REQUEST
Minuteproject generates also a CRUD access on this table.
By filtering we check that the input of the store proc+ additional business field are stored correctly.



 

Conclusion

Statement Driven Development - SDD
  • provides tooling for analyst
  • RAD for developer 
    • any sql statement could now be an advance business UC (sub affection, validation)
    • necessary OX gearing is generated.
  • is a pillar of development productivity
Minuteproject is also generating portlet.xml in order to deploy 'Transient-definition portlet' on portal such as Liferay.


Monday, August 19, 2013

Northwind DB revisited with MP 4 OX

Minuteproject (0.8.4) now supports ms-sqlserver.
It has been tested with MS Northwind DB on sqlserver 2014.
Here are the steps to follow to get an OpenXava application from the northwind DB.

Goal:
Get a working Northwind Openxava application in couple of seconds.
For hungry minds please find the resulting code on googlecode.

Northwind DB

Northwind database is a sample DB provided by Microsoft.
It can be found at http://northwinddatabase.codeplex.com/releases/view/71634

The sql provided to create the schema has DB objects (tables/views) containing space.
This sql has been revisited to remove those spaces a version can be found here.

  • Install SQLServer
  • Create an account
  • Run the script.

MinuteProject with SQLSERVER

Minuteproject
  • uses com.microsoft.sqlserver.jdbc.SQLServerDriver jdbc driver
  • associates 'identity' as a primary key strategy by default on the console
  • associates org.hibernate.dialect.SQLServer2008Dialect for hibernate dialect
  • proposes maven artifact-id=sqljdbc4; group-id=com.microsoft.sqlserver; version=4.0 for pom configuration
Furthermore when retrieving the metadata of your model, set 'dbo' of the schema node.

Minuteproject configuration



<!DOCTYPE root>
<generator-config>
 <configuration>
  <model name="nortewind" version="1.0" package-root="net.sf.mp.demo">
   <data-model>
    <driver name="sqlserver" version="4.0" groupId="com.microsoft.sqlserver"
     artifactId="sqljdbc4"></driver>
    <dataSource>
     <driverClassName>com.microsoft.sqlserver.jdbc.SQLServerDriver</driverClassName>
     <url>jdbc:sqlserver://localhost:1433;databaseName=northwind</url>
     <username>sqlserver</username>
     <password>xxxxxxxx</password>
    </dataSource>
    <!-- for Oracle and DB2 please set the schema <schema> </schema> -->
    <schema>dbo</schema>
    <primaryKeyPolicy oneGlobal="true">
     <primaryKeyPolicyPattern name="identityPattern"></primaryKeyPolicyPattern>
    </primaryKeyPolicy>
   </data-model>
   <business-model>
    <business-package default="business">
        <condition type="package" database-object-type="VIEW" result="review"></condition>   
    </business-package>
    <enrichment>
     <conventions>
          <view-primary-key-convention 
            type="apply-default-primary-key-otherwise-first-one" 
            default-primary-key-names="ID" /> 
         <column-naming-convention
       type="apply-fix-primary-key-column-name-when-no-ambiguity-and-not-natural"
       default-value="ID" />
         <entity-naming-convention type="apply-field-alias-based-on-camelcase"/>
         <column-naming-convention type="apply-field-alias-based-on-camelcase"/>

      <reference-naming-convention
       type="apply-referenced-alias-when-no-ambiguity" is-to-plurialize="false" />
      <reference-naming-convention type="apply-many-to-many-aliasing" is-to-plurialize="false"/>
     </conventions>
    </enrichment>
   </business-model>

  </model>
<!-- -->    <targets catalog-entry="OpenXava">
  </targets>  
<!--    <targets catalog-entry="JPA2" >
  </targets>   -->
 </configuration>
</generator-config>


This configuration will allow you to

  • retrieve sqlserver information for the model northwind (do not forget to specify 'dbo' in schema)
  • assign identity as primary key strategy
  • separate table package from view package. (tables go in package business, views in package review)
  • apply convention
    • use camel case for field and entity (table/view)
    • associate a primary key if not present. The primary key would then be attribute to the field ID if present otherwise the first found.
    • have clean name (here not plurialized because northwind already use plural in DB object name) when possible (i.e. there is no variable name collisition - this occurs when you have more than one relationship between two objects).
  • use the track OpenXava from the catalog.

Steps

Copy this configuration to /mywork/config as northwind-OX.xml 
From a command line run model-generation.cmd/sh northwind-OX.xml 
The result will go in /mywork/output/nortewind/OpenXava

Open a prompt in /mywork/output/nortewind/OpenXava
set OX_HOME and MP_HOME (where you install Openxava and Minuteproject)
  • set OX_HOME= path-to-Openxava // export OX_HOME in linux
  • set MP_HOME= path-to-Minuteproject // export MP_HOME in linux

Run build-nortewind.cmd/sh

This should be enough to get a

Enjoy!

Screenshots

You have Openxava CRUD on all tables and selection on views

Menu entries


Select Order

 Region details
List of products (view)

MinuteProject with console

The code can also be generated with Minuteproject console, it is faster since you do not have to write any configuration. Meanwhile it is more limited since not all the convention are available on the console.

click /bin/start-console.sh/cmd
apply the following parameters



Click on generate and the output will go to /output/northwind/Openxava

You can them process with the same steps as with the configuration.


Conclusion

Minuteproject 0.8.4 offers sqlserver generation and simplifies the configuration of the tracks.
Feel free to pick up others by picking them from the drop down list or by changing the attribute catalog-entry of the node targets by one of the following value:

JPA2
JPA2-ABL
BSLA-JPA2
REST-JEE
REST-SpringMVC
REST-CXF-Spring
REST-CXF-JEE
WS-JEE
JOOQ
Primefaces-Spring
Primefaces-JEE
FitNesse
Solr
OpenXava
Grails
Play
Vaadin
Roo
Maven Spring Hibernate
Maven Spring JPA/Hibernate
SpringService

Remark some tracks are under construction.



Friday, August 9, 2013

Reviewing primefaces starter

I have been contacted to review Primefaces starter book, and here are my impressions as a Primefaces user and with a 'minuteproject' perspective.


This book is a starter targeting developers with JSF knowledge, and it is very rewarding. 
It covers Primefaces (PF) core features as well cool ones that make your application look great! such as Calendar, Google map, interactive chat, reporting diagram.
It is based on concrete data model with a github source code ready to clone and package seamlessly. All the installation procedure and tooling is described making your environment setup easy. This starter spurs up you curiosity to dive in to the code while the chapter provides you the substantial insights of PF JSF components. The result is a comprehensive advanced application based on non-trivial model spanning from CRUD, JEE (JPA/CDI) to advanced PF component.
Another aspect that I liked was the density of information: it sticks to the essential, no fuss, no dozens-of-API-page syndrome.


From a minuteproject perspective, and seeing Primefaces as a major actor in the presentation framework, I am always interested to grab extra bits of info on the subject.
I was particularly interested in the cool features (Calendar, Maps) since I think that with SDD (Statement Driven Development ) some of those could be generated. SDD can be already used in PF to produce graphs

So it was quite an enjoyable review.


Thursday, August 8, 2013

Minuteproject facets for analysts

This article resumes the features Minuteproject offers to the analyst to perform a reverse-analysis of the application model.

Core

Minuteproject core functionality is smart reverse-engineering from a database.
  • Reverse-engineering means that you can deduce lots of artifacts based on the database schema structure.
  • Smart means that you can enrich the database elements individually or globally by convention to get the resulting artifacts finer (with added-value) instead of a default bulk solution. Examples:
    •  Your database objects can be:
      • packaged
      • secured
      • aliased (the name of your development object (in java) can follow different convention than those of your DB
Sample
http://minuteproject.blogspot.be/2012/05/rigajug-demo-2-jpa2.html
But Minuteproject is even more.

Views and alternative models

You can work with views conveniently by enriching them with primary key (a unique field will do) and foreign key (pointing to another table or view). This feature enables you to get quickly alternative model with a graph (parent-child relationship) that you can query.
An illustration is when you want to get a report grouping information per country/dept on one side and on the other side you have some report details.

SDD - Statement driven development

Why just reverse-engineer the structure? We can also reverse-engineer the statement (queries, store-proc calls etc...)
Statement-driven-development focuses on I/O: it goes back to the fundamentals and follow the philosophy WYSIWYG (What You State Is What You Get)
If you know how-to write sql, you are not far from having a REST application or any application in the track that support this feature (at the moment of writing Primefaces, ADF).

Sample
http://minuteproject.blogspot.be/2013/08/sdd-as-productivity-weapon-4-openxava.html

Transfer Objects

Sometimes, you need to have object that may not be persisted or completely persisted.
Example you want ad-hoc forms and actions where some info comes from the database some not.
Minuteproject offers you the possibility to describe and generate for those objects.

Sample
http://minuteproject.blogspot.be/2013/08/transient-definition-as-productivity.html