001 /**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.camel.converter.jaxb;
018
019 import java.io.Closeable;
020 import java.io.InputStream;
021 import java.io.InputStreamReader;
022 import java.io.Reader;
023 import java.io.StringReader;
024 import java.io.StringWriter;
025 import java.io.UnsupportedEncodingException;
026 import java.io.Writer;
027 import java.util.HashMap;
028 import java.util.Map;
029
030 import javax.xml.bind.JAXBContext;
031 import javax.xml.bind.JAXBException;
032 import javax.xml.bind.Marshaller;
033 import javax.xml.bind.Unmarshaller;
034 import javax.xml.bind.annotation.XmlRootElement;
035 import javax.xml.stream.FactoryConfigurationError;
036 import javax.xml.stream.XMLOutputFactory;
037 import javax.xml.stream.XMLStreamException;
038 import javax.xml.stream.XMLStreamWriter;
039 import javax.xml.transform.Source;
040
041 import org.apache.camel.Exchange;
042 import org.apache.camel.NoTypeConversionAvailableException;
043 import org.apache.camel.StreamCache;
044 import org.apache.camel.TypeConverter;
045 import org.apache.camel.converter.IOConverter;
046 import org.apache.camel.spi.TypeConverterAware;
047 import org.apache.camel.util.IOHelper;
048 import org.apache.camel.util.ObjectHelper;
049 import org.apache.commons.logging.Log;
050 import org.apache.commons.logging.LogFactory;
051
052 /**
053 * @version $Revision: 947575 $
054 */
055 public class FallbackTypeConverter implements TypeConverter, TypeConverterAware {
056 private static final transient Log LOG = LogFactory.getLog(FallbackTypeConverter.class);
057 private Map<Class<?>, JAXBContext> contexts = new HashMap<Class<?>, JAXBContext>();
058 private TypeConverter parentTypeConverter;
059 private boolean prettyPrint = true;
060
061 public boolean isPrettyPrint() {
062 return prettyPrint;
063 }
064
065 public void setPrettyPrint(boolean prettyPrint) {
066 this.prettyPrint = prettyPrint;
067 }
068
069 public void setTypeConverter(TypeConverter parentTypeConverter) {
070 this.parentTypeConverter = parentTypeConverter;
071 }
072
073 public <T> T convertTo(Class<T> type, Object value) {
074 return convertTo(type, null, value);
075 }
076
077 private <T> boolean isNotStreamCacheType(Class<T> type) {
078 return !StreamCache.class.isAssignableFrom(type);
079 }
080
081 public <T> T convertTo(Class<T> type, Exchange exchange, Object value) {
082 try {
083 if (isJaxbType(type)) {
084 return unmarshall(type, exchange, value);
085 }
086 if (value != null) {
087 if (isJaxbType(value.getClass()) && isNotStreamCacheType(type)) {
088 return marshall(type, exchange, value);
089 }
090 }
091 return null;
092 } catch (Exception e) {
093 throw ObjectHelper.wrapCamelExecutionException(exchange, e);
094 }
095
096 }
097
098 public <T> T mandatoryConvertTo(Class<T> type, Object value) throws NoTypeConversionAvailableException {
099 return mandatoryConvertTo(type, null, value);
100 }
101
102 public <T> T mandatoryConvertTo(Class<T> type, Exchange exchange, Object value) throws NoTypeConversionAvailableException {
103 T answer = convertTo(type, exchange, value);
104 if (answer == null) {
105 throw new NoTypeConversionAvailableException(value, type);
106 }
107 return answer;
108 }
109
110 protected <T> boolean isJaxbType(Class<T> type) {
111 XmlRootElement element = type.getAnnotation(XmlRootElement.class);
112 return element != null;
113 }
114
115 /**
116 * Lets try parse via JAXB
117 */
118 protected <T> T unmarshall(Class<T> type, Exchange exchange, Object value) throws Exception {
119 if (value == null) {
120 throw new IllegalArgumentException("Cannot convert from null value to JAXBSource");
121 }
122
123 JAXBContext context = createContext(type);
124 // must create a new instance of unmarshaller as its not thred safe
125 Unmarshaller unmarshaller = context.createUnmarshaller();
126
127 if (parentTypeConverter != null) {
128 InputStream inputStream = parentTypeConverter.convertTo(InputStream.class, value);
129 if (inputStream != null) {
130 Object unmarshalled = unmarshal(unmarshaller, exchange, inputStream);
131 return type.cast(unmarshalled);
132 }
133 Reader reader = parentTypeConverter.convertTo(Reader.class, value);
134 if (reader != null) {
135 Object unmarshalled = unmarshal(unmarshaller, exchange, reader);
136 return type.cast(unmarshalled);
137 }
138 Source source = parentTypeConverter.convertTo(Source.class, value);
139 if (source != null) {
140 Object unmarshalled = unmarshal(unmarshaller, exchange, source);
141 return type.cast(unmarshalled);
142 }
143 }
144
145 if (value instanceof String) {
146 value = new StringReader((String) value);
147 }
148 if (value instanceof InputStream || value instanceof Reader) {
149 Object unmarshalled = unmarshal(unmarshaller, exchange, value);
150 return type.cast(unmarshalled);
151 }
152
153 return null;
154 }
155
156 protected <T> T marshall(Class<T> type, Exchange exchange, Object value) throws JAXBException, XMLStreamException, FactoryConfigurationError {
157 T answer = null;
158 if (parentTypeConverter != null) {
159 // lets convert the object to a JAXB source and try convert that to
160 // the required source
161 JAXBContext context = createContext(value.getClass());
162 // must create a new instance of marshaller as its not thread safe
163 Marshaller marshaller = context.createMarshaller();
164 Writer buffer = new StringWriter();
165 marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, isPrettyPrint() ? Boolean.TRUE : Boolean.FALSE);
166 if (exchange != null && exchange.getProperty(Exchange.CHARSET_NAME, String.class) != null) {
167 marshaller.setProperty(Marshaller.JAXB_ENCODING, exchange.getProperty(Exchange.CHARSET_NAME, String.class));
168 }
169 if (needFiltering(exchange)) {
170 XMLStreamWriter writer = XMLOutputFactory.newInstance().createXMLStreamWriter(buffer);
171 FilteringXmlStreamWriter filteringWriter = new FilteringXmlStreamWriter(writer);
172 marshaller.marshal(value, filteringWriter);
173 } else {
174 marshaller.marshal(value, buffer);
175 }
176 answer = parentTypeConverter.convertTo(type, buffer.toString());
177 }
178
179 return answer;
180 }
181
182 /**
183 * Unmarshals the given value with the unmarshaller
184 *
185 * @param unmarshaller the unmarshaller
186 * @param exchange the exchange
187 * @param value the stream to unmarshal (will close it after use, also if exception is thrown)
188 * @return the value
189 * @throws JAXBException is thrown if an exception occur while unmarshalling
190 * @throws UnsupportedEncodingException
191 */
192 protected Object unmarshal(Unmarshaller unmarshaller, Exchange exchange, Object value)
193 throws JAXBException, UnsupportedEncodingException {
194 try {
195 if (value instanceof InputStream) {
196 if (needFiltering(exchange)) {
197 return unmarshaller.unmarshal(new NonXmlFilterReader(new InputStreamReader((InputStream)value, IOConverter.getCharsetName(exchange))));
198 }
199 return unmarshaller.unmarshal((InputStream)value);
200 } else if (value instanceof Reader) {
201 Reader reader = (Reader)value;
202 if (needFiltering(exchange)) {
203 if (!(value instanceof NonXmlFilterReader)) {
204 reader = new NonXmlFilterReader((Reader)value);
205 }
206 }
207 return unmarshaller.unmarshal(reader);
208 } else if (value instanceof Source) {
209 return unmarshaller.unmarshal((Source)value);
210 }
211 } finally {
212 if (value instanceof Closeable) {
213 IOHelper.close((Closeable)value, "Unmarshalling", LOG);
214 }
215 }
216 return null;
217 }
218
219 protected boolean needFiltering(Exchange exchange) {
220 // exchange property takes precedence over data format property
221 return exchange != null && exchange.getProperty(Exchange.FILTER_NON_XML_CHARS, Boolean.FALSE, Boolean.class);
222 }
223
224 protected synchronized <T> JAXBContext createContext(Class<T> type) throws JAXBException {
225 JAXBContext context = contexts.get(type);
226 if (context == null) {
227 context = JAXBContext.newInstance(type);
228 contexts.put(type, context);
229 }
230 return context;
231 }
232
233 }