Skip navigation

Monthly Archives: January 2008



Using middlegen to generate hbm and java

Discussion

This article is about how to use middlegen to generate hibernate mapping xml file and POJO.

ant build.xml


<?xml version="1.0"?>
<!DOCTYPE project[
  <!ENTITY database SYSTEM "file:./db/mysql.xml">
]>
<project name="bug_management" basedir="." default="all">
  <property name="name" value="genkiya" />
  <property name="package" value="jp.co.bug_management.common.entity" />
  <property name="gui" value="true" />
  <property name="lib.dir" value="${basedir}/lib" />
  <property name="src.dir" value="${basedir}/src" />
  <property name="db.dir" value="${basedir}/db" />

  &database;
  <path id="lib.class.path">
    <pathelement path="${database.driver.classpath}"/>
    <fileset dir="${lib.dir}">
      <include name="*.jar"/>
    </fileset>
  </path>
  <!-- ====================== -->
  <!-- Task Initilization                        -->
  <!-- ======================-->
  <target name="init">
    <available 
      property="xdoclet1.2+" 
      classname="xdoclet.modules.hibernate.HibernateDocletTask" 
      classpathref="lib.class.path"/>
  </target>
  
  <!-- ====================== -->
  <!-- Fails if XDoclet 1.2.x is not on classpath -->
  <!-- ====================== -->
  
  <!--target name="fail-if-no-xdoclet-1.2" unless="xdoclet1.2+">
    <fail>
    You must download several jar files before you can build Middlegen.
    Execute the "download-deps" target. Then try to build again.
    
    If you are behind a proxy, you should define the properties
    http.proxyHost and http.proxyPort. Example:
    
    ant -Dhttp.proxyHost=foo.com -Dhttp.proxyPort=8080
    
    It's also possible to download the jars manually.
    </fail>
  </target-->
  
  <!-- ====================== -->
  <!-- Create tables                                -->
  <!-- ====================== -->
  
  <!--target name="create-tables" 
    depends="init,fail-if-no-xdoclet-1.2,
      check-driver-present,panic-if-driver-not-present"
    description="Create tables">
    <echo>Create tables using URL ${database.url}</echo>
    <sql 
      classpath="${database.driver.classpath}"
      driver="${database.driver}"
      url="${database.url}"
      userid="${database.userid}"
      password="${database.password}"
      src="${database.script.file}"
      print="true"
      output="result.txt"/>
  </target-->
  
  <target name="check-driver-present">
    <available file="${database.driver.file}" 
      type="file" property="driver.present"/>
  </target>
  
  <target name="panic-if-driver-not-present" 
    unless="driver.present">
    <fail>
    The JDBC driver you have specified by 
    including one of the files in ${basedir}/lib
    doesn't exist. You have to download this 
    driver separately and put it in ${database.driver.file}
    Please make sure you're using a version 
    that is equal or superior to the one we looked for.
    If you name the driver jar file differently, 
    please update the database.driver.file property
    in the ${basedir}/db/xxx.xml file accordingly.
    </fail>
  </target>
  
  <!-- =================== -->
  <!-- Run Middlegen                   -->
  <!-- =================== -->
  
  <target 
    name="middlegen"
    description="Run Middlegen"
    unless="middlegen.skip"
    depends="init,check-driver-present,
        panic-if-driver-not-present">
    
    
    <taskdef 
      name="middlegen"
      classname="middlegen.MiddlegenTask"
      classpathref="lib.class.path"/>
      
    <middlegen 
      appname="${name}"
      prefsdir="${src.dir}"
      gui="${gui}"
      databaseurl="${database.url}"
      initialContextFactory="${java.naming.factory.initial}"
      providerURL="${java.naming.provider.url}"
      datasourceJNDIName="${datasource.jndi.name}"
      driver="${database.driver}"
      username="${database.userid}"
      password="${database.password}"
      schema="${database.schema}"
      catalog="${database.catalog}"
      includeViews="false">
      
      <hibernate
        destination="${src.dir}/jp/co/bug_management/common/hbm"
        package="${package}"
        genXDocletTags="true"
        javaTypeMapper=
            "middlegen.plugins.hibernate.HibernateJavaTypeMapper">
      </hibernate>
    </middlegen>
  </target>
  
  <!-- =================== -->
  <!-- Build Everything                 -->
  <!-- =================== -->
  
  <target name="all" depends="middlegen" description="Build Everything"/>
  
  <!-- =================== -->
  <!-- Genernate ValueObject From Mapping Files -->
  <!-- =================== -->
  
  <target 
    name="hbm2java" 
    depends="middlegen" 
    description="Generate .java from .hbm files.">
    
    <taskdef 
      name="hbm2java"
      classname=
        "net.sf.hibernate.tool.hbm2java.Hbm2JavaTask"
      classpathref="lib.class.path"/>
      
    <hbm2java 
      output="${src.dir}"
      classpathref="lib.class.path">
      <fileset dir="${src.dir}">
        <include name="**/*.hbm.xml"/>
      </fileset>
    </hbm2java>
  </target>
</project>


file:./db/mysql.xml :
   <!-- ============================================ -->
   <!-- ant properties/targets for mysql                                                      -->
   <!-- note: this is not a proper xml file  (there is no root element)     -->
   <!--       it is intended to be imported from a *real* xml file              -->
   <!-- ============================================ -->

  <property name="database.script.file" 
      value="${db.dir}/create_dlfl.sql"/>
  <property name="database.driver.file"
      value="${lib.dir}/mysql-connector-java-5.0.7-bin.jar"/>
  <property name="database.driver.classpath"
      value="${database.driver.file}"/>
  <property name="database.driver"
      value="com.mysql.jdbc.Driver"/>
  <property name="database.url"
      value="jdbc:mysql://127.0.0.1:3306/bug_management"/>
  <property name="database.userid"
      value="root"/>
  <property name="database.password"
      value="mysql"/>
  <property name="database.schema"
      value="bug_management"/>
  <property name="database.catalog"
      value=""/>


Required jar files:

commons-collections-3.2.jar
commons-lang-2.3.jar
commons-logging-1.1.jar
commons-pool-1.3.jar
commons-validator-1.3.1.jar
hibernate2.jar
hibernate-tools.jar
jdom.jar
log4j-1.2.8.jar
middlegen-2.1.jar
middlegen-hibernate-plugin-2.1.jar
mysql-connector-java-5.0.7-bin.jar
velocity-1.4-dev.jar
xdoclet-hibernate-module-1.2.2-RC1.jar

put all these jar files into /lib directory.
Run ant task [all] or [hbm2java].



Handle Exception two ways in Web app

Discussion
In J2ee web application, there are two types of errors in the Web Tier:


  • HTTP Errors (400 – 500 series response)
    Communicate problems to Web clients.
  • Java Exceptions
    Communicate problems to Web container.



Exceptions can be generated within the tier or passed back from other tiers.
If you want exceptions to be passed on to a client, you must convert them to HTTP errors.



Exceptions thrown to the client

  • javax.servlet.ServletException
  • javax.io.IOException


In J2ee Web application, javax.servlet.ServletException and java.io.IOException will be thrown to the client.
All the exceptions will be wrapped into ServletException and thrown to the client, if you do not handle them by yourself.
So, we decide handling exceptions two ways:

  1. Handle Exceptions which are thrown under Action tier using ExceptionHandler in Struts (see post: Exception Handling in Struts)
  2. Handle Exceptions which are thrown over Action tier by configurating web.xml using tag <error-page>


approach 2:

In web.xml


<error-page>
    <exception-type>javax.servlet.ServletException</exception-type>
    <location>/error.do</location>
</error-page>
<error-page>
    <exception-type>javax.io.IOException</exception-type>
    <location>/error.do</location>
</error-page>


Define ErrorAction to handle exceptions


public class ErrorAction extends Action {
     /**
     * The request attribute under which we 
     * forward an HTTP status message
     * (as an object of type STring) to an error page.
     */
    public static final String ERROR_MESSAGE_ATTR =
        "javax.servlet.error.message";
    /**
     * The request attribute under which we 
     * forward a Java exception type
     * (as an object of type Class) to an error page.
     */
    public static final String EXCEPTION_TYPE_ATTR =
        "javax.servlet.error.exception_type";
    /**
     * The request attribute under which we 
     * forward the request URI
     * (as an object of type String) of the page on which 
     * an error occurred.
     */
    public static final String EXCEPTION_PAGE_ATTR =
        "javax.servlet.error.request_uri";
    /**
     * The request attribute under which we 
     * forward a Java exception
     * (as an object of type Throwable) to an error page.
     */
    public static final String EXCEPTION_ATTR =
        "javax.servlet.error.exception";
    /**
     * The request attribute under which we 
     * forward an HTTP status code
     * (as an object of type Integer) to an error page.
     */
    public static final String STATUS_CODE_ATTR =
        "javax.servlet.error.status_code";

    public ActionForward execute(
            ActionMapping mapping, 
            ActionForm form,
            HttpServletRequest request, 
            HttpServletResponse response)
            throws Exception {
        // ErrorMessage (java.lang.String)
        Object errorMsg = 
            request.getAttribute(ERROR_MESSAGE_ATTR);
        // Exception (java.lang.Exception)
        Object exception = 
            request.getAttribute(EXCEPTION_ATTR);
        // Exception Class (java.lang.Class)
        Object exceptionType = 
            request.getAttribute(EXCEPTION_TYPE_ATTR);
        // uri (java.lang.String)
        Object page = 
            request.getAttribute(EXCEPTION_PAGE_ATTR);
        // Error Code (java.lang.Integer)
        Object errorCode = 
            request.getAttribute(STATUS_CODE_ATTR);
        
        ActionErrors errors = 
            (ActionErrors) request.getAttribute(
                Globals.ERROR_KEY);
        
        if(errors == null) {
            errors = new ActionErrors();
        }
        errors.add("innerError", 
            new ActionMessage("errors.inner.error", 
                errorMsg, exception, errorCode, page));
        saveErrors(request, errors);
        return mapping.findForward("toErrorPage");
    }
}


In struts-config.xml, define ErrorAction


<action
    path="/error"
    type="org.springframework.web.struts.DelegatingActionProxy"
    scope="request">
        <forward name="toErrorPage" path="/error_page.jsp"/>
</action>


MessageResources.properties:


errors.inner.error=Error Message: {0} 
<br />Error Type : {1} <br />Error Code : {2} 
<br />Request URI : {3}


Jsp File


<span class=”errorMsg”><html:errors /></span>


Doing so, and handling exception in struts using ExceptionHandler,
you can handle the exceptions thrown not only under Action tier, but also over the Action tier.



Exception Handling in Struts

Discussion
Exception is hard to aviod in our application. So we just catch it, log it, show error messages in Error Page.
There are lots of appoach to do this. Now I just introduce one of them.
Extending Struts’ ExceptionHandler and using <global-exceptions> tag in struts-config.xml

The following is the sample code:


public class MyExceptionHandler extends ExceptionHandler {
    public ActionForward execute(
            Exception ex, 
            ExceptionConfig ae,
            ActionMapping mapping, 
            ActionForm formInstance,
            HttpServletRequest request, 
            HttpServletResponse response)
            throws ServletException {
        ActionForward forward = null;
        ActionErrors errors = 
            (ActionErrors) request.getAttribute(
                Globals.ERROR_KEY);
        if(ex instanceof PdfGenerateException) {
            if(errors == null) {
                errors = new ActionErrors();
            }
            errors.add(new ActionMessage(ae.getKey()));
        }
        if(ex instanceof WrongQueryStringException) {
            if(errors == null) {
                errors = new ActionErrors();
            }
            errors.add(new ActionMessage(ae.getKey()));
        }
        if(errors != null && !errors.isEmpty()) {
            request.setAttribute(Globals.ERROR_KEY, errors);
        }
        if (ae.getPath() != null) {
            forward = new ActionForward(ae.getPath());
        } else {
            forward = 
                super.execute(
                    ex, ae, mapping, formInstance, request, response);
        }
        return forward;
    }
}

In struts-config.xml, define <global-exceptions>


<global-exceptions>
    <exception
        key="errors.generate.pdf" 
        handler="jp.co.gky.handler.MyExceptionHandler"
        path="/error.do"
        type="jp.co.gky.exception.PdfGenerateException" />
    <exception
        key="errors.request.error" 
        handler="jp.co.gky.handler.MyExceptionHandler"
        path="/error.do"
        type="jp.co.gky.exception.WrongQueryStringException" />
</global-exceptions>

MessageResources.properties:


errors.request.error=QueryString "pageNo" is not a integer.
errors.generate.pdf=Exception occured when generating pdf file.
errors.illegal.argument=Illegal Argument {0}

Jsp File

<span class=”errorMsg”><html:errors /></span>



Implement download file

Discussion
Sometimes maybe the business logic needs to generate some *.csv, *.pdf, *.txt, etc to be available for user downloading.
Struts 1.2x, supports DownloadAction to implement downloading file.
struts-extras-1.2.x.jar must be added in classpath.
DownloadAction is an abstract class, and you should override


protected StreamInfo getStreamInfo( 
    ActionMapping mapping, 
    ActionForm form, 
    HttpServletRequest request, 
    HttpServletResponse response) 
    throws Exception

method.

In DownloadAction, there is an inner interface StreamInfo and two implement inner class FileStreamInfo and ResourceStreamInfo.
The FileStreamInfo simplifies the downloading of a file from the disk. and ResourceStreamInfo simplifies the downloading of a web application resource.

When overriding getStreamInfo() method, just set ContentType, ServletContext, web application resource’s path and response header if using ResourceStreamInfo as
StreamInfo’s implement class.
If using FileStreamInfo, just set ContentType, java.io.File and response’s header, when overriding getStreamInfo() method.

The following is the sample code:

CsvDownloadAction (using ResourceStreamInfo)


public class CsvDownloadAction 
  extends DownloadAction { 
  protected StreamInfo getStreamInfo( 
      ActionMapping mapping, 
      ActionForm form, 
      HttpServletRequest request, 
      HttpServletResponse response) 
      throws Exception { 
      ServletContext servletCtx = 
        request.getSession(false).getServletContext(); 
      String contentType = "application/octet-stream"; 
      String fileName = "abc.csv"; 
      String path="WEB-INF/csv/" + fileName; 
      response.setHeader( 
        "Content-Disposition", 
        "attachment;filename=" + fileName); 
      return 
        new ResourceStreamInfo( 
          contentType, servletCtx, path); 
  } 
}

CsvDownloadAction (using FileStreamInfo)


public class CsvDownloadAction 
  extends DownloadAction { 
  protected StreamInfo getStreamInfo( 
      ActionMapping mapping, 
      ActionForm form, 
      HttpServletRequest request, 
      HttpServletResponse response) 
      throws Exception { 
      ServletContext servletCtx = 
        request.getSession(false).getServletContext(); 
      String contentType = "application/octet-stream"; 
      String fileName = "abc.csv"; 
      String path = servletCtx.getRealPath("/") +
         "WEB-INF/csv/" + fileName;
      response.setHeader( 
        "Content-Disposition", 
        "attachment;filename=" + fileName); 
      return 
        new FileStreamInfo(contentType, new File(path));
  } 
}

Jsp File

<html:link: path=”/csvDownload.do”>abc.csv</html:link>