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                    if (contentTypeHeader == null || contentTypeHeader) {
110                        try {
111                            setProperty(routeContext.getCamelContext(), dataFormat, "contentTypeHeader", Boolean.TRUE);
112                        } catch (Exception e) {
113                            // ignore as this option is optional and not all data formats support this
114                        }
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