001/* 002 * Copyright 2002-2020 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.messaging.converter; 018 019import java.io.ByteArrayInputStream; 020import java.io.ByteArrayOutputStream; 021import java.io.StringReader; 022import java.io.StringWriter; 023import java.io.Writer; 024import java.util.Arrays; 025import javax.xml.transform.Result; 026import javax.xml.transform.Source; 027import javax.xml.transform.stream.StreamResult; 028import javax.xml.transform.stream.StreamSource; 029 030import org.springframework.beans.TypeMismatchException; 031import org.springframework.messaging.Message; 032import org.springframework.messaging.MessageHeaders; 033import org.springframework.oxm.Marshaller; 034import org.springframework.oxm.Unmarshaller; 035import org.springframework.util.Assert; 036import org.springframework.util.MimeType; 037 038/** 039 * Implementation of {@link MessageConverter} that can read and write XML using Spring's 040 * {@link Marshaller} and {@link Unmarshaller} abstractions. 041 * 042 * <p>This converter requires a {@code Marshaller} and {@code Unmarshaller} before it can 043 * be used. These can be injected by the {@linkplain #MarshallingMessageConverter(Marshaller) 044 * constructor} or {@linkplain #setMarshaller(Marshaller) bean properties}. 045 * 046 * @author Arjen Poutsma 047 * @since 4.2 048 * @see Marshaller 049 * @see Unmarshaller 050 */ 051public class MarshallingMessageConverter extends AbstractMessageConverter { 052 053 private Marshaller marshaller; 054 055 private Unmarshaller unmarshaller; 056 057 058 /** 059 * Default construct allowing for {@link #setMarshaller(Marshaller)} and/or 060 * {@link #setUnmarshaller(Unmarshaller)} to be invoked separately. 061 */ 062 public MarshallingMessageConverter() { 063 this(new MimeType("application", "xml"), new MimeType("text", "xml"), 064 new MimeType("application", "*+xml")); 065 } 066 067 /** 068 * Constructor with a given list of MIME types to support. 069 * @param supportedMimeTypes the MIME types 070 */ 071 public MarshallingMessageConverter(MimeType... supportedMimeTypes) { 072 super(Arrays.asList(supportedMimeTypes)); 073 } 074 075 /** 076 * Constructor with {@link Marshaller}. If the given {@link Marshaller} also 077 * implements {@link Unmarshaller}, it is also used for unmarshalling. 078 * <p>Note that all {@code Marshaller} implementations in Spring also implement 079 * {@code Unmarshaller} so that you can safely use this constructor. 080 * @param marshaller object used as marshaller and unmarshaller 081 */ 082 public MarshallingMessageConverter(Marshaller marshaller) { 083 this(); 084 Assert.notNull(marshaller, "Marshaller must not be null"); 085 this.marshaller = marshaller; 086 if (marshaller instanceof Unmarshaller) { 087 this.unmarshaller = (Unmarshaller) marshaller; 088 } 089 } 090 091 092 /** 093 * Set the {@link Marshaller} to be used by this message converter. 094 */ 095 public void setMarshaller(Marshaller marshaller) { 096 this.marshaller = marshaller; 097 } 098 099 /** 100 * Return the configured Marshaller. 101 */ 102 public Marshaller getMarshaller() { 103 return this.marshaller; 104 } 105 106 /** 107 * Set the {@link Unmarshaller} to be used by this message converter. 108 */ 109 public void setUnmarshaller(Unmarshaller unmarshaller) { 110 this.unmarshaller = unmarshaller; 111 } 112 113 /** 114 * Return the configured unmarshaller. 115 */ 116 public Unmarshaller getUnmarshaller() { 117 return this.unmarshaller; 118 } 119 120 121 @Override 122 protected boolean canConvertFrom(Message<?> message, Class<?> targetClass) { 123 return (supportsMimeType(message.getHeaders()) && this.unmarshaller != null && 124 this.unmarshaller.supports(targetClass)); 125 } 126 127 @Override 128 protected boolean canConvertTo(Object payload, MessageHeaders headers) { 129 return (supportsMimeType(headers) && this.marshaller != null && 130 this.marshaller.supports(payload.getClass())); 131 } 132 133 @Override 134 protected boolean supports(Class<?> clazz) { 135 // should not be called, since we override canConvertFrom/canConvertTo instead 136 throw new UnsupportedOperationException(); 137 } 138 139 @Override 140 protected Object convertFromInternal(Message<?> message, Class<?> targetClass, Object conversionHint) { 141 Assert.notNull(this.unmarshaller, "Property 'unmarshaller' is required"); 142 try { 143 Source source = getSource(message.getPayload()); 144 Object result = this.unmarshaller.unmarshal(source); 145 if (!targetClass.isInstance(result)) { 146 throw new TypeMismatchException(result, targetClass); 147 } 148 return result; 149 } 150 catch (Exception ex) { 151 throw new MessageConversionException(message, "Could not unmarshal XML: " + ex.getMessage(), ex); 152 } 153 } 154 155 private Source getSource(Object payload) { 156 if (payload instanceof byte[]) { 157 return new StreamSource(new ByteArrayInputStream((byte[]) payload)); 158 } 159 else { 160 return new StreamSource(new StringReader(payload.toString())); 161 } 162 } 163 164 @Override 165 protected Object convertToInternal(Object payload, MessageHeaders headers, Object conversionHint) { 166 Assert.notNull(this.marshaller, "Property 'marshaller' is required"); 167 try { 168 if (byte[].class == getSerializedPayloadClass()) { 169 ByteArrayOutputStream out = new ByteArrayOutputStream(1024); 170 Result result = new StreamResult(out); 171 this.marshaller.marshal(payload, result); 172 payload = out.toByteArray(); 173 } 174 else { 175 Writer writer = new StringWriter(1024); 176 Result result = new StreamResult(writer); 177 this.marshaller.marshal(payload, result); 178 payload = writer.toString(); 179 } 180 } 181 catch (Throwable ex) { 182 throw new MessageConversionException("Could not marshal XML: " + ex.getMessage(), ex); 183 } 184 return payload; 185 } 186 187}