/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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.apache.cxf.jaxb; import java.io.ByteArrayOutputStream; import java.io.OutputStream; import java.io.StringReader; import java.io.StringWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.logging.Logger; import javax.wsdl.Definition; import javax.wsdl.Service; import javax.wsdl.factory.WSDLFactory; import javax.wsdl.xml.WSDLReader; import javax.xml.bind.JAXBContext; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.adapters.XmlAdapter; import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; import javax.xml.stream.XMLEventReader; import javax.xml.stream.XMLEventWriter; import javax.xml.stream.XMLInputFactory; import javax.xml.stream.XMLOutputFactory; import javax.xml.stream.XMLStreamReader; import javax.xml.stream.XMLStreamWriter; import org.w3c.dom.Node; import org.apache.cxf.Bus; import org.apache.cxf.binding.BindingFactoryManager; import org.apache.cxf.common.logging.LogUtils; import org.apache.cxf.common.util.ASMHelper; import org.apache.cxf.common.util.ReflectionUtil; import org.apache.cxf.databinding.DataReader; import org.apache.cxf.databinding.DataWriter; import org.apache.cxf.helpers.CastUtils; import org.apache.cxf.jaxb.fortest.QualifiedBean; import org.apache.cxf.jaxb.fortest.unqualified.UnqualifiedBean; import org.apache.cxf.jaxb.io.DataReaderImpl; import org.apache.cxf.jaxb.io.DataWriterImpl; import org.apache.cxf.transport.DestinationFactoryManager; import org.apache.cxf.wsdl11.WSDLServiceBuilder; import org.apache.hello_world_soap_http.types.GreetMe; import org.apache.hello_world_soap_http.types.GreetMeOneWay; import org.easymock.EasyMock; import org.easymock.IMocksControl; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; public class JAXBDataBindingTest extends Assert { private static final Logger LOG = LogUtils.getLogger(JAXBDataBindingTest.class); private static final String WSDL_PATH = "/wsdl/jaxb/hello_world.wsdl"; private Definition def; private Service service; private IMocksControl control; private Bus bus; private BindingFactoryManager bindingFactoryManager; private JAXBDataBinding jaxbDataBinding; private DestinationFactoryManager destinationFactoryManager; @Before public void setUp() throws Exception { jaxbDataBinding = new JAXBDataBinding(); String wsdlUrl = getClass().getResource(WSDL_PATH).toString(); LOG.info("the path of wsdl file is " + wsdlUrl); WSDLFactory wsdlFactory = WSDLFactory.newInstance(); WSDLReader wsdlReader = wsdlFactory.newWSDLReader(); wsdlReader.setFeature("javax.wsdl.verbose", false); def = wsdlReader.readWSDL(wsdlUrl); control = EasyMock.createNiceControl(); bus = control.createMock(Bus.class); bindingFactoryManager = control.createMock(BindingFactoryManager.class); destinationFactoryManager = control.createMock(DestinationFactoryManager.class); EasyMock.expect(bus.getExtension(BindingFactoryManager.class)).andStubReturn(bindingFactoryManager); EasyMock.expect(bus.getExtension(DestinationFactoryManager.class)) .andStubReturn(destinationFactoryManager); control.replay(); WSDLServiceBuilder wsdlServiceBuilder = new WSDLServiceBuilder(bus); for (Service serv : CastUtils.cast(def.getServices().values(), Service.class)) { if (serv != null) { service = serv; break; } } wsdlServiceBuilder.buildServices(def, service); } @After public void tearDown() throws Exception { } @Test public void testCreateReader() { DataReader<?> reader = jaxbDataBinding.createReader(XMLStreamReader.class); assertTrue(reader instanceof DataReaderImpl); reader = jaxbDataBinding.createReader(XMLEventReader.class); assertTrue(reader instanceof DataReaderImpl); reader = jaxbDataBinding.createReader(Node.class); assertTrue(reader instanceof DataReaderImpl); reader = jaxbDataBinding.createReader(null); assertNull(reader); } @Test public void testSupportedFormats() { List<Class<?>> cls = Arrays.asList(jaxbDataBinding.getSupportedWriterFormats()); assertNotNull(cls); assertEquals(4, cls.size()); assertTrue(cls.contains(XMLStreamWriter.class)); assertTrue(cls.contains(XMLEventWriter.class)); assertTrue(cls.contains(Node.class)); assertTrue(cls.contains(OutputStream.class)); cls = Arrays.asList(jaxbDataBinding.getSupportedReaderFormats()); assertNotNull(cls); assertEquals(3, cls.size()); assertTrue(cls.contains(XMLStreamReader.class)); assertTrue(cls.contains(XMLEventReader.class)); assertTrue(cls.contains(Node.class)); } @Test public void testCreateWriter() { DataWriter<?> writer = jaxbDataBinding.createWriter(XMLStreamWriter.class); assertTrue(writer instanceof DataWriterImpl); writer = jaxbDataBinding.createWriter(XMLEventWriter.class); assertTrue(writer instanceof DataWriterImpl); writer = jaxbDataBinding.createWriter(Node.class); assertTrue(writer instanceof DataWriterImpl); writer = jaxbDataBinding.createWriter(null); assertNull(writer); } @Test public void testExtraClass() { Class<?>[] extraClass = new Class[] {GreetMe.class, GreetMeOneWay.class}; jaxbDataBinding.setExtraClass(extraClass); assertEquals(jaxbDataBinding.getExtraClass().length, 2); assertEquals(jaxbDataBinding.getExtraClass()[0], GreetMe.class); assertEquals(jaxbDataBinding.getExtraClass()[1], GreetMeOneWay.class); } @Test public void testContextProperties() throws Exception { JAXBDataBinding db = new JAXBDataBinding(); Map<String, String> nsMap = new HashMap<>(); nsMap.put("uri:ultima:thule", ""); db.setNamespaceMap(nsMap); Map<String, Object> contextProperties = new HashMap<>(); contextProperties.put("com.sun.xml.bind.defaultNamespaceRemap", "uri:ultima:thule"); db.setContextProperties(contextProperties); Set<Class<?>> classes = new HashSet<Class<?>>(); classes.add(UnqualifiedBean.class); db.setContext(db.createJAXBContext(classes)); DataWriter<XMLStreamWriter> writer = db.createWriter(XMLStreamWriter.class); XMLOutputFactory writerFactory = XMLOutputFactory.newInstance(); StringWriter stringWriter = new StringWriter(); XMLStreamWriter xmlWriter = writerFactory.createXMLStreamWriter(stringWriter); UnqualifiedBean bean = new UnqualifiedBean(); bean.setAriadne("spider"); writer.write(bean, xmlWriter); xmlWriter.flush(); String xml = stringWriter.toString(); assertTrue(xml, xml.contains("uri:ultima:thule")); } JAXBDataBinding createJaxbContext(boolean internal) throws Exception { JAXBDataBinding db = new JAXBDataBinding(); Map<String, String> nsMap = new HashMap<>(); nsMap.put("uri:ultima:thule", "greenland"); db.setNamespaceMap(nsMap); Map<String, Object> contextProperties = new HashMap<>(); db.setContextProperties(contextProperties); Set<Class<?>> classes = new HashSet<Class<?>>(); classes.add(QualifiedBean.class); //have to fastboot to avoid conflicts of generated accessors System.setProperty("com.sun.xml.bind.v2.runtime.JAXBContextImpl.fastBoot", "true"); System.setProperty("com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl.fastBoot", "true"); if (internal) { System.setProperty(JAXBContext.JAXB_CONTEXT_FACTORY, "com.sun.xml.internal.bind.v2.ContextFactory"); db.setContext(db.createJAXBContext(classes)); System.clearProperty(JAXBContext.JAXB_CONTEXT_FACTORY); } else { db.setContext(db.createJAXBContext(classes)); } System.clearProperty("com.sun.xml.bind.v2.runtime.JAXBContextImpl.fastBoot"); System.clearProperty("com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl.fastBoot"); return db; } void doNamespaceMappingTest(boolean internal, boolean asm) throws Exception { if (internal) { try { Class.forName("com.sun.xml.internal.bind.v2.ContextFactory"); } catch (Throwable t) { //on a JVM (likely IBM's) that doesn't rename the ContextFactory package to include "internal" return; } } try { if (!asm) { ReflectionUtil.setAccessible(ReflectionUtil.getDeclaredField(ASMHelper.class, "badASM")) .set(null, Boolean.TRUE); } JAXBDataBinding db = createJaxbContext(internal); DataWriter<XMLStreamWriter> writer = db.createWriter(XMLStreamWriter.class); XMLOutputFactory writerFactory = XMLOutputFactory.newInstance(); StringWriter stringWriter = new StringWriter(); XMLStreamWriter xmlWriter = writerFactory.createXMLStreamWriter(stringWriter); QualifiedBean bean = new QualifiedBean(); bean.setAriadne("spider"); writer.write(bean, xmlWriter); xmlWriter.flush(); String xml = stringWriter.toString(); assertTrue("Failed to map namespace " + xml, xml.contains("greenland=\"uri:ultima:thule")); } finally { if (!asm) { ReflectionUtil.setAccessible(ReflectionUtil.getDeclaredField(ASMHelper.class, "badASM")) .set(null, Boolean.FALSE); } } } @Test public void testDeclaredNamespaceMappingRI() throws Exception { doNamespaceMappingTest(false, true); } @Test public void testDeclaredNamespaceMappingInternal() throws Exception { doNamespaceMappingTest(true, true); } @Test public void testDeclaredNamespaceMappingInternalNoAsm() throws Exception { try { doNamespaceMappingTest(true, false); fail("Internal needs ASM"); } catch (AssertionError er) { er.getMessage().contains("Failed to map namespace"); } } @Test public void testResursiveType() throws Exception { Set<Class<?>> classes = new HashSet<Class<?>>(); Collection<Object> typeReferences = new ArrayList<>(); Map<String, Object> props = new HashMap<>(); JAXBContextInitializer init = new JAXBContextInitializer(null, classes, typeReferences, props); init.addClass(Type2.class); assertEquals(2, classes.size()); } public abstract static class Type2 extends AddressEntity<Type2> { } public abstract static class AddressEntity<T extends AddressEntity<T>> { public abstract Addressable<T> getEntity(); } public interface Addressable<T extends AddressEntity<T>> { } @Test public void testConfiguredXmlAdapter() throws Exception { Language dutch = new Language("nl_NL", "Dutch"); Language americanEnglish = new Language("en_US", "Americanish"); Person person = new Person(dutch); JAXBDataBinding binding = new JAXBDataBinding(Person.class, Language.class); binding.setConfiguredXmlAdapters(Arrays.<XmlAdapter<?, ?>>asList(new LanguageAdapter(dutch, americanEnglish))); DataWriter<OutputStream> writer = binding.createWriter(OutputStream.class); ByteArrayOutputStream baos = new ByteArrayOutputStream(); writer.write(person, baos); String output = baos.toString(); String xml = "<person motherTongue=\"nl_NL\"/>"; assertEquals(xml, output); DataReader<XMLStreamReader> reader = binding.createReader(XMLStreamReader.class); Person read = (Person) reader.read(XMLInputFactory.newFactory().createXMLStreamReader(new StringReader(xml))); assertEquals(dutch, read.getMotherTongue()); } @XmlRootElement public static class Person { @XmlAttribute @XmlJavaTypeAdapter(LanguageAdapter.class) private final Language motherTongue; public Person() { this(null); } public Person(Language motherTongue) { this.motherTongue = motherTongue; } public Language getMotherTongue() { return motherTongue; } } public static class Language { private final String code; private final String name; public Language() { this(null, null); } public Language(String code, String name) { this.code = code; this.name = name; } public String getCode() { return code; } public String getName() { return name; } } public static class LanguageAdapter extends XmlAdapter<String, Language> { private Map<String, Language> knownLanguages = new HashMap<>(); public LanguageAdapter(Language... languages) { for (Language language : languages) { knownLanguages.put(language.getCode(), language); } } @Override public String marshal(Language language) throws Exception { return language.getCode(); } @Override public Language unmarshal(String code) throws Exception { return knownLanguages.get(code); } } }