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 final boolean contentTypeHeader = this.contentTypeHeader == null || this.contentTypeHeader; 110 try { 111 setProperty(routeContext.getCamelContext(), dataFormat, "contentTypeHeader", contentTypeHeader); 112 } catch (Exception e) { 113 // ignore as this option is optional and not all data formats support this 114 } 115 // configure the rest of the options 116 configureDataFormat(dataFormat, routeContext.getCamelContext()); 117 } else { 118 throw new IllegalArgumentException( 119 "Data format '" + (dataFormatName != null ? dataFormatName : "<null>") + "' could not be created. " 120 + "Ensure that the data format is valid and the associated Camel component is present on the classpath"); 121 } 122 } finally { 123 propertyPlaceholdersChangeReverter.run(); 124 } 125 } 126 return dataFormat; 127 } 128 129 /** 130 * Factory method to create the data format instance 131 */ 132 protected DataFormat createDataFormat(RouteContext routeContext) { 133 // must use getDataFormatName() as we need special logic in json dataformat 134 if (getDataFormatName() != null) { 135 return routeContext.getCamelContext().createDataFormat(getDataFormatName()); 136 } 137 return null; 138 } 139 140 /** 141 * Allows derived classes to customize the data format 142 * 143 * @deprecated use {@link #configureDataFormat(org.apache.camel.spi.DataFormat, org.apache.camel.CamelContext)} 144 */ 145 @Deprecated 146 protected void configureDataFormat(DataFormat dataFormat) { 147 } 148 149 /** 150 * Allows derived classes to customize the data format 151 */ 152 protected void configureDataFormat(DataFormat dataFormat, CamelContext camelContext) { 153 } 154 155 /** 156 * Sets a named property on the data format instance using introspection 157 * 158 * @deprecated use {@link #setProperty(org.apache.camel.CamelContext, Object, String, Object)} 159 */ 160 @Deprecated 161 protected void setProperty(Object bean, String name, Object value) { 162 setProperty(null, bean, name, value); 163 } 164 165 /** 166 * Sets a named property on the data format instance using introspection 167 */ 168 protected void setProperty(CamelContext camelContext, Object bean, String name, Object value) { 169 try { 170 String ref = value instanceof String ? value.toString() : null; 171 if (isReferenceParameter(ref) && camelContext != null) { 172 IntrospectionSupport.setProperty(camelContext, camelContext.getTypeConverter(), bean, name, null, ref, true); 173 } else { 174 IntrospectionSupport.setProperty(bean, name, value); 175 } 176 } catch (Exception e) { 177 throw new IllegalArgumentException("Failed to set property: " + name + " on: " + bean + ". Reason: " + e, e); 178 } 179 } 180 181 public String getDataFormatName() { 182 return dataFormatName; 183 } 184 185 public void setDataFormatName(String dataFormatName) { 186 this.dataFormatName = dataFormatName; 187 } 188 189 public DataFormat getDataFormat() { 190 return dataFormat; 191 } 192 193 public void setDataFormat(DataFormat dataFormat) { 194 this.dataFormat = dataFormat; 195 } 196 197 public Map<QName, Object> getOtherAttributes() { 198 return otherAttributes; 199 } 200 201 /** 202 * Adds an optional attribute 203 */ 204 public void setOtherAttributes(Map<QName, Object> otherAttributes) { 205 this.otherAttributes = otherAttributes; 206 } 207 208 public Boolean getContentTypeHeader() { 209 return contentTypeHeader; 210 } 211 212 /** 213 * Whether the data format should set the <tt>Content-Type</tt> header with the type from the data format if the 214 * data format is capable of doing so. 215 * <p/> 216 * For example <tt>application/xml</tt> for data formats marshalling to XML, or <tt>application/json</tt> 217 * for data formats marshalling to JSon etc. 218 */ 219 public void setContentTypeHeader(Boolean contentTypeHeader) { 220 this.contentTypeHeader = contentTypeHeader; 221 } 222 223 public String getShortName() { 224 String name = getClass().getSimpleName(); 225 if (name.endsWith("DataFormat")) { 226 name = name.substring(0, name.indexOf("DataFormat")); 227 } 228 return name; 229 } 230 231} 232