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