JAXB parent-child cycle problem

I wrote a RESTful web service that was the backend for an iOS app. Since I wrote the the web administration app using Spring @MVC 3, I used that for the web service also instead of Jersey. One cool thing is how transparent the marshaling of your objects. Here is a simple example.

1
2
3
4
5
@RequestMapping("/{familyName}/{givenName}")
public @ResponseBody
Composer findComposerByFullName(@PathVariable String familyName, @PathVariable String givenName) {
   return composerService.findComposerByFullName(familyName,givenName));
}

Notice what you don’t see. (How’s that for a sentence? :) ) The web service simply returns a Composer object (obtained from the composerService) – you don’t see any gyrations for returning XML or JSON! If the client requests XML, then Spring will automatically use JAXB to convert your Java object into XML. If Jackson is on your classpath and the client requests JSON then Spring will automatically marshal your object into JSON. Neat. (You can use other marshaling technologies. See the Resources below.)

Unless you’re returning an object graph that includes a bidirectional parent/child relationship.

Here is an example parent which is also a JPA entity. One composer has many works, and a work belongs to one composer. It is not necessary that it be an Entity for the marshaling to work; it’s just that JPA bidirectional associations are commonly used along with the topic at hand.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@Entity
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Composer implements Serializable, Comparable<Composer> {
   private static final long serialVersionUID = 1L;
 
   @Id
   @GeneratedValue(strategy = GenerationType.IDENTITY)
   private Long id;
 
   @Basic(optional = true)
   @XmlElement(name = "givenName", required = false)
   @Column(name = "givenName", length = 128)
   private String givenName;
 
   @Basic(optional = true)
   @XmlElement(name = "middleName", required = false)
   @Column(name = "middleName", length = 128)
   private String middleName;
 
   @Basic(optional = false)
   @XmlElement(name = "familyName", required = true)
   @Column(name = "familyName", length = 255)
   private String familyName;
 
   @OneToMany(cascade = CascadeType.ALL, mappedBy = "composer", targetEntity = Work.class, fetch = FetchType.EAGER)
   @XmlElementWrapper(name = "works")
   @XmlElement(name = "work", required = true)
   private Set<Work> works = new HashSet<Work>();

And here is an example child. Since the relationship is bidirectional, there is a reference to the composer.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
@Entity
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Work implements Serializable {
 
   private static final long serialVersionUID = 1L;
 
   @Id
   @GeneratedValue(strategy = GenerationType.IDENTITY)
   @Basic(optional = false)
   @Column(name = "id", nullable = false)
   private Long id;
 
   @Basic(optional = false)
   @Column(name = "title")
   private String title;
 
   @Basic(optional = true)
   @Temporal(TemporalType.DATE)
   private Date completionDate;
 
   @Basic(optional = true)
   @Temporal(TemporalType.DATE)
   private Date premiereDate;
 
   @Basic(optional = true)
   @Column(name = "instrumentation")
   private String instrumentation;
 
   @ManyToOne(fetch = FetchType.EAGER, optional = false)
   @JoinColumn(name = "COMPOSER_ID")
   private Composer composer;

Let’s set up a simple object graph with a Composer who has one Work. Then marshal the composer to
stdout with a small helper class.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class JAXBhelper {
   private Logger logger = LoggerFactory.getLogger(this.getClass());
   public static void main(String[] args) {
      Composer composer = new Composer("Ludwig", "Beethoven");
      composer.setId(123L);
      composer.setOtherName("van");
      composer.setBirthDate(new Date());
      composer.setDeathDate(new Date());
 
      Work work = new Work();
      work.setPremiereDate(new Date());
      work.setCompletionDate(new Date());
      work.setInstrumentation("kazoo, comb, wax paper");
      work.setTitle("Cacophony No. 2.718");
      composer.add(work);
      JAXBhelper helper = new JAXBhelper();
      helper.xmlToStdout(composer);
   }
 
   public void xmlToStdout(Composer composer) {
      try {
         JAXBContext jaxbc = JAXBContext.newInstance(Composer.class);
         Marshaller marshaller = jaxbc.createMarshaller();
         marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
         marshaller.marshal(composer, System.out);
      } catch (JAXBException e) {
         e.printStackTrace();
         this.logger.error(e.getMessage());
      }
   }
}

Oops. It looks like JAXB doesn’t like this. This what the program barfs:

1
2
3
4
5
6
7
8
9
javax.xml.bind.MarshalException
 - with linked exception:
[com.sun.istack.SAXException2: A cycle is detected in the object graph. This will cause infinitely deep XML: Composer  id:'123' ln:'Beethoven' gn:'Ludwig' mn:'null'
	Cacophony No. 2.718
 -> com.rockhoppertech.marshal.domain.Work@e78ffa9d id=null title='Cacophony No. 2.718' cid=123 -> Composer  id:'123' ln:'Beethoven' gn:'Ludwig' mn:'null'
	Cacophony No. 2.718
]
	at com.sun.xml.bind.v2.runtime.MarshallerImpl.write(MarshallerImpl.java:318)
etc.

The XML solution

To fix the XML the solution is fairly simple. Just mark the parent as @XmlTransient.

1
2
3
4
5
@ManyToOne(fetch = FetchType.EAGER, optional = false)
@JoinColumn(name = "COMPOSER_ID")
// prevent a cycle
@XmlTransient
private Composer composer;

You now get this output.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<composer>
    <id>123</id>
    <givenName>Ludwig</givenName>
    <familyName>Beethoven</familyName>
    <works>
        <work>
            <title>Cacophony No. 2.718</title>
            <completionDate>2011-10-26T17:28:04.994-04:00</completionDate>
            <premiereDate>2011-10-26T17:28:04.994-04:00</premiereDate>
            <instrumentation>kazoo, comb, wax paper</instrumentation>
        </work>
    </works>
</composer>

The Java solution

What about your Java object graph when this XML is deserialized? There is no attribute in the work element for the composer. But, you see that the work is a child element of composer so that information is not lost. Let’s check what happens with a few more utility methods.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
public void saveXML(Composer composer, String fileName) {
   try {
      JAXBContext jaxbc = JAXBContext.newInstance(Composer.class);
      Marshaller marshaller = jaxbc.createMarshaller();
      marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
      OutputStream out = null;
      try {
         out = new FileOutputStream(fileName);
         marshaller.marshal(composer, out);
      } catch (FileNotFoundException e) {
         e.printStackTrace();
      } finally {
         try {
            out.close();
         } catch (IOException e) {
         }
      }
   } catch (JAXBException e) {
      e.printStackTrace();
      this.logger.error(e.getMessage());
   }
}
 
public Composer readComposerXML(String fileName) {
   Composer composer = null;
   try {
      JAXBContext jaxbc = JAXBContext.newInstance(Composer.class);
      Unmarshaller marshaller = jaxbc.createUnmarshaller();
      InputStream file = null;
      try {
         file = new FileInputStream(fileName);
         composer = (Composer) marshaller.unmarshal(file);
      } catch (FileNotFoundException e) {
         e.printStackTrace();
         this.logger.error(e.getMessage());
      } finally {
         try {
            file.close();
         } catch (IOException e) {
         }
      }
 
   } catch (JAXBException e) {
      e.printStackTrace();
      this.logger.error(e.getMessage());
   }
   return composer;
}

Now in the main method let’s test the marshal/unmarshal process. We save the composer to a file and then read it back. Then we look at each work and print out the composer info.

1
2
3
4
5
6
7
...
helper.saveXML(composer, "composer.xml");
Composer read = helper.readComposerXML("composer.xml");
for(Work w: read.getWorks()) {
   System.out.println("Title: " + w.getTitle());
   System.out.println("Composer: " + w.getComposer());
}

Here is the output.

1
2
Title: Cacophony No. 2.718
Composer: null

Since the composer field in Work has been annotated as being XmlTransient, it is not restored on unmarshal. We have to do that ourselves. Luckily it is simple. Just implement this method in the child class (Work) to restore the reference to the parent.

1
2
3
public void afterUnmarshal(Unmarshaller u, Object parent) {
   this.composer = (Composer) parent;
}

Eye candy

You can specify the ordering of the elements output by JAXB with the XmlType annotation.

1
2
3
4
@XmlType(name = "ComposerType", propOrder = { "id", "familyName", "givenName",
		"middleName", "works" })
public class Composer implements Serializable, Comparable<Composer> {
...

Resources

JAXB home page

Spring MVC RequestBody annotation and converters

Download a Maven project for the code contained in this (and other) articles. Run the web service first via jetty:run, and then run the unit tests. For the simple raw test, simply right JAXBHelper as a Java application.

You probably might also know that if you have curl installed you can do this:

1
2
3
curl -i -H "Accept: application/xml" http://localhost:8080/jaxbmarshal/composerws/Mozart/Wolfgang/Amadeus
 
curl -i -H "Accept: application/json" http://localhost:8080/jaxbmarshal/composerws/composers
Posted in REST, XML | Tagged , | Leave a comment

Modify heap size for Eclipse on OSX

I recently tried to load a sample IRS 1120 tax return in XML format (Example_TransmissionWithConsolidatedReturn.xml in their schema distribution) into the Eclipse XML editor. I shouldn’t need to tell you that the IRS schemas are quite complex. The “simple” return was over 12MB! After a lot of heaving, Eclipse just punted and complained about running out of heap space. So, how do you increase the amount of heap space that Eclipse uses? This is different from the amount of heap space that your application uses.

On Linux and that Microsoft virus Petri dish you simply edit eclipse.ini in the Eclipse installation directory.
For OSX it is slightly more complex. In the Finder navigate to the Eclipse installation. You will see Eclipse.app. Right click on the app and choose “Show package contents”.

Then navigate to Contents/MacOS to find eclipse.ini. Now you can edit your preferred heap size. In my case I increased the max heap size: -Xmx1024m

Posted in Eclipse | Tagged , | Leave a comment

JDK 7 for OSX

In the past, developers obtained the JDK from Apple. Apple has announced that they will no longer provide a JDK. They will support the Open JDK project instead.
Here are he details of the Mac OS X Port Project within OpenJDK.

You can check status of the port on the project’s Wiki

There is also a mailing list.

You can download the builds.

Here are a few notes on the installation process.

Essentially, you need to mount the dmg and then drag and drop to install the JDK. The directory /Library/Java/JavaVirtualMachines need to already exist. If you’ve installed a previous version of the JDK, then it does.

Then run the OSX Java Preferences App to set which JDK you’d like to use. Your previous JDK is probably still at the top of the list. Simply drag and drop the new JDK to the top.

Posted in Java | Tagged , | Leave a comment

Spring 3, Hibernate JPA, Tomcat transactions

I tried to deploy a webapp that uses Spring 3, JPA with the Hibernate implementation and Tomcat. There are domain classes with a DAO layer which is called from the Service layer. The classes are annotated correctly. All of the unit tests work. Actually the whole webapp works deployed on other web containers : but not on Tomcat!

The problem was that in my service’s save() method I required a transaction. Of course! That’s what you have to do. The actual problem is that I needed a transaction in that method but one was not being created.

1
2
3
4
5
 @Override
 @Transactional(propagation = Propagation.REQUIRED, rollbackFor=Exception.class, readOnly = false)
 public Composer create(Composer composer) {
      return this.composerDAO.create(composer);
  }

In the spring config files I did have the usual wiring:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<import resource="dataSource.xml"/>
<tx:annotation-driven transaction-manager="transactionManager" />
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"
		p:dataSource-ref="dataSource" 
		p:entityManagerFactory-ref="entityManagerFactory">
		<property name="jpaDialect" ref="jpaDialect" />
</bean>
<bean id="entityManagerFactory"
		class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
		p:dataSource-ref="dataSource" 
		p:jpaDialect-ref="jpaDialect"
		p:persistence-xml-location="classpath*:${persistence.xml}"
		p:persistenceUnitName="ComposerJPA">
		<property name="jpaVendorAdapter">
			<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"
				p:showSql="${jpaVendorAdapter.showSql}" 
				p:generateDdl="${jpaVendorAdapter.generateDdl}" 
				p:databasePlatform="${hibernate.dialect}" 
				p:database="${jpaVendorAdapter.database}"/>
		</property>
</bean>
<!-- used by tx manager and entityManagerFactory -->
<bean id="jpaDialect" class="org.springframework.orm.jpa.vendor.HibernateJpaDialect" />
 
<context:annotation-config />
<context:property-placeholder location="/WEB-INF/spring/db.properties" />

I know you’ve seen this all before.

It helps to modify your logging level for Spring’s transaction classes. Here’s an excerpt from my log4j.properties.

1
2
3
4
log4j.logger.org.springframework.orm.jpa.JpaTransactionManager=DEBUG
log4j.logger.org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter=DEBUG
log4j.logger.org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean=DEBUG
log4j.logger.org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor=DEBUG

You see that my config did include the tx:annotation-driven element which creates transaction proxies for methods annotated with @Transactional.

<tx:annotation-driven transaction-manager="transactionManager" />

One thing I modified was the transaction manager’s mode.

<tx:annotation-driven transaction-manager="transactionManager" mode="aspectj"/>

Another part of the problem is Tomcat’s classloader. You need to specify a special classloader. I created a context.xml in my webapp directly (you can create it in various other Tomcat locations).

So, I created a file named src/main/resources/META-INF/context.xml

1
2
3
4
<Context path="/ConcertMVC" reloadable="false">
    <Loader
        loaderClass="org.springframework.instrument.classloading.tomcat.TomcatInstrumentableClassLoader"/>
</Context>

Since I was using the SpringSource Tools Suite which includes Tomcat that was it. If you are using a Tomcat distribution from Apache, you need to add a Spring jar to you library directory. This jar is included in the Spring download. If you use Maven then you can add the following dependency:

1
2
3
4
5
<dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-instrument-tomcat</artifactId>
   <version>${org.springframework-version}</version>
</dependency>

The add it to your tomcat library directory by copying from your Maven repository.

Resources

Posted in Hibernate, JPA, Spring Framework | Tagged , , | Leave a comment

Spring @MVC 3 cascading selects using jQuery

I’d be rich if I had a dollar for each time I heard someone ask “How do I dynamically populate a drop down menu based on the current selection in another drop down?” Actually that is probably the poster child usage of AJAX. You can certainly use raw AJAX to accomplish this (just ask the Google). I want to modify the question to “How do I do this using Spring @MVC 3?” So, this post will present one way of doing this using the very popular Javascript library jQuery to accomplish the AJAX part.

One of the great thing about the Spring Framework’s @MVC web framework is that Spring mostly stays out of the way of the underlying view technology. If you’ve used Struts 1 you are familiar with the tag soup with which you pollute your JSPs. There are only a handful of spring specific tags which focus on Spring specific functionality. That means Spring does nothing to help you link one HTML select element to another. Or put in a better way, Spring does not get in your way.

Let’s look at some initial code in the JSP where we have two selects. We want the city select to be populated when we choose the usStates select.

In this fragment we create a form with two select elements using Spring MVC’s tags. This is where some confusion sets in. The form:select tag does not enable our desired cascading behavior. When rendered (inspect the running page with Firebug or look at the source in the browser) you will see a plain old HTML select element.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<c:url value="/signup" var="signupUrl" />
<form:form id="signup" action="${signupUrl}" method="post"
		modelAttribute="signupForm">
 
	<fieldset>
		<form:select id="usStates" path="usState">
		</form:select>
 
		<form:select id="city" path="city">
			<form:option value="">City</form:option>
		</form:select>
	</fieldset>
	<p>
		<button type="submit">Sign Up</button>
	</p>
</form:form>

The Spring MVC controller RegistrationContoller will respond to our requests. It is autowired (I’m using the JEE @Inject annotation instead) with my GeoServer which will return the states and cities. First we want it to create a form backing object when the controller receives an HTTP GET request at the /register URL, i.e. when your browser visits http://localhost:port/context/register
This method returns the name of the view that will be returned. In my Spring config file I specified that my JSPs are under WEB-INF/views and have a suffix of .jsp so here you need to only include the basename. (See org.springframework.web.servlet.view.InternalResourceViewResolver)
Notice that the name of the model attribute for the form backing object in the Controller (signupForm) matches the one specified in the JSP form:form element. The path attributes on the
elements match properties in the form backing object. Spring will automatically set/get these values.

1
2
3
4
5
6
7
8
9
10
11
12
13
@Controller
public class RegistrationController {
	private static final Logger logger = LoggerFactory
			.getLogger(RegistrationController.class);
	@Inject
	private GeoService geoService;
	@RequestMapping(value = "/register", method = RequestMethod.GET)
	public String get(Model model) {
		SignupForm form = new SignupForm();
		model.addAttribute("signupForm", form);
		logger.debug("registering. added form to model and returning register page");
		return "register";
	}

Download the current version of jQuery (see Resources). I copied the source to my WEB-INF/resources directory (I created a jquery folder there) then I linked to it in my JSP header.

1
2
3
4
5
<head>
...
<script type="text/javascript"
	src="<c:url value="/resources/jquery/1.6/jquery-1.6.1.min.js" />"></script>
</head>

As you could see in the initial JSP I did not have any option elements in either select element. So let’s populate the usStates select with some data from the server side. I’ve set up an MVC controller method to respond to URL /states by returning a JSON representation of the states. I’ll cover that next.

In jQuery there is a way to wait until everything on the page has been completely loaded so we will wrap our scripts with this idiom:

1
2
3
$(document).ready(
// at this point the page has been loaded
);

The $ symbol represents the jQuery Javascript object.

I will use the jquery getJSON function to get the data from the Spring controller. You need to specify the url, optional key/value (a map) data to send to the server, and a callback function to handle the data. I used the JSTL c:url tag to create a variable that include the context root for the controller method URL then used that variable in the Javascript. The key/value data I send to the server simply says that I want AJAX. The returned JSON data arrives as a parameter in the callback function. At this point it is up to you to use this data however you see fit. What we need in our example is a set of options for our usStates select element. So I loop over the data and create an option for each datum. Then I set the select element with id usStates to use these new options with the html function.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<c:url var="findStatesURL" value="/states" />
<script type="text/javascript">
$(document).ready(
	function() {
		$.getJSON('${findStatesURL}', {
			ajax : 'true'
		}, function(data) {
			var html = '<option value="">State</option>';
			var len = data.length;
			for ( var i = 0; i < len; i++) {
				html += '<option value="' + data[i].name + '">'
						+ data[i].name + '</option>';
			}
			html += '</option>';
                        //now that we have our options, give them to our select
			$('#usStates').html(html);
		});
	});
</script>

Back in the RegistrationController, we see that the findAllStates() method returns a Set containing State objects obtained from the GeoService. That’s right no JSON; just a collection of Java objects. Spring and Jackson will automatically marshal (convert) your Java objects to JSON.

1
2
3
4
5
6
@RequestMapping(value = "/states", method = RequestMethod.GET)
public @ResponseBody
Set<State> findAllStates() {
	logger.debug("finding all states");
	return this.geoService.findAllStates();
}

To make this work you need to annotate your return value with @ResponseBody. Then in the Spring config file (servlet-context.xml if you’re using the STS template) you use this annotation:

<annotation-driven />

Finally you need to have Jackson on your classpath. (Well, duh). That’s it. Automatic. (Unfortunately Jackson is not so automatic with more complicated object graphs. In particular parent/child relationship require more setup. More on that in another post).

While we’re in the controller let’s peek at our next method which returns the cities. As you can see it is mostly the same as the states method. The big difference (besides returning cities instead of states!) is that I need to specify which state for which I’d like to return the cities, so I add a RequestParam which is forwarded to the GeoService.

1
2
3
4
5
6
7
@RequestMapping(value = "/cities", method = RequestMethod.GET)
public @ResponseBody
Set<City> citiesForState(
		@RequestParam(value = "stateName", required = true) String state) {
	logger.debug("finding cities for state " + state);
	return this.geoService.findCitiesForState(state);
}

Blah, blah, blah, How do I cascade the selects already?

Just look for the change event on the usStates select element via jQuery. The rest of this Javascript looks astonishingly similar to what we saw to populate the usStates element! I added the required stateName parameter and there is a different URL and a different select receives the data but the rest is the same.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<c:url var="findStateCitiesURL" value="/cities" />
 
<script type="text/javascript">
$(document).ready(function() { 
	$('#usStates').change(
		function() {
			$.getJSON('${findStateCitiesURL}', {
				stateName : $(this).val(),
				ajax : 'true'
			}, function(data) {
				var html = '<option value="">City</option>';
				var len = data.length;
				for ( var i = 0; i < len; i++) {
					html += '<option value="' + data[i].name + '">'
							+ data[i].name + '</option>';
				}
				html += '</option>';
 
				$('#city').html(html);
			});
		});
});
</script>

Cool huh?

One final bit of UI sugar would be to set some sort of message when a city is selected on the city select element. In the body of the JSP I’ve added a div element with the id “output”. With this bit of jQuery you can set its child text.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<script type="text/javascript">
	$(document).ready(function(){
		$("#city").change(onSelectChange);
	});
 
	function onSelectChange() {
		var selected = $("#city option:selected");		
		var output = "";
		if(selected.val() != 0){
			output = "You selected City " + selected.text();
		}
		$("#output").html(output);
	}
</script>
</head>
<body>
...
<div id="output"></div>
...

What do you think?

Resources

Unzip the Maven project and import into your IDE or run from the command line.

mvn jetty:run

then point your browser to:

http://localhost:8080/springFormWithCascadingSelects/

or you can run the unit and integration tests. The integration tests will run jetty and Selenium 2.

mvn integration-test
Posted in jQuery, Spring Framework, Spring MVC | Tagged , , | 4 Comments
  • About the Author

    Gene De Lisa Development notes by Gene De Lisa on topics relating to Java and various open source projects such as the Spring Framework, Hibernate and others.

  • Recent Posts

  • Categories

  • Archives