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.reifier.dataformat;
018
019import java.util.HashMap;
020import java.util.Map;
021import java.util.function.Function;
022
023import org.apache.camel.CamelContext;
024import org.apache.camel.ExtendedCamelContext;
025import org.apache.camel.model.DataFormatDefinition;
026import org.apache.camel.model.Model;
027import org.apache.camel.model.ProcessorDefinitionHelper;
028import org.apache.camel.model.dataformat.ASN1DataFormat;
029import org.apache.camel.model.dataformat.Any23DataFormat;
030import org.apache.camel.model.dataformat.AvroDataFormat;
031import org.apache.camel.model.dataformat.BarcodeDataFormat;
032import org.apache.camel.model.dataformat.Base64DataFormat;
033import org.apache.camel.model.dataformat.BeanioDataFormat;
034import org.apache.camel.model.dataformat.BindyDataFormat;
035import org.apache.camel.model.dataformat.CBORDataFormat;
036import org.apache.camel.model.dataformat.CryptoDataFormat;
037import org.apache.camel.model.dataformat.CsvDataFormat;
038import org.apache.camel.model.dataformat.CustomDataFormat;
039import org.apache.camel.model.dataformat.FhirDataformat;
040import org.apache.camel.model.dataformat.FhirJsonDataFormat;
041import org.apache.camel.model.dataformat.FhirXmlDataFormat;
042import org.apache.camel.model.dataformat.FlatpackDataFormat;
043import org.apache.camel.model.dataformat.GrokDataFormat;
044import org.apache.camel.model.dataformat.GzipDataFormat;
045import org.apache.camel.model.dataformat.HL7DataFormat;
046import org.apache.camel.model.dataformat.IcalDataFormat;
047import org.apache.camel.model.dataformat.JacksonXMLDataFormat;
048import org.apache.camel.model.dataformat.JaxbDataFormat;
049import org.apache.camel.model.dataformat.JsonApiDataFormat;
050import org.apache.camel.model.dataformat.JsonDataFormat;
051import org.apache.camel.model.dataformat.LZFDataFormat;
052import org.apache.camel.model.dataformat.MimeMultipartDataFormat;
053import org.apache.camel.model.dataformat.PGPDataFormat;
054import org.apache.camel.model.dataformat.ProtobufDataFormat;
055import org.apache.camel.model.dataformat.RssDataFormat;
056import org.apache.camel.model.dataformat.SoapJaxbDataFormat;
057import org.apache.camel.model.dataformat.SyslogDataFormat;
058import org.apache.camel.model.dataformat.TarFileDataFormat;
059import org.apache.camel.model.dataformat.ThriftDataFormat;
060import org.apache.camel.model.dataformat.TidyMarkupDataFormat;
061import org.apache.camel.model.dataformat.UniVocityCsvDataFormat;
062import org.apache.camel.model.dataformat.UniVocityFixedWidthDataFormat;
063import org.apache.camel.model.dataformat.UniVocityTsvDataFormat;
064import org.apache.camel.model.dataformat.XMLSecurityDataFormat;
065import org.apache.camel.model.dataformat.XStreamDataFormat;
066import org.apache.camel.model.dataformat.XmlRpcDataFormat;
067import org.apache.camel.model.dataformat.YAMLDataFormat;
068import org.apache.camel.model.dataformat.ZipDeflaterDataFormat;
069import org.apache.camel.model.dataformat.ZipFileDataFormat;
070import org.apache.camel.spi.DataFormat;
071import org.apache.camel.util.ObjectHelper;
072
073import static org.apache.camel.support.EndpointHelper.isReferenceParameter;
074
075public abstract class DataFormatReifier<T extends DataFormatDefinition> {
076
077    private static final Map<Class<? extends DataFormatDefinition>, Function<DataFormatDefinition, DataFormatReifier<? extends DataFormatDefinition>>> DATAFORMATS;
078    static {
079        Map<Class<? extends DataFormatDefinition>, Function<DataFormatDefinition, DataFormatReifier<? extends DataFormatDefinition>>> map = new HashMap<>();
080        map.put(Any23DataFormat.class, Any23DataFormatReifier::new);
081        map.put(ASN1DataFormat.class, ASN1DataFormatReifier::new);
082        map.put(AvroDataFormat.class, AvroDataFormatReifier::new);
083        map.put(BarcodeDataFormat.class, BarcodeDataFormatReifier::new);
084        map.put(Base64DataFormat.class, Base64DataFormatReifier::new);
085        map.put(BeanioDataFormat.class, BeanioDataFormatReifier::new);
086        map.put(BindyDataFormat.class, BindyDataFormatReifier::new);
087        map.put(CBORDataFormat.class, CBORDataFormatReifier::new);
088        map.put(CryptoDataFormat.class, CryptoDataFormatReifier::new);
089        map.put(CsvDataFormat.class, CsvDataFormatReifier::new);
090        map.put(CustomDataFormat.class, CustomDataFormatReifier::new);
091        map.put(FhirDataformat.class, FhirDataFormatReifier::new);
092        map.put(FhirJsonDataFormat.class, FhirJsonDataFormatReifier::new);
093        map.put(FhirXmlDataFormat.class, FhirXmlDataFormatReifier::new);
094        map.put(FlatpackDataFormat.class, FlatpackDataFormatReifier::new);
095        map.put(GrokDataFormat.class, GrokDataFormatReifier::new);
096        map.put(GzipDataFormat.class, GzipDataFormatReifier::new);
097        map.put(HL7DataFormat.class, HL7DataFormatReifier::new);
098        map.put(IcalDataFormat.class, IcalDataFormatReifier::new);
099        map.put(JacksonXMLDataFormat.class, JacksonXMLDataFormatReifier::new);
100        map.put(JaxbDataFormat.class, JaxbDataFormatReifier::new);
101        map.put(JsonApiDataFormat.class, JsonApiDataFormatReifier::new);
102        map.put(JsonDataFormat.class, JsonDataFormatReifier::new);
103        map.put(LZFDataFormat.class, LZFDataFormatReifier::new);
104        map.put(MimeMultipartDataFormat.class, MimeMultipartDataFormatReifier::new);
105        map.put(PGPDataFormat.class, PGPDataFormatReifier::new);
106        map.put(ProtobufDataFormat.class, ProtobufDataFormatReifier::new);
107        map.put(RssDataFormat.class, RssDataFormatReifier::new);
108        map.put(SoapJaxbDataFormat.class, SoapJaxbDataFormatReifier::new);
109        map.put(SyslogDataFormat.class, SyslogDataFormatReifier::new);
110        map.put(TarFileDataFormat.class, TarFileDataFormatReifier::new);
111        map.put(ThriftDataFormat.class, ThriftDataFormatReifier::new);
112        map.put(TidyMarkupDataFormat.class, TidyMarkupDataFormatReifier::new);
113        map.put(UniVocityCsvDataFormat.class, UniVocityCsvDataFormatReifier::new);
114        map.put(UniVocityFixedWidthDataFormat.class, UniVocityFixedWidthDataFormatReifier::new);
115        map.put(UniVocityTsvDataFormat.class, UniVocityTsvDataFormatReifier::new);
116        map.put(XmlRpcDataFormat.class, XmlRpcDataFormatReifier::new);
117        map.put(XMLSecurityDataFormat.class, XMLSecurityDataFormatReifier::new);
118        map.put(XStreamDataFormat.class, XStreamDataFormatReifier::new);
119        map.put(YAMLDataFormat.class, YAMLDataFormatReifier::new);
120        map.put(ZipDeflaterDataFormat.class, ZipDataFormatReifier::new);
121        map.put(ZipFileDataFormat.class, ZipFileDataFormatReifier::new);
122        DATAFORMATS = map;
123    }
124
125    protected final T definition;
126
127    public DataFormatReifier(T definition) {
128        this.definition = definition;
129    }
130
131    public static void registerReifier(Class<? extends DataFormatDefinition> dataFormatClass,
132                                       Function<DataFormatDefinition, DataFormatReifier<? extends DataFormatDefinition>> creator) {
133        DATAFORMATS.put(dataFormatClass, creator);
134    }
135
136    /**
137     * Factory method to create the data format
138     *
139     * @param camelContext the camel context
140     * @param type the data format type
141     * @param ref reference to lookup for a data format
142     * @return the data format or null if not possible to create
143     */
144    public static DataFormat getDataFormat(CamelContext camelContext, DataFormatDefinition type, String ref) {
145        if (type == null) {
146            ObjectHelper.notNull(ref, "ref or type");
147
148            DataFormat dataFormat = camelContext.getRegistry().lookupByNameAndType(ref, DataFormat.class);
149            if (dataFormat != null) {
150                return dataFormat;
151            }
152
153            // try to let resolver see if it can resolve it, its not always
154            // possible
155            type = camelContext.getExtension(Model.class).resolveDataFormatDefinition(ref);
156
157            if (type == null) {
158                dataFormat = camelContext.resolveDataFormat(ref);
159                if (dataFormat == null) {
160                    throw new IllegalArgumentException("Cannot find data format in registry with ref: " + ref);
161                }
162
163                return dataFormat;
164            }
165        }
166        if (type.getDataFormat() != null) {
167            return type.getDataFormat();
168        }
169        return reifier(type).createDataFormat(camelContext);
170    }
171
172    public static DataFormatReifier<? extends DataFormatDefinition> reifier(DataFormatDefinition definition) {
173        Function<DataFormatDefinition, DataFormatReifier<? extends DataFormatDefinition>> reifier = DATAFORMATS.get(definition.getClass());
174        if (reifier != null) {
175            return reifier.apply(definition);
176        }
177        throw new IllegalStateException("Unsupported definition: " + definition);
178    }
179
180    public DataFormat createDataFormat(CamelContext camelContext) {
181        DataFormat dataFormat = definition.getDataFormat();
182        if (dataFormat == null) {
183            Runnable propertyPlaceholdersChangeReverter = ProcessorDefinitionHelper.createPropertyPlaceholdersChangeReverter();
184
185            // resolve properties before we create the data format
186            try {
187                ProcessorDefinitionHelper.resolvePropertyPlaceholders(camelContext, definition);
188            } catch (Exception e) {
189                throw new IllegalArgumentException("Error resolving property placeholders on data format: " + definition, e);
190            }
191            try {
192                dataFormat = doCreateDataFormat(camelContext);
193                if (dataFormat != null) {
194                    // is enabled by default so assume true if null
195                    final boolean contentTypeHeader = definition.getContentTypeHeader() == null || definition.getContentTypeHeader();
196                    try {
197                        setProperty(camelContext, dataFormat, "contentTypeHeader", contentTypeHeader);
198                    } catch (Exception e) {
199                        // ignore as this option is optional and not all data
200                        // formats support this
201                    }
202                    // configure the rest of the options
203                    configureDataFormat(dataFormat, camelContext);
204                } else {
205                    throw new IllegalArgumentException("Data format '" + (definition.getDataFormatName() != null ? definition.getDataFormatName() : "<null>")
206                                                       + "' could not be created. "
207                                                       + "Ensure that the data format is valid and the associated Camel component is present on the classpath");
208                }
209            } finally {
210                propertyPlaceholdersChangeReverter.run();
211            }
212        }
213        return dataFormat;
214    }
215
216    /**
217     * Factory method to create the data format instance
218     */
219    protected DataFormat doCreateDataFormat(CamelContext camelContext) {
220        // must use getDataFormatName() as we need special logic in json
221        // dataformat
222        if (definition.getDataFormatName() != null) {
223            return camelContext.createDataFormat(definition.getDataFormatName());
224        }
225        return null;
226    }
227
228    /**
229     * Allows derived classes to customize the data format
230     */
231    protected void configureDataFormat(DataFormat dataFormat, CamelContext camelContext) {
232    }
233
234    /**
235     * Sets a named property on the data format instance using introspection
236     */
237    protected void setProperty(CamelContext camelContext, Object bean, String name, Object value) {
238        try {
239            String ref = value instanceof String ? value.toString() : null;
240            if (isReferenceParameter(ref) && camelContext != null) {
241                camelContext.adapt(ExtendedCamelContext.class).getBeanIntrospection().setProperty(camelContext, camelContext.getTypeConverter(), bean, name, null, ref, true, false, false);
242            } else {
243                camelContext.adapt(ExtendedCamelContext.class).getBeanIntrospection().setProperty(camelContext, bean, name, value);
244            }
245        } catch (Exception e) {
246            throw new IllegalArgumentException("Failed to set property: " + name + " on: " + bean + ". Reason: " + e, e);
247        }
248    }
249
250}