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.TypeConverter;
038 import org.apache.camel.converter.stream.StreamCache;
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: 748450 $
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 protected <T> boolean isJaxbType(Class<T> type) {
090 XmlRootElement element = type.getAnnotation(XmlRootElement.class);
091 return element != null;
092 }
093
094 /**
095 * Lets try parse via JAXB
096 */
097 protected <T> T unmarshall(Class<T> type, Object value) throws JAXBException {
098 if (value == null) {
099 throw new IllegalArgumentException("Cannot convert from null value to JAXBSource");
100 }
101
102 JAXBContext context = createContext(type);
103 // must create a new instance of unmarshaller as its not thred safe
104 Unmarshaller unmarshaller = context.createUnmarshaller();
105
106 if (parentTypeConverter != null) {
107 InputStream inputStream = parentTypeConverter.convertTo(InputStream.class, value);
108 if (inputStream != null) {
109 Object unmarshalled = unmarshal(unmarshaller, inputStream);
110 return type.cast(unmarshalled);
111 }
112 Reader reader = parentTypeConverter.convertTo(Reader.class, value);
113 if (reader != null) {
114 Object unmarshalled = unmarshal(unmarshaller, reader);
115 return type.cast(unmarshalled);
116 }
117 Source source = parentTypeConverter.convertTo(Source.class, value);
118 if (source != null) {
119 Object unmarshalled = unmarshal(unmarshaller, source);
120 return type.cast(unmarshalled);
121 }
122 }
123
124 if (value instanceof String) {
125 value = new StringReader((String) value);
126 }
127 if (value instanceof InputStream || value instanceof Reader) {
128 Object unmarshalled = unmarshal(unmarshaller, value);
129 return type.cast(unmarshalled);
130 }
131
132 return null;
133 }
134
135 protected <T> T marshall(Class<T> type, Object value) throws JAXBException {
136 if (parentTypeConverter != null) {
137 // lets convert the object to a JAXB source and try convert that to
138 // the required source
139 JAXBContext context = createContext(value.getClass());
140 JAXBSource source = new JAXBSource(context, value);
141 try {
142 return parentTypeConverter.convertTo(type, source);
143 } catch (NoTypeConversionAvailableException e) {
144 // lets try a stream
145 StringWriter buffer = new StringWriter();
146 // must create a new instance of marshaller as its not thred safe
147 Marshaller marshaller = context.createMarshaller();
148 marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, isPrettyPrint() ? Boolean.TRUE : Boolean.FALSE);
149 marshaller.marshal(value, buffer);
150 return parentTypeConverter.convertTo(type, buffer.toString());
151 }
152 }
153
154 // lets try convert to the type from JAXB
155 return null;
156 }
157
158 /**
159 * Unmarshals the given value with the unmarshaller
160 *
161 * @param unmarshaller the unmarshaller
162 * @param value the stream to unmarshal (will close it after use, also if exception is thrown)
163 * @return the value
164 * @throws JAXBException is thrown if an exception occur while unmarshalling
165 */
166 protected Object unmarshal(Unmarshaller unmarshaller, Object value) throws JAXBException {
167 try {
168 if (value instanceof InputStream) {
169 return unmarshaller.unmarshal((InputStream) value);
170 } else if (value instanceof Reader) {
171 return unmarshaller.unmarshal((Reader) value);
172 } else if (value instanceof Source) {
173 return unmarshaller.unmarshal((Source) value);
174 }
175 } finally {
176 if (value instanceof Closeable) {
177 ObjectHelper.close((Closeable) value, "Unmarshalling", LOG);
178 }
179 }
180 return null;
181 }
182
183 protected synchronized <T> JAXBContext createContext(Class<T> type) throws JAXBException {
184 JAXBContext context = contexts.get(type);
185 if (context == null) {
186 context = JAXBContext.newInstance(type);
187 contexts.put(type, context);
188 }
189 return context;
190 }
191
192 }