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; 020 021import javax.xml.bind.annotation.XmlAccessType; 022import javax.xml.bind.annotation.XmlAccessorType; 023import javax.xml.bind.annotation.XmlAnyAttribute; 024import javax.xml.bind.annotation.XmlAttribute; 025import javax.xml.bind.annotation.XmlTransient; 026import javax.xml.bind.annotation.XmlType; 027import javax.xml.namespace.QName; 028 029import org.apache.camel.CamelContext; 030import org.apache.camel.spi.DataFormat; 031import org.apache.camel.spi.Metadata; 032import org.apache.camel.spi.RouteContext; 033import org.apache.camel.util.IntrospectionSupport; 034import org.apache.camel.util.ObjectHelper; 035 036import static org.apache.camel.util.EndpointHelper.isReferenceParameter; 037 038/** 039 * Represents a Camel data format 040 */ 041@Metadata(label = "dataformat,transformation") 042@XmlType(name = "dataFormat") 043@XmlAccessorType(XmlAccessType.FIELD) 044public class DataFormatDefinition extends IdentifiedType implements OtherAttributesAware { 045 @XmlTransient 046 private DataFormat dataFormat; 047 @XmlTransient 048 private String dataFormatName; 049 // use xs:any to support optional property placeholders 050 @XmlAnyAttribute 051 private Map<QName, Object> otherAttributes; 052 @XmlAttribute 053 private Boolean contentTypeHeader; 054 055 public DataFormatDefinition() { 056 } 057 058 public DataFormatDefinition(DataFormat dataFormat) { 059 this.dataFormat = dataFormat; 060 } 061 062 protected DataFormatDefinition(String dataFormatName) { 063 this.dataFormatName = dataFormatName; 064 } 065 066 /** 067 * Factory method to create the data format 068 * 069 * @param routeContext route context 070 * @param type the data format type 071 * @param ref reference to lookup for a data format 072 * @return the data format or null if not possible to create 073 */ 074 public static DataFormat getDataFormat(RouteContext routeContext, DataFormatDefinition type, String ref) { 075 if (type == null) { 076 ObjectHelper.notNull(ref, "ref or type"); 077 078 // try to let resolver see if it can resolve it, its not always possible 079 type = routeContext.getCamelContext().resolveDataFormatDefinition(ref); 080 081 if (type != null) { 082 return type.getDataFormat(routeContext); 083 } 084 085 DataFormat dataFormat = routeContext.getCamelContext().resolveDataFormat(ref); 086 if (dataFormat == null) { 087 throw new IllegalArgumentException("Cannot find data format in registry with ref: " + ref); 088 } 089 090 return dataFormat; 091 } else { 092 return type.getDataFormat(routeContext); 093 } 094 } 095 096 public DataFormat getDataFormat(RouteContext routeContext) { 097 if (dataFormat == null) { 098 Runnable propertyPlaceholdersChangeReverter = ProcessorDefinitionHelper.createPropertyPlaceholdersChangeReverter(); 099 100 // resolve properties before we create the data format 101 try { 102 ProcessorDefinitionHelper.resolvePropertyPlaceholders(routeContext.getCamelContext(), this); 103 } catch (Exception e) { 104 throw new IllegalArgumentException("Error resolving property placeholders on data format: " + this, e); 105 } 106 try { 107 dataFormat = createDataFormat(routeContext); 108 if (dataFormat != null) { 109 // is enabled by default so assume true if null 110 final boolean contentTypeHeader = this.contentTypeHeader == null || this.contentTypeHeader; 111 try { 112 setProperty(routeContext.getCamelContext(), dataFormat, "contentTypeHeader", contentTypeHeader); 113 } catch (Exception e) { 114 // ignore as this option is optional and not all data formats support this 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