diff --git a/src/main/java/com/solab/iso8583/MessageFactory.java b/src/main/java/com/solab/iso8583/MessageFactory.java index 950fd71..8cb0c40 100644 --- a/src/main/java/com/solab/iso8583/MessageFactory.java +++ b/src/main/java/com/solab/iso8583/MessageFactory.java @@ -178,6 +178,8 @@ public class MessageFactory { if (templ != null) { for (int i = 2; i <= 128; i++) { if (templ.hasField(i)) { + //We could detect here if there's a custom object with a CustomField, + //but we can't copy the value so there's no point. m.setField(i, templ.getField(i).clone()); } } diff --git a/src/main/java/com/solab/iso8583/parse/ConfigParser.java b/src/main/java/com/solab/iso8583/parse/ConfigParser.java index 7ab4b9a..2144b98 100644 --- a/src/main/java/com/solab/iso8583/parse/ConfigParser.java +++ b/src/main/java/com/solab/iso8583/parse/ConfigParser.java @@ -64,9 +64,7 @@ public class ConfigParser { InputStream ins = MessageFactory.class.getClassLoader().getResourceAsStream(path); MessageFactory mfact = new MessageFactory(); if (ins != null) { - if (log.isDebugEnabled()) { - log.debug("ISO8583 Parsing config from classpath file {}", path); - } + log.debug("ISO8583 Parsing config from classpath file {}", path); try { parse(mfact, ins); } finally { @@ -207,9 +205,7 @@ public class ConfigParser { public static void configureFromClasspathConfig(MessageFactory mfact, String path) throws IOException { InputStream ins = MessageFactory.class.getClassLoader().getResourceAsStream(path); if (ins != null) { - if (log.isDebugEnabled()) { - log.debug("ISO8583 Parsing config from classpath file {}", path); - } + log.debug("ISO8583 Parsing config from classpath file {}", path); try { parse(mfact, ins); } finally { diff --git a/src/site/apt/encoders.apt b/src/site/apt/encoders.apt new file mode 100644 index 0000000..86ad5be --- /dev/null +++ b/src/site/apt/encoders.apt @@ -0,0 +1,75 @@ + ----- + Custom Field Encoders + ----- + +Using Custom Field Encoders + + Sometimes there are fields that contain several sub-fields or separate pieces of data. j8583 will only parse + the field for you, but you still have to parse those pieces of data from the field when you parse a message, + and/or encode several pieces of data into a field when creating a message. + + j8583 can help with this process, by means of the custom field encoders. To use this feature, first you need + to implement the <<>> interface. Let's say for example that you want to handle a list of strings + inside a LLLVAR field. So you need to implement an encoder that will transform a list into a string and decode + a List from a string: + ++----- +public class ListEncoder implements CustomField> { + + //Transform a List of Strings into a String + public String encodeField(List value) { + StringBuilder sb = new StringBuilder(); + for (String s: value) { + sb.append(s).append(','); + } + sb.deleteCharAt(sb.length()-1); + return sb.toString(); + } + + //Transform a String into a List of Strings + public List decodeField(String value) { + return Arrays.asList(value.split(",")); + } + +} ++----- + + Now that you have your field encoder, you need to pass it to your MessageFactory, indicating the field in which + you want to use it, for example field 62. After this, when your message factory parses a message, the resulting + IsoMessage will contain a list of strings in field 62, and you can pass new messages directly a list into that + field, and it will be encoded into a String when writing the message to a stream or creating the byte array with + the ISO8583 data. + ++----- + messageFactory.setCustomField(62, new ListEncoder()); + IsoMessage parsed = messageFactory.parse(someBytes, 0); + assert parsed.getObjectValue(62) instanceof List + IsoMessage nm = messageFactory.newMessage(0x200); + nm.setValue(62, Arrays.asList("a", "b", "c", "d", "e"), IsoType.LLLVAR, 0); + //when nm gets written, field 62 will be "009a,b,c,d,e" ++----- + + If you setup CustomField encoders in a MessageFactory and then you configure it using the XML configuration file, + the values for fields that have CustomField encoders defined will be decoded with them. In this example, if you + use configure the MessageFactory after setting up the ListEncoder for field 62, then a message template containing + a value for field 62 will have the field decoded as a List of Strings. + + <> If you use a CustomField to handle a custom value object which you have in a message template, + you should make your custom object <<>>, and then manually replace the value in your message obtained from + the factory with a new copy. Otherwise, all the messages that the factory creates from that template will have + a references to , and that means trouble if the object's values are modified (basically + you have several IsoMessages sharing state in that field). So you should do something like this: + ++----- +IsoMessage myIso = messageFactory.newMessage(0x200); +//Let's suppose we have some custom object with its CustomField encoder for field 126 +//Our class will have a copy() method or something similar +MyCustomClass customField = ((MyCustomClass)myIso.getObjectValue(126)).copy(); +//Now you can modify this instance +customField.setSomeValue("blabla"); +myIso.setValue(126, customField, IsoType.LLLVAR, 0); ++----- + + Currently j8583 cannot perform this copy operation for you; even if the MessageFactory can detect if your custom + class implements Cloneable, it can't invoke the <<>> method because it's protected and it's not guaranteed + that your implementation will only have made it public. diff --git a/src/site/apt/guide.apt b/src/site/apt/guide.apt index 7d96482..083851e 100644 --- a/src/site/apt/guide.apt +++ b/src/site/apt/guide.apt @@ -76,6 +76,7 @@ Usage Guide CustomField for every field where you want to store custom values, so that parsed messages will return the objects decoded by the CustomFields instead of just strings; and when you set a value in an IsoMessage, you can specify the CustomField to be used to encode the value as a String. + {{{encoders.html}Learn more about custom field encoders}}. * XML configuration diff --git a/src/site/es/apt/encoders.apt b/src/site/es/apt/encoders.apt new file mode 100644 index 0000000..dc88dad --- /dev/null +++ b/src/site/es/apt/encoders.apt @@ -0,0 +1,77 @@ + ----- + Codificadores de campos personalizados + ----- + +Uso de los codificadores de campos personalizados + + En ocasiones hay campos que contienen varios sub-campos, o al menos varios datos distintos codificados en uno solo. + j8583 solamente maneja el campo completo, pero todavía falta separar esos datos al leer un mensaje, y/o codificarlos + al generar mensajes nuevos. + + j8583 puede ayudar con este proceso, por medio de los codificadores de campos personalizados. Para usar esta + característica, lo primero que hay que hacer es implementar la interfaz <<>>. Digamos por ejemplo + que se tiene que manejar una lista de cadenas dentro de un campo LLLVAR; entonces hay que implementar un + codificador que haga la transformación de lista a cadena y también la inversa, de cadena a lista: + ++----- +public class ListEncoder implements CustomField> { + + //Transforma una lista de caadenas en una sola cadena separada por comas + public String encodeField(List value) { + StringBuilder sb = new StringBuilder(); + for (String s: value) { + sb.append(s).append(','); + } + sb.deleteCharAt(sb.length()-1); + return sb.toString(); + } + + //Separa la cadena por las comas y devuelve una lista + public List decodeField(String value) { + return Arrays.asList(value.split(",")); + } + +} ++----- + + Una vez que se tiene el codificador, hay que pasárselo a la fábrica de mensajes, indicando el campo en donde se + tiene que usar. Pongámoslo para este ejemplo en el campo 62; después de esto, cuando la fábrica de mensajes + genere mensajes a partir de arreglos de bytes, el IsoMessage resultante tendrá una lista de cadenas en el campo + 62, y al crear nuevos mensajes se pueden pasar listas de cadenas directamente al campo 62 y serán codificadas + como una cadena al generarse la trama ISO8583. + ++----- + messageFactory.setCustomField(62, new ListEncoder()); + IsoMessage parsed = messageFactory.parse(unosBytes, 0); + assert parsed.getObjectValue(62) instanceof List + IsoMessage nm = messageFactory.newMessage(0x200); + nm.setValue(62, Arrays.asList("a", "b", "c", "d", "e"), IsoType.LLLVAR, 0); + //cuando se genere la trama de nm, el campo 62 será "009a,b,c,d,e" ++----- + + Si se configuran objetos CustomField en una fábrica de mensajes y después se configura con un archivo XML, + los CustomField se usarán para decodificar los campos de las plantillas de mensaje que contengan valores en esas + posiciones. Siguiendo el ejemplo, si se configura esta fábrica de mensajes después de haberle pasado el ListEncoder, + entonces las plantillas de mensaje que tengan valor para el campo 62 contendrán una lista de cadenas en vez + de una simple cadena. + + <> Si se usa un CustomField para manejar objetos de valor propios, en plantillas de mensajes, + es necesario que dichos objetos implementen <<>> y tengan un método para poder obtener una copia; + esto para reemplazar manualmente los valores en los mensajes nuevos, con copias frescas que se puedan modificar + con valores distintos en cada mensaje. De otra forma, todos los mensajes creados por la fábrica tendrán referencia + al mismo objeto en ese campo, lo cual causará problemas cuando se modifiquen los valores del objeto, ya que + básicamente se trata de varios IsoMessages compartiendo estado en ese campo. Por lo tanto hay que hacer algo así: + ++----- +IsoMessage myIso = messageFactory.newMessage(0x200); +//Supongamos que tenemos un objeto con su CustomField para el campo 126 +//La clase que se usa ahí debe tener un método copy() o algo similar +MiClase customField = ((MiClase)myIso.getObjectValue(126)).copy(); +//Ahora se puede modificar esta instancia sin afectar otros mensajes +customField.setSomeValue("blabla"); +myIso.setValue(126, customField, IsoType.LLLVAR, 0); ++----- + + Por el momento, j8583 no puede realizar esta operación internamente; aunque la MessageFactory pueda detectar + que la clase contenida en el campo implementa la interfaz <<>>, no puede invocar el método <<>> + porque es protegido, y no hay garantía de que la clase lo haya reimplementado como público. diff --git a/src/site/es/apt/guide.apt b/src/site/es/apt/guide.apt index b8f0878..44027f9 100644 --- a/src/site/es/apt/guide.apt +++ b/src/site/es/apt/guide.apt @@ -85,7 +85,7 @@ Guía del usuario CustomField a la fábrica de mensajes por cada campo en el cual se necesitan manejar valores especiales, para que los mensajes leídos de tramas ya contengan el valor convertido en el campo. Y cuando se fija un valor en un IsoMessage, se le puede pasar el CustomField para que sepa codificar dicho valor al momento de generar - su trama. + su trama. {{{encoders.html}Más información acerca de los codificadores de campos}}. * Configuración por XML