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.LinkedHashMap;
021import java.util.Map;
022import java.util.function.BiFunction;
023
024import org.apache.camel.CamelContext;
025import org.apache.camel.ExtendedCamelContext;
026import org.apache.camel.model.DataFormatDefinition;
027import org.apache.camel.model.Model;
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.reifier.AbstractReifier;
071import org.apache.camel.spi.DataFormat;
072import org.apache.camel.spi.DataFormatContentTypeHeader;
073import org.apache.camel.spi.PropertyConfigurer;
074import org.apache.camel.spi.PropertyConfigurerAware;
075import org.apache.camel.spi.ReifierStrategy;
076import org.apache.camel.support.CamelContextHelper;
077import org.apache.camel.support.PropertyBindingSupport;
078import org.apache.camel.util.ObjectHelper;
079import org.slf4j.Logger;
080import org.slf4j.LoggerFactory;
081
082public abstract class DataFormatReifier<T extends DataFormatDefinition> extends AbstractReifier {
083
084    private static final Logger LOG = LoggerFactory.getLogger(DataFormatReifier.class);
085
086    private static final Map<Class<? extends DataFormatDefinition>, BiFunction<CamelContext, DataFormatDefinition, DataFormatReifier<? extends DataFormatDefinition>>> DATAFORMATS;
087    static {
088        Map<Class<? extends DataFormatDefinition>, BiFunction<CamelContext, DataFormatDefinition, DataFormatReifier<? extends DataFormatDefinition>>> map = new HashMap<>();
089        map.put(Any23DataFormat.class, Any23DataFormatReifier::new);
090        map.put(ASN1DataFormat.class, ASN1DataFormatReifier::new);
091        map.put(AvroDataFormat.class, AvroDataFormatReifier::new);
092        map.put(BarcodeDataFormat.class, BarcodeDataFormatReifier::new);
093        map.put(Base64DataFormat.class, Base64DataFormatReifier::new);
094        map.put(BeanioDataFormat.class, BeanioDataFormatReifier::new);
095        map.put(BindyDataFormat.class, BindyDataFormatReifier::new);
096        map.put(CBORDataFormat.class, CBORDataFormatReifier::new);
097        map.put(CryptoDataFormat.class, CryptoDataFormatReifier::new);
098        map.put(CsvDataFormat.class, CsvDataFormatReifier::new);
099        map.put(CustomDataFormat.class, CustomDataFormatReifier::new);
100        map.put(FhirDataformat.class, FhirDataFormatReifier::new);
101        map.put(FhirJsonDataFormat.class, FhirJsonDataFormatReifier::new);
102        map.put(FhirXmlDataFormat.class, FhirXmlDataFormatReifier::new);
103        map.put(FlatpackDataFormat.class, FlatpackDataFormatReifier::new);
104        map.put(GrokDataFormat.class, GrokDataFormatReifier::new);
105        map.put(GzipDataFormat.class, GzipDataFormatReifier::new);
106        map.put(HL7DataFormat.class, HL7DataFormatReifier::new);
107        map.put(IcalDataFormat.class, IcalDataFormatReifier::new);
108        map.put(JacksonXMLDataFormat.class, JacksonXMLDataFormatReifier::new);
109        map.put(JaxbDataFormat.class, JaxbDataFormatReifier::new);
110        map.put(JsonApiDataFormat.class, JsonApiDataFormatReifier::new);
111        map.put(JsonDataFormat.class, JsonDataFormatReifier::new);
112        map.put(LZFDataFormat.class, LZFDataFormatReifier::new);
113        map.put(MimeMultipartDataFormat.class, MimeMultipartDataFormatReifier::new);
114        map.put(PGPDataFormat.class, PGPDataFormatReifier::new);
115        map.put(ProtobufDataFormat.class, ProtobufDataFormatReifier::new);
116        map.put(RssDataFormat.class, RssDataFormatReifier::new);
117        map.put(SoapJaxbDataFormat.class, SoapJaxbDataFormatReifier::new);
118        map.put(SyslogDataFormat.class, SyslogDataFormatReifier::new);
119        map.put(TarFileDataFormat.class, TarFileDataFormatReifier::new);
120        map.put(ThriftDataFormat.class, ThriftDataFormatReifier::new);
121        map.put(TidyMarkupDataFormat.class, TidyMarkupDataFormatReifier::new);
122        map.put(UniVocityCsvDataFormat.class, UniVocityCsvDataFormatReifier::new);
123        map.put(UniVocityFixedWidthDataFormat.class, UniVocityFixedWidthDataFormatReifier::new);
124        map.put(UniVocityTsvDataFormat.class, UniVocityTsvDataFormatReifier::new);
125        map.put(XmlRpcDataFormat.class, XmlRpcDataFormatReifier::new);
126        map.put(XMLSecurityDataFormat.class, XMLSecurityDataFormatReifier::new);
127        map.put(XStreamDataFormat.class, XStreamDataFormatReifier::new);
128        map.put(YAMLDataFormat.class, YAMLDataFormatReifier::new);
129        map.put(ZipDeflaterDataFormat.class, ZipDataFormatReifier::new);
130        map.put(ZipFileDataFormat.class, ZipFileDataFormatReifier::new);
131        DATAFORMATS = map;
132        ReifierStrategy.addReifierClearer(DataFormatReifier::clearReifiers);
133    }
134
135    protected final T definition;
136
137    public DataFormatReifier(CamelContext camelContext, T definition) {
138        super(camelContext);
139        this.definition = definition;
140    }
141
142    public static void registerReifier(Class<? extends DataFormatDefinition> dataFormatClass,
143                                       BiFunction<CamelContext, DataFormatDefinition, DataFormatReifier<? extends DataFormatDefinition>> creator) {
144        DATAFORMATS.put(dataFormatClass, creator);
145    }
146
147    public static void clearReifiers() {
148        DATAFORMATS.clear();
149    }
150
151    public static DataFormat getDataFormat(CamelContext camelContext, DataFormatDefinition type) {
152        return getDataFormat(camelContext, ObjectHelper.notNull(type, "type"), null);
153    }
154
155    public static DataFormat getDataFormat(CamelContext camelContext, String ref) {
156        return getDataFormat(camelContext, null, ObjectHelper.notNull(ref, "ref"));
157    }
158
159    /**
160     * Factory method to create the data format
161     *
162     * @param camelContext the camel context
163     * @param type the data format type
164     * @param ref reference to lookup for a data format
165     * @return the data format or null if not possible to create
166     */
167    public static DataFormat getDataFormat(CamelContext camelContext, DataFormatDefinition type, String ref) {
168        if (type == null) {
169            ObjectHelper.notNull(ref, "ref or type");
170
171            DataFormat dataFormat = CamelContextHelper.lookup(camelContext, ref, DataFormat.class);
172            if (dataFormat != null) {
173                return dataFormat;
174            }
175
176            // try to let resolver see if it can resolve it, its not always
177            // possible
178            type = camelContext.getExtension(Model.class).resolveDataFormatDefinition(ref);
179
180            if (type == null) {
181                dataFormat = camelContext.resolveDataFormat(ref);
182                if (dataFormat == null) {
183                    throw new IllegalArgumentException("Cannot find data format in registry with ref: " + ref);
184                }
185
186                return dataFormat;
187            }
188        }
189        if (type.getDataFormat() != null) {
190            return type.getDataFormat();
191        }
192        return reifier(camelContext, type).createDataFormat();
193    }
194
195    public static DataFormatReifier<? extends DataFormatDefinition> reifier(CamelContext camelContext, DataFormatDefinition definition) {
196        BiFunction<CamelContext, DataFormatDefinition, DataFormatReifier<? extends DataFormatDefinition>> reifier = DATAFORMATS.get(definition.getClass());
197        if (reifier != null) {
198            return reifier.apply(camelContext, definition);
199        }
200        throw new IllegalStateException("Unsupported definition: " + definition);
201    }
202
203    public DataFormat createDataFormat() {
204        DataFormat dataFormat = definition.getDataFormat();
205        if (dataFormat == null) {
206            dataFormat = doCreateDataFormat();
207            if (dataFormat != null) {
208                if (dataFormat instanceof DataFormatContentTypeHeader) {
209                    // is enabled by default so assume true if null
210                    final boolean contentTypeHeader = parseBoolean(definition.getContentTypeHeader(), true);
211                    ((DataFormatContentTypeHeader) dataFormat).setContentTypeHeader(contentTypeHeader);
212                }
213                // configure the rest of the options
214                configureDataFormat(dataFormat);
215            } else {
216                throw new IllegalArgumentException("Data format '" + (definition.getDataFormatName() != null ? definition.getDataFormatName() : "<null>")
217                                                   + "' could not be created. "
218                                                   + "Ensure that the data format is valid and the associated Camel component is present on the classpath");
219            }
220        }
221        return dataFormat;
222    }
223
224    /**
225     * Factory method to create the data format instance
226     */
227    protected DataFormat doCreateDataFormat() {
228        // must use getDataFormatName() as we need special logic in json dataformat
229        String dfn = definition.getDataFormatName();
230        if (dfn != null) {
231            return camelContext.createDataFormat(dfn);
232        }
233        return null;
234    }
235
236    private String getDataFormatName() {
237        return definition.getDataFormatName();
238    }
239
240    /**
241     * Allows derived classes to customize the data format
242     */
243    protected void configureDataFormat(DataFormat dataFormat) {
244        Map<String, Object> properties = new LinkedHashMap<>();
245        prepareDataFormatConfig(properties);
246        properties.entrySet().removeIf(e -> e.getValue() == null);
247
248        PropertyConfigurer configurer = findPropertyConfigurer(dataFormat);
249
250        PropertyBindingSupport.build()
251                .withCamelContext(camelContext)
252                .withTarget(dataFormat)
253                .withReference(true)
254                .withMandatory(true)
255                .withConfigurer(configurer)
256                .withProperties(properties)
257                .bind();
258    }
259
260    private PropertyConfigurer findPropertyConfigurer(DataFormat dataFormat) {
261        PropertyConfigurer configurer = null;
262        String name = getDataFormatName();
263        LOG.trace("Discovering optional dataformat property configurer class for dataformat: {}", name);
264        if (dataFormat instanceof PropertyConfigurerAware) {
265            configurer = ((PropertyConfigurerAware) dataFormat).getPropertyConfigurer(dataFormat);
266            if (LOG.isDebugEnabled() && configurer != null) {
267                LOG.debug("Discovered dataformat property configurer using the PropertyConfigurerAware: {} -> {}", name, configurer);
268            }
269        }
270        if (configurer == null) {
271            String configurerName = name + "-dataformat-configurer";
272            configurer = camelContext.adapt(ExtendedCamelContext.class).getConfigurerResolver().resolvePropertyConfigurer(configurerName, camelContext);
273        }
274        return configurer;
275    }
276
277    protected abstract void prepareDataFormatConfig(Map<String, Object> properties);
278
279}