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.dataformat; 018 019import java.util.ArrayList; 020import java.util.List; 021 022import javax.xml.bind.annotation.XmlAccessType; 023import javax.xml.bind.annotation.XmlAccessorType; 024import javax.xml.bind.annotation.XmlAttribute; 025import javax.xml.bind.annotation.XmlElement; 026import javax.xml.bind.annotation.XmlRootElement; 027import javax.xml.bind.annotation.XmlTransient; 028 029import org.apache.camel.CamelContext; 030import org.apache.camel.model.DataFormatDefinition; 031import org.apache.camel.spi.DataFormat; 032import org.apache.camel.spi.Metadata; 033import org.apache.camel.spi.RouteContext; 034import org.apache.camel.util.ObjectHelper; 035 036/** 037 * YAML is a data format to marshal and unmarshal Java objects to and from YAML. 038 * 039 * @version 040 */ 041@Metadata(firstVersion = "2.17.0", label = "dataformat,transformation,yaml", title = "YAML") 042@XmlRootElement(name = "yaml") 043@XmlAccessorType(XmlAccessType.FIELD) 044public class YAMLDataFormat extends DataFormatDefinition { 045 @XmlAttribute @Metadata(defaultValue = "SnakeYAML") 046 private YAMLLibrary library = YAMLLibrary.SnakeYAML; 047 @XmlTransient 048 private ClassLoader classLoader; 049 @XmlTransient 050 private Class<?> unmarshalType; 051 @XmlAttribute 052 private String unmarshalTypeName; 053 @XmlAttribute 054 private String constructor; 055 @XmlAttribute 056 private String representer; 057 @XmlAttribute 058 private String dumperOptions; 059 @XmlAttribute 060 private String resolver; 061 @XmlAttribute @Metadata(defaultValue = "true") 062 private Boolean useApplicationContextClassLoader = true; 063 @XmlAttribute @Metadata(defaultValue = "false") 064 private Boolean prettyFlow = false; 065 @XmlAttribute @Metadata(defaultValue = "false") 066 private Boolean allowAnyType = false; 067 @XmlElement(name = "typeFilter") 068 private List<YAMLTypeFilterDefinition> typeFilters; 069 @XmlAttribute 070 @Metadata(javaType = "java.lang.Integer", defaultValue = "50") 071 private int maxAliasesForCollections = 50; 072 @XmlAttribute 073 @Metadata(javaType = "java.lang.Boolean", defaultValue = "false") 074 private String allowRecursiveKeys; 075 076 public YAMLDataFormat() { 077 this(YAMLLibrary.SnakeYAML); 078 } 079 080 public YAMLDataFormat(YAMLLibrary library) { 081 super("yaml-" + library.name().toLowerCase()); 082 this.library = library; 083 } 084 085 public YAMLDataFormat(YAMLLibrary library, Class<?> unmarshalType) { 086 super("yaml-" + library.name().toLowerCase()); 087 this.library = library; 088 this.unmarshalType = unmarshalType; 089 } 090 091 public YAMLLibrary getLibrary() { 092 return library; 093 } 094 095 /** 096 * Which yaml library to use. 097 * <p/> 098 * By default it is SnakeYAML 099 */ 100 public void setLibrary(YAMLLibrary library) { 101 this.library = library; 102 setDataFormatName("yaml-" + library.name().toLowerCase()); 103 } 104 105 public Class<?> getUnmarshalType() { 106 return unmarshalType; 107 } 108 109 /** 110 * Class of the object to be created 111 */ 112 public void setUnmarshalType(Class<?> type) { 113 this.unmarshalType = type; 114 } 115 116 public String getUnmarshalTypeName() { 117 return unmarshalTypeName; 118 } 119 120 /** 121 * Class name of the java type to use when unarmshalling 122 */ 123 public void setUnmarshalTypeName(String unmarshalTypeName) { 124 this.unmarshalTypeName = unmarshalTypeName; 125 } 126 127 public ClassLoader getClassLoader() { 128 return classLoader; 129 } 130 131 /** 132 * Set a custom classloader 133 */ 134 public void setClassLoader(ClassLoader classLoader) { 135 this.classLoader = classLoader; 136 } 137 138 public String getConstructor() { 139 return constructor; 140 } 141 142 /** 143 * BaseConstructor to construct incoming documents. 144 */ 145 public void setConstructor(String constructor) { 146 this.constructor = constructor; 147 } 148 149 public String getRepresenter() { 150 return representer; 151 } 152 153 /** 154 * Representer to emit outgoing objects. 155 */ 156 public void setRepresenter(String representer) { 157 this.representer = representer; 158 } 159 160 public String getDumperOptions() { 161 return dumperOptions; 162 } 163 164 /** 165 * DumperOptions to configure outgoing objects. 166 */ 167 public void setDumperOptions(String dumperOptions) { 168 this.dumperOptions = dumperOptions; 169 } 170 171 public String getResolver() { 172 return resolver; 173 } 174 175 /** 176 * Resolver to detect implicit type 177 */ 178 public void setResolver(String resolver) { 179 this.resolver = resolver; 180 } 181 182 public boolean isUseApplicationContextClassLoader() { 183 return useApplicationContextClassLoader; 184 } 185 186 /** 187 * Use ApplicationContextClassLoader as custom ClassLoader 188 */ 189 public void setUseApplicationContextClassLoader(boolean useApplicationContextClassLoader) { 190 this.useApplicationContextClassLoader = useApplicationContextClassLoader; 191 } 192 193 public boolean isPrettyFlow() { 194 return prettyFlow; 195 } 196 197 /** 198 * Force the emitter to produce a pretty YAML document when using the flow 199 * style. 200 */ 201 public void setPrettyFlow(boolean prettyFlow) { 202 this.prettyFlow = prettyFlow; 203 } 204 205 public boolean isAllowAnyType() { 206 return allowAnyType; 207 } 208 209 /** 210 * Allow any class to be un-marshaled 211 */ 212 public void setAllowAnyType(boolean allowAnyType) { 213 this.allowAnyType = allowAnyType; 214 } 215 216 public List<YAMLTypeFilterDefinition> getTypeFilters() { 217 return typeFilters; 218 } 219 220 public int getMaxAliasesForCollections() { 221 return maxAliasesForCollections; 222 } 223 224 /** 225 * Set the maximum amount of aliases allowed for collections. 226 */ 227 public void setMaxAliasesForCollections(int maxAliasesForCollections) { 228 this.maxAliasesForCollections = maxAliasesForCollections; 229 } 230 231 public String getAllowRecursiveKeys() { 232 return allowRecursiveKeys; 233 } 234 235 /** 236 * Set whether recursive keys are allowed. 237 */ 238 public void setAllowRecursiveKeys(String allowRecursiveKeys) { 239 this.allowRecursiveKeys = allowRecursiveKeys; 240 } 241 242 /** 243 * Set the types SnakeYAML is allowed to un-marshall 244 */ 245 public void setTypeFilters(List<YAMLTypeFilterDefinition> typeFilters) { 246 this.typeFilters = typeFilters; 247 } 248 249 @Override 250 protected DataFormat createDataFormat(RouteContext routeContext) { 251 if (library == YAMLLibrary.SnakeYAML) { 252 setProperty(routeContext.getCamelContext(), this, "dataFormatName", "yaml-snakeyaml"); 253 } 254 255 return super.createDataFormat(routeContext); 256 } 257 258 @Override 259 protected void configureDataFormat(DataFormat dataFormat, CamelContext camelContext) { 260 if (library == YAMLLibrary.SnakeYAML) { 261 configureSnakeDataFormat(dataFormat, camelContext); 262 } 263 } 264 265 protected void configureSnakeDataFormat(DataFormat dataFormat, CamelContext camelContext) { 266 Class<?> yamlUnmarshalType = unmarshalType; 267 if (yamlUnmarshalType == null && unmarshalTypeName != null) { 268 try { 269 yamlUnmarshalType = camelContext.getClassResolver().resolveMandatoryClass(unmarshalTypeName); 270 } catch (ClassNotFoundException e) { 271 throw ObjectHelper.wrapRuntimeCamelException(e); 272 } 273 } 274 275 setProperty(dataFormat, camelContext, "unmarshalType", yamlUnmarshalType); 276 setProperty(dataFormat, camelContext, "classLoader", classLoader); 277 setProperty(dataFormat, camelContext, "useApplicationContextClassLoader", useApplicationContextClassLoader); 278 setProperty(dataFormat, camelContext, "prettyFlow", prettyFlow); 279 setProperty(dataFormat, camelContext, "allowAnyType", allowAnyType); 280 281 if (typeFilters != null && !typeFilters.isEmpty()) { 282 List<String> typeFilterDefinitions = new ArrayList<>(typeFilters.size()); 283 for (YAMLTypeFilterDefinition definition : typeFilters) { 284 String value = definition.getValue(); 285 286 if (!value.startsWith("type") && !value.startsWith("regexp")) { 287 YAMLTypeFilterType type = definition.getType(); 288 if (type == null) { 289 type = YAMLTypeFilterType.type; 290 } 291 292 value = type.name() + ":" + value; 293 } 294 295 typeFilterDefinitions.add(value); 296 } 297 298 setProperty(dataFormat, camelContext, "typeFilterDefinitions", typeFilterDefinitions); 299 } 300 301 setPropertyRef(dataFormat, camelContext, "constructor", constructor); 302 setPropertyRef(dataFormat, camelContext, "representer", representer); 303 setPropertyRef(dataFormat, camelContext, "dumperOptions", dumperOptions); 304 setPropertyRef(dataFormat, camelContext, "resolver", resolver); 305 } 306 307 protected void setProperty(DataFormat dataFormat, CamelContext camelContext, String propertyName, Object propertyValue) { 308 if (ObjectHelper.isNotEmpty(propertyValue)) { 309 setProperty(camelContext, dataFormat, propertyName, propertyValue); 310 } 311 } 312 313 protected void setPropertyRef(DataFormat dataFormat, CamelContext camelContext, String propertyName, String propertyValue) { 314 if (ObjectHelper.isNotEmpty(propertyValue)) { 315 // must be a reference value 316 String ref = propertyValue.startsWith("#") ? propertyValue : "#" + propertyValue; 317 setProperty(camelContext, dataFormat, propertyName, ref); 318 } 319 } 320 321}