Add documentation for the problem with CustomField encoders for mutable value objects, since we can't really do anything about it.
git-svn-id: https://j8583.svn.sourceforge.net/svnroot/j8583/j8583@73 db60a039-f72e-0410-b1d2-baa000846441
This commit is contained in:
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 <<<CustomField>>> 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<List<String>> {
|
||||
|
||||
//Transform a List of Strings into a String
|
||||
public String encodeField(List<String> 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<String> 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.
|
||||
|
||||
<<IMPORTANT:>> If you use a CustomField to handle a custom value object which you have in a message template,
|
||||
you should make your custom object <<<Cloneable>>>, 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 <the same value object>, 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 <<<clone()>>> method because it's protected and it's not guaranteed
|
||||
that your implementation will only have made it public.
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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 <<<CustomField>>>. 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<List<String>> {
|
||||
|
||||
//Transforma una lista de caadenas en una sola cadena separada por comas
|
||||
public String encodeField(List<String> 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<String> 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.
|
||||
|
||||
<<IMPORTANTE:>> Si se usa un CustomField para manejar objetos de valor propios, en plantillas de mensajes,
|
||||
es necesario que dichos objetos implementen <<<Cloneable>>> 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 <<<Cloneable>>>, no puede invocar el método <<<clone()>>>
|
||||
porque es protegido, y no hay garantía de que la clase lo haya reimplementado como público.
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user