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.Reader;
022 import java.io.StringReader;
023 import java.io.StringWriter;
024 import java.util.HashMap;
025 import java.util.Map;
026 import javax.xml.bind.JAXBContext;
027 import javax.xml.bind.JAXBException;
028 import javax.xml.bind.Marshaller;
029 import javax.xml.bind.Unmarshaller;
030 import javax.xml.bind.annotation.XmlRootElement;
031 import javax.xml.bind.util.JAXBSource;
032 import javax.xml.transform.Source;
033
034 import org.apache.camel.Exchange;
035 import org.apache.camel.NoTypeConversionAvailableException;
036 import org.apache.camel.RuntimeCamelException;
037 import org.apache.camel.StreamCache;
038 import org.apache.camel.TypeConverter;
039 import org.apache.camel.spi.TypeConverterAware;
040 import org.apache.camel.util.ObjectHelper;
041 import org.apache.commons.logging.Log;
042 import org.apache.commons.logging.LogFactory;
043
044 /**
045 * @version $Revision: 834590 $
046 */
047 public class FallbackTypeConverter implements TypeConverter, TypeConverterAware {
048 private static final transient Log LOG = LogFactory.getLog(FallbackTypeConverter.class);
049 private Map<Class<?>, JAXBContext> contexts = new HashMap<Class<?>, JAXBContext>();
050 private TypeConverter parentTypeConverter;
051 private boolean prettyPrint = true;
052
053 public boolean isPrettyPrint() {
054 return prettyPrint;
055 }
056
057 public void setPrettyPrint(boolean prettyPrint) {
058 this.prettyPrint = prettyPrint;
059 }
060
061 public void setTypeConverter(TypeConverter parentTypeConverter) {
062 this.parentTypeConverter = parentTypeConverter;
063 }
064
065 public <T> T convertTo(Class<T> type, Object value) {
066 try {
067 if (isJaxbType(type)) {
068 return unmarshall(type, value);
069 }
070 if (value != null) {
071 if (isJaxbType(value.getClass()) && isNotStreamCacheType(type)) {
072 return marshall(type, value);
073 }
074 }
075 return null;
076 } catch (JAXBException e) {
077 throw new RuntimeCamelException(e);
078 }
079 }
080
081 private <T> boolean isNotStreamCacheType(Class<T> type) {
082 return !StreamCache.class.isAssignableFrom(type);
083 }
084
085 public <T> T convertTo(Class<T> type, Exchange exchange, Object value) {
086 return convertTo(type, value);
087 }
088
089 public <T> T mandatoryConvertTo(Class<T> type, Object value) throws NoTypeConversionAvailableException {
090 T answer = convertTo(type, value);
091 if (answer == null) {
092 throw new NoTypeConversionAvailableException(value, type);
093 }
094 return answer;
095 }
096
097 public <T> T mandatoryConvertTo(Class<T> type, Exchange exchange, Object value) throws NoTypeConversionAvailableException {
098 return mandatoryConvertTo(type, value);
099 }
100
101 protected <T> boolean isJaxbType(Class<T> type) {
102 XmlRootElement element = type.getAnnotation(XmlRootElement.class);
103 return element != null;
104 }
105
106 /**
107 * Lets try parse via JAXB
108 */
109 protected <T> T unmarshall(Class<T> type, Object value) throws JAXBException {
110 if (value == null) {
111 throw new IllegalArgumentException("Cannot convert from null value to JAXBSource");
112 }
113
114 JAXBContext context = createContext(type);
115 // must create a new instance of unmarshaller as its not thred safe
116 Unmarshaller unmarshaller = context.createUnmarshaller();
117
118 if (parentTypeConverter != null) {
119 InputStream inputStream = parentTypeConverter.convertTo(InputStream.class, value);
120 if (inputStream != null) {
121 Object unmarshalled = unmarshal(unmarshaller, inputStream);
122 return type.cast(unmarshalled);
123 }
124 Reader reader = parentTypeConverter.convertTo(Reader.class, value);
125 if (reader != null) {
126 Object unmarshalled = unmarshal(unmarshaller, reader);
127 return type.cast(unmarshalled);
128 }
129 Source source = parentTypeConverter.convertTo(Source.class, value);
130 if (source != null) {
131 Object unmarshalled = unmarshal(unmarshaller, source);
132 return type.cast(unmarshalled);
133 }
134 }
135
136 if (value instanceof String) {
137 value = new StringReader((String) value);
138 }
139 if (value instanceof InputStream || value instanceof Reader) {
140 Object unmarshalled = unmarshal(unmarshaller, value);
141 return type.cast(unmarshalled);
142 }
143
144 return null;
145 }
146
147 protected <T> T marshall(Class<T> type, Object value) throws JAXBException {
148 T answer = null;
149 if (parentTypeConverter != null) {
150 // lets convert the object to a JAXB source and try convert that to
151 // the required source
152 JAXBContext context = createContext(value.getClass());
153 JAXBSource source = new JAXBSource(context, value);
154
155 answer = parentTypeConverter.convertTo(type, source);
156 if (answer == null) {
157 // lets try a stream
158 StringWriter buffer = new StringWriter();
159 // must create a new instance of marshaller as its not thred safe
160 Marshaller marshaller = context.createMarshaller();
161 marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, isPrettyPrint() ? Boolean.TRUE : Boolean.FALSE);
162 marshaller.marshal(value, buffer);
163 answer = parentTypeConverter.convertTo(type, buffer.toString());
164 }
165 }
166
167 return answer;
168 }
169
170 /**
171 * Unmarshals the given value with the unmarshaller
172 *
173 * @param unmarshaller the unmarshaller
174 * @param value the stream to unmarshal (will close it after use, also if exception is thrown)
175 * @return the value
176 * @throws JAXBException is thrown if an exception occur while unmarshalling
177 */
178 protected Object unmarshal(Unmarshaller unmarshaller, Object value) throws JAXBException {
179 try {
180 if (value instanceof InputStream) {
181 return unmarshaller.unmarshal((InputStream) value);
182 } else if (value instanceof Reader) {
183 return unmarshaller.unmarshal((Reader) value);
184 } else if (value instanceof Source) {
185 return unmarshaller.unmarshal((Source) value);
186 }
187 } finally {
188 if (value instanceof Closeable) {
189 ObjectHelper.close((Closeable) value, "Unmarshalling", LOG);
190 }
191 }
192 return null;
193 }
194
195 protected synchronized <T> JAXBContext createContext(Class<T> type) throws JAXBException {
196 JAXBContext context = contexts.get(type);
197 if (context == null) {
198 context = JAXBContext.newInstance(type);
199 contexts.put(type, context);
200 }
201 return context;
202 }
203
204 }