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 */
017package org.apache.camel.impl.transformer;
018
019import java.io.InputStream;
020import org.apache.camel.CamelContext;
021import org.apache.camel.Exchange;
022import org.apache.camel.Message;
023import org.apache.camel.converter.stream.OutputStreamBuilder;
024import org.apache.camel.model.DataFormatDefinition;
025import org.apache.camel.spi.DataFormat;
026import org.apache.camel.spi.DataType;
027import org.apache.camel.spi.Transformer;
028import org.apache.camel.util.ServiceHelper;
029import org.slf4j.Logger;
030import org.slf4j.LoggerFactory;
031
032/**
033 * A {@link Transformer} implementation which leverages {@link DataFormat} to perform transformation.
034 * 
035 * {@see Transformer}
036 */
037public class DataFormatTransformer extends Transformer {
038    private static final Logger LOG = LoggerFactory.getLogger(DataFormatTransformer.class);
039
040    private String dataFormatRef;
041    private DataFormatDefinition dataFormatType;
042    private DataFormat dataFormat;
043    private String transformerString;
044
045    public DataFormatTransformer(CamelContext context) {
046        setCamelContext(context);
047    }
048
049    /**
050     * Perform data transformation with specified from/to type using DataFormat.
051     * @param message message to apply transformation
052     * @param from 'from' data type
053     * @param to 'to' data type
054     */
055    @Override
056    public void transform(Message message, DataType from, DataType to) throws Exception {
057        Exchange exchange = message.getExchange();
058        CamelContext context = exchange.getContext();
059        
060        // Unmarshaling into Java Object
061        if ((to == null || to.isJavaType()) && (from.equals(getFrom()) || from.getModel().equals(getModel()))) {
062            DataFormat dataFormat = getDataFormat(exchange);
063            LOG.debug("Unmarshaling with '{}'", dataFormat);
064            Object answer = dataFormat.unmarshal(exchange, message.getBody(InputStream.class));
065            if (to != null && to.getName() != null) {
066                Class<?> toClass = context.getClassResolver().resolveClass(to.getName());
067                if (!toClass.isAssignableFrom(answer.getClass())) {
068                    LOG.debug("Converting to '{}'", toClass.getName());
069                    answer = context.getTypeConverter().mandatoryConvertTo(toClass, answer);
070                }
071            }
072            message.setBody(answer);
073            
074        // Marshaling from Java Object
075        } else if ((from == null || from.isJavaType()) && (to.equals(getTo()) || to.getModel().equals(getModel()))) {
076            Object input = message.getBody();
077            if (from != null && from.getName() != null) {
078                Class<?> fromClass = context.getClassResolver().resolveClass(from.getName());
079                if (!fromClass.isAssignableFrom(input.getClass())) {
080                    LOG.debug("Converting to '{}'", fromClass.getName());
081                    input = context.getTypeConverter().mandatoryConvertTo(fromClass, input);
082                }
083            }
084            OutputStreamBuilder osb = OutputStreamBuilder.withExchange(exchange);
085            DataFormat dataFormat = getDataFormat(exchange);
086            LOG.debug("Marshaling with '{}'", dataFormat);
087            dataFormat.marshal(exchange, message.getBody(), osb);
088            message.setBody(osb.build());
089            
090        } else {
091            throw new IllegalArgumentException("Unsupported transformation: from='" + from + ", to='" + to + "'");
092        }
093    }
094
095    /**
096     * A bit dirty hack to create DataFormat instance, as it requires a RouteContext anyway.
097     */
098    private DataFormat getDataFormat(Exchange exchange) throws Exception {
099        if (this.dataFormat == null) {
100            this.dataFormat = DataFormatDefinition.getDataFormat(
101                exchange.getUnitOfWork().getRouteContext(), this.dataFormatType, this.dataFormatRef);
102            if (this.dataFormat != null && !getCamelContext().hasService(this.dataFormat)) {
103                getCamelContext().addService(this.dataFormat, false);
104            }
105        }
106        return this.dataFormat;
107    }
108
109    /**
110     * Set DataFormat ref.
111     * @param ref DataFormat ref
112     * @return this DataFormatTransformer instance
113     */
114    public DataFormatTransformer setDataFormatRef(String ref) {
115        this.dataFormatRef = ref;
116        this.transformerString = null;
117        return this;
118    }
119
120    /**
121     * Set DataFormatDefinition.
122     * @param dataFormatType DataFormatDefinition
123     * @return this DataFormatTransformer instance
124     */
125    public DataFormatTransformer setDataFormatType(DataFormatDefinition dataFormatType) {
126        this.dataFormatType = dataFormatType;
127        this.transformerString = null;
128        return this;
129    }
130
131    @Override
132    public String toString() {
133        if (transformerString == null) {
134            transformerString =
135                String.format("DataFormatTransformer[scheme='%s', from='%s', to='%s', ref='%s', type='%s']",
136                    getModel(), getFrom(), getTo(), dataFormatRef, dataFormatType);
137        }
138        return transformerString;
139    }
140
141    @Override
142    public void doStart() throws Exception {
143        // no-op
144    }
145
146    @Override
147    public void doStop() throws Exception {
148        ServiceHelper.stopService(this.dataFormat);
149        getCamelContext().removeService(this.dataFormat);
150    }
151}