SPR-6577 - MarshallingView auto detect model with Jaxb2Marshaller chooses the wrong object

This commit is contained in:
Arjen Poutsma
2010-01-27 10:52:08 +00:00
parent 71d7b22d5e
commit b1e2a2ec3c
5 changed files with 183 additions and 42 deletions

View File

@@ -0,0 +1,37 @@
/*
* Copyright 2002-2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.oxm;
import java.lang.reflect.Type;
/**
* Subinterface of {@link Marshaller} that has support for Java 5 generics.
*
* @author Arjen Poutsma
* @sicne 3.0.1
*/
public interface GenericMarshaller extends Marshaller {
/**
* Indicates whether this marshaller can marshal instances of the supplied generic type.
* @param genericType the type that this marshaller is being asked if it can marshal
* @return <code>true</code> if this marshaller can indeed marshal instances of the supplied type;
* <code>false</code> otherwise
*/
boolean supports(Type genericType);
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright 2002-2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.oxm;
import java.lang.reflect.Type;
/**
* Subinterface of {@link Unmarshaller} that has support for Java 5 generics.
*
* @author Arjen Poutsma
* @sicne 3.0.1
*/
public interface GenericUnmarshaller extends Unmarshaller {
/**
* Indicates whether this marshaller can marshal instances of the supplied generic type.
* @param genericType the type that this marshaller is being asked if it can marshal
* @return <code>true</code> if this marshaller can indeed marshal instances of the supplied type;
* <code>false</code> otherwise
*/
boolean supports(Type genericType);
}

View File

@@ -28,6 +28,8 @@ import java.net.URLEncoder;
import java.util.Arrays;
import java.util.Map;
import java.util.UUID;
import java.lang.reflect.Type;
import java.lang.reflect.ParameterizedType;
import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.xml.XMLConstants;
@@ -41,6 +43,7 @@ import javax.xml.bind.Unmarshaller;
import javax.xml.bind.ValidationEventHandler;
import javax.xml.bind.ValidationException;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import javax.xml.bind.attachment.AttachmentMarshaller;
import javax.xml.bind.attachment.AttachmentUnmarshaller;
@@ -70,6 +73,8 @@ import org.springframework.oxm.UncategorizedMappingException;
import org.springframework.oxm.UnmarshallingFailureException;
import org.springframework.oxm.ValidationFailureException;
import org.springframework.oxm.XmlMappingException;
import org.springframework.oxm.GenericMarshaller;
import org.springframework.oxm.GenericUnmarshaller;
import org.springframework.oxm.mime.MimeContainer;
import org.springframework.oxm.mime.MimeMarshaller;
import org.springframework.oxm.mime.MimeUnmarshaller;
@@ -101,7 +106,9 @@ import org.springframework.util.xml.StaxUtils;
* @see #setAdapters(XmlAdapter[])
* @since 3.0
*/
public class Jaxb2Marshaller implements MimeMarshaller, MimeUnmarshaller, BeanClassLoaderAware, InitializingBean {
public class Jaxb2Marshaller
implements MimeMarshaller, MimeUnmarshaller, GenericMarshaller, GenericUnmarshaller, BeanClassLoaderAware,
InitializingBean {
private static final String CID = "cid:";
@@ -143,6 +150,12 @@ public class Jaxb2Marshaller implements MimeMarshaller, MimeUnmarshaller, BeanCl
private boolean lazyInit = false;
/**
* Returns the JAXB context path.
*/
public String getContextPath() {
return contextPath;
}
/**
* Set a JAXB context path.
@@ -161,6 +174,13 @@ public class Jaxb2Marshaller implements MimeMarshaller, MimeUnmarshaller, BeanCl
this.contextPath = StringUtils.arrayToDelimitedString(contextPaths, ":");
}
/**
* Returns the list of Java classes to be recognized by a newly created JAXBContext.
*/
public Class[] getClassesToBeBound() {
return classesToBeBound;
}
/**
* Set the list of Java classes to be recognized by a newly created JAXBContext.
* Setting this property or {@link #setContextPath "contextPath"} is required.
@@ -280,10 +300,10 @@ public class Jaxb2Marshaller implements MimeMarshaller, MimeUnmarshaller, BeanCl
public final void afterPropertiesSet() throws Exception {
if (StringUtils.hasLength(this.contextPath) && !ObjectUtils.isEmpty(this.classesToBeBound)) {
if (StringUtils.hasLength(getContextPath()) && !ObjectUtils.isEmpty(getClassesToBeBound())) {
throw new IllegalArgumentException("Specify either 'contextPath' or 'classesToBeBound property'; not both");
}
else if (!StringUtils.hasLength(this.contextPath) && ObjectUtils.isEmpty(this.classesToBeBound)) {
else if (!StringUtils.hasLength(getContextPath()) && ObjectUtils.isEmpty(getClassesToBeBound())) {
throw new IllegalArgumentException("Setting either 'contextPath' or 'classesToBeBound' is required");
}
if (!lazyInit) {
@@ -297,10 +317,10 @@ public class Jaxb2Marshaller implements MimeMarshaller, MimeUnmarshaller, BeanCl
protected synchronized JAXBContext getJaxbContext() {
if (this.jaxbContext == null) {
try {
if (StringUtils.hasLength(this.contextPath)) {
if (StringUtils.hasLength(getContextPath())) {
this.jaxbContext = createJaxbContextFromContextPath();
}
else if (!ObjectUtils.isEmpty(this.classesToBeBound)) {
else if (!ObjectUtils.isEmpty(getClassesToBeBound())) {
this.jaxbContext = createJaxbContextFromClasses();
}
}
@@ -313,22 +333,22 @@ public class Jaxb2Marshaller implements MimeMarshaller, MimeUnmarshaller, BeanCl
private JAXBContext createJaxbContextFromContextPath() throws JAXBException {
if (logger.isInfoEnabled()) {
logger.info("Creating JAXBContext with context path [" + this.contextPath + "]");
logger.info("Creating JAXBContext with context path [" + getContextPath() + "]");
}
if (this.jaxbContextProperties != null) {
if (this.beanClassLoader != null) {
return JAXBContext.newInstance(this.contextPath, this.beanClassLoader, this.jaxbContextProperties);
return JAXBContext.newInstance(getContextPath(), this.beanClassLoader, this.jaxbContextProperties);
}
else {
return JAXBContext.newInstance(this.contextPath, ClassUtils.getDefaultClassLoader(), this.jaxbContextProperties);
return JAXBContext.newInstance(getContextPath(), ClassUtils.getDefaultClassLoader(), this.jaxbContextProperties);
}
}
else {
if (this.beanClassLoader != null) {
return JAXBContext.newInstance(this.contextPath, this.beanClassLoader);
return JAXBContext.newInstance(getContextPath(), this.beanClassLoader);
}
else {
return JAXBContext.newInstance(this.contextPath);
return JAXBContext.newInstance(getContextPath());
}
}
}
@@ -336,13 +356,13 @@ public class Jaxb2Marshaller implements MimeMarshaller, MimeUnmarshaller, BeanCl
private JAXBContext createJaxbContextFromClasses() throws JAXBException {
if (logger.isInfoEnabled()) {
logger.info("Creating JAXBContext with classes to be bound [" +
StringUtils.arrayToCommaDelimitedString(this.classesToBeBound) + "]");
StringUtils.arrayToCommaDelimitedString(getClassesToBeBound()) + "]");
}
if (this.jaxbContextProperties != null) {
return JAXBContext.newInstance(this.classesToBeBound, this.jaxbContextProperties);
return JAXBContext.newInstance(getClassesToBeBound(), this.jaxbContextProperties);
}
else {
return JAXBContext.newInstance(this.classesToBeBound);
return JAXBContext.newInstance(getClassesToBeBound());
}
}
@@ -367,15 +387,35 @@ public class Jaxb2Marshaller implements MimeMarshaller, MimeUnmarshaller, BeanCl
public boolean supports(Class<?> clazz) {
if (JAXBElement.class.isAssignableFrom(clazz)) {
return true;
return supportsInternal(clazz, true);
}
public boolean supports(Type genericType) {
if (genericType instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) genericType;
if (JAXBElement.class.equals(parameterizedType.getRawType()) &&
parameterizedType.getActualTypeArguments().length == 1 &&
parameterizedType.getActualTypeArguments()[0] instanceof Class) {
Class typeArgument = (Class) parameterizedType.getActualTypeArguments()[0];
return supportsInternal(typeArgument, false);
}
} else if (genericType instanceof Class) {
Class clazz = (Class) genericType;
return supportsInternal(clazz, true);
}
else if (AnnotationUtils.findAnnotation(clazz, XmlRootElement.class) != null) {
return true;
return false;
}
private boolean supportsInternal(Class<?> clazz, boolean checkForXmlRootElement) {
if (checkForXmlRootElement && AnnotationUtils.findAnnotation(clazz, XmlRootElement.class) == null) {
return false;
}
if (StringUtils.hasLength(this.contextPath)) {
if (AnnotationUtils.findAnnotation(clazz, XmlType.class) == null) {
return false;
}
if (StringUtils.hasLength(getContextPath())) {
String packageName = ClassUtils.getPackageName(clazz);
String[] contextPaths = StringUtils.tokenizeToStringArray(this.contextPath, ":");
String[] contextPaths = StringUtils.tokenizeToStringArray(getContextPath(), ":");
for (String contextPath : contextPaths) {
if (contextPath.equals(packageName)) {
return true;
@@ -383,8 +423,8 @@ public class Jaxb2Marshaller implements MimeMarshaller, MimeUnmarshaller, BeanCl
}
return false;
}
else if (!ObjectUtils.isEmpty(this.classesToBeBound)) {
return Arrays.asList(this.classesToBeBound).contains(clazz);
else if (!ObjectUtils.isEmpty(getClassesToBeBound())) {
return Arrays.asList(getClassesToBeBound()).contains(clazz);
}
return false;
}

View File

@@ -23,7 +23,7 @@ import org.springframework.oxm.Unmarshaller;
import org.springframework.oxm.XmlMappingException;
/**
* Subinterface of {@link org.springframework.oxm.Marshaller} that can use MIME attachments
* Subinterface of {@link org.springframework.oxm.Unmarshaller} that can use MIME attachments
* to optimize storage of binary data. Attachments can be added as MTOM, XOP, or SwA.
*
* @author Arjen Poutsma