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