Mapping Jaxb element using Dozer – A mapping framework

Tags

, , , , ,

After some sort of struggle, I have made my dozer mapping worked with custom converter. My requirement is to convert String to JaxbElement<String>. Though dozer documents stated that they provided JaxbBeanFactory, I didn’t find a way to make it work. So, have written the below simple converter and it works fine for my requirement.

Hope, people who are searching for dozer with jaxb is aware of what is dozer and where to use. Here is the better documentation for it. http://dozer.sourceforge.net/documentation/whymap.html

My application looks like below.

1. Create client (stub) from WSDL
2. Set my application values to the generated stub and pass it to remote application.

The use of dozer here is, the application has elements in certain names and the generated stub has the elements in different names. If i dont use dozer or other mapping framework like dozer, then I need to map each field like below.

stubField.setField1(appField.getSomeNameValue());

Since the naming are different, its difficult to track. Also, if I want to map the application field and stub field in many places, then I need to perform this manual setter/getter in many places.

Also, dozer has many advantages, few are them are listed below.

1. Mapping will be in single place.
2. No need of setter/getter in various places if need to map in many places
3. Simple datatype convertions will happen automatically
4. For complex type mapping, custom converters can help.
5. Bi-directional
6. Have many options like global configuration, null/empty check etc in xml itself.
7. Can use API instead of XML for runtime mapping
8. Comes with listener to perform certain task before/after mapping.

Now will see my project setup in detail.

Generating client from WSDL

I used maven plugin to generate client classes. Though there are many ways to acheive it, maven makes it simple and I dont need to work on multiple windows at a time (The other ways to generate client are, 1. using eclipse dynamic web project and import wsdl url. 2. Execute command line wsimport command etc).

Below is the snippet to generate clients. jaxws-rt and log4j are dependencies for this plugin. So, have it in dependency tag.

<build>
		<finalName>wsdl-client</finalName>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>3.1</version>
				<configuration>
					<source>${java-version}</source>
					<target>${java-version}</target>
					<encoding>${project.build.sourceEncoding}</encoding>
				</configuration>
			</plugin>

			<plugin>
				<groupId>org.codehaus.mojo</groupId>
				<artifactId>jaxws-maven-plugin</artifactId>
				<version>1.12</version>
				<executions>
					<execution>
						<goals>
							<goal>wsimport</goal>
						</goals>
						<configuration>
							<wsdlLocation></wsdlLocation>
							<wsdlDirectory>${basedir}/src/main/resources</wsdlDirectory>
							<wsdlFiles>
								<!-- if no changes in wsdl, it is not compiling new java classes -->
								<wsdlFile>MySampleWSDL.wsdl</wsdlFile>
							</wsdlFiles>
							<sourceDestDir>${project.basedir}/src/main/java</sourceDestDir>
							<!-- enable verbose to see Output messages about what the tool is
								doing -->
							<verbose>true</verbose>
						</configuration>
					</execution>
				</executions>
			</plugin>
		</plugins>
</build>

When executing the above, it generates the client in /src/main/java package. The generated classes are JAXB complient (because of the plugin used in maven). It has ObjectFactory, locator classes. After executing the above step, now I have the client jar generated from maven.

Integrate WSDL client with application using Dozer

To integrate the stub with application, I have to set the stub jar in classpath. Since I am using dozer, I need to write certain dozer specific implementations and they are below.

1. My application pom file will have dependency for generated stub jar and dozer jar file.

2. I have to write a custom mapping file since the application fields and the fields in stub are different. The mapping file is like below.


	<?xml version="1.0" encoding="UTF-8"?>
<mappings xmlns="http://dozer.sourceforge.net" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://dozer.sourceforge.net http://dozer.sourceforge.net/schema/beanmapping.xsd">

	<mapping map-id="client-map">
		<class-a>com.test.application.AppFields</class-a>
        <class-b>com.test.aeclient.StubFields</class-b>
        <field custom-converter="com.test.application.MyJaxbCustomConverter">
	    <a>application</a>
            <b>miscField1</b>
        </field>
        <field custom-converter="com.test.application.MyJaxbCustomConverter">
            <a>userId</a>
           <b>miscField2</b>
        </field>
        <field custom-converter="com.test.application.MyJaxbCustomConverter">
            <a>eventTimeStamp</a>
            <b>miscField3</b>
        </field>
        <field custom-converter="com.test.application.MyJaxbCustomConverter">
            <a>amount</a>
            <b>miscField4</b>
        </field>
    </mapping>
</mappings>

If you map between String to String, then there is no need of converters and all.

I looked for specifying custom converter in class level instead of field level but it didn’t work out for me. For that I need to use CustomConverter interface instead of DozerConverter class from Dozer api as DozerConverter does some strict type check.

The application fields are like below.

Application fields (source)


package com.test.application;

public class AppFields {

	private String application;
	private String userId;
	private String eventTimeStamp;
	private String amount;

	public String getApplication() {
		return application;
	}
	public void setApplication(String application) {
		this.application = application;
	}

	public String getUserId() {
		return userId;
	}
	public void setUserId(String userId) {
		this.userId = userId;
	}

	public String getEventTimeStamp() {
		return eventTimeStamp;
	}
	public void setEventTimeStamp(String eventTimeStamp) {
		this.eventTimeStamp = eventTimeStamp;
	}

	public String getAmount() {
		return amount;
	}
	public void setAmount(String amount) {
		this.amount = amount;
	}
}

Sample target class (Class from generated stub)
I am not giving the complete class as it has jaxb tags which will make the page longer.

import com.test.aeclient;

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "StubFields", propOrder = {
    "miscField1",
    "miscField2",
    "miscField3",
    "miscField4"
	})
public class StubFields {

    @XmlElementRef(name = "Misc_Field1", namespace = "http://schemas.datacontract.org/2004/07/ActivityLoggerService", type = JAXBElement.class)
    protected JAXBElement<String> accountInfoList;
    @XmlElementRef(name = "Misc_Field2", namespace = "http://schemas.datacontract.org/2004/07/ActivityLoggerService", type = JAXBElement.class)
    protected JAXBElement<String> actionStatus;
    @XmlElementRef(name = "Misc_Field3", namespace = "http://schemas.datacontract.org/2004/07/ActivityLoggerService", type = JAXBElement.class)
    protected JAXBElement<String> activityName;
    @XmlElementRef(name = "Misc_Field4", namespace = "http://schemas.datacontract.org/2004/07/ActivityLoggerService", type = JAXBElement.class)

	..........

}

Now we see the converter part. We need a converter since the source class has fields with datatype as ‘String’ and the target class has the fields with datatype as ‘JaxbElement<String>’

MyJaxbCustomConverter.java

package com.test.aeclient;
import javax.xml.bind.JAXBElement;
import javax.xml.namespace.QName;
import com.test.aeclient.someloggerservice.ObjectFactory;
import org.dozer.DozerConverter;

public class MyJaxbCustomConverter extends DozerConverter<String, JAXBElement> {
      public MyJaxbCustomConverter() {
           super(String.class, JAXBElement.class);
      }

@Override
public String convertFrom(JAXBElement source, String destination) {
       return destination;
}

@Override
public JAXBElement convertTo(String source, JAXBElement destination) {
     if(source != null)
     {
	ObjectFactory factory = new ObjectFactory();
	JAXBElement<String> jaxb = new JAXBElement<String>(
					new QName(String.class.getSimpleName()), String.class, null, source);			return jaxb;
     }
	return null;
}
}

That’ it. Now we go to our application code and invoke dozer mapping.

Now in my application, I need to map the source and target classes like below. No need of manual setter/getter between the source and destination here.


		public void sendDataToRemoteApp(AppFields fields)
		{
			StubFields stub = new StubFields();

			List<String> list = new ArrayList<String>();
			// Add the mapping configuration
			list.add("dozerMapping.xml");

			Mapper mapper = new DozerBeanMapper(list);
			mapper.map(fields, stub, "client-map");
		}

That’s it. It will map the fields between AppFields.class and StubFields.class. For example, if I have 100 fields in AppFields.class, I dont need of doing setter/getting in each fields. The values in AppFields are mapping automatically to StubFields and vise versa (since it is bi-directional. We can make it one-way too.).

Follow

Get every new post delivered to your Inbox.