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