Wednesday, December 29, 2010

JAXB : validation against XML schema at marshalling/unmarshalling

1 Introduction

One of the most commonly made mistakes when using JAXB to bind XML to Java objects, is to think that marshalling and unmarshalling operations will automatically validate the processed data values against the XML schema that has been given as input to the XJC program to generate corresponding Java classes, but as a matter of fact they don't.

Practically, lots of programmer won't even notice that aspect because they usually don't defined any constraint in their XML schema (multiplicy, pattern, min/max value, min/max length, ...), or because the values they handle remain valid throughout the different steps of their program.

So here's how I turn validations on when using JAXB...

2 Tools
  • JDK 1.6.0_14
  • JAXB 2.1.10 in JDK 6 
3 XML schema


<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.example.org/users" xmlns:tns="http://www.example.org/users" elementFormDefault="qualified">

    <complexType name="User">
        <sequence>
            <element name="login" type="string" maxOccurs="1"
                minOccurs="1">
            </element>
            <element name="password" maxOccurs="1" minOccurs="1">
                <simpleType>
                    <restriction base="string">
                        <minLength value="6"></minLength>
                        <pattern value=".*\d+.+"></pattern>
                    </restriction>
                </simpleType>
            </element>
            <element name="subscriptionDate" type="date" maxOccurs="1" minOccurs="1"></element>
        </sequence>
    </complexType>

    <complexType name="UserList">
        <sequence>
            <element name="users" type="tns:User" maxOccurs="unbounded" minOccurs="0"></element>
        </sequence>
    </complexType>

    <element name="UserList" type="tns:UserList"></element>
</schema>


As you can see, I've specified several constraints :
  • a user must have a login, a password and a subscription date
  • the password must have at least 8 characters and at least 1 digit

4 Validation when marshalling

Here's some code to illustrate how to turn validation on :

public static void main(String[] args) {
        try {
            JAXBContext context = JAXBContext.newInstance(ObjectFactory.class);
            Marshaller msh = context.createMarshaller();
            // setting a schema on the marshaller instance to activate validation against given XML schema
            msh.setSchema(SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema").newSchema(new File("xsd/users.xsd")));
            // specifies that the XML output must be indented
            msh.setProperty("jaxb.formatted.output", Boolean.TRUE);
           
            // creating a list of user
            UserList userList = new UserList();
           
            // creating an user
            User user = new User();
            user.setLogin("manager");
            user.setPassword("password01");
            user.setSubscriptionDate(DatatypeFactory.newInstance().newXMLGregorianCalendar(new GregorianCalendar(2010, 11, 29)));
           
            // adding the user to the list
            userList.getUsers().add(user);
            JAXBElement<UserList> element = new ObjectFactory().createUserList(userList);
           
            // marshalling the list to XML
            msh.marshal(element, new File("out/users.xml"));
        } catch (Exception e) {
            e.printStackTrace();
        }   
    }

Setting a schema on a marshaller/unmarshaller turns validation on : 

...
Marshaller msh  = context.createMarshaller();
msh.setSchema(SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema").newSchema(new File("xsd/users.xsd")));
...

The highlighted code lines specifies which schema should be used to validate against when marshalling.

Validation for unmarshalling operation is activated the same way : in the above highlighted code line, replace the marshaller instance (msh) by an unmarshaller instance and the trick is done

5 Example of valid and invalid marshalling operations

Based on the XML schema from step 3, trying to marshall a user list with a user whose password is "nopassword" will yield the following exception : 


Marshalling a user without specifying any login will yield the following exception : 


On the other hand, here's an example of valid XML output

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<UserList xmlns="http://www.example.org/users">
    <users>
        <login>manager</login>
        <password>password0</password>
        <subscriptionDate>2010-12-29+01:00</subscriptionDate>
    </users>
</UserList>

6 Source code

For those who aren't that familiar yet with JABX basic functionalities, feel free to leave a comment with your email address and I'll send you the project I used for this post.

Wednesday, November 17, 2010

Intalio : sample process (with external WS invokation and mapping WS <-> Matrix Table)

1 Introduction

In my company, we've been considering, for a while, integrating a BPMS solution to our in-house application. One of the goals of that integration is to involved more actively business and functional analysts, and even our clients, in the definition of processes within our application.

The main requirements that must be fulfilled by the BPMS are :
  •  ease of integration with our application
  • commercial use allowed free of charge (clients that would be interested in upgrade or premium version are free to opt for such licenses)
  • 100% BPMN standards compliance
    After some investigation, Intalio Community Edition seemed to be the perfect candidate...
    The next step was then to implement a sample process in order to get to know Intalio and at the same time, confirm it actually fulfills our expectations.

    2 Tools

    The tools I used to implement and test my first process were : 
    • Intalio-Designer-6.0.3.034 (Community edition)
    • Intalio|Server 6.0.3.022 
    3 Process definition

    Here is an overview of my process :



    4 Key steps

    In this section, I'm going to detail the different steps I went through to define my process (note that I'll highlight the steps I consider rather tricky nay complicated) :

    1° Create an non-executable pool and name it "ForHRM-FrontOffice"
    2° In the "ForHRM-FrontOffice" pool, create a task and name it "Trigger Process" : this task within the "ForHRM-FrontOffice" pool represents our application from which the process will be executed.
    3° Create an executable pool and name it "ProcessIntalioLotProcess"
    4° In this pool, create a "Message start" event
    5° Focus on the "Trigger Process" task and drag outgoing message flow icon to the "Message start" event
    6° Still in the same pool, create a new task and name it "Invoke WS to retrieve data"
    7° Focus on the "Message start" event and drag outgoing message flow icon to the last created task
    8° Create a new non-executable pool and name it "ForHRM-WS" : this pool will contain external web services
    9° In the process explorer, go to the "externalWSDLs" directory and drag the "RetrieveFeuillesPaieForPaie" operation into the "ForHRM-WS" pool, and choose the "single task" type


    10° Focus on this last task and drag the ingoing message flow icon to the "Invoke WS to retrieve data" task
    11° Focus on the same task and drag the outgoing message flow incon to the "Invoke WS to retrieve data" task
    12° Get back in the "ProcessIntalioLotProcess" pool, create the "Begin validation", "End validation" and "Invoke WS to update data" tasks
    13° Create a new non-executable pool and name it "Gestionnaires" : this pool represents specific users to whom a validation form will be displayed
    14° Drag the form named "validation.gi" (located in the "forms" directory") into the last created pool and choose the "create and complete" type
    15° Focus on the "validation-create" task and drag the ingoing message flow icon to the "Begin validation" task. Then drag the outgoing message flow icon to the "Begin validation" task
    16° Focus on the "End validation" task and drag the ingoing message flow icon to the "validation-complete" task. Then drang the outgoing message flow icon to the "validation-complete" task
    17° From the process explorer view, drag the "ValidateFeuillesPaieForPaie" operation into the "ForHRM-WS" pool, and choose the "single task" type


    18° Focus on the "validateFeuillesPaieForPaie" task and drag the ingoing message flow icon to the "Invoke WS to update data" task. Then, drag the outgoing message flow icon to the "Invoke WS to update data" task.
    19° From the "messages" directory, expand the "triggerMessage.xsd" file and drag the "tns:RepertoirePaie" element onto the message flow that goes from the "Trigger Process" task to the "Start Message" event. Then choose the "Set schema element ... as the content of the message"



    This step defines the structure of the message that will be passed to the Intalio process when it is executed from our application. Since the "RepertoirePaie" element is composed of 2 String elements, the process will be executed by calling a web service operation that receives 2 string parameters (read further for more details on that).

    20° Focus on the "Invoke WS to retrieve data" step and open the "Mapper View". Then, connect the nodes as shown below


    This step defines which are the values and where they're actually come from, that will be passed as parameters to the web service operation.
    21°  Focus on the "Begin validation" task and open the "Mapper" View. 
    22°  From the "Mapper Palette" view, drag the "doXslTransform" function into the work area of the "Mapper"


    Then, within the "Mapper" view, create a new operator (by clicking on the red button from the "Mapper" View) and define its value as ""/XSL/feuillePaie.xsl".
     Connect this operator to the "bpel:doXslTranform" function that you just drag-and-dropped.
    Now, connect the input and output nodes to the  "bpel:doXslTranform" function as shown below




     This steps indicates how the response from the "retrieveFeuillesPaieForPaie" WS operation must be mapped to the matrix-table (so that the results could be displayed in the form) I've defined in my form which looks like this :




    23° My sample project (that can be downloaded with the link provided further) comes with all the .XSL files completely written. Here's how I've created them.
    First, create an empty directory within the project and name it "XSL".
    Then create an empty file and name it "feuillePaie.xsl" and put this file in the "XSL" directory.
    Go back in the "Mapper View" and focus on the "Begin validation" task.
    Right-click on the "bpel:doXslTransform" function and select "Generate XSL helper...". This will edit your "feuillePaie.xsl" file and create a "feuillePaie.xsl.xsltest" file (that I've deleted afterwards, since I didn't need it)




    Make sure you're working with the Intalio designer at the 6.0.3.034 version !!! I've tried the previous version of the designer and the XSL helper option didn't work...

     The "feuillePaie.xsl" as it has been completed by the XSL helper then looks like this : 


    <?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        version="2.0" xmlns:tns="http://www.example.org/triggerMessage"
        xmlns:IntalioPaieService="http://wsIntalioProcessLot.webServices.web.forhrm.formatech.be/"
        xmlns:this="http://thisdiagram/ProcessIntalioLotProcess"
        xmlns:validation="http://www.intalio.com/gi/forms/validation.gi"
        xmlns:Gestionnaires="http://thisdiagram/Gestionnaires"
        xmlns:attachments="http://www.intalio.com/gi/forms/validation.gi/attachments"
        xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:diag="http://thisdiagram">
        <!--
            XSL Skeleton generated on Fri Nov 19 15:09:50 CET 2010 for
            F/IntalioPrototype/ProcessIntalioLot.bpm
            pool:_lsCI8OzPEd-OxcY5gTnOlw.bpdm activity: Begin Validation Complete
            doXslTransform: bpel:doXslTransform("/XSL/feuillePaie.xsl",
            $intalioPaieServiceRetrieveFeuillesPaieForPaieResponseMsg.parameters)
            Input document as defined in the mapper:
            $intalioPaieServiceRetrieveFeuillesPaieForPaieResponseMsg.parameters
        -->
        <xsl:output />
        <!--No parameters are currently passed to doXslTransform.-->
        <xsl:template match="/IntalioPaieService:retrieveFeuillesPaieForPaieResponse">
            <DonneesLot>
                <lot-matrix>
                    <empCode>string</empCode>
                    <travCode>string</travCode>
                    <noPaie>string</noPaie>
                    <montantBrut>string</montantBrut>
                    <validated>string</validated>
                </lot-matrix>
            </DonneesLot>
        </xsl:template>
    </xsl:stylesheet>
     

    From this point, I've just removed the comments and add the "for-each" instruction so that each "occurrence" from the WS response would be map to an occurrence within my matrix table. For each field of an occurrence within my matrix table, I've also defined which "element" of the WS response occurrence should be mapped to it. For instance, in the following excerpt, I'm mapping the "empCode" attribute from a "FeuillePaie" occurrence (the WS operation returns an ArrayList<FeuillePaie>) to the column with the "empCode" fields : 

                        <empCode>
                            <xsl:value-of select="empCode" />
                        </empCode>








    So, after completion of the "FeuillePaie.xsl" file, this looks like this : 



    <?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        version="2.0" xmlns:tns="http://www.example.org/triggerMessage"
        xmlns:IntalioPaieService="http://wsIntalioProcessLot.webServices.web.forhrm.formatech.be/"
        xmlns:this="http://thisdiagram/ProcessIntalioLotProcess"
        xmlns:validation="http://www.intalio.com/gi/forms/validation.gi"
        xmlns:Gestionnaires="http://thisdiagram/Gestionnaires"
        xmlns:attachments="http://www.intalio.com/gi/forms/validation.gi/attachments"
        xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:diag="http://thisdiagram">
        <xsl:output />
        <xsl:template match="/IntalioPaieService:retrieveFeuillesPaieForPaieResponse">
            <DonneesLot>
                <xsl:for-each select="*">
                    <lot-matrix>
                        <empCode>
                            <xsl:value-of select="empCode" />
                        </empCode>
                        <travCode>
                            <xsl:value-of select="travCode" />
                        </travCode>
                        <noPaie>
                            <xsl:value-of select="paie" />
                        </noPaie>
                        <montantBrut>
                            <xsl:value-of select="brut" />
                        </montantBrut>
                        <validated>
                        </validated>
                    </lot-matrix>
                </xsl:for-each>
            </DonneesLot>
        </xsl:template>
    </xsl:stylesheet>



    24° Focus on the "Invoke WS to update data" task, go to the "Mapper" view and configure the mapping as shown below. This time, I created an XSL file called "feuillePaieValidee.xsl" and I followed the same logic used at step 23° to complete that. So you should be able to get to the same result by yourself, I guess.
    In this step, what I've done, is mapping the content of my matrix table (that might have been by the "formatech/khy" user - did you see that I've defined a user on the pool that contains the forms ???) to the input parameters of the WS operation named "validateFeuillesPaieForPaie". In my sample, this operation will update the data that have been modified by the user through the Ajax form, into our database.

    5 Conclusion

    This process is quite basic but embedded enough requirements in order to be integrated into an existing application (it actually was a good prototype for us to check that we could integrate Intalio to our in-house application), so feel free to base upon it to create your own prototype and discover Intalio by yourself.

    6 Disclaimer and remarks

    I've never followed any BPM training nor BPMN training, so don't take this sample as 100% correct from a functional process designing point of view.

    For those who would wonder why I did not send back a response to from the end of the process to the "Trigger process" task, here's the reason : once integrated into our application, if the "Trigger process" task were sent back a response message, the execution of the process would have been synchronous. Consequently, the user would have to complete the task within the UI-FW console before the code from our application could resume its execution.

    If you have any question/suggestion, just leave a comment. 


    I will create new posts/topics on Intalio as soon as I'll have time (or be asked at work) to try on other Intalio features, so follow me and stay tuned ;-)

    7 Download link


    I've posted my process in the showcase section of the Intalio community website : 

    http://community.intalio.com/forums/show-cases/basic-process-mapping-ws-with-matrix-table/view.html#25348

    PS : should this link die or my process be deleted from the website, let me know and I'll re-upload it.