From 7370e9ff599f6aa8ea6597bb2d13820555b82ff1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sanjin=20Tula=C4=8D?= Date: Fri, 16 Dec 2016 18:14:01 -0800 Subject: [PATCH 1/2] Fixed CXF-7186: Aegis DataBinding ignores JAXB annotations on a field --- .../aegis/type/java5/AnnotatedTypeInfo.java | 44 ++++++++++++++++++- .../aegis/type/java5/AnnotationReader.java | 2 +- .../cxf/aegis/type/java5/JaxbBean1.java | 10 +++++ .../cxf/aegis/type/java5/JaxbTypeTest.java | 28 +++++++++++- 4 files changed, 79 insertions(+), 5 deletions(-) diff --git a/rt/databinding/aegis/src/main/java/org/apache/cxf/aegis/type/java5/AnnotatedTypeInfo.java b/rt/databinding/aegis/src/main/java/org/apache/cxf/aegis/type/java5/AnnotatedTypeInfo.java index bb447264a8c..80ebb32fde1 100644 --- a/rt/databinding/aegis/src/main/java/org/apache/cxf/aegis/type/java5/AnnotatedTypeInfo.java +++ b/rt/databinding/aegis/src/main/java/org/apache/cxf/aegis/type/java5/AnnotatedTypeInfo.java @@ -19,6 +19,7 @@ package org.apache.cxf.aegis.type.java5; import java.beans.PropertyDescriptor; +import java.lang.reflect.Field; import java.lang.reflect.Method; import javax.xml.namespace.QName; @@ -93,13 +94,45 @@ protected QName createMappedName(PropertyDescriptor desc, boolean qualify) { } protected QName createQName(PropertyDescriptor desc) { + String name = getName(desc); + String namespace = getNamespace(desc); + return new QName(namespace, name); + } + + /** + * XML Name of a field is derived from the following sources in this order of priorities: + * + */ + private String getName(PropertyDescriptor desc) { String name = annotationReader.getName(desc.getReadMethod()); + if (name == null) { + name = annotationReader.getName(getField(desc)); + } if (name == null) { name = desc.getName(); } + return name; + } - // namespace: method, class, package, generated + /** + * XML Namespace of a field is derived from the following sources in this order of priorities: + *
    + *
  1. getter method annotation + *
  2. field annotation + *
  3. class annotation + *
  4. package annotation + *
  5. fully qualified package name + *
+ */ + private String getNamespace(PropertyDescriptor desc) { String namespace = annotationReader.getNamespace(desc.getReadMethod()); + if (namespace == null) { + namespace = annotationReader.getNamespace(getField(desc)); + } if (namespace == null) { namespace = annotationReader.getNamespace(getTypeClass()); } @@ -109,8 +142,15 @@ protected QName createQName(PropertyDescriptor desc) { if (namespace == null) { namespace = NamespaceHelper.makeNamespaceFromClassName(getTypeClass().getName(), "http"); } + return namespace; + } - return new QName(namespace, name); + private Field getField(PropertyDescriptor desc) { + try { + return getTypeClass().getDeclaredField(desc.getName()); + } catch (NoSuchFieldException e) { + return null; + } } public boolean isNillable(QName name) { diff --git a/rt/databinding/aegis/src/main/java/org/apache/cxf/aegis/type/java5/AnnotationReader.java b/rt/databinding/aegis/src/main/java/org/apache/cxf/aegis/type/java5/AnnotationReader.java index 66299a05f9b..6bfe6137b54 100644 --- a/rt/databinding/aegis/src/main/java/org/apache/cxf/aegis/type/java5/AnnotationReader.java +++ b/rt/databinding/aegis/src/main/java/org/apache/cxf/aegis/type/java5/AnnotationReader.java @@ -289,7 +289,7 @@ static Object getAnnotationValue(String name, Class... annotations) { for (Class annotation : annotations) { - if (annotation != null) { + if (annotation != null && element != null) { try { Annotation ann = element.getAnnotation(annotation.asSubclass(Annotation.class)); if (ann != null) { diff --git a/rt/databinding/aegis/src/test/java/org/apache/cxf/aegis/type/java5/JaxbBean1.java b/rt/databinding/aegis/src/test/java/org/apache/cxf/aegis/type/java5/JaxbBean1.java index d3312bb4ad0..64e8d8e7b4a 100644 --- a/rt/databinding/aegis/src/test/java/org/apache/cxf/aegis/type/java5/JaxbBean1.java +++ b/rt/databinding/aegis/src/test/java/org/apache/cxf/aegis/type/java5/JaxbBean1.java @@ -25,6 +25,8 @@ public class JaxbBean1 { private String elementProperty; private String attributeProperty; private String bogusProperty; + @XmlElement(name = "Annotated") + private String annotatedProperty; @XmlAttribute public String getAttributeProperty() { @@ -51,4 +53,12 @@ public String getElementProperty() { public void setElementProperty(String elementProperty) { this.elementProperty = elementProperty; } + + public String getAnnotatedProperty() { + return annotatedProperty; + } + + public void setAnnotatedProperty(String annotatedProperty) { + this.annotatedProperty = annotatedProperty; + } } diff --git a/rt/databinding/aegis/src/test/java/org/apache/cxf/aegis/type/java5/JaxbTypeTest.java b/rt/databinding/aegis/src/test/java/org/apache/cxf/aegis/type/java5/JaxbTypeTest.java index 951ce4a629b..81b383fd541 100644 --- a/rt/databinding/aegis/src/test/java/org/apache/cxf/aegis/type/java5/JaxbTypeTest.java +++ b/rt/databinding/aegis/src/test/java/org/apache/cxf/aegis/type/java5/JaxbTypeTest.java @@ -66,7 +66,7 @@ public void testType() { Iterator elements = info.getElements().iterator(); assertTrue(elements.hasNext()); - QName element = elements.next(); + QName element = elements.next(); //1st element of 3 expected assertTrue(elements.hasNext()); AegisType custom = info.getType(element); @@ -75,10 +75,28 @@ public void testType() { assertTrue(custom instanceof StringType); } else if ("elementProperty".equals(element.getLocalPart())) { assertTrue(custom instanceof CustomStringType); + } else if ("Annotated".equals(element.getLocalPart())) { + assertTrue(custom instanceof StringType); } else { fail("Unexpected element name: " + element.getLocalPart()); } - element = elements.next(); + + assertTrue(elements.hasNext()); + element = elements.next(); //2nd element of 3 expected + assertTrue(elements.hasNext()); + + custom = info.getType(element); + + if ("bogusProperty".equals(element.getLocalPart())) { + assertTrue(custom instanceof StringType); + } else if ("elementProperty".equals(element.getLocalPart())) { + assertTrue(custom instanceof CustomStringType); + } else if ("Annotated".equals(element.getLocalPart())) { + assertTrue(custom instanceof StringType); + } else { + fail("Unexpected element name: " + element.getLocalPart()); + } + element = elements.next(); //3rd element of 3 expected assertFalse(elements.hasNext()); custom = info.getType(element); @@ -87,6 +105,8 @@ public void testType() { assertTrue(custom instanceof StringType); } else if ("elementProperty".equals(element.getLocalPart())) { assertTrue(custom instanceof CustomStringType); + } else if ("Annotated".equals(element.getLocalPart())) { + assertTrue(custom instanceof StringType); } else { fail("Unexpected element name: " + element.getLocalPart()); } @@ -164,6 +184,10 @@ public void testWSDL() throws Exception { "//xsd:complexType[@name='JaxbBean1']/xsd:sequence/xsd:element" + "[@name='bogusProperty']", wsdl); + assertValid( + "//xsd:complexType[@name='JaxbBean1']/xsd:sequence/xsd:element" + + "[@name='Annotated']", + wsdl); assertValid( "//xsd:complexType[@name='JaxbBean2']/xsd:sequence/xsd:element" From 24adedf854516cacc9b6b50f3d0cf7500171fc19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sanjin=20Tula=C4=8D?= Date: Mon, 19 Dec 2016 19:40:16 -0800 Subject: [PATCH 2/2] Fix for CXF-7188: javax.xml.bind.annotation.XmlEnumValue annotation should be honored in Aegis data binding --- .../aegis/type/java5/AnnotationReader.java | 17 ++++++ .../apache/cxf/aegis/type/java5/EnumType.java | 41 ++++++++++++--- .../cxf/aegis/type/java5/EnumTypeTest.java | 52 ++++++++++++++++--- .../cxf/aegis/type/java5/JaxbTestEnum.java | 7 ++- 4 files changed, 99 insertions(+), 18 deletions(-) diff --git a/rt/databinding/aegis/src/main/java/org/apache/cxf/aegis/type/java5/AnnotationReader.java b/rt/databinding/aegis/src/main/java/org/apache/cxf/aegis/type/java5/AnnotationReader.java index 6bfe6137b54..a80130b75f4 100644 --- a/rt/databinding/aegis/src/main/java/org/apache/cxf/aegis/type/java5/AnnotationReader.java +++ b/rt/databinding/aegis/src/main/java/org/apache/cxf/aegis/type/java5/AnnotationReader.java @@ -20,8 +20,11 @@ import java.lang.annotation.Annotation; import java.lang.reflect.AnnotatedElement; +import java.lang.reflect.Field; import java.lang.reflect.Method; +import javax.xml.bind.annotation.XmlEnumValue; + import org.apache.cxf.aegis.type.AegisType; public class AnnotationReader { @@ -385,5 +388,19 @@ public boolean isFlat(Annotation[] annotations) { return false; } + public static String getEnumValue(Enum enumConstant) { + @SuppressWarnings("rawtypes") + Class enumClass = enumConstant.getClass(); + try { + Field constantField = enumClass.getDeclaredField(enumConstant.name()); + XmlEnumValue constantValueAnnotation = constantField.getAnnotation(XmlEnumValue.class); + if (constantValueAnnotation == null) { + return null; + } + return constantValueAnnotation.value(); + } catch (NoSuchFieldException e) { + return null; + } + } } diff --git a/rt/databinding/aegis/src/main/java/org/apache/cxf/aegis/type/java5/EnumType.java b/rt/databinding/aegis/src/main/java/org/apache/cxf/aegis/type/java5/EnumType.java index 794211bac07..91a4eb230d9 100644 --- a/rt/databinding/aegis/src/main/java/org/apache/cxf/aegis/type/java5/EnumType.java +++ b/rt/databinding/aegis/src/main/java/org/apache/cxf/aegis/type/java5/EnumType.java @@ -26,27 +26,24 @@ import org.apache.cxf.aegis.type.AegisType; import org.apache.cxf.aegis.xml.MessageReader; import org.apache.cxf.aegis.xml.MessageWriter; -import org.apache.cxf.common.xmlschema.XmlSchemaConstants; import org.apache.ws.commons.schema.XmlSchema; import org.apache.ws.commons.schema.XmlSchemaEnumerationFacet; import org.apache.ws.commons.schema.XmlSchemaFacet; import org.apache.ws.commons.schema.XmlSchemaSimpleType; import org.apache.ws.commons.schema.XmlSchemaSimpleTypeRestriction; +import org.apache.ws.commons.schema.constants.Constants; public class EnumType extends AegisType { - @SuppressWarnings("unchecked") @Override public Object readObject(MessageReader reader, Context context) { String value = reader.getValue(); - @SuppressWarnings("rawtypes") - Class cls = (Class)getTypeClass(); - return Enum.valueOf(cls, value.trim()); + return matchValue(value); } @Override public void writeObject(Object object, MessageWriter writer, Context context) { // match the reader. - writer.writeValue(((Enum)object).name()); + writer.writeValue(getValue((Enum)object)); } @Override @@ -69,7 +66,7 @@ public void writeSchema(XmlSchema root) { XmlSchemaSimpleType simple = new XmlSchemaSimpleType(root, true); simple.setName(getSchemaType().getLocalPart()); XmlSchemaSimpleTypeRestriction restriction = new XmlSchemaSimpleTypeRestriction(); - restriction.setBaseTypeName(XmlSchemaConstants.STRING_QNAME); + restriction.setBaseTypeName(Constants.XSD_STRING); simple.setContent(restriction); Object[] constants = getTypeClass().getEnumConstants(); @@ -77,11 +74,39 @@ public void writeSchema(XmlSchema root) { List facets = restriction.getFacets(); for (Object constant : constants) { XmlSchemaEnumerationFacet f = new XmlSchemaEnumerationFacet(); - f.setValue(((Enum)constant).name()); + f.setValue(getValue(constant)); facets.add(f); } } + @SuppressWarnings("unchecked") + private Enum matchValue(String value) { + if (value == null) { + return null; + } + @SuppressWarnings("rawtypes") + Class enumClass = (Class)getTypeClass(); + for (Enum enumConstant : enumClass.getEnumConstants()) { + if (value.equals(AnnotationReader.getEnumValue(enumConstant))) { + return enumConstant; + } + } + return Enum.valueOf(enumClass, value.trim()); + } + + + private Object getValue(Object constant) { + if (!(constant instanceof Enum)) { + return null; + } + Enum enumConstant = (Enum)constant; + String annotatedValue = AnnotationReader.getEnumValue(enumConstant); + if (annotatedValue != null) { + return annotatedValue; + } + return enumConstant.name(); + } + @Override public boolean isComplex() { return true; diff --git a/rt/databinding/aegis/src/test/java/org/apache/cxf/aegis/type/java5/EnumTypeTest.java b/rt/databinding/aegis/src/test/java/org/apache/cxf/aegis/type/java5/EnumTypeTest.java index 9758420342c..242bef112e9 100644 --- a/rt/databinding/aegis/src/test/java/org/apache/cxf/aegis/type/java5/EnumTypeTest.java +++ b/rt/databinding/aegis/src/test/java/org/apache/cxf/aegis/type/java5/EnumTypeTest.java @@ -30,10 +30,10 @@ import org.apache.cxf.aegis.type.TypeCreationOptions; import org.apache.cxf.aegis.type.java5.CurrencyService.Currency; import org.apache.cxf.aegis.xml.stax.ElementReader; -import org.apache.cxf.common.util.SOAPConstants; import org.apache.cxf.staxutils.StaxUtils; import org.apache.ws.commons.schema.XmlSchema; import org.apache.ws.commons.schema.XmlSchemaSerializer; +import org.apache.ws.commons.schema.constants.Constants; import org.junit.Before; import org.junit.Test; @@ -41,7 +41,7 @@ public class EnumTypeTest extends AbstractAegisTest { private DefaultTypeMapping tm; - private enum smallEnum { + private enum SmallEnum { VALUE1, VALUE2; @Override @@ -64,12 +64,12 @@ public void setUp() throws Exception { @Test public void testType() throws Exception { EnumType type = new EnumType(); - type.setTypeClass(smallEnum.class); + type.setTypeClass(SmallEnum.class); type.setSchemaType(new QName("urn:test", "test")); tm.register(type); - Element element = writeObjectToElement(type, smallEnum.VALUE1, getContext()); + Element element = writeObjectToElement(type, SmallEnum.VALUE1, getContext()); assertEquals("VALUE1", element.getTextContent()); @@ -77,12 +77,12 @@ public void testType() throws Exception { ElementReader reader = new ElementReader(xreader); Object value = type.readObject(reader, getContext()); - assertEquals(smallEnum.VALUE1, value); + assertEquals(SmallEnum.VALUE1, value); } @Test public void testAutoCreation() throws Exception { - AegisType type = tm.getTypeCreator().createType(smallEnum.class); + AegisType type = tm.getTypeCreator().createType(SmallEnum.class); assertTrue(type instanceof EnumType); } @@ -114,22 +114,58 @@ public void testJaxbTypeAttributeOnEnum() throws Exception { assertTrue(type instanceof EnumType); } + /** + * {@link https://issues.apache.org/jira/browse/CXF-7188} + */ + @Test + public void testTypeWithJaxbAnnotations() throws Exception { + AegisType type = tm.getTypeCreator().createType(JaxbTestEnum.class); + + Element element = writeObjectToElement(type, JaxbTestEnum.VALUE1, getContext()); + + assertEquals("Value1", element.getTextContent()); + + XMLStreamReader xreader = StaxUtils.createXMLStreamReader(element); + ElementReader reader = new ElementReader(xreader); + Object value = type.readObject(reader, getContext()); + + assertEquals(JaxbTestEnum.VALUE1, value); + } + @Test public void testWSDL() throws Exception { EnumType type = new EnumType(); - type.setTypeClass(smallEnum.class); + type.setTypeClass(SmallEnum.class); type.setSchemaType(new QName("urn:test", "test")); XmlSchema schema = newXmlSchema("urn:test"); type.writeSchema(schema); XmlSchemaSerializer ser = new XmlSchemaSerializer(); Document doc = ser.serializeSchema(schema, false)[0]; - addNamespace("xsd", SOAPConstants.XSD); + addNamespace("xsd", Constants.URI_2001_SCHEMA_XSD); assertValid("//xsd:simpleType[@name='test']/xsd:restriction[@base='xsd:string']", doc); assertValid("//xsd:restriction[@base='xsd:string']/xsd:enumeration[@value='VALUE1']", doc); assertValid("//xsd:restriction[@base='xsd:string']/xsd:enumeration[@value='VALUE2']", doc); } + /** + * {@link https://issues.apache.org/jira/browse/CXF-7188} + */ + @Test + public void testWsdlFromJaxbAnnotations() throws Exception { + AegisType type = tm.getTypeCreator().createType(JaxbTestEnum.class); + + XmlSchema schema = newXmlSchema("urn:test"); + type.writeSchema(schema); + + XmlSchemaSerializer ser = new XmlSchemaSerializer(); + Document doc = ser.serializeSchema(schema, false)[0]; + addNamespace("xsd", Constants.URI_2001_SCHEMA_XSD); + assertValid("//xsd:simpleType[@name='bar']/xsd:restriction[@base='xsd:string']", doc); + assertValid("//xsd:restriction[@base='xsd:string']/xsd:enumeration[@value='Value1']", doc); + assertValid("//xsd:restriction[@base='xsd:string']/xsd:enumeration[@value='VALUE2']", doc); + } + @Test public void testCurrencyService() throws Exception { createService(CurrencyService.class); diff --git a/rt/databinding/aegis/src/test/java/org/apache/cxf/aegis/type/java5/JaxbTestEnum.java b/rt/databinding/aegis/src/test/java/org/apache/cxf/aegis/type/java5/JaxbTestEnum.java index aacb8ab77f9..aad0f91f890 100644 --- a/rt/databinding/aegis/src/test/java/org/apache/cxf/aegis/type/java5/JaxbTestEnum.java +++ b/rt/databinding/aegis/src/test/java/org/apache/cxf/aegis/type/java5/JaxbTestEnum.java @@ -18,9 +18,12 @@ */ package org.apache.cxf.aegis.type.java5; +import javax.xml.bind.annotation.XmlEnumValue; import javax.xml.bind.annotation.XmlType; -@XmlType(namespace = "urn:xfire:foo") +@XmlType(namespace = "urn:xfire:foo", name = "bar") public enum JaxbTestEnum { - VALUE1, VALUE2 + @XmlEnumValue("Value1") + VALUE1, + VALUE2 }