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.Arrays; 021import java.util.HashMap; 022import java.util.List; 023import java.util.Map; 024import java.util.Map.Entry; 025 026import javax.xml.bind.annotation.XmlAccessType; 027import javax.xml.bind.annotation.XmlAccessorType; 028import javax.xml.bind.annotation.XmlAttribute; 029import javax.xml.bind.annotation.XmlElement; 030import javax.xml.bind.annotation.XmlRootElement; 031import javax.xml.bind.annotation.XmlTransient; 032import javax.xml.bind.annotation.XmlType; 033import javax.xml.bind.annotation.adapters.XmlAdapter; 034import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; 035 036import org.apache.camel.CamelContext; 037import org.apache.camel.model.DataFormatDefinition; 038import org.apache.camel.spi.DataFormat; 039import org.apache.camel.spi.Metadata; 040import org.apache.camel.spi.RouteContext; 041import org.apache.camel.util.CamelContextHelper; 042import org.apache.camel.util.CollectionStringBuffer; 043import org.apache.camel.util.ObjectHelper; 044 045/** 046 * XSTream data format is used for unmarshal a XML payload to POJO or to marshal POJO back to XML payload. 047 * 048 * @version 049 */ 050@Metadata(firstVersion = "1.3.0", label = "dataformat,transformation,xml,json", title = "XStream") 051@XmlRootElement(name = "xstream") 052@XmlAccessorType(XmlAccessType.NONE) 053public class XStreamDataFormat extends DataFormatDefinition { 054 @XmlAttribute 055 private String permissions; 056 @XmlAttribute 057 private String encoding; 058 @XmlAttribute 059 private String driver; 060 @XmlAttribute 061 private String driverRef; 062 @XmlAttribute 063 private String mode; 064 065 @XmlJavaTypeAdapter(ConvertersAdapter.class) 066 @XmlElement(name = "converters") 067 private List<String> converters; 068 @XmlJavaTypeAdapter(AliasAdapter.class) 069 @XmlElement(name = "aliases") 070 private Map<String, String> aliases; 071 @XmlJavaTypeAdapter(OmitFieldsAdapter.class) 072 @XmlElement(name = "omitFields") 073 private Map<String, String[]> omitFields; 074 @XmlJavaTypeAdapter(ImplicitCollectionsAdapter.class) 075 @XmlElement(name = "implicitCollections") 076 private Map<String, String[]> implicitCollections; 077 078 public XStreamDataFormat() { 079 super("xstream"); 080 } 081 082 public XStreamDataFormat(String encoding) { 083 this(); 084 setEncoding(encoding); 085 } 086 087 public String getEncoding() { 088 return encoding; 089 } 090 091 /** 092 * Sets the encoding to use 093 */ 094 public void setEncoding(String encoding) { 095 this.encoding = encoding; 096 } 097 098 public String getDriver() { 099 return driver; 100 } 101 102 /** 103 * To use a custom XStream driver. 104 * The instance must be of type com.thoughtworks.xstream.io.HierarchicalStreamDriver 105 */ 106 public void setDriver(String driver) { 107 this.driver = driver; 108 } 109 110 public String getDriverRef() { 111 return driverRef; 112 } 113 114 /** 115 * To refer to a custom XStream driver to lookup in the registry. 116 * The instance must be of type com.thoughtworks.xstream.io.HierarchicalStreamDriver 117 */ 118 public void setDriverRef(String driverRef) { 119 this.driverRef = driverRef; 120 } 121 122 public String getMode() { 123 return mode; 124 } 125 126 /** 127 * Mode for dealing with duplicate references The possible values are: 128 * <ul> 129 * <li>NO_REFERENCES</li> 130 * <li>ID_REFERENCES</li> 131 * <li>XPATH_RELATIVE_REFERENCES</li> 132 * <li>XPATH_ABSOLUTE_REFERENCES</li> 133 * <li>SINGLE_NODE_XPATH_RELATIVE_REFERENCES</li> 134 * <li>SINGLE_NODE_XPATH_ABSOLUTE_REFERENCES</li> 135 * </ul> 136 */ 137 public void setMode(String mode) { 138 this.mode = mode; 139 } 140 141 public List<String> getConverters() { 142 return converters; 143 } 144 145 /** 146 * List of class names for using custom XStream converters. 147 * The classes must be of type com.thoughtworks.xstream.converters.Converter 148 */ 149 public void setConverters(List<String> converters) { 150 this.converters = converters; 151 } 152 153 public Map<String, String> getAliases() { 154 return aliases; 155 } 156 157 /** 158 * Alias a Class to a shorter name to be used in XML elements. 159 */ 160 public void setAliases(Map<String, String> aliases) { 161 this.aliases = aliases; 162 } 163 164 public Map<String, String[]> getOmitFields() { 165 return omitFields; 166 } 167 168 /** 169 * Prevents a field from being serialized. To omit a field you must always provide the 170 * declaring type and not necessarily the type that is converted. 171 */ 172 public void setOmitFields(Map<String, String[]> omitFields) { 173 this.omitFields = omitFields; 174 } 175 176 public Map<String, String[]> getImplicitCollections() { 177 return implicitCollections; 178 } 179 180 /** 181 * Adds a default implicit collection which is used for any unmapped XML tag. 182 */ 183 public void setImplicitCollections(Map<String, String[]> implicitCollections) { 184 this.implicitCollections = implicitCollections; 185 } 186 187 public String getPermissions() { 188 return permissions; 189 } 190 191 /** 192 * Adds permissions that controls which Java packages and classes XStream is allowed to use during 193 * unmarshal from xml/json to Java beans. 194 * <p/> 195 * A permission must be configured either here or globally using a JVM system property. The permission 196 * can be specified in a syntax where a plus sign is allow, and minus sign is deny. 197 * <br/> 198 * Wildcards is supported by using <tt>.*</tt> as prefix. For example to allow <tt>com.foo</tt> and all subpackages 199 * then specfy <tt>+com.foo.*</tt>. Multiple permissions can be configured separated by comma, such as 200 * <tt>+com.foo.*,-com.foo.bar.MySecretBean</tt>. 201 * <br/> 202 * The following default permission is always included: <tt>"-*,java.lang.*,java.util.*"</tt> unless 203 * its overridden by specifying a JVM system property with they key <tt>org.apache.camel.xstream.permissions</tt>. 204 */ 205 public void setPermissions(String permissions) { 206 this.permissions = permissions; 207 } 208 209 /** 210 * To add permission for the given pojo classes. 211 * @param type the pojo class(es) xstream should use as allowed permission 212 * @see #setPermissions(String) 213 */ 214 public void setPermissions(Class<?>... type) { 215 CollectionStringBuffer csb = new CollectionStringBuffer(","); 216 for (Class<?> clazz : type) { 217 csb.append("+"); 218 csb.append(clazz.getName()); 219 } 220 setPermissions(csb.toString()); 221 } 222 223 @Override 224 protected DataFormat createDataFormat(RouteContext routeContext) { 225 if ("json".equals(this.driver)) { 226 setProperty(routeContext.getCamelContext(), this, "dataFormatName", "json-xstream"); 227 } 228 DataFormat answer = super.createDataFormat(routeContext); 229 // need to lookup the reference for the xstreamDriver 230 if (ObjectHelper.isNotEmpty(driverRef)) { 231 setProperty(routeContext.getCamelContext(), answer, "xstreamDriver", CamelContextHelper.mandatoryLookup(routeContext.getCamelContext(), driverRef)); 232 } 233 return answer; 234 } 235 236 @Override 237 protected void configureDataFormat(DataFormat dataFormat, CamelContext camelContext) { 238 if (this.permissions != null) { 239 setProperty(camelContext, dataFormat, "permissions", this.permissions); 240 } 241 if (encoding != null) { 242 setProperty(camelContext, dataFormat, "encoding", encoding); 243 } 244 if (this.converters != null) { 245 setProperty(camelContext, dataFormat, "converters", this.converters); 246 } 247 if (this.aliases != null) { 248 setProperty(camelContext, dataFormat, "aliases", this.aliases); 249 } 250 if (this.omitFields != null) { 251 setProperty(camelContext, dataFormat, "omitFields", this.omitFields); 252 } 253 if (this.implicitCollections != null) { 254 setProperty(camelContext, dataFormat, "implicitCollections", this.implicitCollections); 255 } 256 if (this.mode != null) { 257 setProperty(camelContext, dataFormat, "mode", mode); 258 } 259 } 260 261 262 263 @XmlTransient 264 public static class ConvertersAdapter extends XmlAdapter<ConverterList, List<String>> { 265 @Override 266 public ConverterList marshal(List<String> v) throws Exception { 267 if (v == null) { 268 return null; 269 } 270 271 List<ConverterEntry> list = new ArrayList<>(); 272 for (String str : v) { 273 ConverterEntry entry = new ConverterEntry(); 274 entry.setClsName(str); 275 list.add(entry); 276 } 277 ConverterList converterList = new ConverterList(); 278 converterList.setList(list); 279 return converterList; 280 } 281 282 @Override 283 public List<String> unmarshal(ConverterList v) throws Exception { 284 if (v == null) { 285 return null; 286 } 287 288 List<String> list = new ArrayList<>(); 289 for (ConverterEntry entry : v.getList()) { 290 list.add(entry.getClsName()); 291 } 292 return list; 293 } 294 } 295 296 @XmlAccessorType(XmlAccessType.NONE) 297 @XmlType(name = "converterList", namespace = "http://camel.apache.org/schema/spring") 298 public static class ConverterList { 299 @XmlElement(name = "converter", namespace = "http://camel.apache.org/schema/spring") 300 private List<ConverterEntry> list; 301 302 public List<ConverterEntry> getList() { 303 return list; 304 } 305 306 public void setList(List<ConverterEntry> list) { 307 this.list = list; 308 } 309 } 310 311 @XmlAccessorType(XmlAccessType.NONE) 312 @XmlType(name = "converterEntry", namespace = "http://camel.apache.org/schema/spring") 313 public static class ConverterEntry { 314 @XmlAttribute(name = "class") 315 private String clsName; 316 317 public String getClsName() { 318 return clsName; 319 } 320 321 public void setClsName(String clsName) { 322 this.clsName = clsName; 323 } 324 } 325 326 @XmlTransient 327 public static class ImplicitCollectionsAdapter 328 extends XmlAdapter<ImplicitCollectionList, Map<String, String[]>> { 329 330 @Override 331 public ImplicitCollectionList marshal(Map<String, String[]> v) throws Exception { 332 if (v == null || v.isEmpty()) { 333 return null; 334 } 335 336 List<ImplicitCollectionEntry> list = new ArrayList<>(); 337 for (Entry<String, String[]> e : v.entrySet()) { 338 ImplicitCollectionEntry entry = new ImplicitCollectionEntry(e.getKey(), e.getValue()); 339 list.add(entry); 340 } 341 342 ImplicitCollectionList collectionList = new ImplicitCollectionList(); 343 collectionList.setList(list); 344 345 return collectionList; 346 } 347 348 @Override 349 public Map<String, String[]> unmarshal(ImplicitCollectionList v) throws Exception { 350 if (v == null) { 351 return null; 352 } 353 354 Map<String, String[]> map = new HashMap<>(); 355 for (ImplicitCollectionEntry entry : v.getList()) { 356 map.put(entry.getClsName(), entry.getFields()); 357 } 358 return map; 359 } 360 } 361 362 @XmlAccessorType(XmlAccessType.NONE) 363 @XmlType(name = "implicitCollectionList", namespace = "http://camel.apache.org/schema/spring") 364 public static class ImplicitCollectionList { 365 @XmlElement(name = "class", namespace = "http://camel.apache.org/schema/spring") 366 private List<ImplicitCollectionEntry> list; 367 368 public List<ImplicitCollectionEntry> getList() { 369 return list; 370 } 371 372 public void setList(List<ImplicitCollectionEntry> list) { 373 this.list = list; 374 } 375 } 376 377 @XmlAccessorType(XmlAccessType.NONE) 378 @XmlType(name = "implicitCollectionEntry", namespace = "http://camel.apache.org/schema/spring") 379 public static class ImplicitCollectionEntry { 380 @XmlAttribute(name = "name") 381 private String clsName; 382 383 @XmlElement(name = "field", namespace = "http://camel.apache.org/schema/spring") 384 private String[] fields; 385 386 public ImplicitCollectionEntry() { 387 } 388 389 public ImplicitCollectionEntry(String clsName, String[] fields) { 390 this.clsName = clsName; 391 this.fields = fields; 392 } 393 394 public String getClsName() { 395 return clsName; 396 } 397 398 public void setClsName(String clsName) { 399 this.clsName = clsName; 400 } 401 402 public String[] getFields() { 403 return fields; 404 } 405 406 public void setFields(String[] fields) { 407 this.fields = fields; 408 } 409 410 @Override 411 public String toString() { 412 return "Alias[ImplicitCollection=" + clsName + ", fields=" + Arrays.asList(this.fields) + "]"; 413 } 414 } 415 416 @XmlTransient 417 public static class AliasAdapter extends XmlAdapter<AliasList, Map<String, String>> { 418 419 @Override 420 public AliasList marshal(Map<String, String> value) throws Exception { 421 if (value == null || value.isEmpty()) { 422 return null; 423 } 424 425 List<AliasEntry> ret = new ArrayList<>(value.size()); 426 for (Map.Entry<String, String> entry : value.entrySet()) { 427 ret.add(new AliasEntry(entry.getKey(), entry.getValue())); 428 } 429 AliasList jaxbMap = new AliasList(); 430 jaxbMap.setList(ret); 431 return jaxbMap; 432 } 433 434 @Override 435 public Map<String, String> unmarshal(AliasList value) throws Exception { 436 if (value == null || value.getList() == null || value.getList().isEmpty()) { 437 return null; 438 } 439 440 Map<String, String> answer = new HashMap<>(); 441 for (AliasEntry alias : value.getList()) { 442 answer.put(alias.getName(), alias.getClsName()); 443 } 444 return answer; 445 } 446 } 447 448 @XmlAccessorType(XmlAccessType.NONE) 449 @XmlType(name = "aliasList", namespace = "http://camel.apache.org/schema/spring") 450 public static class AliasList { 451 @XmlElement(name = "alias", namespace = "http://camel.apache.org/schema/spring") 452 private List<AliasEntry> list; 453 454 public List<AliasEntry> getList() { 455 return list; 456 } 457 458 public void setList(List<AliasEntry> list) { 459 this.list = list; 460 } 461 } 462 463 @XmlAccessorType(XmlAccessType.NONE) 464 @XmlType(name = "aliasEntry", namespace = "http://camel.apache.org/schema/spring") 465 public static class AliasEntry { 466 467 @XmlAttribute 468 private String name; 469 470 @XmlAttribute(name = "class") 471 private String clsName; 472 473 public AliasEntry() { 474 } 475 476 public AliasEntry(String key, String clsName) { 477 this.name = key; 478 this.clsName = clsName; 479 } 480 481 public String getName() { 482 return name; 483 } 484 485 public void setName(String name) { 486 this.name = name; 487 } 488 489 public String getClsName() { 490 return clsName; 491 } 492 493 public void setClsName(String clsName) { 494 this.clsName = clsName; 495 } 496 497 @Override 498 public String toString() { 499 return "Alias[name=" + name + ", class=" + clsName + "]"; 500 } 501 } 502 503 @XmlTransient 504 public static class OmitFieldsAdapter 505 extends XmlAdapter<OmitFieldList, Map<String, String[]>> { 506 507 @Override 508 public OmitFieldList marshal(Map<String, String[]> v) throws Exception { 509 if (v == null || v.isEmpty()) { 510 return null; 511 } 512 513 List<OmitFieldEntry> list = new ArrayList<>(); 514 for (Entry<String, String[]> e : v.entrySet()) { 515 OmitFieldEntry entry = new OmitFieldEntry(e.getKey(), e.getValue()); 516 list.add(entry); 517 } 518 519 OmitFieldList collectionList = new OmitFieldList(); 520 collectionList.setList(list); 521 522 return collectionList; 523 } 524 525 @Override 526 public Map<String, String[]> unmarshal(OmitFieldList v) throws Exception { 527 if (v == null || v.getList() == null || v.getList().isEmpty()) { 528 return null; 529 } 530 531 Map<String, String[]> map = new HashMap<>(); 532 for (OmitFieldEntry entry : v.getList()) { 533 map.put(entry.getClsName(), entry.getFields()); 534 } 535 return map; 536 } 537 } 538 539 @XmlAccessorType(XmlAccessType.NONE) 540 @XmlType(name = "omitFieldList", namespace = "http://camel.apache.org/schema/spring") 541 public static class OmitFieldList { 542 @XmlElement(name = "omitField", namespace = "http://camel.apache.org/schema/spring") 543 private List<OmitFieldEntry> list; 544 545 public List<OmitFieldEntry> getList() { 546 return list; 547 } 548 549 public void setList(List<OmitFieldEntry> list) { 550 this.list = list; 551 } 552 } 553 554 @XmlAccessorType(XmlAccessType.NONE) 555 @XmlType(name = "omitFieldEntry", namespace = "http://camel.apache.org/schema/spring") 556 public static class OmitFieldEntry { 557 558 @XmlAttribute(name = "class") 559 private String clsName; 560 561 @XmlElement(name = "field", namespace = "http://camel.apache.org/schema/spring") 562 private String[] fields; 563 564 public OmitFieldEntry() { 565 } 566 567 public OmitFieldEntry(String clsName, String[] fields) { 568 this.clsName = clsName; 569 this.fields = fields; 570 } 571 572 public String getClsName() { 573 return clsName; 574 } 575 576 public void setClsName(String clsName) { 577 this.clsName = clsName; 578 } 579 580 public String[] getFields() { 581 return fields; 582 } 583 584 public void setFields(String[] fields) { 585 this.fields = fields; 586 } 587 588 @Override 589 public String toString() { 590 return "OmitField[" + clsName + ", fields=" + Arrays.asList(this.fields) + "]"; 591 } 592 } 593}