Monday, June 27, 2011

MinuteProject 4 JPA2: Lazuly showcase

Intro
The goal of this article is to have a working JPA2 layer on top of a data model by writting 0 (yes ZERO) lines of code.
Lazuly-db is a opensource database hosted on googlecode that represents the structure of a database holding conference related informations.
Lazuly-db will be used as a showcase of various MinuteProject technology tracks.
In version 0.1 it has 13 tables, 2 views.

This article explains the different steps to quickly have a working JPA2 backend.
It is furthermore a tutorial on how to enrich the database model to get customized artifacts providing specific behaviour.
The purpose is to give to user enough material to customize it and go on extending your model.

MinuteProject short story
The idea behind minuteproject is to
  • go fast;
  • reduce the technology learning curve by knowledge cristalization;
  • improve the quality of the artifact;
  • shorten drastically the time to market of the product.
Minuteproject acts as a Productivity provider targeting multiple aspects of the reverse-engineering for a target architecture (here JPA2).
While most of existing reverse-engineering solution focus on one part of your architecture (Domain Object), MinuteProject can generate artifacts related to:
  • columns;
  • entities (tables,views);
  • stored procedures;
  • package;
  • model;
  • application.
It allows the developer to achieve 'reproductability' that is a great complement of reusability.
Reusability allows to bundle common behavior into a package/framework/specification in order to use them but not to code them.
Reproductability allows to reach the same quality of artifacts (config/classes...) matching a target architecture (reused) although the context changes (DB model).

JPA2 track
JPA2 is a JEE specification for persisting objects in a database.
MinuteProject 4 JPA2 (release 0.5.5) offers a reverse-engineering solution targeting JPA2 artifacts:
  • Java Entity classes for tables and views;
  • JPA2 metamodel;
  • persistence xml;
  • maven pom.xml;
  • vendor specific implementation;
  • querydsl integration.
More information can be found at minuteproject4jpa2

Operating principle
MinuteProject works with a configuration file.
This file holds the information regarding the model and how to enrich it. It also specifies the target bundle of templates for a technology track.

Datasource
Lazuly DB main entities: The schema below resumes the lazuly DB main entities (tables/views) and theirs relationships.


This part summarizes the different configurations used:

Here is the sample xml configuration that is applied to the model
<!DOCTYPE root>
<generator-config>
<configuration>
<model name="conference" version="1.0" package-root="net.sf.mp.demo">
<!-- <model name="lazuly" version="1.0" package-root="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://127.0.0.1:3306/conference</url>
<username>root</username>
<password>mysql</password>
</dataSource>
<!-- for Oracle and DB2 please set the schema <schema> </schema> -->
<primaryKeyPolicy oneGlobal="true">
<primaryKeyPolicyPattern name="autoincrementPattern"></primaryKeyPolicyPattern>
</primaryKeyPolicy>
</data-model>
<business-model>
<!-- <generation-condition> <condition type="exclude" startsWith="DUAL"></condition>
</generation-condition> -->
<business-package default="conference">
<condition type="package" startsWith="STAT" result="statistics"></condition>
<condition type="package" startsWith="COUNTRY" result="admin"></condition>
<condition type="package" startsWith="ROLE" result="admin"></condition>
</business-package>
<enrichment>
<conventions>
<column-naming-convention type="apply-strip-column-name-suffix"
pattern-to-strip="_ID" />
<reference-naming-convention
type="apply-referenced-alias-when-no-ambiguity" is-to-plurialize="true" />
</conventions>

<entity name="PRESENTATION">
<field name="STATUS">
<property tag="checkconstraint" alias="presentation_status">
<property name="PROPOSAL" value="PROPOSAL" />
<property name="ACTIVE" value="ACTIVE" />
</property>
</field>
</entity>
<entity name="SPONSOR">
<field name="STATUS">
<property tag="checkconstraint" alias="sponsor_status">
<property name="PENDING" value="PENDING" />
<property name="ACTIVE" value="ACTIVE" />
</property>
</field>
<field name="PRIVILEGE_TYPE">
<property tag="checkconstraint" alias="sponsor_privilege">
<property name="GOLDEN" value="Golden" />
<property name="SILVER" value="Silver" />
<property name="BRONZE" value="Bronze" />
</property>
</field>
</entity>
<!-- views -->
<entity name="stat_mb_per_ctry_conf" alias="MEMBER_PER_COUNTRY_AND_CONFERENCE">
<virtual-primary-key isRealPrimaryKey="true">
<property name="virtualPrimaryKey" value="ID" />
</virtual-primary-key>
</entity>
<entity name="stat_mb_by_role" alias="MEMBER_PER_ROLE_COUNTRY_AND_CONFERENCE">
<virtual-primary-key isRealPrimaryKey="true">
<property name="virtualPrimaryKey" value="id" />
</virtual-primary-key>
<field name="stat_mb_per_ctry_conf_ID" linkToTargetEntity="stat_mb_per_ctry_conf"
linkToTargetField="id"></field>
</entity>
</enrichment>
</business-model>
</model>
<targets>

<target refname="JPA2" fileName="mp-template-config-JPA2.xml"
outputdir-root="../../dev/output/JPA2" templatedir-root="../../template/framework/jpa">
<property name="add-querydsl" value="2.1.2"></property>
<property name="add-jpa2-implementation" value="hibernate"></property>
</target>

<target refname="LIB" fileName="mp-template-config-bsla-LIB-features.xml"
templatedir-root="../../template/framework/bsla">
</target>

</targets>
</configuration>
</generator-config>
The configuration provides:
  • A model name: conference, version and package root;
  • the driver as maven reference;
  • connection parameters;
  • primarykey policy (autoincrement)
  • business packages: entities starting with stat go to 'statistics'...;role, countries goes in administration package
  • convention: strip-column-name-suffix _ID (by convention in the database, I have column naming ending with '_ID' for foreign key. Here the Java naming convention can defer from the DB naming
  • convention: apply-referenced-alias-when-no-ambiguity indicating that when a table has a one2many relationship with other (i.e. has child), if it has not 2 children with the same other side table (no ambiguity) than the name of the variable is based on the other side table, and plurialized (english) collections
  • Entity enrichment: name alias (can have a distinct Java class name than the one derived from the DB);
  • Field enrichment: provide a check constraint that becomes a Java Enum
  • Entity (views) enrichment by providing them a primary key (create a unique key) by adding foreignkey.
The target part of the configuration specifies against which target technology, MinuteProject has to generate. Here the track is JPA2, where it is specified to use querydsl 2.1.2 and a JPA vendor implementation Hibernate.
Those indications are used to generate pom.xml and persistence.xml files.

Sources and installation
Lazuly project
Svn the source as describe on http://code.google.com/p/lazuly/source/checkout

Mysql
Install mysql
run conference-db.0.1.sql on schema conference

Minuteproject
Download and install minuteproject 0.5.5 or higher. Minuteproject is installed in MP_HOME.

Execution
Normally within couple of seconds (1 to 5) the code should be generated (If DB instance and minuteproject run is on same machine)

Alternate execution
  • Open minuteproject console %MP_HOME%/bin/start-console.(cmd/sh)
  • field the connection parameters
  • add package root and model name
  • click generate
This alternative generation works fine to give a first glance at what you can have as output. A better enrichment is available via the xml config as previously described.

Output
The output can be browsed at http://code.google.com/p/lazuly/source/browse/#svn%2Ftrunk%2Flazuly-jpa2

Here are some samples of what has been generated in terms of

Structure
Here the structure open with as an eclipse-maven project


Java class as JPA2 entity with enumeration, OneToMany, ManyToOne, ManyToMany
You can note that the field 'abstract' in the table 'presentation' is converted to 'abstractName' to avoid compilation error. This is true for all the java reserved keywords. The getter and setter are adapted accordingly.
package net.sf.mp.demo.conference.conference;

import java.sql.*;
import java.util.Date;
import java.util.List;
import java.util.ArrayList;
import java.util.Set;
import java.util.HashSet;

import javax.persistence.*;

import net.sf.mp.demo.conference.conference.Evaluation;
import net.sf.mp.demo.conference.conference.PresentationPlace;
import net.sf.mp.demo.conference.conference.Speaker;
import net.sf.mp.demo.conference.enumeration.conference.PresentationStatusEnum;

/**
*
* <p>Title: Presentation</p>
*
* <p>Description: Domain Object describing a Presentation entity</p>
*
*/
@Entity (name="Presentation")
@Table (name="presentation")
public class Presentation {

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

@Column(name="start_time", nullable=false, unique=false)
private Timestamp startTime;
@Column(name="end_time", nullable=false, unique=false)
private Timestamp endTime;
@Column(name="abstract", length=500, nullable=false, unique=false)
private String abstractName;
@Column(name="title", length=255, nullable=false, unique=false)
private String title;
@Enumerated (EnumType.STRING)
private PresentationStatusEnum status;
@Column(name="proposal_time", nullable=true, unique=false)
private Timestamp proposalTime;

@ManyToOne (fetch=FetchType.LAZY )
@JoinColumn(name="presentation_place_id", referencedColumnName = "id", nullable=true, unique=false )
private PresentationPlace presentationPlaceId;

@OneToMany (targetEntity=net.sf.mp.demo.conference.conference.Evaluation.class, fetch=FetchType.LAZY, mappedBy="presentationId", cascade=CascadeType.REMOVE)//, cascade=CascadeType.ALL)
private Set <Evaluation> evaluations = new HashSet<Evaluation>();

@ManyToMany
@JoinTable(name="SPEAKER_PRESENTATION",
joinColumns=@JoinColumn(name="presentation_id"),
inverseJoinColumns=@JoinColumn(name="speaker_id")
)
private Set <Speaker> speakers = new HashSet <Speaker> ();

/**
* Default constructor
*/
public Presentation() {
}

public Long getId() {
return id;
}

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

public Timestamp getStartTime() {
return startTime;
}

public void setStartTime (Timestamp startTime) {
this.startTime = startTime;
}

public Timestamp getEndTime() {
return endTime;
}

public void setEndTime (Timestamp endTime) {
this.endTime = endTime;
}

public String getAbstractName() {
return abstractName;
}

public void setAbstractName (String abstractName) {
this.abstractName = abstractName;
}

public String getTitle() {
return title;
}

public void setTitle (String title) {
this.title = title;
}

public PresentationStatusEnum getStatus() {
return status;
}

public void setStatus (PresentationStatusEnum status) {
this.status = status;
}

public Timestamp getProposalTime() {
return proposalTime;
}

public void setProposalTime (Timestamp proposalTime) {
this.proposalTime = proposalTime;
}

public PresentationPlace getPresentationPlaceId () {
return presentationPlaceId;
}

public void setPresentationPlaceId (PresentationPlace presentationPlaceId) {
this.presentationPlaceId = presentationPlaceId;
}

public Set<Evaluation> getEvaluations() {
if (evaluations == null){
evaluations = new HashSet<Evaluation>();
}
return evaluations;
}

public void setEvaluations (Set<Evaluation> evaluations) {
this.evaluations = evaluations;
}

public void addEvaluations (Evaluation evaluation) {
getEvaluations().add(evaluation);
}

public Set<Speaker> getSpeakers() {
if (speakers == null){
speakers = new HashSet<Speaker>();
}
return speakers;
}

public void setSpeakers (Set<Speaker> speakers) {
this.speakers = speakers;
}

public void addSpeakers (Speaker speakers) {
getSpeakers().add(speakers);
}
}

Enumeration
You can note the 'equals (string s)' method which is quite handy.
package net.sf.mp.demo.conference.enumeration.conference;

import java.util.ArrayList;
import java.util.List;

public enum SponsorPrivilegeTypeEnum {

GOLDEN("Golden"),
SILVER("Silver"),
BRONZE("Bronze");

private final String value;

SponsorPrivilegeTypeEnum(String v) {
value = v;
}

public String value() {
return value;
}


public static SponsorPrivilegeTypeEnum fromValue(String v) {
for (SponsorPrivilegeTypeEnum c : SponsorPrivilegeTypeEnum.values()) {
if (c.value.equals(v)) {
return c;
}
}
return null;
}


/**
* Return a list that contains all the enumeration values
* @return List<SponsorPrivilegeTypeEnum> the that contains all the enumeration values
*/
public static List<SponsorPrivilegeTypeEnum> getList() {
List<SponsorPrivilegeTypeEnum> list = new ArrayList<SponsorPrivilegeTypeEnum>();
for (SponsorPrivilegeTypeEnum c : SponsorPrivilegeTypeEnum.values()) {
list.add(c);
}
return list;
}

/**
* @param value
* @return boolean : true if the value exist in the enum
*/
public static boolean contains (String value) {
for (SponsorPrivilegeTypeEnum c : SponsorPrivilegeTypeEnum.values()) {
if (c.toString().equals(value))
return true;
}
return false;
}

public static boolean containsValue (String value) {
for (SponsorPrivilegeTypeEnum c : SponsorPrivilegeTypeEnum.values()) {
if (c.value().equals(value))
return true;
}
return false;
}

public boolean equals (String s) {
if (s==null) return false;
return s.equals(value);
}

}
Java class as JPA2 entity for view
You can note the enrichment performed is translated into different Java and DB naming conventions for the entity itself as well as its dependencies.
Now you can use the view for all the JPA2 select operations.
package net.sf.mp.demo.conference.statistics;

import java.sql.*;
import java.util.Date;
import java.util.List;
import java.util.ArrayList;
import java.util.Set;
import java.util.HashSet;

import javax.persistence.*;

import net.sf.mp.demo.conference.statistics.MemberPerRoleCountryAndConference;

/**
*
* <p>Title: MemberPerCountryAndConference</p>
*
* <p>Description: Domain Object describing a MemberPerCountryAndConference entity</p>
*
*/
@Entity (name="MemberPerCountryAndConference")
@Table (name="stat_mb_per_ctry_conf")
public class MemberPerCountryAndConference {

@Id @Column(name="id" ,length=300)
private String id;

@Column(name="country", length=45, nullable=false, unique=false)
private String country;
@Column(name="conference_name", length=255, nullable=false, unique=false)
private String conferenceName;
@Column(name="number", nullable=false, unique=false)
private Long number;

@OneToMany (targetEntity=net.sf.mp.demo.conference.statistics.MemberPerRoleCountryAndConference.class, fetch=FetchType.LAZY, mappedBy="statMbPerCtryConfId", cascade=CascadeType.REMOVE)//, cascade=CascadeType.ALL)
private Set <MemberPerRoleCountryAndConference> memberPerRoleCountryAndConferences = new HashSet<MemberPerRoleCountryAndConference>();

/**
* Default constructor
*/
public MemberPerCountryAndConference() {
}

public String getId() {
return id;
}

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

public String getCountry() {
return country;
}

public void setCountry (String country) {
this.country = country;
}

public String getConferenceName() {
return conferenceName;
}

public void setConferenceName (String conferenceName) {
this.conferenceName = conferenceName;
}

public Long getNumber() {
return number;
}

public void setNumber (Long number) {
this.number = number;
}

public Set<MemberPerRoleCountryAndConference> getMemberPerRoleCountryAndConferences() {
if (memberPerRoleCountryAndConferences == null){
memberPerRoleCountryAndConferences = new HashSet<MemberPerRoleCountryAndConference>();
}
return memberPerRoleCountryAndConferences;
}

public void setMemberPerRoleCountryAndConferences (Set<MemberPerRoleCountryAndConference> memberPerRoleCountryAndConferences) {
this.memberPerRoleCountryAndConferences = memberPerRoleCountryAndConferences;
}

public void addMemberPerRoleCountryAndConferences (MemberPerRoleCountryAndConference memberPerRoleCountryAndConference) {
getMemberPerRoleCountryAndConferences().add(memberPerRoleCountryAndConference);
}

}

Run
Since it generate a pom.xml, download maven and run 'mvn install'
It generates the lazuly backend jar containing the entities, the metamodel and the querydsl classes.

MinuteProject 4 Openxava: Lazuly showcase

Intro
The goal of this article is to have a working Openxava on top of a data model by writting 0 (yes ZERO) lines of code. This application has to run as a standalone webapp or as portlets in a portal.
The model used is the Lazuly-db which is a opensource database hosted on googlecode that represents the structure of a database holding conference related informations.
Lazuly-db will be used as a showcase of various MinuteProject technology tracks.
In version 0.1 it has 13 tables, 2 views.

This article explains the different steps to quickly have a create an Openxava application.
It is furthermore a tutorial on how to enrich the database model to get customized Openxava artifacts providing specific behaviour.
The purpose is to give to user enough material to customize it and go on extending your model.

If you want to see the result quickly, I encourage you to see the videos (downwards on this page).

OpenXava
Is an opensource product that makes CRUD RIA modal application in a very quick way.
It enforces the DRY principle with a comprehensive set of annotations.
Openxava is a generic framework that dynamically generates the presentation layer based on custom annotation.

MinuteProject 4 Openxava offers a reverse-engineering solution targeting OX architecture.
Here are the set of artifacts MinuteProject will generate:
  • JPA2 and OpenXava annotated entities;
  • associated metamodel;
  • drop down list artifacts for enumeration;
  • Navigation menu;
  • build script (windows and unix);
  • tomcat datasouce configuration snippet and context.xml;
  • internationalisation.

Operating principle
MinuteProject works with a configuration file.
This file holds the information regarding the model and how to enrich it. It also specifies the target bundle of templates for a technology track (here Openxava).

Datasource
Lazuly DB main entities: The schema below resumes the lazuly DB main entities (tables/views) and theirs relationships.


Configuration
This part summarizes the different configurations used:

<!DOCTYPE root>
<generator-config>
<configuration>
<model name="conference" 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://127.0.0.1:3306/conference</url>
<username></username>
<password></password>
</dataSource>
<!--
for Oracle and DB2 please set the schema <schema> </schema>
-->
<primaryKeyPolicy oneGlobal="true">
<primaryKeyPolicyPattern name="autoincrementPattern"></primaryKeyPolicyPattern>
</primaryKeyPolicy>
</data-model>
<business-model>
<!--
<generation-condition> <condition type="exclude"
startsWith="DUAL"></condition> </generation-condition>
-->
<business-package default="conference">
<condition type="package" startsWith="STAT" result="statistics"></condition>
<condition type="package" startsWith="COUNTRY" result="admin"></condition>
<condition type="package" startsWith="ROLE" result="admin"></condition>
</business-package>
<enrichment>
<conventions>
<column-naming-convention type="apply-strip-column-name-suffix"
pattern-to-strip="_ID" />
<reference-naming-convention
type="apply-referenced-alias-when-no-ambiguity" is-to-plurialize="true" />
</conventions>

<entity name="COUNTRY" content-type="reference-data">
<semantic-reference>
<sql-path path="NAME" />
</semantic-reference>
</entity>
<entity name="CONFERENCE_MEMBER">
<semantic-reference>
<sql-path path="FIRST_NAME" />
<sql-path path="LAST_NAME" />
</semantic-reference>
<field name="STATUS">
<property tag="checkconstraint" alias="conference_member_status">
<property name="PENDING" value="PENDING" />
<property name="ACTIVE" value="ACTIVE" />
</property>
</field>
<field name="EMAIL">
<stereotype stereotype="EMAIL" />
</field>
</entity>
<entity name="SPEAKER">
<field name="BIO">
<stereotype stereotype="HTML_TEXT" />
</field>
<field name="PHOTO">
<stereotype stereotype="PHOTO" />
</field>
<field name="WEB_SITE_URL">
<stereotype stereotype="WEBURL" />
</field>
</entity>
<entity name="PRESENTATION">
<field name="STATUS">
<property tag="checkconstraint" alias="presentation_status">
<property name="PROPOSAL" value="PROPOSAL" />
<property name="ACTIVE" value="ACTIVE" />
</property>
</field>
</entity>
<entity name="SPONSOR">
<field name="STATUS">
<property tag="checkconstraint" alias="sponsor_status">
<property name="PENDING" value="PENDING" />
<property name="ACTIVE" value="ACTIVE" />
</property>
</field>
<field name="PRIVILEGE_TYPE">
<property tag="checkconstraint" alias="sponsor_privilege">
<property name="GOLDEN" value="Golden" />
<property name="SILVER" value="Silver" />
<property name="BRONZE" value="Bronze" />
</property>
</field>
</entity>
<!-- views -->
<entity name="stat_mb_per_ctry_conf" alias="MEMBER_PER_COUNTRY_AND_CONFERENCE">
<virtual-primary-key isRealPrimaryKey="true">
<property name="virtualPrimaryKey" value="ID" />
</virtual-primary-key>
</entity>
<entity name="stat_mb_by_role" alias="MEMBER_PER_ROLE_COUNTRY_AND_CONFERENCE">
<virtual-primary-key isRealPrimaryKey="true">
<property name="virtualPrimaryKey" value="id" />
</virtual-primary-key>
<field name="stat_mb_per_ctry_conf_ID" linkToTargetEntity="stat_mb_per_ctry_conf"
linkToTargetField="id"></field>
</entity>
</enrichment>
</business-model>
</model>
<targets>
<!-- openxava -->
<target refname="OpenXava" name="OpenXava"
fileName="mp-template-config-openxava-last-features.xml"
outputdir-root="../../DEV/output/openxava/conference"
templatedir-root="../../template/framework/openxava">
</target>

<target refname="JPA2-LIB" fileName="mp-template-config-JPA2-LIB.xml"
templatedir-root="../../template/framework/jpa">
</target>

<target refname="BSLA-LIB" fileName="mp-template-config-bsla-LIB-features.xml"
templatedir-root="../../template/framework/bsla">
</target>

</targets>
</configuration>
</generator-config>

Errata

Minuteproject and OpenXava are in constant evolution and at some stage there might be some temporary fix to apply in the configuration

This version works for MP version 0.5.x
For version above 0.6 add
   <target refname="CACHE-LIB" 
      fileName="mp-template-config-CACHE-LIB.xml" 
      templatedir-root="../../template/framework/cache">
   </target>
 
On MP 0.8.2 the semantic reference node on 'CONFERENCE_MEMBER' entity is to comment.




Configuration explained
If you have read the minuteproject 4 JPA2 lazuly showcase article on this blog, you would notice that the configuration is similar to the one for JPA2 track. It is the case for everything regarding the model location (same model) and the enrichment, meanwhile it defers regarding the target, since it uses OpenXava.

The configuration provides:
  • A model name: conference, version and package root;
  • the driver as maven reference;
  • connection parameters;
  • primarykey policy (autoincrement)
  • business packages: entities starting with stat go to 'statistics'...;role, countries goes in administration package. Those will be converted into distinct menu tabs.
  • convention: strip-column-name-suffix _ID (by convention in the database, I have column naming ending with '_ID' for foreign key. Here the Java naming convention can defer from the DB naming
  • convention: apply-referenced-alias-when-no-ambiguity indicating that when a table has a one2many relationship with other (i.e. has child), if it has not 2 children with the same other side table (no ambiguity) than the name of the variable is based on the other side table, and plurialized (english) collections
  • Entity enrichment: name alias (can have a distinct Java class name than the one derived from the DB);
  • Field enrichment: provide a check constraint that becomes an Openxava drop down list
  • Entity (views) enrichment by providing them a primary key (create a unique key) by adding foreignkey. In this way we can have an alternative navigation model on top of some BI views.
  • Tag field content-type="reference-data" apply to ROLE and COUNTRY entry will trigger the following navigation behavior: you cannot navigation from a role entry to children CONFERENCE_MEMBER in the application, but form CONFERENCE_MEMBER you can associate roles.
  • stereotype="EMAIL" will be converted in its associated email stereotype on Openxava triggering email format validation, same for WEB_SITE_URL
  • stereotype="HTML_TEXT" applied to SPEAKER.BIO field will be converted in its associated html_text stereotype on Openxava offering a Rich text format facility
  • stereotype="PHOTO" applied to SPEAKER.PHOTO field will be converted in file upload facility by Openxava
Additional information on how to enrich the model can be found at
Sources and installation
Lazuly project
Svn the source as describe on http://code.google.com/p/lazuly/source/checkout

Mysql
Install mysql
run conference-db.0.1.sql on schema conference

Minuteproject
Download and install minuteproject 0.5.5. Minuteproject is installed in MP_HOME.

Openxava
Download and install openxava 4.1.2. Openxava is installed in OX_HOME.
Remark
This demo is based on OX 4.1.2, if you are using a more recent version there may not be foreward compatibility. Release (MP 0.5.6 will be compliant with OX 4.2.1). In fact there is one artifact generated home.jsp that is based on OX module.jsp. If this artifact changes between OX release, there is potentially an error.

Generation
The resulting generated code goes to %MP_HOME%/DEV/output/openxava/conference

Structure of the Openxava project
Here the structure open with as an eclipse project in the openxava workspace


Execution
  • check mysql.jar is in tomcat/lib
  • check that the system variables: ox_home, mp_home are set to Openxava and MinuteProject installation directories.
Open a prompt on /DEV/output/openxava/conference, and execute build-conference.cmd.
It will
  • Create an Openxava project
  • Build openxava web app
  • Build openxava portlets (to be deployed on a portal such as Liferay)
  • Copy context.xml (take care that it overwrite the existing one)
  • Start tomcat
  • Deploy the application
  • Start you default browser to the Conference Home menu.
The enjoy the navigation.

Videos

Video starting from scratch to deployed application




Video of the resulting application