Showing posts with label rest. Show all posts
Showing posts with label rest. Show all posts

Wednesday, May 30, 2012

RigaJUG demo - REST - SDD

On the model
You want to extract some info via sql such as

select k.key_name, t.translation, t.date_finalization, l.code, l.description, tl.first_name, tl.last_name, tl.email 
from translation t, language l, user tl, translation_key k
where
t.language_id = l.idlanguage
and tl.idUser = t.translator_id
and k.id = t.key_id
order by key_name

And you want to parametarize the extraction by passing some filtering input
select k.key_name, t.translation, t.date_finalization, l.code, l.description, tl.first_name, tl.last_name, tl.email 
from translation t, language l, user tl, translation_key k
where
t.language_id = l.idlanguage
and tl.idUser = t.translator_id
and k.id = t.key_id
and k.key_name like ?
and l.code like ?
order by key_name
limit ?

And you want to have a REST access!
And you want it NOW!

Good news SDD Statement Driven Development is there for you!

From the above statement you can extract 3 info:
  • an input (in java a DTO bean) with keyName, code, limit as params
  • an output  (in java a DTO bean) with keyName, translation, dateFinalization, code, description, firstName, lastName, email
  • a functionality to name
This is enough to get a REST-CXF application instantly!
This page will show you how to reach it!

Intro

This page correspond to a demo to belonging to a more global presentation.
To be able to demostrate it there are couple of prerequisits:
  • download minuteproject 0.8.1+
  • install maven
  • install tomcat
  • install mysql and create the schema (see demo JPA2)
This page will show you how to:
  • configure
  • generate
  • build
  • deploy
  • test

Configuration

Minuteproject works with a configuration file that indicates:

  • where is the model
  • how to enrich it
    • enable Statement Driven Development declartion
  • against which technologies to generate (targets)

Here is the configuration TRANXY-JPA2-Spring-REST-CXF-SDD.xml
<!DOCTYPE root>
<generator-config xmlns="http://minuteproject.sf.net/xsd/mp-config" 
xmlns:xs="http://www.w3.org/2001/XMLSchema-instance" 
xs:noNamespaceSchemaLocation="../config/mp-config.xsd">
 <configuration>
  <conventions>
   <target-convention type="enable-updatable-code-feature" />
  </conventions>
  <model name="tranxy" 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/tranxy</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="QUARTZ"></condition>
    </generation-condition>
    <business-package default="tranxy">
     <condition type="package" startsWith="trans" result="translation"></condition>
    </business-package>
    <enrichment>
     <conventions>
      <!-- manipulate the structure and entities BEFORE manipulating the 
       entities -->
      <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="language_x_translator">
              <field name="language_id" linkReferenceAlias="translating_language" />
              <field name="user_id" linkReferenceAlias="translator" />
          </entity>
          <entity name="LANGUAGE_X_SPEAKER">
              <field name="LANGUAGE_ID" linkToTargetEntity="LANGUAGE"
                  linkToTargetField="IDLANGUAGE" linkReferenceAlias="spoken_language" />
              <field name="user_id" linkReferenceAlias="speaker" />
          </entity>
          <entity name="APPLICATION">
              <field name="TYPE">
                  <property tag="checkconstraint" alias="application_type">
                      <property name="OPENSOURCE"/>
                      <property name="COPYRIGHT" />
                  </property>
              </field>
          </entity>
    </enrichment>
   </business-model>
      <statement-model>
         <queries>
             <query name="get translation info">
                 <query-body><value>
<![CDATA[select k.key_name, t.translation, t.date_finalization, l.code, l.description, tl.first_name, tl.last_name, tl.email from translation t, language l, user tl, translation_key k where t.language_id = l.idlanguage and tl.idUser = t.translator_id and k.id = t.key_id and k.key_name like ? and l.code like ? order by key_name limit ?]]>
                    </value></query-body>
                 <query-params>
                     <query-param name="key" is-mandatory="false" type="STRING" sample="'test'"></query-param>
                     <query-param name="code" is-mandatory="false" type="STRING" sample="'FR'"></query-param>
                     <query-param name="max" is-mandatory="false" type="INT" sample="10"></query-param>
                 </query-params>
             </query>
          </queries>
      </statement-model>
  </model>
  <targets>
            
      <target refname="REST-CXF-BSLA" 
         name="default" 
         fileName="mp-template-config-REST-CXF-Spring.xml" 
         outputdir-root="../../DEV/latvianjug/tranxy/rest"
         templatedir-root="../../template/framework/cxf">
      </target>

      <target refname="BackendOnBsla" 
         name="default" 
         fileName="mp-template-config-JPA2-bsla.xml" 
         outputdir-root="../../DEV/latvianjug/tranxy/bsla"
         templatedir-root="../../template/framework/bsla">
          <property name="add-cache-implementation" value="ehcache"></property>
      </target> 
      
   <target refname="JPA2" fileName="mp-template-config-JPA2.xml"
    outputdir-root="../../DEV/latvianjug/tranxy/jpa" 
        templatedir-root="../../template/framework/jpa">
    <property name="add-querydsl" value="2.1.2"></property>
    <property name="add-jpa2-implementation" value="hibernate"></property>
        <property name="add-cache-implementation" value="ehcache"></property>
        <property name="add-domain-specific-method" value="true"></property>
        <property name="add-xmlbinding" value="true"></property> 
        <property name="add-xml-format" value="lowercase-hyphen"></property> 
   </target>
   
      <target refname="SDD-beans" 
          outputdir-root="../../DEV/latvianjug/tranxy/jpa"
         fileName="mp-template-config-SDD-beans.xml" 
         templatedir-root="../../template/framework/bean">
         <property name="add-xmlbinding" value="true"></property> 
         <property name="add-xml-format" value="lowercase-hyphen"></property>
      </target>

      <target refname="COMMON-LIB" 
         fileName="mp-template-config-COMMON-LIB.xml" 
         templatedir-root="../../template/framework/common">
      </target>   
                        
      <target refname="MavenMaster" 
         name="maven" 
         fileName="mp-template-config-maven.xml" 
         outputdir-root="../../DEV/latvianjug/tranxy"
         templatedir-root="../../template/framework/maven">
      </target>

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

      <target refname="REST-LIB" 
         fileName="mp-template-config-REST-LIB.xml" 
         templatedir-root="../../template/framework/rest">
      </target>
      <target refname="SPRING-LIB" 
         fileName="mp-template-config-SPRING-LIB.xml" 
         templatedir-root="../../template/framework/spring">
      </target>

  </targets>
 </configuration>
</generator-config>
Aside of the 'standard' CXF CRUD generation (see on demo). Here a set of new artifacts are generated to enable the enriched 'get translation infos' function.
The part of the configuration may seem a bit long, I agree. It describes what templates and metadata file to use.
I plan to have simplify version à la minuteproject console where you just reference a track name belonging to a catalog and pass some properties.
The set of target currently displayed can be copied from the sample provided in /demo/config.

Generation

Drop TRANXY-JPA2-Spring-REST-CXF-SDD.xml in /mywork/config and run: model-generation.cmd/sh TRANXY-JPA2-Spring-REST-CXF-SDD.xml
Generated artifacts go to /DEV/latvianjug/tranxy.

Generated artifacts

Here are presented on the new artifacts of the CXF-JPA2 track.
The artifacts are presented from a top down perspective (starting from REST-Frontend down to Persistence layer).
The name of the artifacts are deeply linked to the query name 'get translation info'.

REST resources

2 resources file are generated, one for json rendering the other for xml.

GetTranslationInfoJsonResource.java

/**
 * Copyright (c) minuteproject, minuteproject@gmail.com
 * All rights reserved.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License")
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 * More information on minuteproject:
 * twitter @minuteproject
 * wiki http://minuteproject.wikispaces.com 
 * blog http://minuteproject.blogspot.net
 * 
*/
/**
 * template reference : 
 * - name : CXFSpringSDDJsonResource
 * - file name : CXFSpringSDDResource.vm
*/
package net.sf.mp.demo.tranxy.rest.statement;

//MP-MANAGED-ADDED-AREA-BEGINNING @import@
//MP-MANAGED-ADDED-AREA-ENDING @import@
import java.util.Date;
import java.util.List;
import java.util.ArrayList;
import java.io.*;
import java.sql.*;

import javax.servlet.http.*;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.FormParam;
import javax.ws.rs.QueryParam;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.PUT;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Request;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import javax.xml.bind.JAXBElement;

import net.sf.mp.demo.tranxy.sdd.out.statement.GetTranslationInfoOutList;
import net.sf.mp.demo.tranxy.sdd.in.statement.GetTranslationInfoIn;
import net.sf.mp.demo.tranxy.dao.sdd.face.statement.GetTranslationInfoDaoFace;
/**
 *
 * <p>Title: GetTranslationInfoJsonResource</p>
 *
 * <p>Description: remote interface for GetTranslationInfoJsonResource service </p>
 *
 */
@Produces ({MediaType.APPLICATION_JSON})
@Consumes ({MediaType.APPLICATION_JSON})
@Service ("getTranslationInfoJsonResource")
@Transactional
@Path ("/rest/json/gettranslationinfos")
public class GetTranslationInfoJsonResource {

    @Autowired
    @Qualifier("getTranslationInfoDaoFace")
    GetTranslationInfoDaoFace getTranslationInfoDaoFace;

//MP-MANAGED-UPDATABLE-BEGINNING-DISABLE @SDD_EXECUTE_GET-get translation info@
    @GET
    @Produces (MediaType.APPLICATION_JSON) 

    public GetTranslationInfoOutList executeAndFormatXml (
        @QueryParam ("key") String key ,
        @QueryParam ("code") String code ,
        @QueryParam ("max") Integer max ) {
        return execute(
           key ,
           code ,
           max   
  );
    }
//MP-MANAGED-UPDATABLE-ENDING

//MP-MANAGED-UPDATABLE-BEGINNING-DISABLE @SDD_EXECUTE_GET-get translation info@
    public GetTranslationInfoOutList execute (
        String key ,
        String code ,
        Integer max ) {
  GetTranslationInfoIn getTranslationInfoIn = new GetTranslationInfoIn ();
  getTranslationInfoIn.setKey (key);
  getTranslationInfoIn.setCode (code);
  getTranslationInfoIn.setMax (max);
        return getTranslationInfoDaoFace.execute(getTranslationInfoIn);
    }
//MP-MANAGED-UPDATABLE-ENDING

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

}

GetTranslationInfoXmlResource.java

/**
 * Copyright (c) minuteproject, minuteproject@gmail.com
 * All rights reserved.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License")
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 * More information on minuteproject:
 * twitter @minuteproject
 * wiki http://minuteproject.wikispaces.com 
 * blog http://minuteproject.blogspot.net
 * 
*/
/**
 * template reference : 
 * - name : CXFSpringSDDXmlResource
 * - file name : CXFSpringSDDResource.vm
*/
package net.sf.mp.demo.tranxy.rest.statement;

//MP-MANAGED-ADDED-AREA-BEGINNING @import@
//MP-MANAGED-ADDED-AREA-ENDING @import@
import java.util.Date;
import java.util.List;
import java.util.ArrayList;
import java.io.*;
import java.sql.*;

import javax.servlet.http.*;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.FormParam;
import javax.ws.rs.QueryParam;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.PUT;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Request;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import javax.xml.bind.JAXBElement;

import net.sf.mp.demo.tranxy.sdd.out.statement.GetTranslationInfoOutList;
import net.sf.mp.demo.tranxy.sdd.in.statement.GetTranslationInfoIn;
import net.sf.mp.demo.tranxy.dao.sdd.face.statement.GetTranslationInfoDaoFace;
/**
 *
 * <p>Title: GetTranslationInfoXmlResource</p>
 *
 * <p>Description: remote interface for GetTranslationInfoXmlResource service </p>
 *
 */
@Produces ({MediaType.APPLICATION_XML})
@Consumes ({MediaType.APPLICATION_XML})
@Service ("getTranslationInfoXmlResource")
@Transactional
@Path ("/rest/xml/gettranslationinfos")
public class GetTranslationInfoXmlResource {

    @Autowired
    @Qualifier("getTranslationInfoDaoFace")
    GetTranslationInfoDaoFace getTranslationInfoDaoFace;

//MP-MANAGED-UPDATABLE-BEGINNING-DISABLE @SDD_EXECUTE_GET-get translation info@
    @GET
    @Produces (MediaType.APPLICATION_XML) 

    public GetTranslationInfoOutList executeAndFormatXml (
        @QueryParam ("key") String key ,
        @QueryParam ("code") String code ,
        @QueryParam ("max") Integer max ) {
        return execute(
           key ,
           code ,
           max   
  );
    }
//MP-MANAGED-UPDATABLE-ENDING

//MP-MANAGED-UPDATABLE-BEGINNING-DISABLE @SDD_EXECUTE_GET-get translation info@
    public GetTranslationInfoOutList execute (
        String key ,
        String code ,
        Integer max ) {
  GetTranslationInfoIn getTranslationInfoIn = new GetTranslationInfoIn ();
  getTranslationInfoIn.setKey (key);
  getTranslationInfoIn.setCode (code);
  getTranslationInfoIn.setMax (max);
        return getTranslationInfoDaoFace.execute(getTranslationInfoIn);
    }
//MP-MANAGED-UPDATABLE-ENDING

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

}

Spring configuration

application-context.xml

It is adapted to reflect the new beans
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
 xmlns:context="http://www.springframework.org/schema/context"
 xmlns:tx="http://www.springframework.org/schema/tx"
 xmlns:jaxrs="http://cxf.apache.org/jaxrs"
 xsi:schemaLocation="
  http://www.springframework.org/schema/beans
  http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
  http://www.springframework.org/schema/context 
  http://www.springframework.org/schema/context/spring-context-3.0.xsd
  http://www.springframework.org/schema/tx
  http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
  http://cxf.apache.org/jaxrs
  http://cxf.apache.org/schemas/jaxrs.xsd">

    <import resource="classpath:META-INF/cxf/cxf.xml" />
    <import resource="classpath:META-INF/cxf/cxf-extension-jaxrs-binding.xml" />
    <import resource="classpath:META-INF/cxf/cxf-servlet.xml" />
    
    <context:component-scan base-package="net.sf.mp.demo.tranxy.rest"/>
    <context:component-scan base-package="net.sf.mp.demo.tranxy.dao.sdd.impl"/>

    <import resource="classpath:net/sf/mp/demo/tranxy/factory/spring/spring-config-Tranxy-BE-main.xml"/>    
 
    <jaxrs:server id="restContainer" address="/">
        <jaxrs:serviceBeans>
   <!-- tranxy --> 
   <ref bean="applicationResource"/>
   <ref bean="languageResource"/>
   <ref bean="userResource"/>
   <!-- translation --> 
   <ref bean="translationResource"/>
   <ref bean="translationKeyResource"/>
   <ref bean="translationRequestResource"/>
 
   <!-- statements -->
   <ref bean="getTranslationInfoXmlResource"/>
   <ref bean="getTranslationInfoJsonResource"/>
        </jaxrs:serviceBeans>
    </jaxrs:server> 

</beans> 

DAO layer

Interface GetTranslationInfoDaoFace.java

/**
 * Copyright (c) minuteproject, minuteproject@gmail.com
 * All rights reserved.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License")
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 * More information on minuteproject:
 * twitter @minuteproject
 * wiki http://minuteproject.wikispaces.com 
 * blog http://minuteproject.blogspot.net
 * 
*/
/**
 * template reference : 
 * - name : SDDDaoInterface
 * - file name : SDDDaoInterface.vm
*/
package net.sf.mp.demo.tranxy.dao.sdd.face.statement;

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

import net.sf.mp.demo.tranxy.sdd.out.statement.GetTranslationInfoOutList;
import net.sf.mp.demo.tranxy.sdd.in.statement.GetTranslationInfoIn;

/**
 *
 * <p>Title: GetTranslationInfoDaoFace</p>
 *
 * <p>Description: remote interface for GetTranslationInfoDaoFace service </p>
 *
 */
public interface GetTranslationInfoDaoFace {

//MP-MANAGED-UPDATABLE-BEGINNING-DISABLE @SDD_EXECUTE_GET-get translation info@
    public GetTranslationInfoOutList execute (GetTranslationInfoIn getTranslationInfoIn) ;
//MP-MANAGED-UPDATABLE-ENDING

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


} 

Implementation GetTranslationInfoRepository.java

/**
 * Copyright (c) minuteproject, minuteproject@gmail.com
 * All rights reserved.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License")
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 * More information on minuteproject:
 * twitter @minuteproject
 * wiki http://minuteproject.wikispaces.com 
 * blog http://minuteproject.blogspot.net
 * 
*/
/**
 * template reference : 
 * - name : SDDSpringJPADao
 * - file name : SDDSpringJPADao.vm
*/
package net.sf.mp.demo.tranxy.dao.sdd.impl.statement;

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

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;

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

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

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import net.sf.mp.demo.tranxy.sdd.out.statement.GetTranslationInfoOutList;
import net.sf.mp.demo.tranxy.sdd.out.statement.GetTranslationInfoOut;
import net.sf.mp.demo.tranxy.sdd.in.statement.GetTranslationInfoIn;
import net.sf.mp.demo.tranxy.dao.sdd.face.statement.GetTranslationInfoDaoFace;

/**
 *
 * <p>Title: GetTranslationInfoRepository</p>
 *
 * <p>Description: SDD DAO Spring JPA implementation </p>
 *
 */
@Repository ("getTranslationInfoDaoFace")
@Transactional(propagation = Propagation.REQUIRED) 
public class GetTranslationInfoRepository implements GetTranslationInfoDaoFace{

 public static final String QUERY_NATIVE = "select k.key_name, t.translation, t.date_finalization, l.code, l.description, tl.first_name, tl.last_name, tl.email from translation t, language l, user tl, translation_key k where t.language_id = l.idlanguage and tl.idUser = t.translator_id and k.id = t.key_id and k.key_name like ? and l.code like ? order by key_name limit ?";

 @PersistenceContext(unitName = "tranxy")  
    EntityManager entityManager;  
//MP-MANAGED-UPDATABLE-BEGINNING-DISABLE @SDD_EXECUTE_GET-get translation info@
    public GetTranslationInfoOutList execute (GetTranslationInfoIn getTranslationInfoIn) {
  GetTranslationInfoOutList getTranslationInfoOutList = new GetTranslationInfoOutList();
  List<GetTranslationInfoOut> list = executeJDBC (getTranslationInfoIn);
  getTranslationInfoOutList.setGetTranslationInfoOuts (list);
        return getTranslationInfoOutList;
    }
//MP-MANAGED-UPDATABLE-ENDING

//MP-MANAGED-UPDATABLE-BEGINNING-DISABLE @SDD_EXECUTE_JDBC-get translation info@
 public List<GetTranslationInfoOut> executeJDBC(GetTranslationInfoIn getTranslationInfoIn) {
  List<GetTranslationInfoOut> list = new ArrayList<GetTranslationInfoOut>();
  PreparedStatement pstmt = null;
  ResultSet rs = null;
  Connection conn = null;
  try {
   conn = getConnection();
   pstmt = conn.prepareStatement(QUERY_NATIVE);
   pstmt.setString(1, getTranslationInfoIn.getKey()); 
   pstmt.setString(2, getTranslationInfoIn.getCode()); 
   pstmt.setInt(3, getTranslationInfoIn.getMax()); 
   rs = pstmt.executeQuery();
   while (rs.next()) {
    GetTranslationInfoOut getTranslationInfoOut = new GetTranslationInfoOut();
    getTranslationInfoOut.setKeyName(rs.getString(1)); 
    getTranslationInfoOut.setTranslation(rs.getString(2)); 
    getTranslationInfoOut.setDateFinalization(rs.getString(3)); 
    getTranslationInfoOut.setCode(rs.getString(4)); 
    getTranslationInfoOut.setDescription(rs.getString(5)); 
    getTranslationInfoOut.setFirstName(rs.getString(6)); 
    getTranslationInfoOut.setLastName(rs.getString(7)); 
    getTranslationInfoOut.setEmail(rs.getString(8)); 
    list.add(getTranslationInfoOut);
         }
  } 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) entityManager.getDelegate();  
     return session;  
    }
 
//MP-MANAGED-ADDED-AREA-BEGINNING @implementation@
//MP-MANAGED-ADDED-AREA-ENDING @implementation@

}

Beans!!

Welcome back DTO! You do not need to re-wrap your sql against ORM!
Go straight: your I/O = your DTOs

Input bean

GetTranslationInfoIn.java


/**
 * Copyright (c) minuteproject, minuteproject@gmail.com
 * All rights reserved.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License")
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 * More information on minuteproject:
 * twitter @minuteproject
 * wiki http://minuteproject.wikispaces.com 
 * blog http://minuteproject.blogspot.net
 * 
*/
/**
 * template reference : 
 * - name : SDDInputBean
 * - file name : JavaBean.vm
*/
package net.sf.mp.demo.tranxy.sdd.in.statement;

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

//MP-MANAGED-ADDED-AREA-BEGINNING @import@
//MP-MANAGED-ADDED-AREA-ENDING @import@
/**
 *
 * <p>Title: GetTranslationInfoIn</p>
 *
 * <p>Description: Java Bean containing a collection of GetTranslationInfo </p>
 *
 */
public class GetTranslationInfoIn {

    private String key;
    private String code;
    private Integer max;

    /**
    * Default constructor
    */
    public GetTranslationInfoIn() {
    }
 
    public String getKey() {
        return key;
    }
 
    public void setKey (String key) {
        this.key =  key;
    }

    public String getCode() {
        return code;
    }
 
    public void setCode (String code) {
        this.code =  code;
    }

    public Integer getMax() {
        return max;
    }
 
    public void setMax (Integer max) {
        this.max =  max;
    }


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

Output beans

GetTranslationInfoOut


/**
 * Copyright (c) minuteproject, minuteproject@gmail.com
 * All rights reserved.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License")
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 * More information on minuteproject:
 * twitter @minuteproject
 * wiki http://minuteproject.wikispaces.com 
 * blog http://minuteproject.blogspot.net
 * 
*/
/**
 * template reference : 
 * - name : SDDOutputBean
 * - file name : JavaBean.vm
*/
package net.sf.mp.demo.tranxy.sdd.out.statement;

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

//MP-MANAGED-ADDED-AREA-BEGINNING @import@
//MP-MANAGED-ADDED-AREA-ENDING @import@
/**
 *
 * <p>Title: GetTranslationInfoOut</p>
 *
 * <p>Description: Java Bean containing a collection of GetTranslationInfo </p>
 *
 */
public class GetTranslationInfoOut {

    private String keyName;
    private String translation;
    private String dateFinalization;
    private String code;
    private String description;
    private String firstName;
    private String lastName;
    private String email;

    /**
    * Default constructor
    */
    public GetTranslationInfoOut() {
    }
 
    public String getKeyName() {
        return keyName;
    }
 
    public void setKeyName (String keyName) {
        this.keyName =  keyName;
    }

    public String getTranslation() {
        return translation;
    }
 
    public void setTranslation (String translation) {
        this.translation =  translation;
    }

    public String getDateFinalization() {
        return dateFinalization;
    }
 
    public void setDateFinalization (String dateFinalization) {
        this.dateFinalization =  dateFinalization;
    }

    public String getCode() {
        return code;
    }
 
    public void setCode (String code) {
        this.code =  code;
    }

    public String getDescription() {
        return description;
    }
 
    public void setDescription (String description) {
        this.description =  description;
    }

    public String getFirstName() {
        return firstName;
    }
 
    public void setFirstName (String firstName) {
        this.firstName =  firstName;
    }

    public String getLastName() {
        return lastName;
    }
 
    public void setLastName (String lastName) {
        this.lastName =  lastName;
    }

    public String getEmail() {
        return email;
    }
 
    public void setEmail (String email) {
        this.email =  email;
    }


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

GetTranslationInfoOutList


/**
 * Copyright (c) minuteproject, minuteproject@gmail.com
 * All rights reserved.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License")
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 * More information on minuteproject:
 * twitter @minuteproject
 * wiki http://minuteproject.wikispaces.com 
 * blog http://minuteproject.blogspot.net
 * 
*/
/**
 * template reference : 
 * - name : SDDOutputBeanCollection
 * - file name : JavaBeanCollection.vm
*/
package net.sf.mp.demo.tranxy.sdd.out.statement;

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

import javax.xml.bind.annotation.*;

import net.sf.mp.demo.tranxy.sdd.out.statement.GetTranslationInfoOut;

//MP-MANAGED-ADDED-AREA-BEGINNING @import@
//MP-MANAGED-ADDED-AREA-ENDING @import@
/**
 *
 * <p>Title: GetTranslationInfoOutList</p>
 *
 * <p>Description: Java Bean GetTranslationInfoOutList </p>
 *
 */
@XmlRootElement (name="GetTranslationInfoOutList")
public class GetTranslationInfoOutList {

    @XmlElement (name="GetTranslationInfoOut") //(name="gettranslationinfoouts")
    private List<GetTranslationInfoOut> getTranslationInfoOuts;

    /**
    * Default constructor
    */
    public GetTranslationInfoOutList() {
    }
 
    public void setGetTranslationInfoOuts (List<GetTranslationInfoOut> getTranslationInfoOuts) {
        this.getTranslationInfoOuts = getTranslationInfoOuts;
    }

    @XmlTransient
    public List<GetTranslationInfoOut> getGetTranslationInfoOuts () {
        if (getTranslationInfoOuts==null)
            getTranslationInfoOuts = new ArrayList<GetTranslationInfoOut>();
        return getTranslationInfoOuts;
    }

    public void addGetTranslationInfoOut (GetTranslationInfoOut getTranslationInfoOut) {
        getGetTranslationInfoOuts ().add(getTranslationInfoOut);
    }


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

Improvements

SDD concept is brand new inside minuteproject, a number of improvement are foreseen:
  • error handling
  • validation (type, mandatory)
  • default value

Build deploy and test

Build

Run >mvn clean package from the root application

Deploy

Drop the resulting artefact tranxyRestCxfApp.war in tomcat/webapps
Start tomcat

Test

2 URLs - one for json format the other for xml format are available

REST URL with parameters returning json format
http://localhost:8080/tranxyRestCxfApp/rest/json/gettranslationinfos?key=%25&code=%25&max=2
Search for all key : % and all code : % and max number of return value is 2
REST URL with parameters returning xml format
Search for all key : % and all code : % and max number of return value is 1

Now you can enjoy and make cool front-end (ajax, jquery friendly and other js).

Conclusion

If you want to go fast, and when working with ORM abstraction takes to long, then have a look at Statement Driven Development.

The war between pro Domain Object vs. DTO is irrelevant for Minuteproject.
On this project both live aside.
Use the approach that best suits your needs.


Wednesday, May 23, 2012

RigaJUG - demo - REST CXF

In this demo, we will generate a REST/CXF application with spring/jpa2 backend.
This demo presents the track REST-CXF of minuteproject.

The model from demo 2 remains the same.

The enrichment stays the same.

But the tracks changes
What is added are 2 layers:
  • a DAO layer with spring integration on top of JPA2
  • a REST-CXF layer with JAX-RS annotation
The JPA2 entities are annotated by JAXB annotations.

All this is done to provide a CRUD interface on top of the model entities.

Configuration

Here is the configuration TRANXY-JPA2-Spring-REST-CXF.xml
<!DOCTYPE root>
<generator-config xmlns="http://minuteproject.sf.net/xsd/mp-config" 
xmlns:xs="http://www.w3.org/2001/XMLSchema-instance" 
xs:noNamespaceSchemaLocation="../config/mp-config.xsd">
 <configuration>
  <conventions>
   <target-convention type="enable-updatable-code-feature" />
  </conventions>
  <model name="tranxy" 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/tranxy</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="QUARTZ"></condition>
    </generation-condition>
    <business-package default="tranxy">
     <condition type="package" startsWith="trans" result="translation"></condition>
    </business-package>
    <enrichment>
     <conventions>
      <!-- manipulate the structure and entities BEFORE manipulating the 
       entities -->
      <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="language_x_translator">
                        <field name="language_id" linkReferenceAlias="translating_language" />
                        <field name="user_id" linkReferenceAlias="translator" />
                    </entity>
                    <entity name="LANGUAGE_X_SPEAKER">
                        <field name="LANGUAGE_ID" linkToTargetEntity="LANGUAGE"
                            linkToTargetField="IDLANGUAGE" linkReferenceAlias="spoken_language" />
                        <field name="user_id" linkReferenceAlias="speaker" />
                    </entity>
                    <entity name="APPLICATION">
                        <field name="TYPE">
                            <property tag="checkconstraint" alias="application_type">
                                <property name="OPENSOURCE"/>
                                <property name="COPYRIGHT" />
                            </property>
                        </field>
                    </entity>
    </enrichment>
   </business-model>
  </model>
  <targets>
            
      <target refname="REST-CXF-BSLA" 
         name="default" 
         fileName="mp-template-config-REST-CXF-Spring.xml" 
         outputdir-root="../../DEV/latvianjug/tranxy/rest"
         templatedir-root="../../template/framework/cxf">
      </target>

      <target refname="BackendOnBsla" 
         name="default" 
         fileName="mp-template-config-JPA2-bsla.xml" 
         outputdir-root="../../DEV/latvianjug/tranxy/bsla"
         templatedir-root="../../template/framework/bsla">
          <property name="add-cache-implementation" value="ehcache"></property>
      </target> 
      
   <target refname="JPA2" fileName="mp-template-config-JPA2.xml"
    outputdir-root="../../DEV/latvianjug/tranxy/jpa" 
        templatedir-root="../../template/framework/jpa">
 <property name="add-querydsl" value="2.1.2"></property>
 <property name="add-jpa2-implementation" value="hibernate"></property>
        <property name="add-cache-implementation" value="ehcache"></property>
        <property name="add-domain-specific-method" value="true"></property>
        <property name="add-xmlbinding" value="true"></property> 
        <property name="add-xml-format" value="lowercase-hyphen"></property> 
   </target>
      <target refname="MavenMaster" 
         name="maven" 
         fileName="mp-template-config-maven.xml" 
         outputdir-root="../../DEV/latvianjug/tranxy"
         templatedir-root="../../template/framework/maven">
      </target>

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

      <target refname="REST-LIB" 
         fileName="mp-template-config-REST-LIB.xml" 
         templatedir-root="../../template/framework/rest">
      </target>
      <target refname="SPRING-LIB" 
         fileName="mp-template-config-SPRING-LIB.xml" 
         templatedir-root="../../template/framework/spring">
      </target>

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

It is possible to enrich each link of the many-to-many relationship table (ex: language_x_translator and language_x_speaker) to provide the other side relationship with an alias and thus avoid long composite variable name.

Generation

Set TRANXY-JPA2-Spring-REST-CXF.xml in /mywork/config
Run
>model-generation.cmd TRANXY-JPA2-Spring-REST-CXF.xml
The output goes in /dev/latvianjug/tranxy

Resulting artefacts

A maven project structure with 3 modules
  • JPA2 layer
  • Spring DAO layer
  • CXF layer
JPA2 layer has been visited in Demo 1 and Demo 2.

Spring DAO layer

It consists of transactional services one for each entity

CRUD DAO layer on top of JPA2: This layer is called BSLA (Basic Spring Layer Architecture).

Two interfaces and implementation are generated for each entity
Example for Translation entity

DAO Interfaces

/**
 * Copyright (c) minuteproject, minuteproject@gmail.com
 * All rights reserved.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License")
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 * More information on minuteproject:
 * twitter @minuteproject
 * wiki http://minuteproject.wikispaces.com 
 * blog http://minuteproject.blogspot.net
 * 
*/
/**
 * template reference : 
 * - name : BslaDaoInterfaceUML
 * - file name : BslaDaoInterfaceUML.vm
*/
package net.sf.mp.demo.tranxy.dao.face.translation;

import net.sf.mp.demo.tranxy.domain.translation.Translation;
import java.util.List;
import net.sf.minuteProject.architecture.bsla.bean.criteria.PaginationCriteria;
import net.sf.minuteProject.architecture.bsla.dao.face.DataAccessObject;


/**
 *
 * <p>Title: TranslationDao</p>
 *
 * <p>Description: Interface of a Data access object dealing with TranslationDao
 * persistence. It offers a set of methods which allow for saving,
 * deleting and searching translation objects</p>
 *
 */
public interface TranslationDao extends DataAccessObject {

    /**
     * Inserts a Translation entity 
     * @param Translation translation
     */
    public void insertTranslation(Translation translation) ;
 
    /**
     * Inserts a list of Translation entity 
     * @param List<Translation> translations
     */
    public void insertTranslations(List<Translation> translations) ;
        
    /**
     * Updates a Translation entity 
     * @param Translation translation
     */
    public Translation updateTranslation(Translation translation) ;

  /**
     * Updates a Translation entity with only the attributes set into Translation.
  * The primary keys are to be set for this method to operate.
  * This is a performance friendly feature, which remove the udibiquous full load and full update when an
  * update is to be done
   * Remark: The primary keys cannot be update by this methods, nor are the attributes that must be set to null.
   * @param Translation translation
   */
    public int updateNotNullOnlyTranslation(Translation translation) ;
  
 public int updateNotNullOnlyPrototypeTranslation(Translation translation, Translation prototypeCriteria);
 
     /**
     * Saves a Translation entity 
     * @param Translation translation
     */
    public void saveTranslation(Translation translation);
    
    /**
     * Deletes a Translation entity 
     * @param Translation translation
     */
    public void deleteTranslation(Translation translation) ;
 
    /**
     * Loads the Translation entity which is related to an instance of
     * Translation
     * @param Long id
     * @return Translation The Translation entity
     
    public Translation loadTranslation(Long id);
*/
    /**
     * Loads the Translation entity which is related to an instance of
     * Translation
     * @param java.lang.Long Id
     * @return Translation The Translation entity
     */
    public Translation loadTranslation(java.lang.Long id);    

    /**
     * Loads a list of Translation entity 
     * @param List<java.lang.Long> ids
     * @return List<Translation> The Translation entity
     */
    public List<Translation> loadTranslationListByTranslation (List<Translation> translations);
    
    /**
     * Loads a list of Translation entity 
     * @param List<java.lang.Long> ids
     * @return List<Translation> The Translation entity
     */
    public List<Translation> loadTranslationListById(List<java.lang.Long> ids);
    
    /**
     * Loads the Translation entity which is related to an instance of
     * Translation and its dependent one to many objects
     * @param Long id
     * @return Translation The Translation entity
     */
    public Translation loadFullFirstLevelTranslation(java.lang.Long id);
    
    /**
     * Loads the Translation entity which is related to an instance of
     * Translation
     * @param Translation translation
     * @return Translation The Translation entity
     */
    public Translation loadFullFirstLevelTranslation(Translation translation);    
    
    
    /**
     * Loads the Translation entity which is related to an instance of
     * Translation and its dependent objects one to many
     * @param Long id
     * @return Translation The Translation entity
     */
    public Translation loadFullTranslation(Long id) ;

    /**
     * Searches a list of Translation entity based on a Translation containing Translation matching criteria
     * @param Translation translation
     * @return List<Translation>
     */
    public List<Translation> searchPrototypeTranslation(Translation translation) ;
    
    /**
     * Searches a list of Translation entity based on a list of Translation containing Translation matching criteria
     * @param List<Translation> translations
     * @return List<Translation>
     */
    public List<Translation> searchPrototypeTranslation(List<Translation> translations) ;    
     
 /**
     * Searches a list of Translation entity 
     * @param Translation translation
     * @return List
     */
    public List<Translation> searchPrototypeTranslation(Translation translationPositive, Translation translationNegative) ;
        
    /**
     * Load a paginated list of Translation entity dependent of pagination criteria
     * @param PaginationCriteria paginationCriteria
     * @return List
     */
    public List<Translation> loadPaginatedTranslation (Translation translation, PaginationCriteria paginationCriteria) ;
            
}

/**
 * Copyright (c) minuteproject, minuteproject@gmail.com
 * All rights reserved.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License")
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 * More information on minuteproject:
 * twitter @minuteproject
 * wiki http://minuteproject.wikispaces.com 
 * blog http://minuteproject.blogspot.net
 * 
*/
/**
 * template reference : 
 * - name : BslaDaoInterfaceExtendedUML
 * - file name : BslaDaoInterfaceKFUML.vm
*/
package net.sf.mp.demo.tranxy.dao.face.translation;

import net.sf.mp.demo.tranxy.domain.translation.Translation;
import java.util.List;
import net.sf.minuteProject.architecture.filter.data.Criteria;
import net.sf.minuteProject.architecture.bsla.dao.face.DataAccessObject;

/**
 *
 * <p>Title: TranslationExtDao</p>
 *
 * <p>Description: Interface of a Data access object dealing with TranslationExtDao
 * persistence. It offers extended DAO functionalities</p>
 *
 */
public interface TranslationExtDao extends DataAccessObject {
     
     /**
     * Inserts a Translation entity with cascade of its children
     * @param Translation translation
     */
    public void insertTranslationWithCascade(Translation translation) ;
    
    /**
     * Inserts a list of Translation entity with cascade of its children
     * @param List<Translation> translations
     */
    public void insertTranslationsWithCascade(List<Translation> translations) ;        
   
    /**
     * lookup Translation entity Translation, criteria and max result number
     */
    public List<Translation> lookupTranslation(Translation translation, Criteria criteria, Integer numberOfResult);
 
 public Integer updateNotNullOnlyTranslation (Translation translation, Criteria criteria);

 /**
  * Affect the first translation retrieved corresponding to the translation criteria.
  * Blank criteria are mapped to null.
  * If no criteria is found, null is returned.
  */
    public Translation affectTranslation (Translation translation);
    
    public Translation affectTranslationUseCache (Translation translation);
     
 /**
  * Assign the first translation retrieved corresponding to the translation criteria.
  * Blank criteria are mapped to null.
  * If no criteria is found, null is returned.
  * If there is no translation corresponding in the database. Then translation is inserted and returned with its primary key(s). 
  */
    public Translation assignTranslation (Translation translation);

 /**
  * Assign the first translation retrieved corresponding to the mask criteria.
  * Blank criteria are mapped to null.
  * If no criteria is found, null is returned.
  * If there is no translation corresponding in the database. 
  * Then translation is inserted and returned with its primary key(s). 
  * Mask servers usually to set unique keys or the semantic reference
  */
    public Translation assignTranslation (Translation translation, Translation mask);
 
    public Translation assignTranslationUseCache (Translation translation);
         
    /**
    * return the first Translation entity found
    */           
    public Translation getFirstTranslation (Translation translation);
    
    /**
    * checks if the Translation entity exists
    */           
    public boolean existsTranslation (Translation translation);    
    
    public boolean existsTranslationWhereConditionsAre (Translation translation);

    /**
    * partial load enables to specify the fields you want to load explicitly
    */            
    public List<Translation> partialLoadTranslation(Translation translation, Translation positiveTranslation, Translation negativeTranslation);

    /**
    * partial load with parent entities
    * variation (list, first, distinct decorator)
    * variation2 (with cache)
    */
    public List<Translation> partialLoadWithParentTranslation(Translation translation, Translation positiveTranslation, Translation negativeTranslation);

    public List<Translation> partialLoadWithParentTranslationUseCache(Translation translation, Translation positiveTranslation, Translation negativeTranslation, Boolean useCache);

    public List<Translation> partialLoadWithParentTranslationUseCacheOnResult(Translation translation, Translation positiveTranslation, Translation negativeTranslation, Boolean useCache);

    /**
    * variation first
    */
    public Translation partialLoadWithParentFirstTranslation(Translation translationWhat, Translation positiveTranslation, Translation negativeTranslation);
    
    public Translation partialLoadWithParentFirstTranslationUseCache(Translation translationWhat, Translation positiveTranslation, Translation negativeTranslation, Boolean useCache);

    public Translation partialLoadWithParentFirstTranslationUseCacheOnResult(Translation translationWhat, Translation positiveTranslation, Translation negativeTranslation, Boolean useCache);

    /**
    * variation distinct
    */
    public List<Translation> getDistinctTranslation(Translation translationWhat, Translation positiveTranslation, Translation negativeTranslation);

    //
    public List partialLoadWithParentForBean(Object bean, Translation translation, Translation positiveTranslation, Translation negativeTranslation);

    /**
    * search on prototype with cache
    */
    public List<Translation> searchPrototypeWithCacheTranslation (Translation translation);
      
    
    /**
     * Searches a list of distinct Translation entity based on a Translation mask and a list of Translation containing Translation matching criteria
     * @param Translation translation
     * @param List<Translation> translations
     * @return List<Translation>
     */
    public List<Translation> searchDistinctPrototypeTranslation(Translation translationMask, List<Translation> translations) ;    

 public List<Translation> countDistinct (Translation whatMask, Translation whereEqCriteria);
 
 public Long count (Translation whereEqCriteria);
 
 public List<Translation> loadGraph(Translation graphMaskWhat, List<Translation> whereMask);  
 
 public List<Translation> loadGraphFromParentKey (Translation graphMaskWhat, List<Translation> parents); 
 
    /**
     * generic to move after in superclass
     */
    public List<Object[]> getSQLQueryResult(String query);     
 
}

DAO implementations

TranslationJPAImpl and TranslationJPAExtImpl (code not copied).

In the future Generic DAO will be used for cross-entity redundant aspects.
Adaptation to spring 3.x will be perform (i.e no more JPASupport extension by EntityManager injection)
Meanwhile the code here above works fine with spring 2.5+

Spring configurations

spring-config-Tranxy-BE-main.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">

<beans>

    <!-- Dao JPA -->
    <import resource="classpath:net/sf/mp/demo/tranxy/factory/spring/spring-config-JPA-Tranxy-dao.xml"/>    

<!--MP-MANAGED-UPDATABLE-BEGINNING-DISABLE @JPAtranxyFactory-tranxy@-->
    <!-- hibernate config to put in an appart config file-->
    <bean id="JPAtranxyFactory" autowire="byName"
        class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
  <!-- all connection information are retrieve from the persistence file-->
  <!--
   <property name="dataSource" ref="..."/>
   <property name="persistenceUnitName" value="..."/>
  -->
        <property name="persistenceXmlLocation" value="classpath:META-INF/persistence.xml" />
    </bean>
<!--MP-MANAGED-UPDATABLE-ENDING-->
    <!-- Database -->
    <import resource="classpath:net/sf/mp/demo/tranxy/factory/spring/spring-config-Tranxy-database.xml"/>    

</beans>

spring-config-Tranxy-database.xml
<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:jndi="http://www.springframework.org/schema/jee"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
                           http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd
                           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
                           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">


    <bean id="placeHolderConfig"
        class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="location"><value>classpath:net/sf/mp/demo/tranxy/factory/spring/spring-config-Tranxy.properties</value></property>
    </bean>     
 
    <bean id="tranxyTransactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="JPAtranxyFactory"/>
    </bean>

    <!-- to get the entity manager -->
    <tx:annotation-driven transaction-manager="tranxyTransactionManager"/>
    
</beans>

spring-config-Tranxy-BE-main
jdbc.tranxy.driverClassName=org.gjt.mm.mysql.Driver
jdbc.tranxy.url=jdbc:mysql://127.0.0.1:3306/tranxy
jdbc.tranxy.username=root
jdbc.tranxy.password=mysql
jdbc.tranxy.jndi=jdbc/tranxy
hibernate.dialect=org.hibernate.dialect.MySQLDialect

spring-config-JPA-Tranxy-dao.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">

<beans>

    <!-- Import Dao definitions for business components -->

   <!-- tranxy -->
   <import resource="classpath:net/sf/mp/demo/tranxy/factory/spring/tranxy/dao-JPA-Tranxy.xml"/>
   <!-- translation -->
   <import resource="classpath:net/sf/mp/demo/tranxy/factory/spring/translation/dao-JPA-Translation.xml"/>

    <!-- Import Ext Dao definitions for business components -->
   <!-- tranxy extended dao -->
   <import resource="classpath:net/sf/mp/demo/tranxy/factory/spring/tranxy/dao-ext-JPA-Tranxy.xml"/>
   <!-- translation extended dao -->
   <import resource="classpath:net/sf/mp/demo/tranxy/factory/spring/translation/dao-ext-JPA-Translation.xml"/>

</beans>

dao-JPA-Translation.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>

    <bean id="translationDao" class="net.sf.mp.demo.tranxy.dao.impl.jpa.translation.TranslationJPAImpl" singleton="false" >
        <property name="entityManagerFactory"><ref bean="JPAtranxyFactory"/></property>
    </bean>
    <bean id="translationKeyDao" class="net.sf.mp.demo.tranxy.dao.impl.jpa.translation.TranslationKeyJPAImpl" singleton="false" >
        <property name="entityManagerFactory"><ref bean="JPAtranxyFactory"/></property>
    </bean>
    <bean id="translationRequestDao" class="net.sf.mp.demo.tranxy.dao.impl.jpa.translation.TranslationRequestJPAImpl" singleton="false" >
        <property name="entityManagerFactory"><ref bean="JPAtranxyFactory"/></property>
    </bean>

</beans>
It is the same for the dao-ext-JPA-Translation.xml, dao-ext-JPA-Tranxy.xml, dao-JPA-Tranxy.xml files

But wait a minute... How can I unit test?
You need two other artifacts before writting your own test.
One is persistence.xml...
Again? Yes, with a embedded connection pool, because the shipped with the build of your JPA2 layer may refere a JNDI Datasource (in case the property environment is set to remote).
Since it is under /src/test/resources/META-INF it will override the one in the JPA2 package.

Two is an adapter that extends AbstractTransactionalJUnit4SpringContextTests: it is generated in /src/test/java

package net.sf.mp.demo.tranxy.dao.face;

import javax.sql.DataSource;

import org.apache.commons.lang.StringUtils;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.jdbc.core.simple.SimpleJdbcTemplate;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.transaction.TransactionConfiguration;
import org.springframework.transaction.annotation.Transactional;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={
     "classpath:net/sf/mp/demo/tranxy/factory/spring/spring-config-Tranxy-BE-main.xml"
})
@TransactionConfiguration(transactionManager = "tranxyTransactionManager") 
@Transactional
public class AdapterTranxyTestDao extends AbstractTransactionalJUnit4SpringContextTests { 

 @Override
 @Autowired
 public void setDataSource(@Qualifier(value = "tranxyDataSource") DataSource dataSource) {
    this.simpleJdbcTemplate = new SimpleJdbcTemplate(dataSource);
 }
...

CXF layer

Each entity have a Rest Resource artifact with JAX-RS annotations to enable CRUD access.
Example with Translation

/**
 * Copyright (c) minuteproject, minuteproject@gmail.com
 * All rights reserved.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License")
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 * More information on minuteproject:
 * twitter @minuteproject
 * wiki http://minuteproject.wikispaces.com 
 * blog http://minuteproject.blogspot.net
 * 
*/
/**
 * template reference : 
 * - name : CXFSpringEntityResource
 * - file name : CXFSpringEntityResource.vm
*/
package net.sf.mp.demo.tranxy.rest.translation;


import java.util.Date;
import java.util.List;
import java.util.ArrayList;
import java.io.*;
import java.sql.*;

import javax.servlet.http.*;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.FormParam;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.PUT;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Request;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import javax.xml.bind.JAXBElement;

import net.sf.mp.demo.tranxy.dao.face.translation.TranslationDao;
import net.sf.mp.demo.tranxy.dao.face.translation.TranslationExtDao;
import net.sf.mp.demo.tranxy.domain.translation.Translation;

/**
 *
 * <p>Title: TranslationResource</p>
 *
 * <p>Description: remote interface for TranslationResource service </p>
 *
 */
@Path ("/rest/xml/translations")
@Produces ({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
@Consumes ({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
@Service
@Transactional
public class TranslationResource  {
 

 @Autowired
 @Qualifier("translationDao")
 TranslationDao translationDao;
 
 @Autowired
 @Qualifier("translationExtDao")
 TranslationExtDao translationExtDao;

//MP-MANAGED-UPDATABLE-BEGINNING-DISABLE @FIND_ALL-translation@
    @GET
    @Produces ({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) 
    public List<Translation> findAll () {
  List<Translation> r = new ArrayList<Translation>();
        List<Translation> l = translationDao.searchPrototypeTranslation(new Translation());
  for (Translation translation : l) {
   r.add(translation.flat());
  }
  return r;
    }
//MP-MANAGED-UPDATABLE-ENDING

//MP-MANAGED-UPDATABLE-BEGINNING-DISABLE @FIND_BY_ID-translation@
    @GET
    @Path("{id}")
    @Produces ({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})  
    public Translation findById (@PathParam ("id") java.lang.Long id) {
        Translation _translation = new Translation ();
  _translation.setId(id);
  _translation = translationExtDao.getFirstTranslation(_translation);
  if (_translation!=null) return _translation.flat();
  return new Translation ();
    }
//MP-MANAGED-UPDATABLE-ENDING

    @DELETE
    @Path("{id}")
    public void delete (@PathParam ("id") Long id) {
        Translation translation = new Translation ();
        translation.setId(id);
        translationDao.deleteTranslation(translation);
    }

    @POST
    @Produces ({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) 
    @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
    public Translation create (
        @FormParam("id") Long id,
        @FormParam("translation") String translation,
        @FormParam("language") Integer language,
        @FormParam("key") Long key,
        @FormParam("isFinal") Short isFinal,
        @FormParam("dateFinalization") Date dateFinalization,
        @FormParam("translator") Long translator,
        @Context HttpServletResponse servletResponse
        ) throws IOException {
        Translation _translation = new Translation (
           id,
           translation,
           language,
           key,
           isFinal,
           dateFinalization,
           translator);
        return save(_translation);
    }

    @PUT
    @Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
    public Translation save(JAXBElement<Translation> jaxbTranslation) {
        Translation translation = jaxbTranslation.getValue();
        if (translation.getId()!=null)
            return translationDao.updateTranslation(translation);
        return save(translation);
    }

 public Translation save (Translation translation) {
  translationDao.saveTranslation(translation);
  return translation;
 }


}

And two files for the web application and spring in /src/main/resources/webapp/WEB-INF

Web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">

    <display-name>tranxy CXF REST</display-name>
    <description>tranxy CXF REST access</description>

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/application-context.xml</param-value>
    </context-param>

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <servlet>
        <servlet-name>CXFServlet</servlet-name>
        <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>CXFServlet</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>

</web-app>

application-context.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
 xmlns:context="http://www.springframework.org/schema/context"
 xmlns:tx="http://www.springframework.org/schema/tx"
 xmlns:jaxrs="http://cxf.apache.org/jaxrs"
 xsi:schemaLocation="
  http://www.springframework.org/schema/beans
  http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
  http://www.springframework.org/schema/context 
  http://www.springframework.org/schema/context/spring-context-3.0.xsd
  http://www.springframework.org/schema/tx
  http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
  http://cxf.apache.org/jaxrs
  http://cxf.apache.org/schemas/jaxrs.xsd">

    <import resource="classpath:META-INF/cxf/cxf.xml" />
    <import resource="classpath:META-INF/cxf/cxf-extension-jaxrs-binding.xml" />
    <import resource="classpath:META-INF/cxf/cxf-servlet.xml" />
    
    <context:component-scan base-package="net.sf.mp.demo.tranxy.rest"/>

    <import resource="classpath:net/sf/mp/demo/tranxy/factory/spring/spring-config-Tranxy-BE-main.xml"/>    
 
    <jaxrs:server id="restContainer" address="/">
        <jaxrs:serviceBeans>
   <!-- tranxy --> 
   <ref bean="applicationResource"/>
   <ref bean="languageResource"/>
   <ref bean="userResource"/>
   <!-- translation --> 
   <ref bean="translationResource"/>
   <ref bean="translationKeyResource"/>
   <ref bean="translationRequestResource"/>
 
   <!-- statements -->
        </jaxrs:serviceBeans>
    </jaxrs:server> 

</beans>

Package, deployment and test

Package

Before building the package there is a dependency shipped with minuteproject mp-bsla.x.y.jar to install. In /target/mp-bsla/ Run script: maven-install.cmd/sh
Build: >mvn clean package

The result is tranxyRestCxfApp.war in /rest/target

Deployment

Start tomcat
Drop tranxyRestCxfApp.war in /webapps
There is an embedded connection pool, so no configuration is needed on tomcat.

Test

USE `tranxy` ;
DELETE FROM application_x_key;
DELETE FROM translation;
DELETE FROM language_x_translator;
DELETE FROM language_x_speaker;
DELETE FROM request_key;

DELETE FROM application;
DELETE FROM translation_key;
DELETE FROM user;
DELETE FROM language;

INSERT INTO application (idapplication,  name, description, type) VALUES (-1,'Porphyry', 'OS application holding environment app', 'OPENSOURCE');
INSERT INTO application (idapplication,  name, description, type) VALUES (-2,'Minuteproject', 'Minuteproject app', 'OPENSOURCE');

INSERT INTO user (iduser, first_name, last_name, email) VALUES (-1,'test', 'lastName', 'test@test.me');
INSERT INTO user (iduser, first_name, last_name, email) VALUES (-2,'test2', 'lastName2', 'test2@test.me');

INSERT INTO language (idlanguage, code, description, locale) VALUES (-1, 'FR', 'France', 'fr');
INSERT INTO language (idlanguage, code, description, locale) VALUES (-2, 'ES', 'Spanish', 'es');                                                                                               
INSERT INTO language (idlanguage, code, description, locale) VALUES (-3, 'EN', 'English', 'en');          

INSERT INTO language_x_translator (language_id, user_id) VALUES (-1, -1);
INSERT INTO language_x_translator (language_id, user_id) VALUES (-2, -1);
INSERT INTO language_x_speaker (language_id, user_id) VALUES (-1, -1);
INSERT INTO language_x_speaker (language_id, user_id) VALUES (-2, -1);
   
INSERT INTO language_x_translator (language_id, user_id) VALUES (-1, -2);
INSERT INTO language_x_translator (language_id, user_id) VALUES (-2, -2);
INSERT INTO language_x_translator (language_id, user_id) VALUES (-3, -2);    

INSERT INTO translation_key (id, key_name, description) VALUES (-1, 'msg.user.name', 'user name');
INSERT INTO translation (id, translation, language_id, key_id, is_final, date_finalization, translator_id) 
VALUES (-1, 'nom', -1, -1, 1, '2012-04-04', -1);
INSERT INTO translation (id, translation, language_id, key_id, is_final, date_finalization, translator_id) 
VALUES (-2, 'apellido', -1, -2, 1, CURDATE(), -1);                                                                                                               
Now enter
http://localhost:8080/tranxyRestCxfApp/rest/xml/languages to get all the languages

This is the result

Now enter
http://localhost:8080/tranxyRestCxfApp/rest/xml/users/-1 to get the first user

This is the result

Conclusion

This article presented you how to get quickly a CRUD REST interface on top of your DB model.
Of course, you may not need CRUD for all entities and may you need more coarse grain functions to manipulate your model.
Next article will present you how with Statement Driven Development we can get closer to Use Case.

Friday, May 18, 2012

Minuteproject reverse-engineering short stories

Latvian JUG Riga presentation abstract

I have been contacted by the Latvian JUG to make a demonstration-oriented presentation over Minuteproject. This page covers the agenda of what I intend to show at Riga.
All the demos will start from scratch i.e. a DB model that is evolving across the presentation.
Main demos will target technologies: JPA2, REST, Spring, CXF, Openxava for reverse-engineering.
A new feature of Minuteproject is introduced: Statement Driven Development with a demostration for REST track.
Eventually a last demo will show how to get instantly a web site with Openxava.

Agenda

  • Minuteproject overview
  • Demo 1
    • Model sample (3 tables)
    •  JPA2 track generation
    • code review
    • write unit test
    • alter and customize generated code (ex: with validation annotations)
  • Enrichment facilities
    • Customisation
    • Declaritive conventions
  • Demo 2
    • Model has changed! (10+ tables)
    • generation & code review (convensions, what happened to your modified code?)
  • Generated-code integration technics
  • RESTIFY your backend
  • Demo 3
    • Add REST track on top of JPA2
    • Generate for REST-CXF/SpringMVC
    • Deploy on tomcat and test
  • SDD - Statement Driven Development
  • Demo 4
    • Add custom statement
    • Generate for REST
    • Deploy on tomcat and test
  • Goodies...
  • Demo 5 
    • Get instantly an Openxava web app
    • Deploy on tomcat and test
  • Extend Minuteproject
    • Add your own templates and tracks
  • Conclusion
  • FAQ

Wednesday, May 16, 2012

Statement driven development WYSIWYG for REST

In my previous article, I mentioned that minuteproject will have statement driven development features.
This page I disclose the potential of SDD while developing REST application.
Regarding the technologies I take:
  • REST with CXF
  • Spring Bean for DAO
  • JPA2 as persistence layer
Original WYSIWYG is revisited into What You STATE Is What You Get.

As fundamentals you need a Statement with is surrounding I/O.
The model against which the statement is applied is important but secundary.

As example for SQL, I take 3 queries:
  • select * from address where latitude between ? and ? and longitude between ? and ? and lcase(city) like ?
  • select * from address where addressid between ? and ?
  • select * from address where lcase(city) like ?
Those queries must be given 
  • a name
  • an input (replace question marks by sample value and provide name)
  • an output (deduce from the metadata recieved after executing the query)
And basically that's it.

Thursday, May 10, 2012

Statement driven development

Today most methodologies used model oriented approach.
It can be domain-driven or reverse-engineering, one common point is that they start from a static definition of a model. For domain-driven, it is the domain issued by the development language itself. For the reverse-engineering it starts from a static model structure definition ex: a xsd, wsdl, database schema.
Statement driven development focuses on the actions the developer can run on some models and not the models themselves.
Example: a select statement fetching all the group members a user with group-administrator-role can manage.
Here the statement can be materialized into a search query with sql as implementation.
The important thing for the developer is the query and its I/O not really the underneath model and ... not the decorative technology.

Why Statement-Driven-Development

Couple of articles and limitations make me think that statement driven approach can be a flexible and fast alternative for developpers.
Usually after defining an abstract model such as ORM, the user have to write his UC statements. This means that there is still this second step to perform.


The model is sometimes/often overkill. As a developer, you do not have to apprehend the entire model complexity before yielding productivity.

Limitation on standard

Like in the JOOQ article about function and stored procedure extracting the meta-information can reach some limits when entering in vendor specific particularities.

Native DSL

There is a trend that states that SQL is the only DSL you need.
Why should you wrap it with other technology abstraction that limit its power?

Bye-Bye Domain Objects

...Welcome DTO.
I/O are by essence DTO (why should they be the exact reflection of a persistence layer?). This situation is just a specific exception.
This exception is widely used by multiple apps/framework (strong resuability but limitations).
Remark:
This article is not there to cover the 'everlasting-debate' DO vs. DTO.
SDD just brings a new approach that do not exclude but complement the DDD/Rev-Eng one.

Concretely

MinuteProject for release 0.8.1+ (mid-may-2012) will offer Statement-Driven-Development facilities.

  • The user will focus one SQL statement.
  • The output will be deduce by the execution of it.
  • The input will be easily configurable.

Example

Here is a simple configuration. Statement-model is the new node.

  <model>
    ...
      <statement-model>
           <queries>
               <query name="get address street">
                  <query-body><value><![CDATA[select * from address where addressid > ?]]></value></query-body>
                  <query-params>
                      <query-param name="identifier_Address" is-mandatory="false" type="INT" sample="1"></query-param>
                  </query-params>
               </query>
           </queries>
      </statement-model>
  </model>

This configuration should be enough to get:
  • Input bean
  • Output bean
  • Output list bean
  •  All the technology decoration ex for 
    • REST CXF: 
      • Resource bean with REST path
      • spring config
      • Native DAO
Demonstration will be shipped in Minuteproject next release (0.8.1).

Integrating with REST is pretty much statement-driven: basically you just need to know the URL + its I/O.

Conclusion

  • With SDD you focus on the statement and the I/O.
  • Minuteproject simplifies the rest (technology wrapper).






Sunday, September 18, 2011

RESTication of Openxava app and Vaadin client

Article under construction

The purpose of this article is to provide REST interface for Openxava (OX) application.
It shows how-to
  • integrate jersey engine into the OX build
  • extends the JPA2 entities with JAXB and JAXRS annotations
  • write a little UC with methods that returns xml/json message
  • test it
  • Make a sample Vaadin client application for those REST services
To illustrate this, I will take an Openxava application generated by minuteproject (http://minuteproject.blogspot.com/2011/09/sql-select-to-web-application-in-couple.html)

Integrate Jersey engine in OX build

Jersey lib integration
Add the following libraries in your project /web/WEB-INF/lib
  • jsr305-1.3.2.jar
  • jersey-core-1.9-ea03.jar
  • jersey-server-1.9-ea03.jar
  • asm-3.1.jar (although asm.jar is shipped with OX this version is to used)
Here the dependency is on jersey-server-1.9-ea03

In your OX project add the file servlets.xml into /web/WEB-INF

   <servlet>
<servlet-name>petshop Jersey Web Application</servlet-name>
<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>com.sun.jersey.config.property.resourceConfigClass</param-name>
<param-value>com.sun.jersey.api.core.PackagesResourceConfig</param-value>
</init-param>
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>my.cool.report</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
<servlet-name>petshop Jersey Web Application</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>

Openxava build takes what you define in servlets.xml file and insert it in the web.xml
The snippet above specifies the following:
  • Jersey engine will parse the package for JAXRS annotation in the package "my.cool.report"
  • the context path used for REST will be /rest/*
JAXB & JAXRS integration

Add Jaxb and Jaxrs annotation in OX jpa2 entities

Class MyConferenceCoolReport
package my.cool.report.conference.domain.report;

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 javax.ws.rs.*;
import javax.ws.rs.core.*;
import javax.xml.bind.annotation.*;

import my.cool.report.conference.dto.report.*;

import org.openxava.annotations.*;
import org.openxava.jpa.*;


/**
*
*

Title: MyConferenceCoolReport


*
*

Description: Domain Object describing a MyConferenceCoolReport entity


*
*/
@Entity (name="MyConferenceCoolReport")
@Table (name="my_conference_cool_report")
@Views({
@View(
name="base",
members=
""
+ "firstName ; "
+ "lastName ; "
+ "email ; "
+ "status ; "
+ "addressStreet1 ; "
+ "addressStreet2 ; "
+ "countryCode ; "
+ "conferenceName ; "
+ "conferenceBeginDate ; "
+ "conferenceEndDate ; "
),
@View(
name="Create",
extendsView="base"
),
@View(
name="Update",
extendsView="base",
members=
""
),
@View(extendsView="base",
members=
""
),
@View(name="myConferenceCoolReportDEFAULT_VIEW",
members=
" id ;"
+ "firstName ; "
+ "lastName ; "
+ "email ; "
+ "status ; "
+ "addressStreet1 ; "
+ "addressStreet2 ; "
+ "countryCode ; "
+ "conferenceName ; "
+ "conferenceBeginDate ; "
+ "conferenceEndDate ; "
)
})

@Tabs({
@Tab(
properties=
" firstName "
+", lastName "
+", email "
+", status "
+", addressStreet1 "
+", addressStreet2 "
+", countryCode "
+", conferenceName "
+", conferenceBeginDate "
+", conferenceEndDate "
)
,
@Tab(
name = "MyConferenceCoolReportTab",
properties=
" firstName "
+", lastName "
+", email "
+", status "
+", addressStreet1 "
+", addressStreet2 "
+", countryCode "
+", conferenceName "
+", conferenceBeginDate "
+", conferenceEndDate "
)
,
@Tab(
name = "MyConferenceCoolReportTabWithRef",
properties=
" firstName "
+", lastName "
+", email "
+", status "
+", addressStreet1 "
+", addressStreet2 "
+", countryCode "
+", conferenceName "
+", conferenceBeginDate "
+", conferenceEndDate "
)
})
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType
@XmlRootElement
@Path ("/myreports")
public class MyConferenceCoolReport {

@Hidden @Id @Column(name="id" )
private Long id;

@Column(name="first_name", length=255, nullable=false, unique=false)
@Required
private String firstName;
@Column(name="last_name", length=255, nullable=false, unique=false)
@Required
private String lastName;
@Column(name="email", length=255, nullable=false, unique=false)
@Required
private String email;
@Column(name="status", length=45, nullable=false, unique=false)
@Required
private String status;
@Column(name="address_street1", length=255, nullable=true, unique=false)
private String addressStreet1;
@Column(name="address_street2", length=255, nullable=true, unique=false)
private String addressStreet2;
@Column(name="country_code", length=45, nullable=false, unique=false)
@Required
private String countryCode;
@Column(name="conference_name", length=255, nullable=false, unique=false)
@Required
private String conferenceName;
@Column(name="conference_begin_date", nullable=true, unique=false)
private Date conferenceBeginDate;
@Column(name="conference_end_date", nullable=true, unique=false)
private Date conferenceEndDate;

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

public Long getId() {
return id;
}

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

public String getFirstName() {
return firstName;
}

public void setFirstName (String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}

public void setLastName (String lastName) {
this.lastName = lastName;
}
public String getEmail() {
return email;
}

public void setEmail (String email) {
this.email = email;
}
public String getStatus() {
return status;
}

public void setStatus (String status) {
this.status = status;
}
public String getAddressStreet1() {
return addressStreet1;
}

public void setAddressStreet1 (String addressStreet1) {
this.addressStreet1 = addressStreet1;
}
public String getAddressStreet2() {
return addressStreet2;
}

public void setAddressStreet2 (String addressStreet2) {
this.addressStreet2 = addressStreet2;
}
public String getCountryCode() {
return countryCode;
}

public void setCountryCode (String countryCode) {
this.countryCode = countryCode;
}
public String getConferenceName() {
return conferenceName;
}

public void setConferenceName (String conferenceName) {
this.conferenceName = conferenceName;
}
public Date getConferenceBeginDate() {
return conferenceBeginDate;
}

public void setConferenceBeginDate (Date conferenceBeginDate) {
this.conferenceBeginDate = conferenceBeginDate;
}
public Date getConferenceEndDate() {
return conferenceEndDate;
}

public void setConferenceEndDate (Date conferenceEndDate) {
this.conferenceEndDate = conferenceEndDate;
}

@Transient
@GET
@Path("{reportId}")
@Produces ({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public MyConferenceCoolReport findById (@PathParam ("reportId") Long reportId) {
EntityManager em = XPersistence.getManager();
MyConferenceCoolReport p = em.find(MyConferenceCoolReport.class, reportId);
return p;
}

@Transient
@GET
@QueryParam("{countryCode}")
@Produces ({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public MyConfReports find (@QueryParam ("countryCode") String countryCode) {
String q = null;
EntityManager em = XPersistence.getManager();
if (countryCode!=null) {
q = "select m from MyConferenceCoolReport m where countryCode = '"+countryCode+"'";
} else
q = "select m from MyConferenceCoolReport m ";
Query query = em.createQuery(q);
List<myconferencecoolreport> l = query.getResultList();
MyConfReports m = new MyConfReports();
m.setMyCoolReport(l);
return m;
}

}

Annotation of entities
The previous snippet indicates that entity MyConferenceCoolReport contains a transient method (findById) use for REST GET operation and using the param reportId to load an entity. It can be returned either as Xml or in a Json format.
It contains also a method find that takes a parameter a country code and return a DTO based on the class MyConfReports

package my.cool.report.conference.dto.report;
package my.cool.report.conference.dto.report;

import java.util.*;

import javax.xml.bind.annotation.*;

import my.cool.report.conference.domain.report.*;

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType
@XmlRootElement
public class MyConfReports {

private List<MyConferenceCoolReport> myCoolReport;

public MyConfReports () {
}

public List<MyConferenceCoolReport> getMyCoolReport() {
return myCoolReport;
}

public void setMyCoolReport(List<MyConferenceCoolReport> myCoolReport) {
this.myCoolReport = myCoolReport;
}

}
Deploy
Use the ant build on your project.
Remark: do not forget when using openxava with eclipse to set "Build Automatically" on the "Project" tab.

Testing
Via the browser type
http://localhost:8080/conference/rest/myreports?countryCode=BE to see all the


http://localhost:8080/conference/rest/myreports/ it returns in an XML format.




Vaadin app client
//todo

Conclusion
//todo