Return-Path: X-Original-To: apmail-camel-commits-archive@www.apache.org Delivered-To: apmail-camel-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id CEF4318974 for ; Wed, 27 Jan 2016 14:31:37 +0000 (UTC) Received: (qmail 66671 invoked by uid 500); 27 Jan 2016 14:30:39 -0000 Delivered-To: apmail-camel-commits-archive@camel.apache.org Received: (qmail 66621 invoked by uid 500); 27 Jan 2016 14:30:39 -0000 Mailing-List: contact commits-help@camel.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@camel.apache.org Delivered-To: mailing list commits@camel.apache.org Received: (qmail 66612 invoked by uid 99); 27 Jan 2016 14:30:39 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 27 Jan 2016 14:30:39 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id 3F27ADFE61; Wed, 27 Jan 2016 14:30:39 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit From: acosentino@apache.org To: commits@camel.apache.org Message-Id: X-Mailer: ASF-Git Admin Mailer Subject: camel git commit: Added camel-bindy docs to gitbook Date: Wed, 27 Jan 2016 14:30:39 +0000 (UTC) Repository: camel Updated Branches: refs/heads/master d6f3285e9 -> 5af2c600f Added camel-bindy docs to gitbook Project: http://git-wip-us.apache.org/repos/asf/camel/repo Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/5af2c600 Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/5af2c600 Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/5af2c600 Branch: refs/heads/master Commit: 5af2c600fd183366fb38a533da42f62d99a12c3f Parents: d6f3285 Author: Andrea Cosentino Authored: Wed Jan 27 15:30:02 2016 +0100 Committer: Andrea Cosentino Committed: Wed Jan 27 15:30:21 2016 +0100 ---------------------------------------------------------------------- components/camel-bindy/src/main/docs/bindy.adoc | 1737 ++++++++++++++++++ docs/user-manual/en/SUMMARY.md | 1 + 2 files changed, 1738 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/camel/blob/5af2c600/components/camel-bindy/src/main/docs/bindy.adoc ---------------------------------------------------------------------- diff --git a/components/camel-bindy/src/main/docs/bindy.adoc b/components/camel-bindy/src/main/docs/bindy.adoc new file mode 100644 index 0000000..cd6260e --- /dev/null +++ b/components/camel-bindy/src/main/docs/bindy.adoc @@ -0,0 +1,1737 @@ +[[Bindy-Bindy]] +Bindy +~~~~~ + +The goal of this component is to allow the parsing/binding of +non-structured data (or to be more precise non-XML data) + + to/from Java Beans that have binding mappings defined with annotations. +Using Bindy, you can bind data from sources such as : + +* CSV records, +* Fixed-length records, +* FIX messages, +* or almost any other non-structured data + +to one or many Plain Old Java Object (POJO). Bindy converts the data +according to the type of the java property. POJOs can be linked together +with one-to-many relationships available in some cases. Moreover, for +data type like Date, Double, Float, Integer, Short, Long and BigDecimal, +you can provide the pattern to apply during the formatting of the +property. + +For the BigDecimal numbers, you can also define the precision and the +decimal or grouping separators. + +[width="100%",cols="10%,10%,10%,70%",options="header",] +|======================================================================= +|Type |Format Type |Pattern example |Link + +|Date |DateFormat |`dd-MM-yyyy` |http://java.sun.com/j2se/1.5.0/docs/api/java/text/SimpleDateFormat.html[http://java.sun.com/j2se/1.5.0/docs/api/java/text/SimpleDateFormat.html] + +|Decimal* |Decimalformat |`##.###.###` |http://java.sun.com/j2se/1.5.0/docs/api/java/text/DecimalFormat.html[http://java.sun.com/j2se/1.5.0/docs/api/java/text/DecimalFormat.html] +|======================================================================= + +Decimal* = Double, Integer, Float, Short, Long + + *Format supported* + +This first release only support comma separated values fields and key +value pair fields (e.g. : FIX messages). + +To work with camel-bindy, you must first define your model in a package +(e.g. com.acme.model) and for each model class (e.g. Order, Client, +Instrument, ...) add the required annotations (described hereafter) to +the Class or field. + + *Multiple models* + +If you use multiple models, each model has to be placed in it's own +package to prevent unpredictable results. + +From *Camel 2.16* onwards this is no longer the case, as you can safely +have multiple models in the same package, as you configure bindy using +class names instead of package names now. + +[[Bindy-Annotations]] +Annotations +~~~~~~~~~~~ + +The annotations created allow to map different concept of your model to +the POJO like : + +* Type of record (csv, key value pair (e.g. FIX message), fixed length +...), +* Link (to link object in another object), +* DataField and their properties (int, type, ...), +* KeyValuePairField (for key = value format like we have in FIX +financial messages), +* Section (to identify header, body and footer section), +* OneToMany + +This section will describe them : + +[[Bindy-1.CsvRecord]] +1. CsvRecord +^^^^^^^^^^^^ + +The CsvRecord annotation is used to identified the root class of the +model. It represents a record = a line of a CSV file and can be linked +to several children model classes. + +[width="100%",cols="10%,10%,80%",options="header",] +|======================================================================= +|Annotation name |Record type |Level + +|*CsvRecord* |csv |Class +|======================================================================= + +[width="100%",cols="10%,10%,80%",options="header",] +|======================================================================= +|Parameter name |type |Info + +|separator |string |mandatory - can be ',' or ';' or 'anything'. This value is interpreted +as a regular expression. If you want to use a sign which has a special +meaning in regular expressions, e.g. the '|' sign, than you have to mask +it, like '|' + +|skipFirstLine |boolean |optional - default value = false - allow to skip the first line of the +CSV file + +|crlf |string |optional - possible values = WINDOWS,UNIX,MAC, or custom; default value += WINDOWS - allow to define the carriage return character to use. If you +specify a value other than the three listed before, the value you enter +(custom) will be used as the CRLF character(s) + +|generateHeaderColumns |boolean |optional - default value = false - uses to generate the header columns +of the CSV generates + +|autospanLine |boolean |*Camel 2.13/2.12.2:* optional - default value = false - if enabled then +the last column is auto spanned to end of line, for example if its a +comment, etc this allows the line to contain all characters, also the +delimiter char. + +|isOrdered |boolean |optional - default value = false - allow to change the order of the +fields when CSV is generated + +|quote |String |*Camel 2.8.3/2.9:* option - allow to specify a quote character of the +fields when CSV is generated. This annotation is associated to the root class of the model and must be +declared one time. + +|quoting |boolean |*Camel 2.11:*optional - default value = false - Indicate if the values +must be quoted when marshaling when CSV is generated. +|======================================================================= + +*case 1 : separator = ','* + +The separator used to segregate the fields in the CSV record is ',' : + +10, J, Pauline, M, XD12345678, Fortis Dynamic 15/15, 2500, +USD,08-01-2009 + +[source,java] +----------------------------- +@CsvRecord( separator = "," ) +public Class Order { +... +} +----------------------------- + +*case 2 : separator = ';'* + +Compare to the previous case, the separator here is ';' instead of ',' : + +10; J; Pauline; M; XD12345678; Fortis Dynamic 15/15; 2500; USD; +08-01-2009 + +[source,java] +----------------------------- +@CsvRecord( separator = ";" ) +public Class Order { +... +} +----------------------------- + +*case 3 : separator = '|'* + +Compare to the previous case, the separator here is '|' instead of ';' : + +10| J| Pauline| M| XD12345678| Fortis Dynamic 15/15| 2500| USD| +08-01-2009 + +[source,java] +------------------------------- +@CsvRecord( separator = "\\|" ) +public Class Order { +... +} +------------------------------- + +*case 4 : separator = '\",\"'* + + *Applies for Camel 2.8.2 or older* + +When the field to be parsed of the CSV record contains ',' or ';' which +is also used as separator, we whould find another strategy + + to tell camel bindy how to handle this case. To define the field +containing the data with a comma, you will use simple or double quotes + + as delimiter (e.g : '10', 'Street 10, NY', 'USA' or "10", "Street 10, +NY", "USA"). + + Remark : In this case, the first and last character of the line which +are a simple or double quotes will removed by bindy + +"10","J","Pauline"," M","XD12345678","Fortis Dynamic 15,15" +2500","USD","08-01-2009" + +[source,java] +--------------------------------- +@CsvRecord( separator = "\",\"" ) +public Class Order { +... +} +--------------------------------- + +From *Camel 2.8.3/2.9 or never* bindy will automatic detect if the +record is enclosed with either single or double quotes and automatic +remove those quotes when unmarshalling from CSV to Object. Therefore do +*not* include the quotes in the separator, but simple do as below: + +"10","J","Pauline"," M","XD12345678","Fortis Dynamic 15,15" +2500","USD","08-01-2009" + +[source,java] +----------------------------- +@CsvRecord( separator = "," ) +public Class Order { +... +} +----------------------------- + +Notice that if you want to marshal from Object to CSV and use quotes, +then you need to specify which quote character to use, using the `quote` +attribute on the @CsvRecord as shown below: + +[source,java] +------------------------------------------- +@CsvRecord( separator = ",", quote = "\"" ) +public Class Order { +... +} +------------------------------------------- + +*case 5 : separator & skipfirstline* + +The feature is interesting when the client wants to have in the first +line of the file, the name of the data fields : + +order id, client id, first name, last name, isin code, instrument name, +quantity, currency, date + +To inform bindy that this first line must be skipped during the parsing +process, then we use the attribute : + +[source,java] +------------------------------------------------- +@CsvRecord(separator = ",", skipFirstLine = true) +public Class Order { +... +} +------------------------------------------------- + +*case 6 : generateHeaderColumns* + +To add at the first line of the CSV generated, the attribute +generateHeaderColumns must be set to true in the annotation like this : + +[source,java] +------------------------------------------ +@CsvRecord( generateHeaderColumns = true ) +public Class Order { +... +} +------------------------------------------ + +As a result, Bindy during the unmarshaling process will generate CSV +like this : + +order id, client id, first name, last name, isin code, instrument name, +quantity, currency, date + + 10, J, Pauline, M, XD12345678, Fortis Dynamic 15/15, 2500, +USD,08-01-2009 + +*case 7 : carriage return* + +If the platform where camel-bindy will run is not Windows but Macintosh +or Unix, than you can change the crlf property like this. Three values +are available : WINDOWS, UNIX or MAC + +[source,java] +--------------------------------------- +@CsvRecord(separator = ",", crlf="MAC") +public Class Order { +... +} +--------------------------------------- + +Additionally, if for some reason you need to add a different line ending +character, you can opt to specify it using the crlf parameter. In the +following example, we can end the line with a comma followed by the +newline character: + +[source,java] +--------------------------------------- +@CsvRecord(separator = ",", crlf=",\n") +public Class Order { +... +} +--------------------------------------- + +*case 8 : isOrdered* + +Sometimes, the order to follow during the creation of the CSV record +from the model is different from the order used during the parsing. +Then, in this case, we can use the attribute isOrdered = true to +indicate this in combination with attribute 'position' of the DataField +annotation. + +[source,java] +------------------------------------- +@CsvRecord(isOrdered = true) +public Class Order { + + @DataField(pos = 1, position = 11) + private int orderNr; + + @DataField(pos = 2, position = 10) + private String clientNr; + +... +} +------------------------------------- + +Remark : pos is used to parse the file, stream while positions is used +to generate the CSV + +[[Bindy-2.Link]] +2. Link +^^^^^^^ + +The link annotation will allow to link objects together. + +[width="100%",cols="10%,10%,80%",options="header",] +|======================================================================= +|Annotation name |Record type |Level + +|*Link* |all |Class & Property +|======================================================================= + +[width="100%",cols="10%,10%,80%",options="header",] +|======================================================================= +|Parameter name |type |Info + +|linkType |LinkType |optional - by default the value is LinkType.oneToOne - so you are not +obliged to mention it + +|======================================================================= + +Only one-to-one relation is allowed. + +e.g : If the model Class Client is linked to the Order class, then use +annotation Link in the Order class like this : + +*Property Link* + +[source,java] +--------------------------- +@CsvRecord(separator = ",") +public class Order { + + @DataField(pos = 1) + private int orderNr; + + @Link + private Client client; +... +--------------------------- + +AND for the class Client : + +*Class Link* + +[source,java] +--------------------- +@Link +public class Client { +... +} +--------------------- + +[[Bindy-3.DataField]] +3. DataField +^^^^^^^^^^^^ + +The DataField annotation defines the property of the field. Each +datafield is identified by its position in the record, a type (string, +int, date, ...) and optionally of a pattern + +[width="100%",cols="10%,10%,80%",options="header",] +|======================================================================= +|Annotation name |Record type |Level + +|*DataField* |all |Property +|======================================================================= + + +[width="100%",cols="10%,10%,80%",options="header",] +|======================================================================= +|Parameter name |type |Info + +|pos |int |mandatory - The *input* position of the field. digit number starting +from 1 to ... - See the position parameter. + +|pattern |string |optional - default value = "" - will be used to format Decimal, Date, +... + +|length |int |optional - represents the length of the field for fixed length format + +|precision |int |optional - represents the precision to be used when the Decimal number +will be formatted/parsed + +|pattern |string |optional - default value = "" - is used by the Java formatter +(SimpleDateFormat by example) to format/validate data. If using pattern, +then setting locale on bindy data format is recommended. Either set to a +known locale such as "us" or use "default" to use platform default +locale. Notice that "default" requires Camel 2.14/2.13.3/2.12.5. + +|position |int |optional - must be used when the position of the field in the CSV +generated (output message) must be different compare to input position +(pos). See the pos parameter. + +|required |boolean |optional - default value = "false" + +|trim |boolean |optional - default value = "false" + +|defaultValue |string |*Camel 2.10:* optional - default value = "" - defines the field's +default value when the respective CSV field is empty/not available + +|impliedDecimalSeparator |boolean |*Camel 2.11:* optional - default value = "false" - Indicates if there is +a decimal point implied at a specified location + +|lengthPos |int |*Camel 2.11*: optional - can be used to identify a data field in a +fixed-length record that defines the fixed length for this field + +|delimiter |string |*Camel 2.11:* optional - can be used to demarcate the end of a variable-length field within a fixed-length record +|======================================================================= + +*case 1 : pos* + +This parameter/attribute represents the position of the field in the csv +record + +*Position* + +[source,java] +---------------------------- +@CsvRecord(separator = ",") +public class Order { + + @DataField(pos = 1) + private int orderNr; + + @DataField(pos = 5) + private String isinCode; + +... +} +---------------------------- + +As you can see in this example the position starts at '1' but continues +at '5' in the class Order. The numbers from '2' to '4' are defined in +the class Client (see here after). + +*Position continues in another model class* + +[source,java] +----------------------------- +public class Client { + + @DataField(pos = 2) + private String clientNr; + + @DataField(pos = 3) + private String firstName; + + @DataField(pos = 4) + private String lastName; +... +} +----------------------------- + +*case 2 : pattern* + +The pattern allows to enrich or validates the format of your data + +*Pattern* + +[source,java] +---------------------------------------------------------------------------------------------------------- +@CsvRecord(separator = ",") +public class Order { + + @DataField(pos = 1) + private int orderNr; + + @DataField(pos = 5) + private String isinCode; + + @DataField(name = "Name", pos = 6) + private String instrumentName; + + @DataField(pos = 7, precision = 2) + private BigDecimal amount; + + @DataField(pos = 8) + private String currency; + + @DataField(pos = 9, pattern = "dd-MM-yyyy") -- pattern used during parsing or when the date is created + private Date orderDate; +... +} +---------------------------------------------------------------------------------------------------------- + +*case 3 : precision* + +The precision is helpful when you want to define the decimal part of +your number + +*Precision* + +[source,java] +--------------------------------------------------- +@CsvRecord(separator = ",") +public class Order { + + @DataField(pos = 1) + private int orderNr; + + @Link + private Client client; + + @DataField(pos = 5) + private String isinCode; + + @DataField(name = "Name", pos = 6) + private String instrumentName; + + @DataField(pos = 7, precision = 2) -- precision + private BigDecimal amount; + + @DataField(pos = 8) + private String currency; + + @DataField(pos = 9, pattern = "dd-MM-yyyy") + private Date orderDate; +... +} +--------------------------------------------------- + +*case 4 : Position is different in output* + +The position attribute will inform bindy how to place the field in the +CSV record generated. By default, the position used corresponds to the +position defined with the attribute 'pos'. If the position is different +(that means that we have an asymetric processus comparing marshaling +from unmarshaling) than we can use 'position' to indicate this. + +Here is an example + +*Position is different in output* + +[source,java] +---------------------------------------------------------- +@CsvRecord(separator = ",") +public class Order { +@CsvRecord(separator = ",", isOrdered = true) +public class Order { + + // Positions of the fields start from 1 and not from 0 + + @DataField(pos = 1, position = 11) + private int orderNr; + + @DataField(pos = 2, position = 10) + private String clientNr; + + @DataField(pos = 3, position = 9) + private String firstName; + + @DataField(pos = 4, position = 8) + private String lastName; + + @DataField(pos = 5, position = 7) + private String instrumentCode; + + @DataField(pos = 6, position = 6) + private String instrumentNumber; +... +} +---------------------------------------------------------- + +This attribute of the annotation @DataField must be used in combination +with attribute isOrdered = true of the annotation @CsvRecord + +*case 5 : required* + +If a field is mandatory, simply use the attribute 'required' setted to +true + +*Required* + +[source,java] +---------------------------------------- +@CsvRecord(separator = ",") +public class Order { + + @DataField(pos = 1) + private int orderNr; + + @DataField(pos = 2, required = true) + private String clientNr; + + @DataField(pos = 3, required = true) + private String firstName; + + @DataField(pos = 4, required = true) + private String lastName; +... +} +---------------------------------------- + +If this field is not present in the record, than an error will be raised +by the parser with the following information : + +Some fields are missing (optional or mandatory), line : + +*case 6 : trim* + +If a field has leading and/or trailing spaces which should be removed +before they are processed, simply use the attribute 'trim' setted to +true + +*Trim* + +[source,java] +---------------------------------------- +@CsvRecord(separator = ",") +public class Order { + + @DataField(pos = 1, trim = true) + private int orderNr; + + @DataField(pos = 2, trim = true) + private Integer clientNr; + + @DataField(pos = 3, required = true) + private String firstName; + + @DataField(pos = 4) + private String lastName; +... +} +---------------------------------------- + +*case 7 : defaultValue* + +If a field is not defined then uses the value indicated by the +defaultValue attribute + +*Default value* + +[source,java] +----------------------------------------------- +@CsvRecord(separator = ",") +public class Order { + + @DataField(pos = 1) + private int orderNr; + + @DataField(pos = 2) + private Integer clientNr; + + @DataField(pos = 3, required = true) + private String firstName; + + @DataField(pos = 4, defaultValue = "Barin") + private String lastName; +... +} +----------------------------------------------- + +This attribute is only applicable to optional fields. + +[[Bindy-4.FixedLengthRecord]] +4. FixedLengthRecord +^^^^^^^^^^^^^^^^^^^^ + +The FixedLengthRecord annotation is used to identified the root class of +the model. It represents a record = a line of a file/message containing +data fixed length formatted and can be linked to several children model +classes. This format is a bit particular beause data of a field can be +aligned to the right or to the left. + + When the size of the data does not fill completely the length of the +field, we can then add 'padd' characters. + +[width="100%",cols="10%,10%,80%",options="header",] +|======================================================================= +|Annotation name |Record type |Level + +|*FixedLengthRecord* |fixed |Class +|======================================================================= + +[width="100%",cols="10%,10%,80%",options="header",] +|======================================================================= +|Parameter name |type |Info + +|crlf |string |optional - possible values = WINDOWS,UNIX,MAC, or custom; default value += WINDOWS - allow to define the carriage return character to use. If you +specify a value other than the three listed before, the value you enter +(custom) will be used as the CRLF character(s) + +|paddingChar |char |mandatory - default value = ' ' + +|length |int |mandatory = size of the fixed length record + +|hasHeader |boolean |*Camel 2.11* - optional - Indicates that the record(s) of this type may +be preceded by a single header record at the beginning of the file / +stream + +|hasFooter |boolean |*Camel 2.11* - optional - Indicates that the record(s) of this type may +be followed by a single footer record at the end of the file / stream + +|skipHeader |boolean |*Camel 2.11* - optional - Configures the data format to skip marshalling +/ unmarshalling of the header record. Configure this parameter on the +primary record (e.g., not the header or footer). + +|skipFooter |boolean |*Camel 2.11* - optional - Configures the data format to skip marshalling +/ unmarshalling of the footer record Configure this parameter on the +primary record (e.g., not the header or footer).. + +|isHeader |boolean |*Camel 2.11* - optional - Identifies this FixedLengthRecord as a header +record + +|isFooter |boolean |*Camel 2.11* - optional - Identifies this FixedLengthRecords as a footer +record + +|ignoreTrailingChars |boolean |*Camel 2.11.1* - optional - Indicates that characters beyond the last +mapped filed can be ignored when unmarshalling / parsing. This annotation is associated to the root class of the model and must be +declared one time. +|======================================================================= + + +The hasHeader/hasFooter parameters are mutually exclusive with +isHeader/isFooter. A record may not be both a header/footer and a +primary fixed-length record. + +*case 1 : Simple fixed length record* + +This simple example shows how to design the model to parse/format a +fixed message + +10A9PaulineMISINXD12345678BUYShare2500.45USD01-08-2009 + +*Fixed-simple* + +[source,java] +--------------------------------------------------------------- + @FixedLengthRecord(length=54, paddingChar=' ') + public static class Order { + + @DataField(pos = 1, length=2) + private int orderNr; + + @DataField(pos = 3, length=2) + private String clientNr; + + @DataField(pos = 5, length=7) + private String firstName; + + @DataField(pos = 12, length=1, align="L") + private String lastName; + + @DataField(pos = 13, length=4) + private String instrumentCode; + + @DataField(pos = 17, length=10) + private String instrumentNumber; + + @DataField(pos = 27, length=3) + private String orderType; + + @DataField(pos = 30, length=5) + private String instrumentType; + + @DataField(pos = 35, precision = 2, length=7) + private BigDecimal amount; + + @DataField(pos = 42, length=3) + private String currency; + + @DataField(pos = 45, length=10, pattern = "dd-MM-yyyy") + private Date orderDate; + ... +--------------------------------------------------------------- + +*case 2 : Fixed length record with alignment and padding* + +This more elaborated example show how to define the alignment for a +field and how to assign a padding character which is ' ' here'' + +10A9 PaulineM ISINXD12345678BUYShare2500.45USD01-08-2009 + +*Fixed-padding-align* + +[source,java] +----------------------------------------------------------------------------------------------- + @FixedLengthRecord(length=60, paddingChar=' ') + public static class Order { + + @DataField(pos = 1, length=2) + private int orderNr; + + @DataField(pos = 3, length=2) + private String clientNr; + + @DataField(pos = 5, length=9) + private String firstName; + + @DataField(pos = 14, length=5, align="L") // align text to the LEFT zone of the block + private String lastName; + + @DataField(pos = 19, length=4) + private String instrumentCode; + + @DataField(pos = 23, length=10) + private String instrumentNumber; + + @DataField(pos = 33, length=3) + private String orderType; + + @DataField(pos = 36, length=5) + private String instrumentType; + + @DataField(pos = 41, precision = 2, length=7) + private BigDecimal amount; + + @DataField(pos = 48, length=3) + private String currency; + + @DataField(pos = 51, length=10, pattern = "dd-MM-yyyy") + private Date orderDate; + ... +----------------------------------------------------------------------------------------------- + +*case 3 : Field padding* + +Sometimes, the default padding defined for record cannnot be applied to +the field as we have a number format where we would like to padd with +'0' instead of ' '. In this case, you can use in the model the attribute +paddingField to set this value. + +10A9 PaulineM ISINXD12345678BUYShare000002500.45USD01-08-2009 + +*Fixed-padding-field* + +[source,java] +--------------------------------------------------------------------------- + @FixedLengthRecord(length = 65, paddingChar = ' ') + public static class Order { + + @DataField(pos = 1, length = 2) + private int orderNr; + + @DataField(pos = 3, length = 2) + private String clientNr; + + @DataField(pos = 5, length = 9) + private String firstName; + + @DataField(pos = 14, length = 5, align = "L") + private String lastName; + + @DataField(pos = 19, length = 4) + private String instrumentCode; + + @DataField(pos = 23, length = 10) + private String instrumentNumber; + + @DataField(pos = 33, length = 3) + private String orderType; + + @DataField(pos = 36, length = 5) + private String instrumentType; + + @DataField(pos = 41, precision = 2, length = 12, paddingChar = '0') + private BigDecimal amount; + + @DataField(pos = 53, length = 3) + private String currency; + + @DataField(pos = 56, length = 10, pattern = "dd-MM-yyyy") + private Date orderDate; + ... +--------------------------------------------------------------------------- + +*case 4: Fixed length record with delimiter* + +Fixed-length records sometimes have delimited content within the record. +The firstName and lastName fields are delimited with the '^' character +in the following example: + +10A9Pauline^M^ISINXD12345678BUYShare000002500.45USD01-08-2009 + +*Fixed-delimited* + +[source,java] +-------------------------------------------------------------------------- + @FixedLengthRecord() + public static class Order { + + @DataField(pos = 1, length = 2) + private int orderNr; + + @DataField(pos = 2, length = 2) + private String clientNr; + + @DataField(pos = 3, delimiter = "^") + private String firstName; + + @DataField(pos = 4, delimiter = "^") + private String lastName; + + @DataField(pos = 5, length = 4) + private String instrumentCode; + + @DataField(pos = 6, length = 10) + private String instrumentNumber; + + @DataField(pos = 7, length = 3) + private String orderType; + + @DataField(pos = 8, length = 5) + private String instrumentType; + + @DataField(pos = 9, precision = 2, length = 12, paddingChar = '0') + private BigDecimal amount; + + @DataField(pos = 10, length = 3) + private String currency; + + @DataField(pos = 11, length = 10, pattern = "dd-MM-yyyy") + private Date orderDate; +-------------------------------------------------------------------------- + +As of *Camel 2.11* the 'pos' value(s) in a fixed-length record may +optionally be defined using ordinal, sequential values instead of +precise column numbers. + +*case 5 : Fixed length record with record-defined field length* + +Occasionally a fixed-length record may contain a field that define the +expected length of another field within the same record. In the +following example the length of the instrumentNumber field value is +defined by the value of instrumentNumberLen field in the record. + +10A9Pauline^M^ISIN10XD12345678BUYShare000002500.45USD01-08-2009 + +*Fixed-delimited* + +[source,java] +--------------------------------------------------------------------------- + @FixedLengthRecord() + public static class Order { + + @DataField(pos = 1, length = 2) + private int orderNr; + + @DataField(pos = 2, length = 2) + private String clientNr; + + @DataField(pos = 3, delimiter = "^") + private String firstName; + + @DataField(pos = 4, delimiter = "^") + private String lastName; + + @DataField(pos = 5, length = 4) + private String instrumentCode; + + @DataField(pos = 6, length = 2, align = "R", paddingChar = '0') + private int instrumentNumberLen; + + @DataField(pos = 7, lengthPos=6) + private String instrumentNumber; + + @DataField(pos = 8, length = 3) + private String orderType; + + @DataField(pos = 9, length = 5) + private String instrumentType; + + @DataField(pos = 10, precision = 2, length = 12, paddingChar = '0') + private BigDecimal amount; + + @DataField(pos = 11, length = 3) + private String currency; + + @DataField(pos = 12, length = 10, pattern = "dd-MM-yyyy") + private Date orderDate; +--------------------------------------------------------------------------- + +*case 6 : Fixed length record with header and footer* + +Bindy will discover fixed-length header and footer records that are +configured as part of the model – provided that the annotated classes +exist either in the same package as the primary @FixedLengthRecord +class, or within one of the configured scan packages. The following text +illustrates two fixed-length records that are bracketed by a header +record and footer record. + +101-08-2009 + + 10A9 PaulineM ISINXD12345678BUYShare000002500.45USD01-08-2009 + + 10A9 RichN ISINXD12345678BUYShare000002700.45USD01-08-2009 + + 9000000002 + +*Fixed-header-and-footer-main-class* + +[source,java] +---------------------------------------------------------------------- +@FixedLengthRecord(hasHeader = true, hasFooter = true) +public class Order { + + @DataField(pos = 1, length = 2) + private int orderNr; + + @DataField(pos = 2, length = 2) + private String clientNr; + + @DataField(pos = 3, length = 9) + private String firstName; + + @DataField(pos = 4, length = 5, align = "L") + private String lastName; + + @DataField(pos = 5, length = 4) + private String instrumentCode; + + @DataField(pos = 6, length = 10) + private String instrumentNumber; + + @DataField(pos = 7, length = 3) + private String orderType; + + @DataField(pos = 8, length = 5) + private String instrumentType; + + @DataField(pos = 9, precision = 2, length = 12, paddingChar = '0') + private BigDecimal amount; + + @DataField(pos = 10, length = 3) + private String currency; + + @DataField(pos = 11, length = 10, pattern = "dd-MM-yyyy") + private Date orderDate; +... +} + + +@FixedLengthRecord(isHeader = true) +public class OrderHeader { + @DataField(pos = 1, length = 1) + private int recordType = 1; + + @DataField(pos = 2, length = 10, pattern = "dd-MM-yyyy") + private Date recordDate; + +... +} + + +@FixedLengthRecord(isFooter = true) +public class OrderFooter { + + @DataField(pos = 1, length = 1) + private int recordType = 9; + + @DataField(pos = 2, length = 9, align = "R", paddingChar = '0') + private int numberOfRecordsInTheFile; + +... +} +---------------------------------------------------------------------- + +*case 7 : Skipping content when parsing a fixed length record. (Camel +2.11.1)* + +It is common to integrate with systems that provide fixed-length records +containing more information than needed for the target use case. It is +useful in this situation to skip the declaration and parsing of those +fields that we do not need. To accomodate this, Bindy will skip forward +to the next mapped field within a record if the 'pos' value of the next +declared field is beyond the cursor position of the last parsed field. +Using absolute 'pos' locations for the fields of interest (instead of +ordinal values) causes Bindy to skip content between two fields. + +Similarly, it is possible that none of the content beyond some field is +of interest. In this case, you can tell Bindy to skip parsing of +everything beyond the last mapped field by setting the +*ignoreTrailingChars* property on the @FixedLengthRecord declaration. + +[source,java] +------------------------------------------------------------------------------- +@FixedLengthRecord(ignoreTrailingChars = true) +public static class Order { + + @DataField(pos = 1, length = 2) + private int orderNr; + + @DataField(pos = 3, length = 2) + private String clientNr; + + ... any characters that appear beyond the last mapped field will be ignored + +} +------------------------------------------------------------------------------- + +[[Bindy-5.Message]] +5. Message +^^^^^^^^^^ + +The Message annotation is used to identified the class of your model who +will contain key value pairs fields. This kind of format is used mainly +in Financial Exchange Protocol Messages (FIX). Nevertheless, this +annotation can be used for any other format where data are identified by +keys. The key pair values are separated each other by a separator which +can be a special character like a tab delimitor (unicode representation +: \u0009) or a start of heading (unicode representation : \u0001) + + *"FIX information"* + +More information about FIX can be found on this web site : +http://www.fixprotocol.org/[http://www.fixprotocol.org/]. To work with +FIX messages, the model must contain a Header and Trailer classes linked +to the root message class which could be a Order class. This is not +mandatory but will be very helpful when you will use camel-bindy in +combination with camel-fix which is a Fix gateway based on quickFix +project http://www.quickfixj.org/[http://www.quickfixj.org/]. + +[width="100%",cols="10%,10%,80%",options="header",] +|======================================================================= +|Annotation name |Record type |Level + +|*Message* |key value pair |Class +|======================================================================= + +[width="100%",cols="10%,10%,80%",options="header",] +|======================================================================= +|Parameter name |type |Info + +|pairSeparator |string |mandatory - can be '=' or ';' or 'anything' + +|keyValuePairSeparair |string |mandatory - can be '\u0001', '\u0009', '#' or 'anything' + +|crlf |string |optional - possible values = WINDOWS,UNIX,MAC, or custom; default value += WINDOWS - allow to define the carriage return character to use. If you +specify a value other than the three listed before, the value you enter +(custom) will be used as the CRLF character(s) + +|type |string |optional - define the type of message (e.g. FIX, EMX, ...) + +|version |string |optional - version of the message (e.g. 4.1) + +|isOrdered |boolean |optional - default value = false - allow to change the order of the +fields when FIX message is generated. This annotation is associated to the message class of the model and must +be declared one time. +|======================================================================= + +*case 1 : separator = 'u0001'* + +The separator used to segregate the key value pair fields in a FIX +message is the ASCII '01' character or in unicode format '\u0001'. This +character must be escaped a second time to avoid a java runtime error. +Here is an example : + +8=FIX.4.1 9=20 34=1 35=0 49=INVMGR 56=BRKR 1=BE.CHM.001 11=CHM0001-01 +22=4 ... + +and how to use the annotation + +*FIX - message* + +[source,java] +------------------------------------------------------------------------------------------ +@Message(keyValuePairSeparator = "=", pairSeparator = "\u0001", type="FIX", version="4.1") +public class Order { +... +} +------------------------------------------------------------------------------------------ + + *Look at test cases* + +The ASCII character like tab, ... cannot be displayed in WIKI page. So, +have a look to the test case of camel-bindy to see exactly how the FIX +message looks like (src\test\data\fix\fix.txt) and the Order, Trailer, +Header classes +(src\test\java\org\apache\camel\dataformat\bindy\model\fix\simple\Order.java) + +[[Bindy-6.KeyValuePairField]] +6. KeyValuePairField +^^^^^^^^^^^^^^^^^^^^ + +The KeyValuePairField annotation defines the property of a key value +pair field. Each KeyValuePairField is identified by a tag (= key) and +its value associated, a type (string, int, date, ...), optionaly a +pattern and if the field is required + +[width="100%",cols="10%,10%,80%",options="header",] +|======================================================================= +|Annotation name |Record type |Level + +|*KeyValuePairField* |Key Value Pair - FIX |Property +|======================================================================= + +[width="100%",cols="10%,10%,80%",options="header",] +|======================================================================= +|Parameter name |type |Info + +|tag |int |mandatory - digit number identifying the field in the message - must be +unique + +|pattern |string |optional - default value = "" - will be used to format Decimal, Date, +... + +|precision |int |optional - digit number - represents the precision to be used when the +Decimal number will be formatted/parsed + +|position |int |optional - must be used when the position of the key/tag in the FIX +message must be different + +|required |boolean |optional - default value = "false" + +|impliedDecimalSeparator |boolean |*Camel 2.11:* optional - default value = "false" - Indicates if there is +a decimal point implied at a specified location +|======================================================================= + +*case 1 : tag* + +This parameter represents the key of the field in the message + +*FIX message - Tag* + +[source,java] +------------------------------------------------------------------------------------------ +@Message(keyValuePairSeparator = "=", pairSeparator = "\u0001", type="FIX", version="4.1") +public class Order { + + @Link Header header; + + @Link Trailer trailer; + + @KeyValuePairField(tag = 1) // Client reference + private String Account; + + @KeyValuePairField(tag = 11) // Order reference + private String ClOrdId; + + @KeyValuePairField(tag = 22) // Fund ID type (Sedol, ISIN, ...) + private String IDSource; + + @KeyValuePairField(tag = 48) // Fund code + private String SecurityId; + + @KeyValuePairField(tag = 54) // Movement type ( 1 = Buy, 2 = sell) + private String Side; + + @KeyValuePairField(tag = 58) // Free text + private String Text; + +... +} +------------------------------------------------------------------------------------------ + +*case 2 : Different position in output* + +If the tags/keys that we will put in the FIX message must be sorted +according to a predefine order, then use the attribute 'position' of the +annotation @KeyValuePairField + +*FIX message - Tag - sort* + +[source,java] +----------------------------------------------------------------------------------------------------------------- +@Message(keyValuePairSeparator = "=", pairSeparator = "\\u0001", type = "FIX", version = "4.1", isOrdered = true) +public class Order { + + @Link Header header; + + @Link Trailer trailer; + + @KeyValuePairField(tag = 1, position = 1) // Client reference + private String account; + + @KeyValuePairField(tag = 11, position = 3) // Order reference + private String clOrdId; + +... +} +----------------------------------------------------------------------------------------------------------------- + +[[Bindy-7.Section]] +7. Section +^^^^^^^^^^ + +In FIX message of fixed length records, it is common to have different +sections in the representation of the information : header, body and +section. The purpose of the annotation @Section is to inform bindy about +which class of the model represents the header (= section 1), body (= +section 2) and footer (= section 3) + +Only one attribute/parameter exists for this annotation. + +[width="100%",cols="10%,10%,80%",options="header",] +|======================================================================= +|Annotation name |Record type |Level + +|*Section* |FIX |Class +|======================================================================= + +[width="100%",cols="10%,10%,80%",options="header",] +|======================================================================= +|Parameter name |type |Info + +|number |int |digit number identifying the section position +|======================================================================= + +*case 1 : Section* + +A. Definition of the header section + +*FIX message - Section - Header* + +[source,java] +--------------------------------------------------------------- +@Section(number = 1) +public class Header { + + @KeyValuePairField(tag = 8, position = 1) // Message Header + private String beginString; + + @KeyValuePairField(tag = 9, position = 2) // Checksum + private int bodyLength; +... +} +--------------------------------------------------------------- + +B. Definition of the body section + +*FIX message - Section - Body* + +[source,java] +----------------------------------------------------------------------------------------------------------------- +@Section(number = 2) +@Message(keyValuePairSeparator = "=", pairSeparator = "\\u0001", type = "FIX", version = "4.1", isOrdered = true) +public class Order { + + @Link Header header; + + @Link Trailer trailer; + + @KeyValuePairField(tag = 1, position = 1) // Client reference + private String account; + + @KeyValuePairField(tag = 11, position = 3) // Order reference + private String clOrdId; +----------------------------------------------------------------------------------------------------------------- + +C. Definition of the footer section + +*FIX message - Section - Footer* + +[source,java] +---------------------------------------------- +@Section(number = 3) +public class Trailer { + + @KeyValuePairField(tag = 10, position = 1) + // CheckSum + private int checkSum; + + public int getCheckSum() { + return checkSum; + } +---------------------------------------------- + +[[Bindy-8.OneToMany]] +8. OneToMany +^^^^^^^^^^^^ + +The purpose of the annotation @OneToMany is to allow to work with a +List field defined a POJO class or from a record containing +repetitive groups. + + *Restrictions OneToMany* + +Be careful, the one to many of bindy does not allow to handle +repetitions defined on several levels of the hierarchy + +The relation OneToMany ONLY WORKS in the following cases : + +* Reading a FIX message containing repetitive groups (= group of +tags/keys) +* Generating a CSV with repetitive data + +[width="100%",cols="10%,10%,80%",options="header",] +|======================================================================= +|Annotation name |Record type |Level + +|*OneToMany* |all |property +|======================================================================= + +[width="100%",cols="10%,10%,80%",options="header",] +|======================================================================= +|Parameter name |type |Info + +|mappedTo |string |optional - string - class name associated to the type of the List +|======================================================================= + +*case 1 : Generating CSV with repetitive data* + +Here is the CSV output that we want : + +Claus,Ibsen,Camel in Action 1,2010,35 + + Claus,Ibsen,Camel in Action 2,2012,35 + + Claus,Ibsen,Camel in Action 3,2013,35 + + Claus,Ibsen,Camel in Action 4,2014,35 + +Remark : the repetitive data concern the title of the book and its +publication date while first, last name and age are common + +and the classes used to modeling this. The Author class contains a List +of Book. + +*Generate CSV with repetitive data* + +[source,java] +----------------------------- +@CsvRecord(separator=",") +public class Author { + + @DataField(pos = 1) + private String firstName; + + @DataField(pos = 2) + private String lastName; + + @OneToMany + private List books; + + @DataField(pos = 5) + private String Age; +... + + +public class Book { + + @DataField(pos = 3) + private String title; + + @DataField(pos = 4) + private String year; +----------------------------- + +Very simple isn't it !!! + +*case 2 : Reading FIX message containing group of tags/keys* + +Here is the message that we would like to process in our model : + +"8=FIX 4.19=2034=135=049=INVMGR56=BRKR" + + "1=BE.CHM.00111=CHM0001-0158=this is a camel - bindy test" + + "22=448=BE000124567854=1" + + "22=548=BE000987654354=2" + + "22=648=BE000999999954=3" + + "10=220" + +tags 22, 48 and 54 are repeated + +and the code + +*Reading FIX message containing group of tags/keys* + +[source,java] +--------------------------------------------------------------------------------------------------- +public class Order { + + @Link Header header; + + @Link Trailer trailer; + + @KeyValuePairField(tag = 1) // Client reference + private String account; + + @KeyValuePairField(tag = 11) // Order reference + private String clOrdId; + + @KeyValuePairField(tag = 58) // Free text + private String text; + + @OneToMany(mappedTo = "org.apache.camel.dataformat.bindy.model.fix.complex.onetomany.Security") + List securities; +... + +public class Security { + + @KeyValuePairField(tag = 22) // Fund ID type (Sedol, ISIN, ...) + private String idSource; + + @KeyValuePairField(tag = 48) // Fund code + private String securityCode; + + @KeyValuePairField(tag = 54) // Movement type ( 1 = Buy, 2 = sell) + private String side; +--------------------------------------------------------------------------------------------------- + +[[Bindy-UsingtheJavaDSL]] +Using the Java DSL +^^^^^^^^^^^^^^^^^^ + +The next step consists in instantiating the DataFormat _bindy_ class +associated with this record type and providing Java package name(s) as +parameter. + +For example the following uses the class `BindyCsvDataFormat` (who +correspond to the class associated with the CSV record type) which is +configured with "com.acme.model" + + package name to initialize the model objects configured in this +package. + +[source,java] +------------------------------------------------------------------------ +// Camel 2.15 or older (configure by package name) +DataFormat bindy = new BindyCsvDataFormat("com.acme.model"); + +  +// Camel 2.16 onwards (configure by class name) +DataFormat bindy = new BindyCsvDataFormat(com.acme.model.MyModel.class); +------------------------------------------------------------------------ + +[[Bindy-Settinglocale]] +Setting locale +++++++++++++++ + +Bindy supports configuring the locale on the dataformat, such as  + +[source,java] +-------------------------------------------------------------------------------- +// Camel 2.15 or older (configure by package name) +BindyCsvDataFormat bindy = new BindyCsvDataFormat("com.acme.model"); +// Camel 2.16 onwards (configure by class name) +BindyCsvDataFormat bindy = new BindyCsvDataFormat(com.acme.model.MyModel.class); + +bindy.setLocale("us"); +-------------------------------------------------------------------------------- + +Or to use the platform default locale then use "default" as the locale +name. Notice this requires Camel 2.14/2.13.3/2.12.5. + +[source,java] +-------------------------------------------------------------------------------- +// Camel 2.15 or older (configure by package name) +BindyCsvDataFormat bindy = new BindyCsvDataFormat("com.acme.model"); +// Camel 2.16 onwards (configure by class name) +BindyCsvDataFormat bindy = new BindyCsvDataFormat(com.acme.model.MyModel.class); + +bindy.setLocale("default"); +-------------------------------------------------------------------------------- + +for older releases you can set it using Java code as shown + +[source,java] +-------------------------------------------------------------------------------- +// Camel 2.15 or older (configure by package name) +BindyCsvDataFormat bindy = new BindyCsvDataFormat("com.acme.model"); +// Camel 2.16 onwards (configure by class name) +BindyCsvDataFormat bindy = new BindyCsvDataFormat(com.acme.model.MyModel.class); + + +bindy.setLocale(Locale.getDefault().getISO3Country()); +-------------------------------------------------------------------------------- + +[[Bindy-Unmarshaling]] +Unmarshaling +++++++++++++ + +[source,java] +----------------------------- +from("file://inbox") + .unmarshal(bindy) + .to("direct:handleOrders"); +----------------------------- + +Alternatively, you can use a named reference to a data format which can +then be defined in your link:registry.html[Registry] e.g. your +link:spring.html[Spring] XML file: + +[source,java] +--------------------------------- +from("file://inbox") + .unmarshal("myBindyDataFormat") + .to("direct:handleOrders"); +--------------------------------- + +The Camel route will pick-up files in the inbox directory, unmarshall +CSV records into a collection of model objects and send the collection + + to the route referenced by 'handleOrders'. + +The collection returned is a *List of Map* objects. Each Map within the +list contains the model objects that were marshalled out of each line of +the CSV. The reason behind this is that _each line can correspond to +more than one object_. This can be confusing when you simply expect one +object to be returned per line. + +Each object can be retrieve using its class name. + +[source,java] +--------------------------------------------------------------------------------------------------------- + List> unmarshaledModels = (List>) exchange.getIn().getBody(); + + int modelCount = 0; + for (Map model : unmarshaledModels) { + for (String className : model.keySet()) { + Object obj = model.get(className); + LOG.info("Count : " + modelCount + ", " + obj.toString()); + } + modelCount++; + } + + LOG.info("Total CSV records received by the csv bean : " + modelCount); +--------------------------------------------------------------------------------------------------------- + +Assuming that you want to extract a single Order object from this map +for processing in a route, you could use a combination of a +link:splitter.html[Splitter] and a link:processor.html[Processor] as per +the following: + +[source,java] +---------------------------------------------------------------------------------- +from("file://inbox") + .unmarshal(bindy) + .split(body()) + .process(new Processor() { + public void process(Exchange exchange) throws Exception { + Message in = exchange.getIn(); + Map modelMap = (Map) in.getBody(); + in.setBody(modelMap.get(Order.class.getCanonicalName())); + } + }) + .to("direct:handleSingleOrder") + .end(); +---------------------------------------------------------------------------------- + +[[Bindy-Marshaling]] +Marshaling +++++++++++ + +To generate CSV records from a collection of model objects, you create +the following route : + +[source,java] +--------------------------- +from("direct:handleOrders") + .marshal(bindy) + .to("file://outbox") +--------------------------- + +[[Bindy-UsingSpringXML]] +Using Spring XML +^^^^^^^^^^^^^^^^ + +This is really easy to use Spring as your favorite DSL language to +declare the routes to be used for camel-bindy. The following example +shows two routes where the first will pick-up records from files, +unmarshal the content and bind it to their model. The result is then +send to a pojo (doing nothing special) and place them into a queue. + +The second route will extract the pojos from the queue and marshal the +content to generate a file containing the csv record. The example above +is for using Camel 2.16 onwards. + +*spring dsl* + +[source,java] +------------------------------------------------------------------------------------------------- + + + + + + + + + + + + +  + + + + + + + + + + + + + + + + + + +------------------------------------------------------------------------------------------------- + +[Note] +==== + *Be careful* + +Please verify that your model classes implements serializable otherwise +the queue manager will raise an error + +==== + +[[Bindy-Dependencies]] +Dependencies +^^^^^^^^^^^^ + +To use Bindy in your camel routes you need to add the a dependency on +*camel-bindy* which implements this data format. + +If you use maven you could just add the following to your pom.xml, +substituting the version number for the latest & greatest release (see +link:download.html[the download page for the latest versions]). + +[source,java] +-------------------------------------- + + org.apache.camel + camel-bindy + x.x.x + +-------------------------------------- http://git-wip-us.apache.org/repos/asf/camel/blob/5af2c600/docs/user-manual/en/SUMMARY.md ---------------------------------------------------------------------- diff --git a/docs/user-manual/en/SUMMARY.md b/docs/user-manual/en/SUMMARY.md index 7fced91..face150 100644 --- a/docs/user-manual/en/SUMMARY.md +++ b/docs/user-manual/en/SUMMARY.md @@ -94,6 +94,7 @@ * [BeanIO](beanio.adoc) * [Beanstalk](beanstalk.adoc) * [Bean-validator](bean-validator.adoc) + * [Bindy](bindy.adoc) * [CDI](cdi.adoc) * [JMS](jms.adoc) * [Metrics](metrics.adoc)