Tutorial 04 - Improving the JAXB code and Adding Wrapper Classes to the Client

For the completed code see – ElandingsClient4.zip

Up to this point we have demonstrated code in a simplistic fashion. Let¿s improve the code a bit.
As you might have noticed, we can create a marshaller and unmashaller in two ways.
The first option is to define the unmarshaller based on the class that we want right now:

javax.xml.bind.JAXBContext jaxbCtx = javax.xml.bind.JAXBContext.newInstance(user.getClass().getPackage().getName());
javax.xml.bind.Unmarshaller unmarshaller = jaxbCtx.createUnmarshaller();

This approach works fine but is limiting. Marshallers and Unmarshallers take a long time to build. It is preferable to build them once and reuse them rather than building them again and again. This approach limits us in that we have to know what JAXB generated class we are working with.

A more general approach defines the jaxb context based on the generated jaxb java classes folder:

javax.xml.bind.JAXBContext jaxbCtx = javax.xml.bind.JAXBContext.newInstance("generated");
javax.xml.bind.Unmarshaller unmarshaller = jaxbCtx.createUnmarshaller();

We will begin this exercise by defining the marshaller and unmarshaller as class variables and initializing them in the ElandingsClient constructor
At the beginning of the class add:

static String JAXB_CONTEXT = "generated";
public javax.xml.bind.JAXBContext jaxbCtx;
public javax.xml.bind.Unmarshaller unmarshaller;
public javax.xml.bind.Marshaller marshaller;

Within the constructor add:

jaxbCtx = javax.xml.bind.JAXBContext.newInstance(JAXB_CONTEXT);
unmarshaller = jaxbCtx.createUnmarshaller();
marshaller = jaxbCtx.createMarshaller();
marshaller.setProperty(javax.xml.bind.Marshaller.JAXB_ENCODING, "UTF-8"); //NOI18N
marshaller.setProperty(javax.xml.bind.Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);

We now have a marshaller and unmarshaller available for reuse.

Scroll down to the getLandingReportList() that we created in the last exercise.

Notice how many steps are involved with creating a JAXB generated class LandingReportInfo. These same steps will need to be done every time we access the eLandings web service and request a list of landing report summaries. We will break this code out into its own wrapper class to make life a little easier in the future.

Right click on the elandingsclient package > New > Java Class

Type in the name LandingReportInfoWrapper
Click Finish
We now have a new class called LandingReportInfoWrapper.
Within the class, right click and click insert code
You will see a dialog picker
Click on the Constructor

We want to make two constructors. Make a copy of the constructor and add the following code.

package elandingsclient;

import generated.LandingReportInfo;
import java.io.StringReader;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.transform.stream.StreamSource;

public class LandingReportInfoWrapper {

    private LandingReportInfo info;

    public LandingReportInfoWrapper() {
        info = new LandingReportInfo();
    }

    public LandingReportInfoWrapper(String xml, Unmarshaller unmarshaller) throws JAXBException {
        info = new LandingReportInfo();
        StreamSource s = new StreamSource(new StringReader(xml));
        info = (LandingReportInfo)unmarshaller.unmarshal(s);
    }
}

Add several lines at the end of the class above the last closing tag
On an empty line above the last closing tag right click and Insert Code > getters and setters
Select the LandingReportInfoWrapper
Click Generate
We now have a getInfo() and setInfo()
Go back to the ElandingsClient.java file and find the getLandingReportList()
We can now change this method to use our new LandingReportInfoWrapper class

Replace:

LandingReportInfo summary = new LandingReportInfo();

javax.xml.bind.JAXBContext jaxbCtx = javax.xml.bind.JAXBContext.newInstance("generated");
javax.xml.bind.Unmarshaller unmarshaller = jaxbCtx.createUnmarshaller();

StreamSource s = new StreamSource(new StringReader(xml));
summary = (LandingReportInfo)unmarshaller.unmarshal(s);

int size = summary.getLandingReportSummary().size();
for(int x = 0; x<size; x++){
    String landingReportId = summary.getLandingReportSummary().get(x).getLandingReportId().getValue().toString();
    System.out.println((x+1) + " "+ landingReportId);
}

With:

LandingReportInfoWrapper infoWrapper = new LandingReportInfoWrapper(xml, unmarshaller);

int size = infoWrapper.getInfo().getLandingReportSummary().size();
for(int x = 0; x<size; x++){
  String landingReportId = infoWrapper.getInfo().getLandingReportSummary().get(x).getLandingReportId().getValue().toString();
  System.out.println((x+1) + " "+ landingReportId);
}

This simplifies the code slightly.

If we run the code, we can see that we get the same results as before.

We can now convert the xml string to a LandingReportInfoWrapper easily but we haven't built any method to go from JAXB class back to xml string. Let's do that now. Back in LandingReportInfoWrapper.java add in another method

public String toXmlString(Marshaller marshaller) throws JAXBException{
   StringWriter stringWriter = new StringWriter();
   marshaller.marshal(info, stringWriter);
   String xml = stringWriter.toString();
   return xml;
}

By looking at the Public Web Service documentation getLandingReport() and the previous tutorials, we know we will be working with LandingReportInfo objects as well. We will create a LandingReportWrapper class to handle that now.

Repeat the steps we took to create the LandingReportInfoWrapper class but this time call the class LandingReportWrapper

  1. Right click on the elandingsclient package. Click New>Java Class
  2. Name the class LandingReportWrapper
  3. Click Finished
    Netbeans will create a new class called LandingReportWrapper.
    Add two constructors just like we did in the LandingReportInfoWrapper class by right clicking within the class and clicking Insert Code. Click Generate Constructor

In the second constructor pass in the String xml and the Unmarshaller just like in the LandingReportInfoWrapper class.

We will want to create a JAXB class of type LandingReportInfo to hold the data rather than a LandingReportInfo object. We will call this LandingReportInfo object rpt. Convert the xml string in the same way we did in the LandingReportInfoWrapper.

Add a few lines to the end of the class and right click, click on Insert Code and select Generate Getters and Setters. Select LandingReport rpt and click generate.

Finally add in the toXmlString()

Here is the completed code:

package elandingsclient;

import generated.LandingReport;
import java.io.StringReader;
import java.io.StringWriter;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.transform.stream.StreamSource;

public class LandingReportWrapper {
    private LandingReport rpt;

    public LandingReportWrapper() {
        rpt = new LandingReport();
    }

    public LandingReportWrapper(String xml, Unmarshaller unmarshaller) throws JAXBException {
        rpt = new LandingReport();
        StreamSource s = new StreamSource(new StringReader(xml));
        rpt = (LandingReport)unmarshaller.unmarshal(s);
    }

    public String toXmlString(Marshaller marshaller) throws JAXBException{
         StringWriter stringWriter = new StringWriter();
         marshaller.marshal(rpt, stringWriter);
         String xml = stringWriter.toString();
         return xml;
    }

    public LandingReport getRpt() {
        return rpt;
    }

    public void setRpt(LandingReport rpt) {
        this.rpt = rpt;
    }
}

Go back to the ElandingsClient.java and find the getLandingReportList(). Currently the logic is very simply. We call the web service to get a list of landing report summaries that have changed since 8/10/2011 and then display that list of landing report ids.

Using the LandingReportWrapper, let's call the web services again and get the full landing report record for each of these landing report ids.
Change the code to:

public void getLandingReportList(){
        try {
            Calendar fromLastModifedDate = GregorianCalendar.getInstance();
            fromLastModifedDate.set(Calendar.YEAR, 2011);
            fromLastModifedDate.set(Calendar.MONTH, 7);
            fromLastModifedDate.set(Calendar.DAY_OF_MONTH, 10);//15 no records
            fromLastModifedDate.set(Calendar.HOUR_OF_DAY, 0);
            fromLastModifedDate.set(Calendar.MINUTE, 0);
            fromLastModifedDate.set(Calendar.SECOND, 0);
            fromLastModifedDate.set(Calendar.MILLISECOND, 0);

            DatatypeFactory datatypeFactory = DatatypeFactory.newInstance();

            XMLGregorianCalendar fromLastUpdateDateArg = (null == fromLastModifedDate) ? null : datatypeFactory.newXMLGregorianCalendar((GregorianCalendar) fromLastModifedDate);

            String xml = svc.findUserLandingReports001("amarx", "A_marx", "2.1", null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, fromLastUpdateDateArg, null);
            System.out.println(xml);

            LandingReportInfoWrapper infoWrapper = new LandingReportInfoWrapper(xml, unmarshaller);
            ArrayList<LandingReportWrapper> landingReports = new ArrayList<LandingReportWrapper>();
            int size = infoWrapper.getInfo().getLandingReportSummary().size();
            for(int x = 0; x<size; x++){
                String landingReportId = infoWrapper.getInfo().getLandingReportSummary().get(x).getLandingReportId().getValue().toString();
                System.out.println((x+1) + " "+ landingReportId);

                String landingReportXml = svc.getLandingReport("amarx", "A_marx", "2.1", landingReportId);
                System.out.println(landingReportXml);
                LandingReportWrapper landingReportWrapper = new LandingReportWrapper(landingReportXml, unmarshaller);
                landingReports.add(landingReportWrapper);
            }

            System.out.println("The landingReports arrayList contains "+landingReports.size()+" reports ");

        } catch (JAXBException ex) {
            Logger.getLogger(ElandingsClient.class.getName()).log(Level.SEVERE, null, ex);
        } catch (DatatypeConfigurationException ex) {
            ex.printStackTrace();
        }
    }

Now when we run the code, we produce an ArrayList containing all of the landing reports that have been modified since the lastModifiedDate of 8/10/2011

From here we could write the xml to a local file or write some code to insert the LandingReportWrapper data into a database.

We have worked with ProcessorUserInfo data and will probably do so again. We might as well create a Wrapper class for it now as well.

Repeat this process for ProcessorUserInfoWrapper. Here is the completed code:

package elandingsclient;

import generated.ProcessorUserInfo;
import java.io.StringReader;
import java.io.StringWriter;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.transform.stream.StreamSource;

public class ProcessorUserInfoWrapper {
    private ProcessorUserInfo userInfo;
    public ProcessorUserInfoWrapper() {
        userInfo = new ProcessorUserInfo();
    }
     public ProcessorUserInfoWrapper(String xml, Unmarshaller unmarshaller) throws JAXBException {
         userInfo = new ProcessorUserInfo();
         StreamSource s = new StreamSource(new StringReader(xml));
         userInfo = (ProcessorUserInfo)unmarshaller.unmarshal(s);
    }

    public String toXmlString(Marshaller marshaller) throws JAXBException{
         StringWriter stringWriter = new StringWriter();
         marshaller.marshal(userInfo, stringWriter);
         String xml = stringWriter.toString();
         return xml;
    }

    public ProcessorUserInfo getUserInfo() {
        return userInfo;
    }

    public void setUserInfo(ProcessorUserInfo userInfo) {
        this.userInfo = userInfo;
    }
}

Back in ElandingsClient.java find the connectToWebService().
Using our new ProcessorUserInfoWrapper class, we can simplify this code from:

public void connectToWebService() {
        try {
            String xml = svc.getUserInfo("amarx", "A_marx", "2.1");
            System.out.println(xml);

            ProcessorUserInfo user = new ProcessorUserInfo();

            javax.xml.bind.JAXBContext jaxbCtx = javax.xml.bind.JAXBContext.newInstance(user.getClass().getPackage().getName());
            javax.xml.bind.Unmarshaller unmarshaller = jaxbCtx.createUnmarshaller();

            StreamSource s = new StreamSource(new StringReader(xml));
            user = (ProcessorUserInfo)unmarshaller.unmarshal(s);

            String userName = user.getProcessorUser().get(0).getUserName().getValue();
            System.out.println("User Name "+userName);

            UserName un = user.getProcessorUser().get(0).getUserName();
            un.setValue("Audry A Marx");
            user.getProcessorUser().get(0).setUserName(un);
            userName = user.getProcessorUser().get(0).getUserName().getValue();
            System.out.println("User Name "+userName);

            javax.xml.bind.Marshaller marshaller = jaxbCtx.createMarshaller();
            marshaller.setProperty(javax.xml.bind.Marshaller.JAXB_ENCODING, "UTF-8"); //NOI18N
            marshaller.setProperty(javax.xml.bind.Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);

            StringWriter stringWriter = new StringWriter();
            marshaller.marshal(user, stringWriter);
            String result = stringWriter.toString();
            System.out.println(result);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

To:

public void connectToWebService() {
        try {
            String xml = svc.getUserInfo("amarx", "A_marx", "2.1");
            System.out.println(xml);
            ProcessorUserInfoWrapper userInfoWrapper = new ProcessorUserInfoWrapper(xml, unmarshaller);

            String userName = userInfoWrapper.getUserInfo().getProcessorUser().get(0).getUserName().getValue();
            System.out.println("User Name "+userName);

            UserName un = userInfoWrapper.getUserInfo().getProcessorUser().get(0).getUserName();
            un.setValue("Audry A Marx");
            userInfoWrapper.getUserInfo().getProcessorUser().get(0).setUserName(un);
            userName = userInfoWrapper.getUserInfo().getProcessorUser().get(0).getUserName().getValue();
            System.out.println("User Name "+userName);

            String result = userInfoWrapper.toXmlString(marshaller);
            System.out.println(result);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

Again, this just makes the code a little easier to read and understand.

We now have a wrapper class to simplify working with users, landing report summaries and landing reports. This should help us as we add more complex behavior to our web service client. As we discover common actions, we can create specialized methods in the wrapper classes to make life easier.