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