Sunday, June 6, 2010

Networking between host and guest VM with VirtualBox

I had assumed that the host and guest would be able to "see" each other on the network by default but this is not the case.   A little setup is required.


Create a 2nd Network adapter set to host only in VirutalBox
It should now appear in Windows as a new adapter.  Set the IP properties to have an IP and subnet of your choosing, eg IP of 10.1.1.10


Now fire up the VM and set the new host-only adapter to have the IP of eg 10.1.1.11 and the same subnet....voila..


In a nutshell thats it they should now be part of the same network.

Monday, May 31, 2010

Maven dependency helpers

All my projects now use Maven, most of these have a parent and many sub-projects and sub-projects etc as it is logical to breakup your project.  There is even a "common" project for core functionality.  Each project has a number of dependencies and it is common to have developed one sub-project that has different version of a dependency.  Always good if these can be cleaned up so there are Maven plugins that can help with this kind of thing.


Checking for new plugin updates:
http://mojo.codehaus.org/versions-maven-plugin/examples/display-plugin-updates.html



mvn versions:display-dependency-updates


Checking for version clashes:
http://hexapixel.com/software/maven-version-clash-plugin
Follow the install and run instructions on the above link.
I found this one best put in your parent pom as a build plugin so its run each compile - use a profile if you don't want it to run like this.



Dependency tree:
http://maven.apache.org/plugins/maven-dependency-plugin/tree-mojo.html
mvn dependency:tree


Thursday, April 8, 2010

Cayenne JNDI under JBoss

Cayenne JNDI under JBoss

I had already used Cayenne with JNDI under Tomcat without issues.  It is documented simply and is easy to implement.  But under JBoss if you have little experience with the product you run into a few more hurdles.

But here are the step I used under Cayenne 3 and JBoss 5.1

1. Copy the JDBC drivers to be used under server/default/lib

2. Cayenne modeler jar is required under WEB-INF/lib (excluding openmvc and jgoodies).  I used Maven to drive this by adding the following to your pom.xml:
        <dependency>
            <groupId>org.apache.cayenne</groupId>
            <artifactId>cayenne-modeler</artifactId>
            <version>3.0RC2</version>
            <exclusions>
                <exclusion>
                    <groupId>org.scopemvc</groupId>
                    <artifactId>scopemvc</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>jgoodies</groupId>
                    <artifactId>looks</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

3. For each DB connection create a file under ${jboss-home}\server\default\deploy create a *-ds.xml file:
<datasources>
    <local-tx-datasource>
        <jndi-name>BREDomain</jndi-name>
        <connection-url>jdbc:jtds:sqlserver://xxxxxxxxxx</connection-url>
        <driver-class>net.sourceforge.jtds.jdbc.Driver</driver-class>
        <user-name>jboss</user-name>
        <password>xxxxxxxxx</password>
    </local-tx-datasource>
</datasources>

It just needs the extension "-ds.xml" and JBoss will pick it up automatically

 
4. Using cayenne modeler select JNDIDataSourceFactory and set location to "jdbc/MyDS"

A couple of steps required in your web application (war file) 
5. WEB-INF/web.xml:
  <resource-ref>
        <res-ref-name>jdbc/MyDS</res-ref-name>
        <res-type>javax.sql.DataSource</res-type>
        <res-auth>Container</res-auth>
    </resource-ref>
 
6. WEB-INF/jboss-web.xml:
<jboss-web>
    <resource-ref>
        <res-ref-name>jdbc/MyDS</res-ref-name>
        <res-type>javax.sql.DataSource</res-type>
        <jndi-name>java:/MyDS</jndi-name>
    </resource-ref>
 




Tuesday, March 23, 2010

Maven Documentation woes

Maven Documentation generation woes 


For a while I have used APT (Almost Plain Text) for documenting a lot of my Java projects that use Maven.  There are good reasons behind this but at times the wheels do start to fall off a little.  They did while I was trying putting together (and still am) my notes on developing a web app with Apache projects Click and Cayenne.  I write the doco alongside the code and was always aiming to have Maven generate it so I could send it up as a blog.  Well it is easy enough to change the default Maven site layout when generating your doco, as I will describe below.  But the problem was I could not with the Google based blog tools upload the HTML and the images without editing it again on the blog site (pointing the image locations in the HTML).  So I thought, ok I'll upload/import the RTF or some other Maven/Doxia generated output to Google docs (which can then be sent to this blog).  The RTF output does require a lot more work and is IMO clunky, needing work for sure.  Such a good start to a product but still lacking momentum.  So anyway I will quickly show how I did the above 2 differtn things.

Change your site layout


Download the Velocity template default-site.vm and copy it to your Maven documentation project under src/site/site.vm.  If you know a thing or 2 about Velocity it will be easy to change this to your needs.

To include this in the "mvn site" run add this to your pom.xml:

            <plugin>
                <artifactId>maven-site-plugin</artifactId>
                <configuration>
                    <locales>en</locales>
                    <templateDirectory>src/site</templateDirectory>
                    <template>site.vm</template>
                </configuration>
            </plugin>

Then run "mvn clean site" and the changes made to the site.vm will be picked up.  I used this as a simple way to remove the nav and header panel and just create a single page.

Generate PDF/RTF etc

To do this the solution is right now not right OOB.  You can generate pdf by using "mvn pdf:pdf" and get a pretty nice result.  But if you want some other outputs then you have to use the Doxia plugin.  All these output are sourced from your APT and FML etc from the doco for your project.

The one challenge again is to do with images.  If you see an error such as "could not find URL"

Edit your pom.xml for the following.

Copy the src images to the target before the target is run:

        <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-antrun-plugin</artifactId>
                <executions>
                    <execution>
                        <id>book</id>
                        <phase>pre-site</phase>
                        <configuration>
                            <tasks>
                                <copy todir="${project.build.directory}/generated-site/pdf/book">  <!-- the book folder must be the same name as the ID tag of the book in the src/book.xml defined in the doxia-maven plugin below -->
                                    <fileset dir="${basedir}/src/site/resources/"/>
                                </copy>
                                <copy todir="${project.build.directory}/generated-site/rtf/book">
                                    <fileset dir="${basedir}/src/site/resources/"/>
                                </copy>
                            </tasks>
                        </configuration>
                        <goals>
                            <goal>run</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>

Run the doxia plugin when the site phase is run:

  <plugin>
                <groupId>org.apache.maven.doxia</groupId>
                <artifactId>doxia-maven-plugin</artifactId>
                <version>1.1.2</version>
                <executions>
                    <execution>
                        <phase>site</phase>
                        <goals>
                            <goal>render-books</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <books>
                        <book>
                            <directory>src/site/apt</directory>
                            <descriptor>src/book.xml</descriptor>
                            <formats>
                                <!--format>
                                  <id>latex</id>
                                </format>
                                <format>
                                  <id>xdoc</id>
                                </format>
                                <format>
                                  <id>pdf</id>
                                </format-->
                                <format>
                                    <id>rtf</id>
                                </format>
                            </formats>
                        </book>
                    </books>
                </configuration>
            </plugin>


The "book.xml" defines the structure of the output doc:

<book xmlns="http://maven.apache.org/BOOK/1.0.0"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://maven.apache.org/BOOK/1.0.0 ../../../doxia/doxia-book/target/generated-site/xsd/book-1.0.0.xsd">
    <id>book</id>
    <title>Click Cayenne Recipies</title>
    <chapters>
        <chapter>
            <id>intro</id>
            <title>Intro</title>
            <sections>
                <section>
                    <id>intro</id>
                </section>
            </sections>
        </chapter>

Sunday, March 21, 2010

Scheduling with Quartz and Spring

Scheduling with Quartz and Spring

This has been covered in various forms for quite some time but here is my preferred solution to running a job from your app server.  Typical use cases for this are reports, system status checks or cleanup operations.


What is the JavaBean/POJO to be called?

 <bean name="systemMonitorBean" class="com.myco.HostPortMonitor">

        <property name="serverList" value="${system.monitoring.serverport.locations}"/>
        <property name="timeout" value="${system.monitoring.serverport.timeout}"/>
        <property name="emailTo" value="${system.monitoring.serverport.emailto}"/>
        <property name="emailSender" ref="emailSender"/>


 The properties above are grabbed from a file on the classpath: 

<context:property-placeholder location="classpath*:myapp.properties"/>


I find the best way to call the Java class above is to get Spring to call a method on it:

   <bean id="methodInvokingJob" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
        <property name="targetObject" ref="systemMonitorBean"/>
        <property name="targetMethod" value="checkSystems"/>
    </bean>


Now this effectively is a Quartz "job", ie the pojo and its specified method have become the subject of a Quartz job and can therefore be called by Quartz.  So the rest of the setup is to have a trigger, in this case a simple check every hour (time is defined in milliseconds):


    
    <bean id="serverPortMonitoringTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerBean">
        <property name="jobDetail" ref="methodInvokingJob"/>
        <property name="startDelay" value="10000"/>
        <property name="repeatInterval" value="3600000"/>
    </bean>




You could use a CRON trigger as well: http://www.quartz-scheduler.org/docs/tutorials/crontrigger.html

Final step is to have a scheduler running that can call multiple triggers:

    <bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
        <property name="triggers">
            <list>
                <ref bean="serverPortMonitoringTrigger"/>
            </list>
        </property>
    </bean>

Very easy.

Used in this were some other helper classes.  The Java class being called is just a simple check if a server:port is running and email out:  

public class HostPortMonitor extends BaseSystemMonitor {

    private String serverList;
    private Integer timeout;

    private List<ServerPort> serverPortList = new ArrayList<ServerPort>();

    @Override
    public void checkSystems() {
        if (ObjectUtil.isEmpty(timeout) || timeout < 100) {
            timeout = 10000;
        }
        for (ServerPort serverPort : serverPortList) {
            log.info("Checking system up/down for: " + serverPort.getServerName() + ":" + serverPort.getPort());
            try {
                if (NetworkUtils.checkServerPort(serverPort.getServerName(), serverPort.getPort(), timeout)) {
                    String checkStatus = "OK";
                    sendEmail(serverPort, checkStatus);
                } else {
                    String checkStatus = "FAILED - No Connect";
                    sendEmail(serverPort, checkStatus);
                }
            } catch (UtilException e) {
                log.error("Could not check server:port - " + serverPort.getServerName() + ":" + serverPort.getPort(), e);
                String checkStatus = "FAILED - " + e.getMessage();
                sendEmail(serverPort, checkStatus);
            }
        }
    }

    private void sendEmail(ServerPort serverPort, String checkStatus) {
        StringBuilder body = buildMessageBody(serverPort, checkStatus);
        try {
            log.info("Sending email for system up/down: " + body);
            emailSender.sendGenericEmail(emailTo, "Monitor: " + checkStatus, body.toString());
        } catch (MessagingException e) {
            log.warn("While emailing system monitor report I received error: " + e.getMessage());
        }
    }

    private StringBuilder buildMessageBody(ServerPort serverPort, String checkStatus) {
        StringBuilder body = new StringBuilder();
        body.append("System check: ").append(checkStatus).append(TextUtil.getLineSeparator());
        body.append("To server:port - ").append(serverPort.getServerName());
        body.append("At time: ").append(new Date()).append(TextUtil.getLineSeparator());
        body.append(":").append(serverPort.getPort()).append(TextUtil.getLineSeparator());
        return body;
    }

    public Integer getTimeout() {
        return timeout;
    }

    public void setTimeout(Integer timeout) {
        this.timeout = timeout;
    }

    public String getServerList() {
        return serverList;
    }

    public void setServerList(String serverList) {
        this.serverList = serverList;
        String[] serverPorts = serverList.split(";");
        for (String serverPort : serverPorts) {
            String[] split = serverPort.split(":", 2);
            serverPortList.add(new ServerPort(split[0], split[1]));
        }
    }

    class ServerPort {
        String serverName;
        Integer port;

        ServerPort(String serverName, String portString) {
            this.serverName = serverName;

            if (ObjectUtil.isEmpty(portString)) {
                this.port = 80;
            } else {
                try {
                    this.port = Integer.parseInt(portString);
                } catch (NumberFormatException e) {
                    this.port = 80;
                }
            }
        }

        public String getServerName() {
            return serverName;
        }

        public void setServerName(String serverName) {
            this.serverName = serverName;
        }

        public Integer getPort() {
            return port;
        }

        public void setPort(Integer port) {
            this.port = port;
        }
    }
}

The properties are read from a file included in the classpath using standard Java properties syntax

system.monitoring.serverport.locations=goolge.com:80
system.monitoring.serverport.timeout=3000
# etc...


The basic code for the EmailSender is:

public class EmailSender extends JavaMailSenderImpl {


    public void sendGenericEmail(String emailIds, String subject, String body, EmailAttachment... attachments) throws MessagingException {
        MimeMessage message = super.createMimeMessage();

        // use the true flag to indicate you need a multipart message
        MimeMessageHelper helper = new MimeMessageHelper(message, true);
        helper.setTo(emailIds);

        // use the true flag to indicate the text included is HTML
        helper.setSubject(subject);
        helper.setText(body, false);

        for (EmailAttachment attachment : attachments) {
            helper.addAttachment(attachment.getName(), attachment.getBodyAsStream());
        }

        // let's include the infamous windows Sample file (this time copied to c:/)
        /*FileSystemResource res = new FileSystemResource(new File("c:/Sample.jpg"));
        helper.addInline("identifier1234", res);*/
        send(message);
    }
}


   



system.monitoring.serverport.locations=goolge.com:80
system.monitoring.serverport.timeout=3000
# etc...

Thursday, March 11, 2010

Compiled java class info

Recently I had developed a small web application with Apache Click, Spring  and Cayenne.  I develop usually locally on Tomcat 6.x without much issue.  These days I am running Windows 7 and JDK6+, basically all the latest.  I test on another server Win XP running JBoss 5 on Java 6 as that is closer to the deployment target of (as I found out after development!) Linux with JBoss 5 running on JDK5...I dont even know the Linux build or version I expect it is Redhat of some flavour.

All was fine until I dropped my war file up to the remote JBoss and I start getting the bad class version errors:
java.lang.UnsupportedClassVersionError: Bad version number in .class file
and subsequent stacktraces of NoClassDefFound

;Fair enough, I've compiled them locally on Java 6.  Using maven I have as standard a in my top-level pom.xml the maven-compiler-plugin as a build plugin the setting was target of 1.6 :


Easy enough, replace the 6's with 5's, clean the build and rebuild. "mvn clean install"

I kept getting the error so wondered how I could check the version of the class file?

(Before going on the error was fixed with another clean and rebuild of a dependent project, arghhh)

The answer is "javap".  Run it with "-verbose" and you get more than enough info.  Up the top is the version table.  Mine said 49, which is found in the following version table


major  minor Java platform version 
45       3           1.0
45       3           1.1
46       0           1.2
47       0           1.3
48       0           1.4
49       0           1.5
50       0           1.6


Make sure you run with the short class name as the name parameter not the filename of the class itself.


Run:
javap -verbose DataException


Result:
Compiled from "DataException.java"
public class com.myco.common.data.DataException extends java.lang.Exception
  SourceFile: "DataException.java"
  minor version: 0
  major version: 49
  Constant pool:
const #1 = Method       #5.#25; //  java/lang/Exception."":(Ljava/lang/String;)V
const #2 = Method       #5.#26; //  java/lang/Exception."":()V
const #3 = Field        #4.#27; //  com/myco/common/data/DataException.hiddenException:Ljava/lang/Exception;
const #4 = class        #28;    //  com/myco/common/data/DataException
const #5 = class        #29;    //  java/lang/Exception
const #6 = Asciz        hiddenException;
const #7 = Asciz        Ljava/lang/Exception;;
const #8 = Asciz        ;
const #9 = Asciz        (Ljava/lang/String;)V;
const #10 = Asciz       Code;
const #11 = Asciz       LineNumberTable;
const #12 = Asciz       LocalVariableTable;
const #13 = Asciz       this;
const #14 = Asciz       Lcom/myco/common/data/DataException;;
const #15 = Asciz       msg;
const #16 = Asciz       Ljava/lang/String;;
const #17 = Asciz       ()V;
const #18 = Asciz       (Ljava/lang/String;Ljava/lang/Exception;)V;
const #19 = Asciz       error;
const #20 = Asciz       excp;
const #21 = Asciz       getHiddenException;
const #22 = Asciz       ()Ljava/lang/Exception;;
const #23 = Asciz       SourceFile;
const #24 = Asciz       DataException.java;
const #25 = NameAndType #8:#9;//  "":(Ljava/lang/String;)V
const #26 = NameAndType #8:#17;//  "":()V
const #27 = NameAndType #6:#7;//  hiddenException:Ljava/lang/Exception;
const #28 = Asciz       com/myco/common/data/DataException;
const #29 = Asciz       java/lang/Exception;


{
public com.myco.common.data.DataException(java.lang.String);
  Code:
   Stack=2, Locals=2, Args_size=2

Monday, March 8, 2010

Mapping XML data to database data

Mapping XML data to database data 


Introduction


When working with XML it is often the need to store key values into a database.  The reasons may vary but include running reports or building interfaces over the data.  Imagine a purchase order system, where an Order represented in XML comes through your system, a response comes back from an external party and later an invoice is returned.  There are many other related documents that could be included in one such business transaction.  There could be many asynchronous XML messages flowing over time that in the end all relate to form a single trading transaction; ie fulfillment of goods or services.  Some sort of reporting system will need to be built around that.  Building such a system on top of raw XML is not an option it would be too difficult, slow and cumbersome.  In such a case it is common to extract key pieces of the XML into database tables.  The may be many other use cases for extraction data from XML but the focus of this will be on the plain old order process (POOP!)

How your design turns out will be dependent on your needs and the source and target data.  Some differences will apply such as:
  1. XML data may hold a lot more data than is required for the database
  2. XML data can have a very deep hierarchy
  3. Table data will be most likely flattened down
  4. Table data may be more generic, ie XML may have Order, ShipmentNotification and Invoice as different definitions but they may be stored in the same table(s)
  5. Storing into the tables gives the opportunity to relate easily all the XML documents

Using Java libraries we can make this job pain free (disclaimer: we are playing with technology here so expect a little ouch!) .  Note: I expect .Net can do similar or even easier using LINQ but I have not yet explored that far yet

By example I use the following frameworks:
  1. JAXB to turn XML data into the object domain
  2. Cayenne an Object relational framework to go from the object domain to database tables
  3. Dozer an object mapping framework to map from JAXB to Cayenne objects.  The use of this is optional but can speed up development, offers great flexibility and improve maintenance tasks.

By using these 3 we don't have to write any XML and database manipulation code.  This could cut down development time, improve reliability and reduce maintenance tasks by many factors.

Let get into to it by using real word examples.

JAXB - Map XML to Objects

Version used 2.2.

JAXB as of JDK1.6 is included with the JDK making it a convenient choice.  Of course there are choices to this in the Java world such as XMLBeans

In my experience (and carrying on from the intro Order example) one of the largest XML schemas I have worked with is XCBL.  This is a large and comprehensive free set of procurement XML schemas.

Whatever the schema's you are using (or not) you will need to have JAXB generate Java source for you based on the schema.  If you only have XML and dont have a schema you can generate them for you based on XML using a tool such as OxygenXML.

So elts generate a bunch of Java source code based  on the schema.  Assuming you are using xcbl and have the schemas extracted into a folder run (Java installed is a great option too for xjc - XML Java compiler):

xjc -d java -p com.mycompany.jaxb.xcbl xcbl30/XCBL30.xsd -verbose

Xcbl is a complex set of schemas.  The top-level schema XCBL30.xsd includes all other top level schemas and in turn the other include other schemas.  After a moment of running you will see output like:

parsing a schema...
compiling a schema...
[INFO] generating code
unknown location

com\mycompany\jaxb\xcbl30\ASNBaseItemDetail.java
com\mycompany\jaxb\xcbl30\ASNDates.java
com\mycompany\jaxb\xcbl30\ASNDetail.java
com\mycompany\jaxb\xcbl30\ASNHeader.java
com\mycompany\jaxb\xcbl30\ASNItemDetail.java
...etc


The Java generated for Order will be:

//
// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, vJAXB 2.1.10 in JDK 6 
// See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a> 
// Any modifications to this file will be lost upon recompilation of the source schema. 
// Generated on: 2010.03.05 at 02:59:26 PM EST 
//


package com.mycompany.jaxb.xcbl30;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlType;


/**
 * <p>Java class for Order complex type.
 * 
 * <p>The following schema fragment specifies the expected content contained within this class.
 * 
 * <pre>
 * &lt;complexType name="Order">
 *   &lt;complexContent>
 *     &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
 *       &lt;sequence>
 *         &lt;element ref="{rrn:org.xcbl:schemas/xcbl/v3_0/xcbl30.xsd}OrderHeader"/>
 *         &lt;element ref="{rrn:org.xcbl:schemas/xcbl/v3_0/xcbl30.xsd}OrderDetail" minOccurs="0"/>
 *         &lt;element ref="{rrn:org.xcbl:schemas/xcbl/v3_0/xcbl30.xsd}OrderSummary" minOccurs="0"/>
 *       &lt;/sequence>
 *     &lt;/restriction>
 *   &lt;/complexContent>
 * &lt;/complexType>
 * </pre>
 * 
 * 
 */
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "Order", propOrder = {
    "orderHeader",
    "orderDetail",
    "orderSummary"
})
public class Order {

    @XmlElement(name = "OrderHeader", required = true)
    protected OrderHeader orderHeader;
    @XmlElement(name = "OrderDetail")
    protected OrderDetail orderDetail;
    @XmlElement(name = "OrderSummary")
    protected OrderSummary orderSummary;

    /**
     * Gets the value of the orderHeader property.
     * 
     * @return
     *     possible object is
     *     {@link OrderHeader }
     *     
     */
    public OrderHeader getOrderHeader() {
        return orderHeader;
    }

    /**
     * Sets the value of the orderHeader property.
     * 
     * @param value
     *     allowed object is
     *     {@link OrderHeader }
     *     
     */
    public void setOrderHeader(OrderHeader value) {
        this.orderHeader = value;
    }

    /**
     * Gets the value of the orderDetail property.
     * 
     * @return
     *     possible object is
     *     {@link OrderDetail }
     *     
     */
    public OrderDetail getOrderDetail() {
        return orderDetail;
    }

    /**
     * Sets the value of the orderDetail property.
     * 
     * @param value
     *     allowed object is
     *     {@link OrderDetail }
     *     
     */
    public void setOrderDetail(OrderDetail value) {
        this.orderDetail = value;
    }

    /**
     * Gets the value of the orderSummary property.
     * 
     * @return
     *     possible object is
     *     {@link OrderSummary }
     *     
     */
    public OrderSummary getOrderSummary() {
        return orderSummary;
    }

    /**
     * Sets the value of the orderSummary property.
     * 
     * @param value
     *     allowed object is
     *     {@link OrderSummary }
     *     
     */
    public void setOrderSummary(OrderSummary value) {
        this.orderSummary = value;
    }

}




Simple enough?

So now we have a set of Java sources ready to be populated with data.  JAXB and other similar technologies call this pushing of XML data into Objects "unmarshal".  Similarly going from Objects back to XML (as a String say) is  a "marshal" operation.

For this exercise we are only going from XML to Objects (and then to database) but not back.

So to unmarshal from an XML String:


        StringReader stringReader = new StringReader(someXMLString);
     StreamSource source = new StreamSource(stringReader);
   
     Order order = JAXB.unmarshal(source, Order.class);



That is it.  Make sure your input XML does match the schema including namespaces (do it all by the book) or it will not work...such as:

<?xml version="1.0" encoding="utf-8"?>
<Order xmlns="rrn:org.xcbl:schemas/xcbl/v3_0/xcbl30.xsd"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="rrn:org.xcbl:schemas/xcbl/v3_0/xcbl30.xsd XCBL30.xsd">
    <OrderHeader>
        <OrderNumber>
            <BuyerOrderNumber>
etc etc...


So now we have objects containing data that represent the input XML.  In code we can now query the values that have been unmarshal'd such as:

order.getOrderHeader().getOrderNumber().getBuyerOrderNumber()
      
List<ItemDetail> itemDetails = order.getOrderDetail().getListOfItemDetail().getItemDetail();

All very nice, something big, simplified.

Lets turn to the database layer.


Cayenne - Objects to database

Version used 3.0RC2

Cayenne and other Object relational tools provide a great abstraction from mundane database and JDBC operations.  I like Cayenne and have used it on a number of projects with great success and very little issues to overcome along the way.  I must say I have never used it to its fullest.  It has a rich feature set, great support community and I believe a bright future.

Features at a glance:
  1. Reverse engineer a database to Cayenne objects
  2. Database neutral
  3. Has a great modelling tool
  4. insert data - create new objects, call set methods and commit
  5. update data - select existing objects, call set methods and commit
  6. delete data - select existing objects, call delete
  7. select data - methods to choose from depending on complexity of  select, simple object graph selection to EJBQL
  8. Remote Object Persistence
  9. Caching strategies
  10. XML serialization

and much more.

There are 2 options for getting your Cayenne project off the ground, which I have both used succesfully.
  1. Building your database schema from scratch
  2. Reverse engineering an existing schema

I have another set of tutorials/examples to work from for more detail on this as does the Cayenne community but in a nutshell, lets assume we will create a Cayenne dataset from scratch.

Assuming you have downloaded Cayenne, fired up the modeler, have connected to a database and had a quick look around (ie you know a trick or 2) lets create a a very basic couple of entities to save our XML values into.

The basic structure is a top level entity 'Document' which has:
  1. 1 and only 1 'Heade'r entity
  2. 0 to many 'Item' entities
  3. 1 and only 1 'Summary' entity 

I don't want to go into much Cayenne specifics here so here are a couple of screenshots of the entities:

Document entity:



The generated Document class has no attributes only relationships:

Item class attributes:


Basic Cayenne use
Obtain a "DataContext", this can be done a number of ways depending on your application, the important thing is that it can find your cayenne.xml file in the classpath.  There are a few ways to do this.

Main method/standalone app:
DataContext dataContext = DataContext.createDataContext();

Web app (using Apache Click):
 <filter>
        <filter-name>DataContextFilter</filter-name>
        <filter-class>org.apache.click.extras.cayenne.DataContextFilter</filter-class>
        <!--<filter-class>com.quadrem.common.click.cayenne.ObjectContextFilter</filter-class>-->
        <init-param>
            <param-name>session-scope</param-name>
            <param-value>true</param-value>
        </init-param>
        <init-param>
            <param-name>shared-cache</param-name>
            <param-value>true</param-value>
        </init-param>


Spring :
    <bean id="cayenneConfiguration" class="org.apache.cayenne.conf.DefaultConfiguration"
          init-method="initializeSharedConfiguration" >        
    </bean>


    <bean id="context" class="org.apache.cayenne.access.DataContext"
          factory-method="createDataContext" depends-on="cayenneConfiguration">
        <constructor-arg value="Domain"/>
    </bean>




Once we have a handle on a DataContext we can create (insert will be generated for you):
Header header = new Header();
header.setDocNumber("1234");
header.setDocument(document);
dataContext.registerNewObject(header);
dataContext.commitChanges();
                                                                                

Basic select:

final Expression where = ExpressionFactory.matchExp(Header.DOC_NUMBER_PROPERTY, "1234");

SelectQuery query = new SelectQuery(Header.class, where);
List values = dataContext.performQuery(query);


Update:

// select an object as per above ...
Header header = (Header) values.get(0);
header.setDocNumber("2222");
dataContext.commitChanges();

Delete:

// select an object as per above ...
Header header = (Header) values.get(0);
dataContext.deleteObject(header);
dataContext.commitChanges();



A lot of power without even writing any SQL and that has just scratched the surface.  And the really nice thing is you can switch to whatever database you need and only have to change a config value or 2, the DB specific syntax Cayenne does for you.

So now it would be possible to manually map the JAXB classes to Cayenne all in Java, but that could become tedious.

Dozer - Object to Object mapping

Version used: 5.2.0

Dozer is a framework that provides a simple configurable way of mapping objects.  If the objects methods or attributes being mapped have the same name then mapping is automatic.  If not then you can declare what is mapped to where.  I admit in mapping a deep XML tree to the database classes above you loose a lot of the automatic mapping power which does diminish the value of Dozer a little in this case.  However you are still making the mapping more of a config exercise rather than hardcoding.  At the time of writing a Dozer GUI tool was no longer available but I believe some work was being done to restore it, such a task lends itself well to a GUI tool.

3 steps are required to get going with Dozer:
  1. Obtain a Dozer mapper class
  2. Write your mapping
  3. Run the mapping with your instantiated classes

Obtain Dozer mapper:
In code:
Mapper mapper = new DozerBeanMapper();


Using Spring:

    <bean id="dataExtractMapper" class="org.dozer.DozerBeanMapper">
        <property name="mappingFiles">
            <list>
                <value>dozer-global-configuration.xml</value>
                <value>dozer-order-mappings.xml</value>
            </list>
        </property>
    </bean>


Write a mapping:



    <mapping wildcard="false" type="one-way">
        <class-a>com.myco.jaxb.xcbl30.OrderHeader</class-a>
        <class-b>com.myco.model.Header</class-b>
        <field>
            <a>listOfStructuredNote.structuredNote[0].generalNote</a>
            <b>description</b>
        </field>
        <field>
            <a>orderNumber.buyerOrderNumber</a>
            <b>docNumber</b>
        </field>
        <field>
            <a>orderDates.promiseDate</a>
            <b>dueDate</b>
        </field>
        <field>
            <a>orderIssueDate</a>
            <b>orderDate</b>
        </field>
        <field>
            <a>orderType.orderTypeCoded</a>
            <b>xcblOrderType</b>
        </field>
    </mapping>


Run the mapping:



Header header = new Header();

StringReader stringReader = new StringReader(someXMLString);
StreamSource source = new StreamSource(stringReader);
Order order = JAXB.unmarshal(source, Order.class);

getBeanMapper().map(order.getOrderHeader(), header);
dataContext.registerNewObject(header);


List<ItemDetail> itemDetails = order.getOrderDetail().getListOfItemDetail().getItemDetail();
for (ItemDetail itemDetail : itemDetails) {
    Item item = new Item();
    dataContext.registerNewObject(item);
    item.setDocument(document);
    getBeanMapper().map(itemDetail, item);
}

dataContext.commitChanges();



Studying the above mapping process notice that it is driven from the data classes.  ie the target data drives the process of mapping.

Tuesday, March 2, 2010

.Net Parser error when running application





I had just posted up a change to my WebService which I had not touched in a while and received this error:


"It is an error to use a section registered as allowdefinition='MachineOnly' beyond machine.config."


Solution:
Remove an attribute in the machine.config file, that is located in your %windows dir%\Microsoft.NET\Framework\version\CONFIG directory. 


Locate the following line :

and delete the "allowDefinition" attribute.