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