Thursday, June 18, 2015

Openxava 5.3 - minuteproject 0.9.3 - ehcache quick fix

Minuteproject 0.9.4- is not compatible with Openxava 5.3 regarding hibernate - ehcache integration.

Openxava hibernate dependency has been upgraded.
Hibernate now requires the property hibernate.cache.region.factory_class instead of hibernate.cache.provider_class
In the case of ehcache cache provider then the value is org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory

This situation prevents OX generated App to start.

Patch 1

Download Minuteproject 0.9.5 to disable minuteproject 4 openxava ehcache wiring.
Add this property

< property name="openxava-disable-sdd" value="true" />


Patch 2

So the fix to apply in the generated code is to patch the generated persistence.xml with

< property name="hibernate.cache.region.factory_class" 
value="org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory" />


Meanwhile it is not enough since now hibernate core do not bundle the cache package.
The package is now in the archive hibernate-ehcache.xxx.jar

Alternative


Disable the openxava ehcache hibernate config by removing the hibernate properties related to cache in persistence.xml

Follow-up

In next release minuteproject will come with the property to disable openxava-hibernate-cache.


Thursday, July 17, 2014

review Building an App UI with PrimeFaces

A packt video on how-to develop Primefaces apps has been released.

I had the opportunity to make a little review. Here are my main points.

It is quite an interesting set of video, that guide you step by step through the build of a Primefaces over MongoDB web app, spring gluing the component for smooth integration.
It is worth following, since you can quickly cover and being introduce to couple of market-trendy technologies.
The videos are developers oriented, via the use of NetBeans to fasten your primefaces component development. JSF beginner to intermediate definitely should have a lot at them.


Thursday, July 3, 2014

Packt's 10-year anniversary

Join in the celebrations of Packt's 10-year anniversary! All eBooks & videos are available for just $10! Visit http://bit.ly/1q8vtgN


Tuesday, May 6, 2014

Packt celebrates International Day Against DRM, May 6th 2014






According to the definition of DRM on Wikipedia, Digital Rights Management (DRM) is a class of technologies that are used by hardware manufacturers, publishers, copyright holders, and individuals with the intent to control the use of digital content and devices after sale.

However, Packt Publishing firmly believes that you should be able to read and interact with your content when you want, where you want, and how you want – to that end they have been advocates of DRM-free content since their very first eBook was published back in 2004.

To show their continuing support for Day Against DRM, Packt Publishing is offering all its DRM-free content at $10 for 24 hours only on May 6th – that’s all 2000+ eBooks and Videos. Check it out at: http://bit.ly/1q6bpha.



Tuesday, April 22, 2014

Primefaces extension in minuteproject

Primefaces extension well-detailed in packt book offer to primefaces users interesting add-ons over the primefaces framework :

  • reporting facilities
  • smart input components
  • layout and screen blocking components
  • time-tracking and scheduling components
It complements seamlessly the primefaces ecosystem - the integration with maven can be done alongside primefaces dependency.

Primefaces extension book guides you thru a those component providing both API explanations (JSF and beans) as well as source code examples.

Regarding minuteproject 4 primefaces, integration of reporting elements and validation is a feature to add in next releases.

Thursday, March 20, 2014

packt 2000 titles

Packt promo!


Celebrate 2000 Titles with Packt with the BUY ONE, GET ONE FREE offer! 

Check it out at http://bit.ly/1j26nPN! #Packt2k



Monday, January 6, 2014

how-to cloudbees minuteproject 4 openxava

Minuteproject releases a demo Openxava application to be hosted on cloudbees at http://petshopox.minuteproject.cloudbees.net/

Following those steps, you can recreate an Openxava application with minuteproject and host it on cloudbees.
All those steps are IDE-free (i.e. productivity oriented) all is done by command line!
This is a requirement for automation, since it is very easy to integrate with a CI tool such as Jenkins.
Meanwhile the code is compatible with Eclipse (for java), Mysql Workbench (for DB design + release) and Cloudbees website for manual release.

Principles

Generate an openxava application with minuteproject base on an DB structure.
The database use here is petshop; for the generation the database can be local or already hosted on cloudbees PaaS. At running the database is the one of the cloudbees PaaS.

The generated Openxava compliant artifacts are built into an Openxava application.
The application is to be deployed on a tomcat server.
The application relies on a JNDI Datasource on the tomcat server.

After setting the database on cloudbees and the connection pool cloudbees tomcat server, the application can be deployed.

Ingredients

Infrastructure

JDK 1.6+
Minuteproject last version
Openxava 4.9
Mysql DB
Tomcat
Cloudbees

  • sdk
  • account

Set up Cloudbees

Database

Create a mysql db schema on cloudbees
bees db:create petshopDB
   
Retrieve the DB info (connection info: server, port, user...) all you need to create you connection pool

bees app:info -db petshopDB 


Application

Create application

bees app:create -petshopox
     

Datasource

This is use get the alias use in the persistence.xml datasource (java:comp/env/jdbc/petshopDS)

bees app:bind -db petshopDB -a petshopox -as petshopDS 


Database setup

Run you database script from the information receive via $bees db:info -db petshopDB

Sample
mysql --host=ec2-50-19-213-178.compute-1.amazonaws.com --user=xxxx --password=xxxx --port=3306 xxx < petshop.sql
petshop-mysql.sql can be found in MP_HOME/sample/schema/

Generation

Full details at http://minuteproject.wikispaces.com/OpenXava
The generation is done by Minuteproject.
Minuteproject generation by command line comes by passing a configuration file name to model-generation.

Sample
model-generation petshop.xml

Sample petshop.xml configuration
<!DOCTYPE root>
<generator-config xmlns="http://minuteproject.sf.net/xsd/mp-config"
	xmlns:xs="http://www.w3.org/2001/XMLSchema-instance"
	xs:noNamespaceSchemaLocation="mp-config.xsd">
	<!-- adapted for cloudbees petshop model -->
	<configuration>
		<conventions>
			<target-convention type="enable-updatable-code-feature" />
		</conventions>
		<model name="petshop" version="1.0" package-root="net.sf.mp.demo">
			<data-model>
				<driver name="mysql" version="5.1.16" groupId="mysql"
					artifactId="mysql-connector-java"></driver>
				<dataSource>
					<driverClassName>org.gjt.mm.mysql.Driver</driverClassName>
					<url>jdbc:mysql://localhost:3306/petshop</url>
					<username>root</username>
					<password>mysql</password>
				</dataSource>
				<primaryKeyPolicy oneGlobal="false">
					<primaryKeyPolicyPattern name="autoincrementPattern"></primaryKeyPolicyPattern>
				</primaryKeyPolicy>
			</data-model>
			<business-model>
				<generation-condition>
					<condition type="exclude" startsWith="DUAL"></condition>
					<condition type="exclude" startsWith="ID_GEN"></condition>
					<condition type="exclude" startsWith="SEQUENCE"></condition>
				</generation-condition>
				<business-package default="pet">
					<condition type="package" startsWith="PRODUCT" result="product"></condition>
					<condition type="package" startsWith="ITEM" result="product"></condition>
				</business-package>
				<enrichment>
					<conventions>
						<entity-naming-convention type="apply-strip-table-name-prefix"
							pattern-to-strip="SYS,FIN" />
						<reference-naming-convention
							type="apply-referenced-alias-when-no-ambiguity" is-to-plurialize="true"></reference-naming-convention>
					</conventions>
					<package name="product">
						<entity-group entities="PRODUCT"></entity-group>
						<entity-group entities="ITEM"></entity-group>
					</package>
					<entity name="PRODUCT" alias="MY_GOOD_PRODUCT">
					</entity>
					<entity name="ITEM" alias="MY_GOOD_ITEM" comment="my item table">
						<field name="PRODUCTID" alias="THIS_IS_MY_PRODUCT" comment="my product field reference"></field>
					</entity>
					<entity name="CATEGORY" content-type="reference-data">
						<field name="DESCRIPTION" ordering="asc" label="my description"
							is-searchable="true"></field>
						<field name="NAME" ordering="asc"></field>
					</entity>
					<entity name="TAG" content-type="reference-data">
						<field name="TAG" ordering="asc">
							<semantic-reference>
								<sql-path path="NAME" />
							</semantic-reference>
						</field>
					</entity>
				</enrichment>
			</business-model>
			<statement-model>
				<enrichment>
					<conventions>
						<sdd-dummy-primarykey-convention add="true" />
					</conventions>
				</enrichment>
				<queries>
					<query name="get address abstract" id="dashAddress" type="dashboard"
						category="pie-chart">
						<query-body> <!-- dimensions column first -->
							<value>
<![CDATA[select city, count(*) as nb from address group by city order by count(*) desc limit ?]]>
                            </value>
						</query-body>
						<query-params>
							<query-param name="top city" is-mandatory="false"
								type="INT" sample="37" default="10"></query-param>
						</query-params>
					</query>
					<query name="get address summary" id="dashCity" type="dashboard"
						category="bar-chart">
						<query-body> <!-- dimensions column first -->
							<value>
<![CDATA[select city, count(*) as nb, count(*) as nb2 from address group by city order by count(*) desc]]>
                            </value>
						</query-body>
					</query>
					<query name="get addresses by criteria" id="c">
						<query-body>
							<value>
<![CDATA[select * from address where lcase(city) like ?]]>
                            </value>
						</query-body>
						<query-params>
							<query-param name="city" type="STRING" sample="'S'"
								convert="lowercase,append%" default="%">
							</query-param>
						</query-params>
					</query>
				</queries>
			</statement-model>
		</model>
		<targets catalog-entry="OpenXava" >
			<property name="environment" value="remote"></property>
			<property name="cloud-platform" value="cloudbees"></property>
			<property name="embed-driver" value="true"></property>
		</targets>
	</configuration>
</generator-config>

Appart from the 'classical' enrichment facilities, the interesting point are the properties under the targets node:
name="environment" value="remote" 
Implies to use a connection pool (for persistence.xml)
name="cloud-platform" value="cloudbees" 
Performs additional build facilities for Openxava on cloudbees
The OX war has to hold jta.jar, ejb.jar, mysql driver jar.
  • jta.jar and ejb.jar are embedded in tomcat/lib on the OX delivery.
name="embed-driver" value="true" 

  • mysql driver jar is shipped on MP delivery

  • Remark: the last 2 options are available on Minuteproject 0.8.6

    Build

    On the generated directory
    set/export OX_HOME
    set/export MP_HOME
    Run

    build-petshop.cmd/sh

    The application petshop.war goes into OX_HOME/workspace/petshop.dist

    The application is ready to be deployed on Cloudbees.

    Deploy

    bees app:deploy -a petshopox -t tomcat7 -Rjava_version=1.7 petshop.war

    petshopox is the name of the application on cloudbees

    Enjoy

    Monday, November 18, 2013

    Minuteproject with Ant HOW-TO

    This is a sample ant integration

    This page is a HOW-TO integrate Minuteproject with Ant build tool.

    Set up

     

    Minuteproject distribution

    Download Minuteproject zip and install in MP_HOME directory.

    Environment variable

    $set MP_HOME=mp-home.directory

    Minuteproject config

    Have you minuteproject configuration in MP_HOME/mywork/config or MP_HOME/demo/config.
    Remark:
     MP_HOME/demo/config is shipped with many configuration working with HSQLDB database located on the file system. 
    For this article we will use mp-config-JPA2.xml


    Ant Script

    Sample ant script to place in MP_HOME/mywork/config or MP_HOME/demo/config.
    The script has an ant macro that calls java command on Minuteproject kernel main class passing the minuteproject config.


    <?xml version="1.0" encoding="UTF-8"?>
    <project default="generation-sample" basedir="." name="MinuteProject - ANT integration">
     <description>This is a sample ant integration</description>
    
     <!-- ===================================================================== -->
     <!-- properties definitions -->
     <!-- ===================================================================== -->
    
     <property environment="env"/>
     <property name="minuteproject.home" value="${env.MP_HOME}" />
    
     <!-- ===================================================================== -->
     <!-- classpath definitions -->
     <!-- ===================================================================== -->
    
     <!-- 
        this is used to get the minuteproject release, dependencies and config    
     -->
     
     <path id="mp.library.path">
      <pathelement location="${env.MP_HOME}/demo/config" />
      <fileset dir="${env.MP_HOME}/application">
       <include name="**/*.jar" />
      </fileset>
     </path>
     
     <!-- ===================================================================== -->
     <!-- macro definitions -->
     <!-- ===================================================================== -->
     
     <macrodef name="macro_generate">
      <attribute name="config" />
      <sequential>
       <echo message="Generate for config @{config}" />
       <java fork="true" classname="net.sf.minuteProject.application.ModelViewGenerator">
        <classpath>
         <path refid="mp.library.path" />
        </classpath>
        <arg line="@{config}" />
       </java>
      </sequential>
     </macrodef>
    
     <!-- ===================================================================== -->
     <!-- ant generation tasks definitions -->
     <!-- ===================================================================== -->
     
     <target name="generation-sample">
      <!-- 
          check that your configuration file mp-config-xxx  are place in 
          ${env.MP_HOME}/demo/config
       
       The ouput of the generation with go to the outputdir specified in the configuration
       If none it goes in ${env.MP_HOME}/demo/output
       
       Remark:
        this sample works with Minuteproject distribution.
        The configuration relies on a hsqldb database located on the file system.
        For advanced test check that the database is up and running at the url specified 
        in the url node of the configuration
      -->
      
            <macro_generate config="mp-config-JPA2.xml" />
      
     </target>
    
    </project> 

    The result will go to MP_HOME/demo/output


    Wednesday, October 16, 2013

    Packt Columbus action days


    For hungry minds that look to satisfy their IT knowledge, there are Packt Columbus Days  

    The exclusive 50% discount code COL50 will be active on all eBooks and Videos until Thursday October 17th

    Couple of books that I recommend

    Thursday, October 3, 2013

    minuteproject 4 propel motivations

    Propel is based on some forward engineering technics that creates your Php Object Domain and ORM classes for your based on a file describing your model.

    Propel ORM also offers reverse-engineering facilities to generate this input file.
    Minuteproject meanwhile creates a track for propel. Why? What are the motivations?

    Minuteproject 4 propel track will be delivered in version 0.8.5.

    Polyglot generator


    Minuteproject was essentially generating java framework oriented artifact but the php tracks with propel and soon doctrine offers another perspective.
    Whatever your language and your framework, minuteproject can offer you reverse-engineering facilities.


    Get Php knowledge

    Testing with php was quite easy and interesting.

    Propel specifics

    As mentionned previously Propel offers reverse-engineering solution.

    Interesting points

    Their solutions is interesting because for each database Propel support the tool issue database query-specific-data-dictionnary lookup. It is the same philosophy that JOOQ framework with JOOQ meta is following.

    Minuteproject 4 propel advantages

    Propel reverse-engineering solution propose raw reverse-engineering, while minuteproject offers you the possibility to make a reverse-analysis before.

    Enrichment

    All the enrichment facilities are available for Propel track. Which means that you can have smart reverse-engineering aspects:
    • Alias for tables (DB table naming convention can differ from you Php classes conventions)
      • In schema.xml 'name' is filled by DB name and phpName by your given name.
    • Alias for columns same principle as above
    • Apply alias (column or view) globally by conventions or individually
      • Ex tables starting with T_ such as T_USER can get User in php.
      • Ex field ending with FK (ex PRODUCT_FK) have the variable $product.
    • Granularity select only tables or views you need
      • No need to reverse-engineer all the entities

    Multiple artifacts

    Minuteproject 4 propel generate other artifacts such as
    • runtime-conf.xml
    • build.properties
    • command line scripts
    • setup.php

    Updatable code

    Generator tools are often see as generate once and no more...
    It is not the case of minuteproject!

    The code that you generate is updatable! 
    If you do not like it change it! 
    If it misses something add it!

    Next time you generate YOUR ALTERATIONS WILL BE KEPT!


    This is call updatable code feature. And all the minuteproject 4 propel templates have this feature!


    Which means that if you do not like what  you have in the schema.xml node you can change them. If your model changes add couples of db object (table/view), change cardinality between objects, the next time you generate, new object and relationship will be added BUT your modification (ex table phpName) will be kept!

    Friday, September 20, 2013

    nested SDD

    SDD (Statement driven development) is a powerfull productivity weapon.

    Until now minuteproject SDD configuration was limited to a single statement.
    This is enough when you want to query to display a list or graph or even call a store proc.

    But often your business need is make of nested action. The output of some query serves as the input for other actions.
    This is this capacity to handle multiple successive actions based on statement that is called nested SDD.

    Something concrete


    Imagine the following scenario
    I have DB connections that are stucked and I want to kill them. How I want to do that is via a web application.
    And I want to have this application without writing a single LOC and within 1 minute...
    Here is the kind of productivity challenge Minuteproject is bound to solve with nested SDD.

    Decomposition of the scenario

    Infrastructure

    Oracle DB.

    Queries

    I have DB connections that are stucked and I want to kill them.
    http://appsdbanew.wordpress.com/2007/11/05/how-to-find-blocking-session-and-kill-the-session-from-database/
    It can be reduced in writing 2 queries:

    Retrieve the connections (and stucked ones)
    select process,sid, serial#, blocking_session from v$session where blocking_session is not null
    

    Kill collections
    alter system kill session '#SID,#SERIAL' immediate

    Nesting

    The output of the first query (sid and serial#) serves as input of the second.


    Minuteproject

    Those two queries show be enough to get a application in Primefaces for quite handy for the help desked.



    Thursday, September 5, 2013

    piping generators with minuteproject

    There are couple of really interesting generators working based on some input file and execution principles.
    It is easy to understand and tooling-oriented. Input 2 Tool gives Result Code.

    Minuteproject principle is the same

    When working with generators dealing with RDBMS persistence (Rev-Eng or Fwd-Eng), minuteproject can generate initial input for those generators.

    This strategy is called generator piping.

    Each generator has its own particularities and kind of DSL.
    The goal is then to degrade as less as possible the 'other-generator' input in order to keep the added value as high as possible.


    Minuteproject is particularily interested by the following OS generators:
    • Liferay Service Builder
    • JOOQ
    • Benerator
    • Roo (input script instructions)
    • RoR (input script instructions)
    • Propel

    Tooling to deal with other generators available in minuteproject
    • Plugin
      • write a mp plugin (java bean match target technology specifics)
      • this plugin is available at template level to query target-generator-centric info.
    • Parametrization
      • offer track customization
      • a classical parameter could be the version of the target generator
    • Updatable code feature
      • tune your output without losing your altertions over consecutive generations
    • Enrichment
      • Conventions
        • Some configuration can be tedious because quite verbose, fortunately minuteproject can pin common caracteristics via conventions
      • Aliasing
        • provides indirection level to have DB names look like DB names and Java name matching Java conventions
    The main difficulty is to know what is the input for field type (is it an SQL, UML, Java, own DSL type) that has to be treated at plugin level.

    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.