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.model;
018
019import java.util.Map;
020import javax.xml.bind.annotation.XmlAccessType;
021import javax.xml.bind.annotation.XmlAccessorType;
022import javax.xml.bind.annotation.XmlAnyAttribute;
023import javax.xml.bind.annotation.XmlAttribute;
024import javax.xml.bind.annotation.XmlTransient;
025import javax.xml.bind.annotation.XmlType;
026import javax.xml.namespace.QName;
027
028import org.apache.camel.CamelContext;
029import org.apache.camel.spi.DataFormat;
030import org.apache.camel.spi.Metadata;
031import org.apache.camel.spi.RouteContext;
032import org.apache.camel.util.IntrospectionSupport;
033import org.apache.camel.util.ObjectHelper;
034
035import static org.apache.camel.util.EndpointHelper.isReferenceParameter;
036
037/**
038 * Represents a Camel data format
039 */
040@Metadata(label = "dataformat,transformation")
041@XmlType(name = "dataFormat")
042@XmlAccessorType(XmlAccessType.FIELD)
043public class DataFormatDefinition extends IdentifiedType implements OtherAttributesAware {
044    @XmlTransient
045    private DataFormat dataFormat;
046    @XmlTransient
047    private String dataFormatName;
048    // use xs:any to support optional property placeholders
049    @XmlAnyAttribute
050    private Map<QName, Object> otherAttributes;
051    @XmlAttribute
052    private Boolean contentTypeHeader;
053
054    public DataFormatDefinition() {
055    }
056
057    public DataFormatDefinition(DataFormat dataFormat) {
058        this.dataFormat = dataFormat;
059    }
060
061    protected DataFormatDefinition(String dataFormatName) {
062        this.dataFormatName = dataFormatName;
063    }
064
065    /**
066     * Factory method to create the data format
067     *
068     * @param routeContext route context
069     * @param type         the data format type
070     * @param ref          reference to lookup for a data format
071     * @return the data format or null if not possible to create
072     */
073    public static DataFormat getDataFormat(RouteContext routeContext, DataFormatDefinition type, String ref) {
074        if (type == null) {
075            ObjectHelper.notNull(ref, "ref or type");
076
077            // try to let resolver see if it can resolve it, its not always possible
078            type = routeContext.getCamelContext().resolveDataFormatDefinition(ref);
079
080            if (type != null) {
081                return type.getDataFormat(routeContext);
082            }
083
084            DataFormat dataFormat = routeContext.getCamelContext().resolveDataFormat(ref);
085            if (dataFormat == null) {
086                throw new IllegalArgumentException("Cannot find data format in registry with ref: " + ref);
087            }
088
089            return dataFormat;
090        } else {
091            return type.getDataFormat(routeContext);
092        }
093    }
094
095    public DataFormat getDataFormat(RouteContext routeContext) {
096        if (dataFormat == null) {
097            Runnable propertyPlaceholdersChangeReverter = ProcessorDefinitionHelper.createPropertyPlaceholdersChangeReverter();
098
099            // resolve properties before we create the data format
100            try {
101                ProcessorDefinitionHelper.resolvePropertyPlaceholders(routeContext.getCamelContext(), this);
102            } catch (Exception e) {
103                throw new IllegalArgumentException("Error resolving property placeholders on data format: " + this, e);
104            }
105            try {
106                dataFormat = createDataFormat(routeContext);
107                if (dataFormat != null) {
108                    // is enabled by default so assume true if null
109                    final boolean contentTypeHeader = this.contentTypeHeader == null || this.contentTypeHeader;
110                    try {
111                        setProperty(routeContext.getCamelContext(), dataFormat, "contentTypeHeader", contentTypeHeader);
112                    } catch (Exception e) {
113                        // ignore as this option is optional and not all data formats support this
114                    }
115                    // configure the rest of the options
116                    configureDataFormat(dataFormat, routeContext.getCamelContext());
117                } else {
118                    throw new IllegalArgumentException(
119                            "Data format '" + (dataFormatName != null ? dataFormatName : "<null>") + "' could not be created. "
120                                    + "Ensure that the data format is valid and the associated Camel component is present on the classpath");
121                }
122            } finally {
123                propertyPlaceholdersChangeReverter.run();
124            }
125        }
126        return dataFormat;
127    }
128
129    /**
130     * Factory method to create the data format instance
131     */
132    protected DataFormat createDataFormat(RouteContext routeContext) {
133        // must use getDataFormatName() as we need special logic in json dataformat
134        if (getDataFormatName() != null) {
135            return routeContext.getCamelContext().createDataFormat(getDataFormatName());
136        }
137        return null;
138    }
139
140    /**
141     * Allows derived classes to customize the data format
142     *
143     * @deprecated use {@link #configureDataFormat(org.apache.camel.spi.DataFormat, org.apache.camel.CamelContext)}
144     */
145    @Deprecated
146    protected void configureDataFormat(DataFormat dataFormat) {
147    }
148
149    /**
150     * Allows derived classes to customize the data format
151     */
152    protected void configureDataFormat(DataFormat dataFormat, CamelContext camelContext) {
153    }
154
155    /**
156     * Sets a named property on the data format instance using introspection
157     *
158     * @deprecated use {@link #setProperty(org.apache.camel.CamelContext, Object, String, Object)}
159     */
160    @Deprecated
161    protected void setProperty(Object bean, String name, Object value) {
162        setProperty(null, bean, name, value);
163    }
164
165    /**
166     * Sets a named property on the data format instance using introspection
167     */
168    protected void setProperty(CamelContext camelContext, Object bean, String name, Object value) {
169        try {
170            String ref = value instanceof String ? value.toString() : null;
171            if (isReferenceParameter(ref) && camelContext != null) {
172                IntrospectionSupport.setProperty(camelContext, camelContext.getTypeConverter(), bean, name, null, ref, true);
173            } else {
174                IntrospectionSupport.setProperty(bean, name, value);
175            }
176        } catch (Exception e) {
177            throw new IllegalArgumentException("Failed to set property: " + name + " on: " + bean + ". Reason: " + e, e);
178        }
179    }
180
181    public String getDataFormatName() {
182        return dataFormatName;
183    }
184
185    public void setDataFormatName(String dataFormatName) {
186        this.dataFormatName = dataFormatName;
187    }
188
189    public DataFormat getDataFormat() {
190        return dataFormat;
191    }
192
193    public void setDataFormat(DataFormat dataFormat) {
194        this.dataFormat = dataFormat;
195    }
196
197    public Map<QName, Object> getOtherAttributes() {
198        return otherAttributes;
199    }
200
201    /**
202     * Adds an optional attribute
203     */
204    public void setOtherAttributes(Map<QName, Object> otherAttributes) {
205        this.otherAttributes = otherAttributes;
206    }
207
208    public Boolean getContentTypeHeader() {
209        return contentTypeHeader;
210    }
211
212    /**
213     * Whether the data format should set the <tt>Content-Type</tt> header with the type from the data format if the
214     * data format is capable of doing so.
215     * <p/>
216     * For example <tt>application/xml</tt> for data formats marshalling to XML, or <tt>application/json</tt>
217     * for data formats marshalling to JSon etc.
218     */
219    public void setContentTypeHeader(Boolean contentTypeHeader) {
220        this.contentTypeHeader = contentTypeHeader;
221    }
222
223    public String getShortName() {
224        String name = getClass().getSimpleName();
225        if (name.endsWith("DataFormat")) {
226            name = name.substring(0, name.indexOf("DataFormat"));
227        }
228        return name;
229    }
230
231}
232