jPOS API文档


jPOS Programmer's Guide Version: 1.6.1 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) Copyright © 2008 by Alejandro Revilla ............................................................................................. v Foreword ........................................................................................................................................vi 1. The jPOS Project ........................................................................................................................1 1.1. About jPOS.org .................................................................................................................. 1 1.2. jPOS License .....................................................................................................................1 1.3. Downloading jPOS ............................................................................................................. 1 1.4. Directory Structure ............................................................................................................. 2 1.5. Building jPOS .................................................................................................................... 4 2. An ISO-8583 primer ....................................................................................................................7 2.1. International standard ISO 8583 .......................................................................................... 7 2.2. Message structure ............................................................................................................... 7 2.2.1. ISO-8583 fields ....................................................................................................... 9 2.3. Wire protocol ................................................................................................................... 12 2.4. Message flow ................................................................................................................... 13 3. jPOS approach to ISO-8583 ...................................................................................................... 16 3.1. ISOMsg & Co. ................................................................................................................. 16 3.2. Packing and unpacking ..................................................................................................... 18 3.3. Creating custom packagers ............................................................................................... 20 3.4. Managing the wire protocol with ISOChannel .................................................................... 22 3.4.1. Filtered Channels .................................................................................................. 26 3.5. Accepting connections with ISOServer .............................................................................. 27 3.6. Multiplexing an ISOChannel with a MUX ......................................................................... 30 4. jPOS supporting classes ............................................................................................................ 34 4.1. jPOS' Logger ................................................................................................................... 34 4.2. NameRegistrar ................................................................................................................. 37 4.3. Configuration ................................................................................................................... 39 4.4. SystemMonitor ................................................................................................................ 40 4.5. Profiler ............................................................................................................................ 42 4.6. DirPoll ............................................................................................................................ 43 4.7. ThreadPool ...................................................................................................................... 45 5. Implementing Custom Packagers .............................................................................................. 46 5.1. GenericPackager .............................................................................................................. 47 6. Channel Implementations ......................................................................................................... 51 6.1. TCP/IP Socket-based channels .......................................................................................... 51 6.2. LoopbackChannel ............................................................................................................ 53 6.3. ChannelPool .................................................................................................................... 54 6.4. SSL ................................................................................................................................. 54 7. jPOS Space ............................................................................................................................... 56 7.1. Overview ......................................................................................................................... 56 7.2. SpaceFactory ................................................................................................................... 57 7.3. Using the space ................................................................................................................ 58 7.4. SpaceTap ......................................................................................................................... 60 8. Q2 - second generation QSP ...................................................................................................... 62 8.1. About Q2 ......................................................................................................................... 62 8.2. Building Q2 ..................................................................................................................... 62 8.3. Building Q2 Modules ....................................................................................................... 62 8.4. Q2 Primer ........................................................................................................................ 62 8.5. Q2 Dynamic ClassLoading ............................................................................................... 71 8.6. Q2 Scripts ........................................................................................................................ 72 8.7. Q2 SpaceLet .................................................................................................................... 73 9. Q2Mod jPOS ............................................................................................................................. 75 9.1. ChannelAdaptor ............................................................................................................... 75 1.6.1 ii The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) 9.2. QMUX ............................................................................................................................ 78 9.3. QServer ........................................................................................................................... 79 9.4. DirPoll ............................................................................................................................ 80 9.5. TaskAdaptor .................................................................................................................... 81 9.6. DailyTaskAdaptor ............................................................................................................ 81 9.7. SMAdaptor ...................................................................................................................... 81 9.8. KeyStoreAdaptor ............................................................................................................. 82 9.9. QExec ............................................................................................................................. 82 9.10. Jetty Integration ............................................................................................................. 82 10. TransactionManager ............................................................................................................... 83 10.1. Overview ....................................................................................................................... 83 10.2. Transaction Constants ..................................................................................................... 83 10.3. Transaction Context ....................................................................................................... 84 10.4. Context Recovery interface ............................................................................................. 85 10.5. AbortParticipant ............................................................................................................. 85 10.6. TransactionManager ....................................................................................................... 86 10.7. Integration with ISORequestListener ............................................................................... 87 10.8. GroupSelector ................................................................................................................ 87 10.9. Context implementation .................................................................................................. 89 11. UI Framework ......................................................................................................................... 93 11.1. Overview ....................................................................................................................... 93 11.2. UI configuration ............................................................................................................. 94 11.3. menubar ......................................................................................................................... 95 11.4. UI Factories ................................................................................................................... 97 11.4.1. JLabelFactory .................................................................................................... 100 11.4.2. JButtonFactory .................................................................................................. 101 11.4.3. TextFactory ....................................................................................................... 101 11.4.4. HtmlFactory ...................................................................................................... 102 11.4.5. PanelFactory ..................................................................................................... 103 11.4.6. BorderLayoutFactory ......................................................................................... 103 11.4.7. GridLayoutFactory ............................................................................................ 104 11.4.8. HSplitFactory .................................................................................................... 104 11.4.9. VSplitFactory .................................................................................................... 105 11.4.10. JTabbedPaneFactory ........................................................................................ 105 11.4.11. JTreeFactory ................................................................................................... 106 11.4.12. LogListenerFactory .......................................................................................... 107 11.4.13. ISOMeterFactory ............................................................................................. 109 12. Filter Implementations .......................................................................................................... 112 12.1. MD5Filter .................................................................................................................... 112 12.2. MacroFilter .................................................................................................................. 113 12.3. XSLTFilter .................................................................................................................. 114 13. Legacy components ............................................................................................................... 117 13.1. QSP - jPOS' component assembler ................................................................................ 117 13.1.1. About QSP ........................................................................................................ 117 13.1.2. Installing QSP ................................................................................................... 118 13.1.3. Configuration .................................................................................................... 120 13.1.4. ........................................................................................................... 120 13.1.5. ........................................................................................................... 129 13.1.6. On-the-fly reconfiguration .................................................................................. 132 13.1.7. ............................................................................................................... 132 13.1.8. ......................................................................................................... 133 13.1.8.1. ................................................................................................... 135 jPOS Programmer's Guide 1.6.1 iii The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) 13.1.9. .............................................................................................................. 136 13.1.10. .......................................................................................................... 137 13.1.11. and ............................................................................ 139 13.1.12. .................................................................................................. 140 13.1.13. .................................................................................................... 141 13.1.14. ........................................................................................... 142 13.1.15. .......................................................................................... 143 13.1.16. ........................................................................................................ 144 13.1.17. .................................................................................................... 146 13.1.18. ................................................................................................... 146 13.1.19. Security Module .............................................................................................. 147 13.1.20. Sequencers ...................................................................................................... 148 13.1.21. BlockingQueue ................................................................................................ 149 13.1.22. LockManager .................................................................................................. 150 13.1.23. PersistentPeer .................................................................................................. 150 13.1.24. PersistentEngine .............................................................................................. 161 13.1.25. ConnectionPool ............................................................................................... 161 13.1.26. V24 ................................................................................................................ 162 Glossary ....................................................................................................................................... 165 A. Getting involved ...................................................................................................................... 166 A.1. ..................................................................................................................................... 166 B. Bibliography ............................................................................................................................ 167 C. ISOFieldPackagers ................................................................................................................... 168 D. qsp-config.dtd .......................................................................................................................... 171 E. QTest and QTestMBean source code ......................................................................................... 174 F. ChangeLog ............................................................................................................................... 176 F.1. jPOS 1.4.9 ..................................................................................................................... 176 F.2. jPOS 1.5.0 ..................................................................................................................... 177 F.3. jPOS 1.5.1 ..................................................................................................................... 178 F.4. jPOS 1.5.2 ..................................................................................................................... 179 F.5. jPOS 1.6.0 ..................................................................................................................... 179 F.6. jPOS 1.6.1 ..................................................................................................................... 182 G. Software Copyright .................................................................................................................. 183 H. GNU Affero General Public License version 3 ........................................................................... 184 I. Acknowledgments ..................................................................................................................... 194 Index ........................................................................................................................................... 195 jPOS Programmer's Guide 1.6.1 iv The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) Copyright © 2008 by Alejandro Revilla Company RUT 212 752 380 016 - Uruguay All rights reserved. No part of this book may be reproduced in any form or by any electronic or mechanical means, including information storage and retrieval systems, without permission in writing from Alejandro Re- villa, except by a reviewer who may quote brief passages in a review. 1.6.1 v The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) Foreword This book is designed for developers that have to use jPOS for a given project and have to learn how to use it very quickly. Although many old-timers from the financial interchange arena are using jPOS, we've identified a pattern where most of new users have to learn how to use jPOS, but also have to learn about credit card processing, ISO-8583, etc. So we'll try to address those items here as well. This book covers the jPOS 6 series. jPOS 6 is a major reorganization of our previous projects that provides a software development environment that enable users to work with jPOS, Q2 and jPOS-EE. 1.6.1 vi The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) Chapter 1. The jPOS Project 1.1. About jPOS.org The jPOS project is hosted by jPOS.org [http://jpos.org]. In order to stay up-to-date with jPOS news, you may want to regularly visit our website [http://jpos.org] and follow our blog [http://jpos.org/blog]. In addition, you may want to subscribe to our users' mailing list (jpos-users@googlegroups.com) [groups.google.com/group/jpos-users] and to the developers' mailing list (jpos-dev@yahoogroups.com) [groups.yahoo.com/group/jpos-dev]. or your can follow them over their RSS feeds: • jPOS Users [http://groups.google.com/group/jpos-users/feed/msgs.xml] • jPOS Developers [http://rss.gmane.org/gmane.comp.java.jpos.devel] Tip The jPOS activity can be tracked in the jpos-commits@groups.google.com [groups.google.com/group/jpos-commits] which also has an RSS feed [ht- tp://groups.google.com/group/jpos-users/feed/msgs.xml]. Changes are updated in our ChangeLog [ht- tp://jpos.org/wiki/ChangeLog]. 1.2. jPOS License jPOS is distributed under the GNU Affero General Public License version 3. (see Appendix H, GNU Affero General Public License version 3 for details). IMPORTANT NOTICE If you don't plan to release your jPOS based application under a compatible license (see frequently asked questions http://www.fsf.org/licensing/licenses/agpl-3.0.html where you can find a license com- patibility matrix) you need to buy a commercial license (you can contact ht- tp://jpos.org/contact?p=CL.Proguide). 1.3. Downloading jPOS From time to time, we post new jPOS releases at jPOS.org (http://www.jpos.org). [http://www.jpos.org] but if you want to stay up to date with jPOS development and you want to get the latest features and bug fixes, we strongly suggest you to take the time to install a Subversion client and get the latest and greatest version from http://sourceforge.net/projects/jpos [http://sf.net/projects/jpos] SVN repository. Tip The repository has many branches and tags. Unless you are dealing with a legacy jPOS application, You want to get: trunk/jpos6 using the command 1.6.1 1 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) svn co https://jpos.svn.sourceforge.net/svnroot/jpos/trunk/jpos6 jpos . 1.4. Directory Structure jpos6 |-- COPYRIGHT (1) |-- CREDITS (2) |-- LICENSE (3) |-- README (4) |-- bin (5) | |-- bsh | |-- q2 | `-- release |-- build.xml (6) |-- build.properties (7) |-- lib (8) | |-- bsh-2.0b4.jar | |-- jdbm-1.0.jar | |-- jdom-1.0.jar | `-- log4j-1.2.8.jar `-- modules (9) |-- ignore.list (10) |-- include.list |-- jpos (11) | |-- cfg | |-- src | `-- test |-- compat_1_5_2 (12) | `-- src |-- q2 (13) | |-- deploy | |-- lib | `-- src |-- q2mod_jpos (14) | |-- src | `-- test |-- security (15) | `-- src `-- txnmgr (16) |-- src `-- test (1) jPOS' Copyright notice as described in Appendix G, Software Copyright. (2) A partial list of developers who have made jPOS possible. (3) GNU General Public License as described in Appendix H, GNU Affero General Public License version 3 . (4) Information about how to build jPOS. (5) Unix/Cygwin scripts that can be used to start the Q2 application. (6) Jakarta Ant [http://ant.apache.org] configuration file. Try ant -projecthelp for details. (7) Build configuration properties. (8) The lib directory contains supporting libraries (jars) that are expected to be available in all jPOS applica- tions. This is a small list that is incremented by module-specific supporting libraries. (9) jPOS is composed of modules that can be combined in order to build a jPOS application. Depending on your target application, some of those are required and some are optional. The jPOS Project 1.6.1 2 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) 1courtesy of Hani S. Kirollos with Egiptian Banks jPOS build system can be extended with user-created modules. See Section 1.5, “Building jPOS” for further details. (10) The ignore.list as well as the include.list files are created the first time that ant is invoked. They can be used to customize the list of modules that are to be considered while building a given target jPOS application. If both are empty, all modules in the modules directory are included as part of the build. (11) The jpos module is the main module in the jPOS system containing -- among others -- the ISO-8583 sup- port package (org.jpos.iso). (12) The compat_1_5_2 contains components that were available in legacy versions up to 1.5.2 (and actually the old development branch 1.5.3) but are probably going to be deprecated. Tip We recommend not using them in new applications but if you find them some of them useful, you may want to share with us your use case so we can consider them for re-inclusion in the main set of mod- ules. (13) Q2 is our JMX-MicroKernel. See Chapter 8, Q2 - second generation QSP. (14) Q2 uses services that we call QBeans. QBeans can be used for many different purposes such as configur- ing and interconnecting jPOS components. This module implements popular components such as the ChannelAdaptor, QServer, QMUX, etc. (15) This module contains a JCE based org.jpos.security.SMAdapter implementation. 1 (16) The Transaction Manager is a recent addition to the jPOS project that is rapidly gaining popularity among jPOS developers due to the way it fosters code reuse. See Chapter 10, TransactionManager for details. The jPOS Project 1.6.1 3 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) 1.5. Building jPOS In order to build jPOS you need Apache Ant [http://ant.apache.org] version 1.6.5 or higher installed. In order to run jPOS a JRE 1.5+ is required. As an initial sanity check, you want to run: ant -projecthelp You should see an output like this: Buildfile: build.xml Main targets: clean clean up build and dist directories compile compile all dist Builds source distribution jar creates jpos.jar javadoc generate javadocs run invoke jPOS under Ant's JVM singlejar creates a single jpos.jar including all supporting jars test perform unit tests zip create zip distribution Default target: compile A typical jPOS application has several runtime directories such as the deploy directory that is scanned by the Q2 application in order to start and stop its services, the cfg directory used to store runtime configuration and property files, lib directory containing supporting jars as well as other directories used for specific applications (such as the webapps used by web-enabled applications). In addition, they have an src directory where the source code java files are located as well as a test directory which is the place for unit tests. To recap, we have: src test deploy cfg jPOS build subsystem merges these directories off the available modules and flattens them inside the automat- ically created build directory. If we have the files: module/mymoduleA/src/org/jpos/MyClassA.java module/mymoduleA/lib/mydep1.jar module/mymoduleB/src/org/jpos/MyClassB.java The jPOS Project 1.6.1 4 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) module/mymoduleB/lib/mydep2.jar and we call ant in order to build a jPOS application, as a first step ant will copy the files to the build/src dir- ectory and build/lib directory in order to end up with a directory structure like this: build/src/org/jpos/MyClassA.java build/src/org/jpos/MyClassB.java build/lib/mydep1.jar build/lib/mydep2.jar Same goes for other directories such as cfg, and webapps. Figure 1.1. Build Merge Process Tip If you want to create a local copy of jPOS' javadocs, you can easily issue the following command: ant javadoc You can also browse the javadocs online at http://jpos.org/doc/javadoc/index.html 1. Using jPOS from your IDE jPOS is IDE-agnostic. If you want to use it in your favorite IDE we recommend that you create the jpos.jar The jPOS Project 1.6.1 5 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) using Ant once and then import it to it. In order to be able to access the source code from your IDE, we recommend you to move the merged source tree in build/src into a directory accesible to your IDE. 2. Running jPOS Depending on how you get to use jPOS, you can consider it a Library, a Framework or an Application. While using it as a library, running jPOS boils down to running your application, which in turns will use jPOS. When you use it as an Application/Framework what you are actually running is Q2, jPOS' container. (we cover Q2 in Chapter 8, Q2 - second generation QSP. Running Q2 is as simple as calling: java -jar jpos.jar which produces output like this: Q2 started, deployDir=/home/jpos/svn/jpos/jpos6/build/deploy You can run Q2 off the “build“ directory (for testing purposes) but you want to move it to another destination for a production setup. Tip You may consider using the jPOS-EE SDK where jPOS becomes just another jPOS-EE module and so does your own application. There's a unix shell script (that you can run in Windows inside Cygwin or you can use as a sample to create your own .BAT file) called bin/q2 that can be used to start Q2. (it basically cd build; java -jar q2.jar). In addition, there's an Ant task called “run“ that you can use to run Q2 under Ant's control. See ant - projecthelp for details. The jPOS Project 1.6.1 6 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) Chapter 2. An ISO-8583 primer This chapter contains general information about the ISO-8583 International Standard. ISO-8583 support is an important part of jPOS. This section outlines ISO-8583 message format, wire protocol and message flow. 2.1. International standard ISO 8583 Financial transaction card-originated messages Interchange message specifications You have to read it, period. And you have to read the correct one (1987/1993/2003) for your particular inter- change. And you have to read your interchange specs as well. But while you manage to gather all that informa- tion, let's have a look at this brief introduction. When talking about ISO-8583, we have to be aware of the difference between message format (its binary rep- resentation), wire protocol (how a message is transmitted over the wire), and message flow (e.g., send request for authorization, wait for response, retransmit, reversal, etc.). 2.2. Message structure ISO-8583 messages are composed by fields, which are represented in different ways. Basically we have the fol- lowing structure: Table 2.1. ISO-8583 message structure Field # Description 0 - MTI Message Type Indicator 1 - Bitmap 64 (or 128) bits indicating presence/absence of other fields 2 .. 128 Other fields as specified in bitmap So let's have a look at a simple example: Table 2.2. Sample 0800 message # Name Value Hex Value 0 MTI 0800 08 00 1 PRIMARY BITMAP Indicates presence of fields 3, 11 and 41 20 20 00 00 00 80 00 00 3 PROCESSING CODE 000000 00 00 00 11 SYSTEM TRACE AUDIT NUMBER 000001 00 00 01 41 CARD ACCEPTOR TER- MINAL IDENTIFICACION 29110001 32 39 31 31 30 30 30 31 1.6.1 7 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) Here is the binary representation of our 0800 message: 080020200000008000000000000000013239313130303031 In the previous example, 0800 is the message type indicator ('MTI'); The first position represents ISO-8583 version number: • 0 for version 1987 • 1 for version 1993 • 2 for version 2003 • 3-7 reserved for ISO use • 8 is reserved for national use • 9 is reserved for private use The second position represents message class: • 0 is reserved for ISO use • 1 authorization • 2 financial • 3 file update • 4 reversals and chargebacks • 5 reconciliation • 6 administrative • 7 fee collection • 8 network management • 9 reserved for ISO use The third position is the message function: • 0 request • 1 request response • 2 advice • 3 advice response • 4 notification • 5-9 reserved for ISO use An ISO-8583 primer 1.6.1 8 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) And the last position is used to indicate the transaction originator: • 0 acquirer • 1 acquirer repeat • 2 card issuer • 3 card issuer repeat • 4 other • 5 other repeat • 6-9 reserved for ISO use So “0800“ is a version 1987 network management request. Next we have field 1, the primary bitmap: Table 2.3. Primary Bitmap byte hex value bit value field # 0 20 0010 0000 3 1 20 0010 0000 11 2 00 0000 0000 3 00 0000 0000 4 00 0000 0000 5 80 1000 0000 41 6 00 0000 0000 7 00 0000 0000 So now that we've parsed the MTI (0800) and bitmap (2020000000800000), we know that fields 3, 11 and 41 are present. So our next field is number 3. 2.2.1. ISO-8583 fields ISO-8583 specifies different kinds of fields, which basically fall into the following categories: • Fixed length • Numeric • Alphanumeric • Binary • Variable length An ISO-8583 primer 1.6.1 9 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) • Maxlength 99 • Numeric • Alphanumeric • Binary • Maxlength 999 • Numeric • Alphanumeric • Binary • Maxlength 9999 (ISO-8583 version 2003 only) • Numeric • Alphanumeric • Binary • Nested message So far, so good, this is very simple stuff, isn't it? The problem is not complexity but diversity, ISO-8583 is not specific about how a given field is represented, so you can have a numeric field represented as a sequence of ASCII characters, EBCDIC characters, BCD, etc. Variable length fields have a prefix specifying its length, but how this is represented is not defined. Different vendors use different representations (e.g., BCD, EBCDIC, binary value). In our example, field #3 is using a BCD representation, so a value of “000000“ is represented with just three bytes whose hex values are “00 00 00“. Same goes for field #11 whose value is “000001“ - it is represented as “00 00 01“. In our example, field #41 is an eight-byte alphanumeric field represented as eight ASCII characters Message: 08002020 00000080 00000000 00000001 32393131 30303031 MTI: 0800 Bitmap: 20200000 00800000 Field 03: 000000 Field 11: 000001 field 41: 3239313130303031 (ASCII for “29110001“) Let's have a look at another sample message: Table 2.4. Another 0800 message # Name Value Hex Value 0 MTI 0800 08 00 1.a PRIMARY BITMAP Indicates presence of sec- ondary bitmap plus fields A0 20 00 00 00 80 00 10 An ISO-8583 primer 1.6.1 10 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) # Name Value Hex Value 3, 11 and 41 1.b SECONDARY BITMAP Indicates presence of field 70 04 00 00 00 00 00 00 00 3 PROCESSING CODE 000000 00 00 00 11 SYSTEM TRACE AUDIT NUMBER 000001 00 00 01 41 CARD ACCEPTOR TERMINAL IDEN- TIFICACION 29110001 32 39 31 31 30 30 30 31 60 RESERVED FOR PRIVATE USE jPOS 1.4.1 00 10 6A 50 4F 53 20 31 2E 34 2E 31 70 NETWORK MANAGE- MENT INFORMATION CODE 301 03 01 Two new fields are present: #60 and #70. Here is our message representation: Message: 0800A020 00000080 00100400 00000000 00000000 00000001 32393131 30303031 00106A50 4F532031 2E342E31 0301 MTI: 0800 Primary bitmap: A0200000 00800010 Secondary bitmap: 04000000 00000000 Field 03: 000000 Field 11: 000001 Field 41: 3239313130303031 (ASCII for “29110001“) Field 60: 0010 6A504F5320312E342E31 (length=10, value=“jPOS 1.4.1“) Field 70: 0301 Let's break down this bitmap: Table 2.5. Primary Bitmap byte hex value bit value field # 0 A0 1010 0000 secondary bitmap present plus #3 1 20 0010 0000 11 2 00 0000 0000 3 00 0000 0000 4 00 0000 0000 5 80 1000 0000 41 6 00 0000 0000 7 10 0001 0000 60 An ISO-8583 primer 1.6.1 11 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) Table 2.6. Secondary Bitmap byte hex value bit value field # 0 04 0000 0100 70 1 00 0000 0000 2 00 0000 0000 3 00 0000 0000 4 00 0000 0000 5 80 0000 0000 6 00 0000 0000 7 00 0000 0000 To make things complex to developers, different vendors choose different padding styles when handling odd- length BCD fields. So in order to represent “003“ one vendor may use two bytes with the values “00 03“ while others may use “00 30“ or even “00 3F“. Same goes for variable-length fields: field length as well as field values can be padded right or left (that's not defined by ISO-8583, it's just a matter of fact of different implementations). Then we have nested fields - some implementations use “reserved for private use“ fields to carry other ISO- 8583 messages. These messages are usually packed as variable-length binary fields as seen by the outer mes- sage. Later we will see how jPOS handles this problem in a very simple way so you don't have to worry about this low-level stuff. 2.3. Wire protocol Once we have a binary representation of a given ISO-8583 message, we have to transmit it over the wire using some communication protocol (e.g., TCP/IP, UDP/IP, X.25, SDLC, SNA, ASYNC, QTP, SSL, etc.). That communication protocol is not part of the ISO-8583 definition, so different vendors have choosen differ- ent protocols. Many implementations (especially the older ones) require support for some kind of routing information (e.g., a CICS transaction name), so they use different sorts of headers. A few of them (especially stream-based ones) require some kind of trailers as well. So wire protocol is composed by: • An optional header • ISO-8583 message data • An optional trailer An ISO-8583 primer 1.6.1 12 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) A TCP/IP-based implementation may use a couple of bytes to indicate message length, so our 0800 example described earlier would be sent as: 00 46 08 00 A0 20 00 00 00 80 00 10 04 00 00 00 00 00 00 00 00 00 00 00 00 01 32 39 31 31 30 30 30 31 00 10 6A 50 4F 53 20 31 2E 34 2E 31 03 01 0046 being the message length expressed in network byte order. But this is just one way of specifying message length. Other implementations may choose to send four ASCII bytes, e.g.: 30 30 34 36 08 00 A0 20 00 00 00 80 00 10 04 00 00 00 00 00 00 00 00 00 00 00 00 01 32 39 31 31 30 30 30 31 00 10 6A 50 4F 53 20 31 2E 34 2E 31 03 01 30 30 34 36 being the ASCII representation of “0046“. A few of them perform odd things with those headers, flagging rejected messages (e.g., you send a 0100 and in- stead of receiving a 0110 with a suitable response code you get back your own 0100 with some proprietary flag in the header indicating for example a temporarily failure such as destination unreachable). Tip It's very important to read your interchange specification(s) as soon as possible. Later you'll see how jPOS deals with wire protocol by using a set of classes called ISOChannels, which hide wire protocol details and transparently interact with other jPOS components. 2.4. Message flow Message flow will vary depending on your particular interchange specification. But let's have a look at a simple example: Table 2.7. Sample authorization Time Acquirer Issuer Description t0 0100 --> authorization request t1 <-- 0110 authorization response While this is the typical case (you send a request, you get a response), sometimes there are temporary failures, and you don't get a response. You have to reverse the previously transmited transaction and then either retry your authorization request, abort that transaction or get an authorization approval by other means (e.g., by phone) and send an advice. Table 2.8. Authorization timeout Time Acquirer Issuer Description t0 0100 --> authorization request An ISO-8583 primer 1.6.1 13 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) Time Acquirer Issuer Description t1 no response t3 0400 --> reverse previous authorization t4 <-- 0410 reverse received t5 0120 --> authorization advice t6 <-- 0130 advice received Depending on your particular implementation, you may be able to send retransmissions as well (e.g., 0101 after an unanswered 0100). Some implementations use private messages (e.g., 9600) to request extended time to pro- cess a transaction. So you can see it is very important to become familiar with your interchange specifications as soon as possible. jPOS provides tools to deal with message structure, wire protocol and message flow, but it's the responsibility of your higher-level application to interface the message flow with your business logic. A real example may help you get the idea of what kind of information is exchanged during an authorization re- quest and response. See below: Table 2.9. Sample authorization request Field # Description Value Comments 0 MTI 0100 Authorization request 2 Primary Account Number 4321123443211234 3 Processing Code 000000 4 Amount transaction 000000012300 i.e., 123.00 7 Transmission data/ time 0304054133 MMYYHHMMSS 11 System trace audit number 001205 14 Expiration date 0205 YYMM 18 Merchant Type 5399 22 POS Entry Mode 022 Swiped Card 25 POS Condition Code 00 35 Track 2 4321123443211234=02051231 2312332 37 Retrieval Reference Number 206305000014 41 Terminal ID 29110001 42 Merchant ID 1001001 An ISO-8583 primer 1.6.1 14 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) Field # Description Value Comments 49 Currency 840 American Dollars Table 2.10. Sample authorization response Field # Description Value Comments 0 MTI 0110 Authorization response 2 Primary Account Number 4321123443211234 3 Processing Code 000000 4 Amount transaction 000000012300 i.e., 123.00 7 Transmission data/ time 0304054133 MMYYHHMMSS 11 System trace audit number 001205 14 Expiration date 0205 YYMM 18 Merchant Type 5399 22 POS Entry Mode 022 Swiped Card 25 POS Condition Code 00 35 Track 2 4321123443211234=02051231 2312332 37 Retrieval Reference Number 206305000014 38 Authorization num- ber 010305 39 Response code 00 Approved 41 Terminal ID 29110001 42 Merchant ID 1001001 49 Currency 840 American Dollars An ISO-8583 primer 1.6.1 15 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) Chapter 3. jPOS approach to ISO-8583 This chapter describes how jPOS handles ISO-8583 messages. 3.1. ISOMsg & Co. jPOS' internal representation of an ISO-8583 message is usually an ISOMsg object (or an ISOMsg's subclass). The ISOMsg class uses the so-called Composite pattern (see Design Patterns, elements of Reusable Object- Oriented Software by Gamma, Helm, Johnson and Vlissides) ISOMsg, ISOField, ISOBitMapField, ISOBinaryField and any custom field type that you may implement are subclasses of ISOComponent. Let's have a look at ISOComponent's methods: public abstract class ISOComponent implements Cloneable { public void set (ISOComponent c) throws ISOException; public void unset (int fldno) throws ISOException; public ISOComponent getComposite(); public Object getKey() throws ISOException; public Object getValue() throws ISOException; public byte[] getBytes() throws ISOException; public int getMaxField(); public Hashtable getChildren(); public abstract void setFieldNumber (int fieldNumber); public abstract void setValue(Object obj) throws ISOException; public abstract byte[] pack() throws ISOException; public abstract int unpack(byte[] b) throws ISOException; public abstract void dump (PrintStream p, String indent); public abstract void pack (OutputStream out) throws IOException, ISOException; public abstract void unpack (InputStream in) throws IOException, ISOException; } This approach has proven to be really useful and maps quite well to the ISO-8583 message structure. There are many situations where some methods are not applicable (i.e., getChildren() has no meaning in a leaf field, same goes for methods such as getMaxField()), but as a general rule, using the same super-class for ISO- Msg and ISOFields has proven to be a good thing. You can easily assign an ISOMsg as a field of an outer ISO- Msg. The following diagram shows how some ISOComponents interact with each other. 1.6.1 16 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) The following code can be used to create an internal representation of our 0800 message (described in [An ISO- 8583 primer] chapter). Example 3.1. ISOMsg/ISOField import org.jpos.iso.*; ISOMsg m = new ISOMsg(); m.set (new ISOField (0, “0800“)); m.set (new ISOField (3, “000000“)); m.set (new ISOField (11, “000001“)); m.set (new ISOField (41, “29110001“)); m.set (new ISOField (60, “jPOS 6“)); m.set (new ISOField (70, “301“)); We are just calling ISOComponent.set (ISOComponent) method. In order to reduce typing and improve code readability, ISOMsg provides some handy methods such as ISOMsg.setMTI (String) and ISOMsg.set (int fieldNumber, String fieldValue) implemented like this: public void set (int fldno, String value) throws ISOException { set (new ISOField (fldno, value)); } jPOS approach to ISO-8583 1.6.1 17 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) public void setMTI (String mti) throws ISOException { if (isInner()) throw new ISOException (“can't setMTI on inner message“); set (new ISOField (0, mti)); } So the previous example can be written like this: Example 3.2. ISOMsg ISOMsg m = new ISOMsg(); m.setMTI (“0800“); m.set (3, “000000“); m.set (11, “000001“); m.set (41, “29110001“); m.set (60, “jPOS 6“); m.set (70, “301“); Tip ISOMsg is one of the most used classes in typical ISO-8583-based jPOS applications. While you can subclass it, you probably won't have to. If there's a single class in all jPOS that you want to study in great detail, this is it. We recommend you to have a look at its API documentation [http://jpos.org/doc/javadoc/org/jpos/iso/ISOMsg.html] and play with its helper methods such as clone, merge, unset, etc. 3.2. Packing and unpacking ISOComponents have two useful methods called: public abstract byte[] pack() throws ISOException; public abstract int unpack(byte[] b) throws ISOException; pack returns a byte[] containing the binary representation of a given component (can be just a field or the whole ISOMsg); unpack does the opposite and also returns the number of consumed bytes. jPOS uses yet another pattern called Peer pattern that allows a given ISOComponent to be packed and un- packed by a peer class, “plugged“ at runtime. So ISOMsg has a method called: public void setPackager (ISOPackager p); where one can assign a given packager to an ISOMsg, so you can write code like this: Example 3.3. ISOPackager ISOPackager customPackager = MyCustomPackager (); ISOMsg m = new ISOMsg(); jPOS approach to ISO-8583 1.6.1 18 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) m.setMTI (“0800“); m.set (3, “000000“); m.set (11, “000001“); m.set (41, “29110001“); m.set (60, “jPOS 6“); m.set (70, “301“); m.setPackager (customPackager); byte[] binaryImage = m.pack(); In order to unpack this binary image you may write code like this: ISOPackager customPackager = MyCustomPackager (); ISOMsg m = new ISOMsg(); m.setPackager (customPackager); m.unpack (binaryImage); It is very easy to create protocol converters using jPOS, e.g.: ISOPackager packagerA = MyCustomPackagerA (); ISOPackager packagerB = MyCustomPackagerB (); ISOMsg m = new ISOMsg(); m.setPackager (packagerA); m.unpack (binaryImage); m.setPackager (packagerB); byte[] convertedBinaryImage = m.pack(); ISOMsg.pack() delegates message packing/unpacking operations to its underlying “peer“ ISOPackager. The code looks like this: public byte[] pack() throws ISOException { synchronized (this) { recalcBitMap(); return packager.pack(this); } } packager.pack (ISOComponent) also delegates its packing/unpacking duties to an underlying ISOFieldPack- ager. There are ISOFieldPackager implementations for many different ways of representing a field. It is very easy to create your own, if required. The following code is used by an ISOFieldPackager implementation to pack and unpack fixed-length alphanu- meric fields: public byte[] pack (ISOComponent c) throws ISOException { String s = (String) c.getValue(); if (s.length() > getLength()) s = s.substring(0, getLength()); return (ISOUtil.strpad (s, getLength())).getBytes(); } public int unpack (ISOComponent c, byte[] b, int offset) throws ISOException { c.setValue(new String(b, offset, getLength())); return getLength(); } jPOS approach to ISO-8583 1.6.1 19 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) jPOS comes with many ISOFieldPackager implementations. You'll probably never have to write your own. Names choosen are somewhat cryptic, but so many people are using them for their own custom packagers so we'll probably have to live with those names for a while. As a general rule, all ISOFieldPackagers live under package org.jpos.iso and start with the name IF* which stands for “ISO Field“. So we have things like this: Table 3.1. ISOFieldPackagers Name Purpose IF_CHAR Fixed length alphanumeric (ASCII) IFE_CHAR Fixed length alphanumeric (EBCDIC) IFA_NUMERIC Fixed length numeric (ASCII) IFE_NUMERIC Fixed length numeric (EBCDIC) IFB_NUMERIC Fixed length numeric (BCD) IFB_LLNUM Variable length numeric (BCD, maxlength=99) IFB_LLLNUM Variable length numeric (BCD, maxlength=999) IFB_LLLLNUM Variable length numeric (BCD, maxlength=9999) ... ... ... ... [See appendix B for a complete reference] 3.3. Creating custom packagers jPOS provides the ability to create customized packagers for different kind of ISO-8583 implementations. Over the last few years, several developers have contributed their customized ISOPackagers and ISOFieldPackagers, so chances are good that you can find an implementation suitable for you, or something very close to what you need as part of jPOS distribution. Tip Before writing your own packager, have a look at the modules/ jpos/src/main/org/jpos/iso/packager directory. Writing a packager is very easy. There's a support class called ISOBasePackager that you can easily extend, e.g.: public class ISO93APackager extends ISOBasePackager { protected ISOFieldPackager fld[] = { /*000*/ new IFA_NUMERIC ( 4, “Message Type Indicator“), /*001*/ new IFA_BITMAP ( 16, “Bitmap“), /*002*/ new IFA_LLNUM ( 19, “Primary Account number“), /*003*/ new IFA_NUMERIC ( 6, “Processing Code“), /*004*/ new IFA_NUMERIC ( 12, “Amount, Transaction“), jPOS approach to ISO-8583 1.6.1 20 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) /*005*/ new IFA_NUMERIC ( 12, “Amount, Reconciliation“), ... ... ... public ISO93APackager() { super(); setFieldPackager(fld); } } So the programmer's task (BTW, an easy but boring one) is to verify that every single field in your packager configuration matches your interchange specifications. An ISOPackager is not required to extend the supporting class ISOBasePackager, but we've found it quite con- venient for most situations. Tip while you write your own packager implementation, we recommend you to write a unit test for it. Have a look at modules/jpos/test/org/jpos/iso directory to find some sample unit tests that can be used as a starting point. After adding several packagers to our repository, jPOS developer Eoin Flood came up with a good idea: a Gen- ericPackager that one could configure by means of an XML file. The GenericPackager configuration looks like this: - 07/2009 (r2966) id=“6“ length=“12“ name=“Amount, Cardholder billing“ class=“org.jpos.iso.IFA_NUMERIC“/> ... ... ... So now we have XML configurations for most packagers under the org.jpos.iso.packager package. They are available in the src/config/packager directory. If you are to develop a custom packager, we encourage you to use GenericPackager with a suitable custom configuration file instead. It will greately simplify your task. Example 3.4. We recommend that whenever possible, you use code like this: import org.jpos.iso.ISOPackager; import org.jpos.iso.packager.GenericPackager; import org.jpos.iso.packager.ISO87BPackager; ISOPackager p = new GenericPackager (“iso87binary.xml“); ...instead of like this: import org.jpos.iso.ISOPackager; import org.jpos.iso.packager.ISO87BPackager; ISOPackager p = new ISO87BPackager(); Tip If you're using Q2 to configure your packagers, GenericPackager uses the “packager-config“ property in order to determine its configuration file. Tip If you need support for nested messages, you may want to have a look at src/con- fig/packager/genericpackager.dtd as well as examples such as europay.xml (see field 48) or base1.xml (see field 127). 3.4. Managing the wire protocol with ISOChannel jPOS uses an interface called ISOChannel to encapsulate wire protocol details. ISOChannel is used to send and receive ISOMsg objects. It leverages the peer pattern where its peer is an ISOPackager instance. It has send and receive methods as well as means to set and get a peer packager: ... public void send (ISOMsg m) throws IOException, ISOException; public ISOMsg receive() throws IOException, ISOException; public void setPackager(ISOPackager p); public ISOPackager getPackager(); ... jPOS approach to ISO-8583 1.6.1 22 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) 1 See Design Patterns, elements of Reusable Object-Oriented Software Although not meaningful under all possible situations, ISOChannel has a few connection-related methods as well: ... public void connect () throws IOException; public void disconnect () throws IOException; public void reconnect() throws IOException; public void setUsable(boolean b); public boolean isConnected(); ... In order for applications to bind jPOS components at runtime, there's a Singleton 1class called org.jpos.util.NameRegistrar where you can register and get references to Objects. The ISOChannel inter- face provides handy methods to access ISOChannels at runtime by their name. ... public void setName (String name); public String getName(); ... ISOChannel extends ISOSource which reads like this: public interface ISOSource { public void send (ISOMsg m) throws IOException, ISOException, VetoException; public boolean isConnected(); } Different interchanges use different wire protocols. jPOS encapsulates that functionality in completely isolated ISOChannel implementations. It comes with many implementations and it's easy to write your own. jPOS approach to ISO-8583 1.6.1 23 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) Table 3.2. Sample ISOChannel implementations Name Description ASCIIChannel 4 bytes message length plus ISO-8583 data LogChannel Can be used to read jPOS's logs and inject messages into other chan- nels LoopbackChannel Every message sent gets received (possibly apply- ing filters). Very useful for testing purposes. PADChannel Used to connect to X.25 packet assembler/dis- samblers XMLChannel jPOS Internal XML rep- resentation for ISO-8583 messages ... ... ... ... Tip (see org.jpos.iso.channel.* for a complete list) Example 3.5. ISOChannel This example sends a simple 0800 message into a local echo server: Tip If you are running Unix, please check your inetd setup. import org.jpos.iso.*; import org.jpos.util.*; import org.jpos.iso.channel.*; import org.jpos.iso.packager.*; public class Test { public static void main (String[] args) throws Exception { Logger logger = new Logger(); logger.addListener (new SimpleLogListener (System.out)); ISOChannel channel = new ASCIIChannel ( “localhost“, 7, new ISO87APackager() ); ((LogSource)channel).setLogger (logger, “test-channel“); channel.connect (); ISOMsg m = new ISOMsg (); m.setMTI (“0800“); m.set (3, “000000“); jPOS approach to ISO-8583 1.6.1 24 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) m.set (41, “00000001“); m.set (70, “301“); channel.send (m); ISOMsg r = channel.receive (); channel.disconnect (); } } If everything works fine, you'll see the following output: localhost:7 localhost:7 Tip While we'll see many examples similar to the previous one throughout this document, where a simple main() method takes care of instantiating and configuring several jPOS components, later we'll intro- duce Q2, jPOS's component assembler/configurator. We strongly recommend to use Q2 to run jPOS. It will make your life easier. Q2 lets you define your jPOS-based application in a very simple, easy to create and easy to maintain set of XML configuration files. We recommend that you wait until we talk about Q2 before diving into coding your own jPOS-based application. Using code like the previous example is good to learn jPOS but not to run it in a produc- tion environment. If you have a look at the ISOChannel implementations (most of them live in org.jpos.iso.channel package) you'll notice that many of them extend org.jpos.iso.BaseChannel. jPOS approach to ISO-8583 1.6.1 25 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) BaseChannel is an abstract class that provides hooks and default implementations for several methods that are useful when writing custom channels. While you don't necesarily have to extend BaseChannel to write a cus- tom channel, you'll probably find it very useful. Depending on your wire protocol, you'll probably only need to extend BaseChannel and just override a few methods, i.e: protected void sendMessageLength(int len) throws IOException; protected int getMessageLength() throws IOException, ISOException; (see modules/jpos/src/org/jpos/iso/channel/CSChannel.java) You may want to have a look at the LoopbackChannel implementation for an example of an ISOChannel that doesn't extend BaseChannel. 3.4.1. Filtered Channels Many ISOChannels implement FilteredChannel which looks like this: public interface FilteredChannel extends ISOChannel { public void addIncomingFilter (ISOFilter filter); public void addOutgoingFilter (ISOFilter filter); public void addFilter (ISOFilter filter); public void removeFilter (ISOFilter filter); public void removeIncomingFilter (ISOFilter filter); public void removeOutgoingFilter (ISOFilter filter); public Collection getIncomingFilters(); public Collection getOutgoingFilters(); public void setIncomingFilters (Collection filters); public void setOutgoingFilters (Collection filters); } ISOFilter is very simple as well: public interface ISOFilter { public ISOMsg filter (ISOChannel channel, ISOMsg m, LogEvent evt) throws VetoException; } Whenever you add a filter (be it incoming, outgoing, or both) to a FilteredChannel, all messages sent or re- ceived by that channel are passed through that filter. Filters give you the opportunity to stop a given message from being sent or received by that channel, by throw- ing an ISOFilter.VetoException. Let's have a look at a very simple filter, DelayFilter: public class DelayFilter implements ISOFilter, ReConfigurable { int delay; public DelayFilter() { super(); delay = 0; } /** * @param delay desired delay, expressed in milliseconds */ jPOS approach to ISO-8583 1.6.1 26 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) public DelayFilter(int delay) { super(); this.delay = delay; } /** * @param cfg *
    *
  • delay (expressed in milliseconds) *
*/ public void setConfiguration (Configuration cfg) { delay = cfg.getInt (“delay“); } public ISOMsg filter (ISOChannel channel, ISOMsg m, LogEvent evt) { evt.addMessage (““); if (delay > 0) { try { Thread.sleep (delay); } catch (InterruptedException e) { } } return m; } } DelayFilter simply applies a given delay to all traffic being sent or received by a given channel. It can be used to simulate remote host delays, a good tool for testing purposes. Tip The previous code introduces a few classes and interfaces, namely Configuration, ReConfigurable, Lo- gEvent. We'll talk about these important parts of jPOS soon. jPOS comes with many general purpose filters: MD5Filter can be used to authenticate messages; MacroFilter can be used to expand internal variables and sequencers; and XSLTFilter can be used to apply XSLT Trans- formations to ISO-8583 messages. [ See the reference section of this document to get detailed information about available filters. ] There's a popular filter called BSHFilter that can execute BeanShell code placed in an external file that can be modified at runtime without restarting the system, providing an excellent way to make quick changes (which are welcome during tests and initial rounds of certifications - the BSH code can be easily migrated to Java later). 3.5. Accepting connections with ISOServer ISOServer listens in a given port for incoming connections and takes care of accepting them and passing con- trol to an underlying ISOChannel implementation. Once a new connection is accepted and an ISOChannel is created, a ThreadPool-controlled Thread takes care of receiving messages from it. Those messages are passed to an ISORequestListener implementation. Example 3.6. ISOServer import org.jpos.iso.*; import org.jpos.util.*; import org.jpos.iso.channel.*; import org.jpos.iso.packager.*; jPOS approach to ISO-8583 1.6.1 27 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) public class Test { public static void main (String[] args) throws Exception { Logger logger = new Logger (); logger.addListener (new SimpleLogListener (System.out)); ServerChannel channel = new XMLChannel (new XMLPackager()); ((LogSource)channel).setLogger (logger, “channel“); ISOServer server = new ISOServer (8000, channel, null); server.setLogger (logger, “server“); new Thread (server).start (); } } Tip The third argument of ISOServer's constructor is an optional ThreadPool. Should you pass a null para- meter there, a new ThreadPool is created for you, which defaults to 100 threads. (new ThreadPool (1,100)) In order to test the previous server Test program (which is listening on port 8000), you can use a simple 'telnet' client where you will be able to type an XML-formatted ISO-8583 message, e.g.: $ telnet localhost 8000 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. Now if you have a look at your running Test program you'll see something like this: listening on port 8000 Back on your telnet session, you can type in an XML formatted ISO-8583 message like this: (please note XMLChannel expects as well as to be placed as the first thing in a line) Your test program will then show: As stated above, you can add an ISORequestListener to your ISOServer that will take care of actually pro- jPOS approach to ISO-8583 1.6.1 28 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) cessing the incoming messages. So let's modify our little Test program to answer our messages. Our Test class has to implement ISORequestListener, e.g.: public class Test implements ISORequestListener { ... ... public boolean process (ISOSource source, ISOMsg m) { try { m.setResponseMTI (); m.set (39, “00“); source.send (m); } catch (ISOException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return true; } ... ... } You have to assign this request listener to your server. You can do this assignment with the following instruc- tion: server.addISORequestListener (new Test ()); The full program looks like this: import java.io.*; import org.jpos.iso.*; import org.jpos.util.*; import org.jpos.iso.channel.*; import org.jpos.iso.packager.*; public class Test implements ISORequestListener { public Test () { super(); } public boolean process (ISOSource source, ISOMsg m) { try { m.setResponseMTI (); m.set (39, “00“); source.send (m); } catch (ISOException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return true; } public static void main (String[] args) throws Exception { Logger logger = new Logger (); logger.addListener (new SimpleLogListener (System.out)); ServerChannel channel = new XMLChannel (new XMLPackager()); ((LogSource)channel).setLogger (logger, “channel“); ISOServer server = new ISOServer (8000, channel, null); server.setLogger (logger, “server“); server.addISORequestListener (new Test ()); new Thread (server).start (); } jPOS approach to ISO-8583 1.6.1 29 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) } Now try to telnet to port 8000 and send another XML-formatted ISO-8583 message. You'll get a response, with a retcode “00“ (field 39), e.g.: (you type) (and you should get) ISOServer uses a ThreadPool in order to be able to accept multiple connections at the same time. Every socket connection is handled by a single thread. If your request listener implementation takes too long to reply, new messages arriving over that session will have to wait for their response. To solve this problem, your ISORequestListener implementation should run in its own thread pool so that its process(...) method will just queue requests to be processed by a peer thread. ISOServer uses ISOChannel implementations to pull ISOMsgs from the wire. These ISOChannels can, of course, have associated filters as described earlier. Note In modern jPOS applications ISOServer is usually managed by the QServer service (see Section 9.3, “QServer”). The ISORequestListener is usually a thin implementation that forwards the request to the TransactionManager (see Chapter 10, TransactionManager). 3.6. Multiplexing an ISOChannel with a MUX Imagine an acquirer implementation that receives several requests at a time from several POS terminals and has to route them to an issuer institution by means of an ISOChannel. While you can establish one socket connection per transaction, it is common use to setup just one socket con- nection (handled by an ISOChannel instance) and multiplex it. So a MUX is basically a channel multiplexer. Once you have instantiated a MUX, you just send a request and wait for the response. jPOS approach to ISO-8583 1.6.1 30 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) 2r2510 July 2007 Originally, the MUX interface look like this: public interface MUX { public ISOMsg request (ISOMsg m, long timeout) throws ISOException; public boolean isConnected(); } • The ISOMsg request(ISOMsg, long) method queues a request to be sent by the underlying ISOChannel(s) and waits for the response up to the timeout specified in milliseconds. It either returns a response or null. • isConnected() is self explanatory, it returns true if the underlying channel(s) are connected. Note MUX is an interface that can have many different implementations. Depending on the implementation and the configuration the value returned by isConnected() might not be reliable (it could return true even on an unconnected channel). Recently 2 we've added the ability to asynchroneusly queue requests, the new MUX interface another request method that returns inmediately and calls an ISOResponseListener (with an optional handBack Object). public interface MUX { ... ... public void request (ISOMsg m, long timeout, ISOResponseListener r, Object handBack) throws ISOException; } Note This new asynchroneous way of calling the MUX is available in the QMUX implementation of the MUX in- terface but it has not been back-ported to the ISOMUX implementation which is going to be deprecated in future versions of jPOS. ISOMUX has a queue method that can be used to achieve a similar asynchroneous behavior. In order to send responses to the appropiate sending thread, a MUX implementation uses selected fields from the original ISOMsg request expected to be present in the ISOMsg response. Although not part of the MUX interface, implementations such as QMUX (the new one) and ISOMUX (the old one) have a protected method called String getKey(ISOMsg m) that returns a matching key based on the ISOMsg content. jPOS approach to ISO-8583 1.6.1 31 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) The default implementation uses fields such as 41 (Terminal ID) plus field 11 (Serial Trace Audit Number) to create an unique key. You can override getKey() in order to use other fields. Example 3.7. MUX example ... ... MUX mux = (MUX) NameRegister.get (“mux.mymultiplexer“); ... ... Once you have a reference to a MUX implementation you can send requests and wait for responses using code like this: ISOMsg m = new ISOMsg(); m.setMTI (“0800“); m.set (11, “000001“); m.set (41, “00000001“); ISOMsg response = mux.request (m, 30000); if (response != null) { // you've got a response } else { // request has timed out - you may want to reverse } When a message arrives to MUX's underlying ISOChannel, the MUX implementation checks to see if that mes- sage's 'key' is registered as a pending request. Should that key match a pending request, the response is handed to the waiting thread. If the key was registered as a request, or the response comes in too late then that response is (depending on the configuration) ignored, forwarded to an ISORequestListener or to a well defined Space queue. (see QMUX for details). Under many situations, the same channel that a client application may use to send requests and wait for re- sponses may also receive requests coming from the remote server. Those unmatched requests coming from the remote server are delegated to an ISORequestListener (or a well defined “unhandled“ Space queue). Let's have a look at the ISORequestListener interface: public interface ISORequestListener { public boolean process (ISOSource source, ISOMsg m); } Imagine we want to answer the 0800 echo requests arriving to our MUX. We can write the following imple- mentation: public class EchoHandler extends Log implements ISORequestListener { public boolean process (ISOSource source, ISOMsg m) { try { if (“0800“.equals (m.getMTI())) { jPOS approach to ISO-8583 1.6.1 32 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) m.setResponseMTI (); m.set (39, “00“); source.send (m); } } catch (Exception e) { warn (“echo-handler“, e); } return true; } } jPOS approach to ISO-8583 1.6.1 33 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) Chapter 4. jPOS supporting classes util and core This chapter describes jPOS's core and util packages which include components such as the Logger subsystem, the Name Registrar, Configuration stuff, Sequencer, Profiler, ThreadPool, etc. Tip While you probably want to carefully read Section 4.1, “jPOS' Logger”, Section 4.2, “NameRegistrar” and Section 4.3, “Configuration”, feel free to skip the rest (Profiler, DirPoll, ThreadPool) and use it as a reference only. 4.1. jPOS' Logger Yet another Logger subsystem You may wonder why we've choosen to develop our own Logger subsystem. The answer is very simple: when we wrote it, there were no other suitable logger subsystems available. Log4j was just a toy hosted in al- phaWorks. The main difference between our logger sub-system and other logger sub-systems out there is that we deal with live objects. A LogEvent holds live objects that can be handled by the LogListeners, for example to protect sensitive information (PCI requirement) or to act on special conditions (i.e. e-mailing an Operator on an Excep- tion). Note While other logger subsystems are mostly “line oriented“, jPOS' is mostly “transaction oriented“. A jPOS LogEvent is likely to carry information for the whole transaction making it very suitable for audit and debugging purposes. jPOS's logger subsystem is very easy to extend, so one can easily plug in other logger engines (such as Log4j, commons logging or the new JDK's 1.4 logging stuff). Our logger is implemented by the following main classes: Table 4.1. Logger's main classes Class Description Logger Main logger class LogListener Listens to log events LogSource A log event producer has to implement LogSource LogEvent The Log Event The Logger class has the following important methods: public class Logger { public static void log (LogEvent ev); ... public void addListener (LogListener l); public void removeListener (LogListener l); 1.6.1 34 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) public boolean hasListeners(); ... ... } LogSource looks like this: public interface LogSource { public void setLogger (Logger logger, String realm); public String getRealm (); public Logger getLogger (); } And LogEvent: public class LogEvent extends EventObject { public LogEvent (LogSource source, String tag); ... ... public void addMessage (Object msg); ... } (please take a look at jPOS's javadoc or source code for a full description) Here is a simple way to create a Logger: Logger logger = new Logger(); logger.addListener (new SimpleLogListener (System.out)); Now you can easily attach that logger to any jPOS component implementing LogSource such as channels, packagers, multiplexers, etc. You can easily call: component.setLogger (logger, “some-component-description“); You can use jPOS's logger subsystem to log events of your own. In those cases, you have to either implement LogSource or extend the SimpleLogSource class. Then you can write code like this: LogEvent evt = new LogEvent (yourLogSource, “my-event“); evt.addMessage (“A String message“); evt.addMessage (anyLoggeableObject); Logger.log (evt); The Loggeable interface is a very simple way of letting an object render itself: public interface Loggeable { public void dump (PrintStream p, String indent); } Most of jPOS's components already implement the Loggeable interface, but you can easily wrap any given ob- ject with a Loggeable class that holds the former object as its payload, e.g.: jPOS supporting classes 1.6.1 35 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) package net.swini.util; import java.io.PrintStream; import org.jpos.util.Loggeable; public abstract class LoggeableBase implements Loggeable { protected String toXML (String tag, String value, String indent) { StringBuffer sb = new StringBuffer (indent); sb.append ('<'); sb.append (tag); sb.append ('>'); sb.append (value); sb.append (“'); return sb.toString (); } public abstract void dump (PrintStream p, String indent); } package net.swini.util; import java.io.PrintStream; import net.jini.core.lookup.ServiceItem; import net.jini.lookup.entry.ServiceInfo; public class LoggeableServiceItem extends LoggeableBase { String tag; ServiceItem item; public LoggeableServiceItem (String tag, ServiceItem item) { super(); this.tag = tag; this.item = item; } public void dump (PrintStream p, String indent) { String inner = indent + “ “; p.println (indent + “<“ + tag + “>“); if (item.service != null) { p.println (toXML (“class“, item.service.getClass().getName(), inner)); } else { p.println (inner + “null item.service - (check http server)“); } p.println (toXML (“id“, item.serviceID.toString(), inner)); for (int i=0 ; i“); p.println (inner + “ “+item.attributeSets[i].toString()); p.println (inner + ““); } } p.println (indent + ““); } } There's a general purpose Loggeable class called SimpleMsg which has an overloaded constructor for several jPOS supporting classes 1.6.1 36 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) commonly used Java types. You can easily add a SimpleMsg to your log stream with code like this: ... ... evt.addMessage (new SimpleMsg (“demo“, “boolean“, true)); evt.addMessage (new SimpleMsg (“demo“, “time“, System.currentTimeMillis())); evt.addMessage (new SimpleMsg (“demo“, “dump“, “TEST“.getBytes())); ... ... jPOS comes with several LogListener implementations and it's very easy to write your own. The ready avail- able ones include: Table 4.2. LogListener Class Description SimpleLogListener Dumps log events to a PrintStream (such as Sys- tem.out) RotateLogListener Automatically rotate logs based on file size and time window DailyLogListener Automatically rotate logs daily. Has the ability to compress old log files OperatorLogListener Applies some filtering and e-mails log-events to an operator Log4JListener Forwards LogEvents to Log4J logger subsystem Tip In the jPOS-EE code base you can find some additional logger implementations such as IRCLogListen- er that forwards LogEVents to an irc channel. LogListeners are called synchronously, so one listener has the chance to modify a given LogEvent; for ex- ample, ProtectedLogListener analyzes received LogEvents and protects important information (such as track-2 data). 4.2. NameRegistrar org.jpos.util.NameRegistrar is a very simple singleton class that can be used to register and locate jPOS com- ponents. It's nothing but a simple, well-known Map where one can easily find components by an arbitrary name. NameRegistrar has the following static methods: public static void register (String key, Object value); public static void unregister (String key); public static Object get (String key) throws NameRegistrar.NotFoundException; public static Object getIfExists (String key); jPOS supporting classes 1.6.1 37 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) So you can write code like this: ... ... ISOMUX mux = new ISOMUX (...); NameRegistrar.register (“myMUX“, mux); ... ... and elsewhere in your application you can get a reference to your MUX with code like this: try { ISOMUX mux = (ISOMUX) NameRegistrar.get (“myMUX“); } catch (NameRegistrar.NotFoundeException e) { ... ... } or ISOMUX mux = (ISOMUX) NameRegistrar.getIfExists (“myMUX“); if (mux != null) { ... ... } Although we can use NameRegistrar in order to register jPOS components, sometimes it's better to use the component's setName(String name) method when available. Most components have a setName (String name) method implemented like this: public class ISOMUX { ... ... public void setName (String name) { this.name = name; NameRegistrar.register (“mux.“+name, this); } ... ... The prefix “mux.“ is used here in order to avoid a clash of names in the registrar between different classes of components using the same name (i.e. “mux.institutionABC“ and “channel.institutionABC“). Different components use different prefixes as shown in the following table: Table 4.3. NameRegistrar's prefix Component Prefix Getter ConnectionPool “connection.pool.“ N/A ControlPanel “panel.“ N/A DirPoll “qsp.dirpoll.“ N/A BaseChannel “channel.“ BaseChannel.getChannel jPOS supporting classes 1.6.1 38 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) Component Prefix Getter ISOMUX “mux.“ ISOMUX.getMUX QMUX “mux.“ QMUX.getMUX ISOServer “server.“ ISOServer.getServer KeyStore “keystore.“ N/A Logger “logger.“ Logger.getLogger LogListener “log-listener.“ N/A PersistentEngine “persistent.engine.“ N/A SMAdapter “s-m-adapter.“ BaseSMAdapter.getSMAdapter Tip While we try to keep the previous prefix table up to date, we suggest that you double-check it against the source code if you have problems getting references to your components. Using the getter (when available) lets us write code like this: try { ISOMUX mux = ISOMUX.get (“myMUX“); } catch (NameRegistrar.NotFoundeException e) { ... ... } that will in turn call NameRegistrar.get (“mux.myMUX“). Later, we'll see that NameRegistrar is extensively used by jPOS' Q2 applications. Q2 takes care of configuring several jPOS components for you, but your code will have to locate them by a given name. That's where NameRegistrar comes in to play. Tip The NameRegistrar is a Loggeable object (see Section 4.1, “jPOS' Logger”) so its instance (NameRe- gistrar.getInstance()) can be added to a LogEvent in order to assist you during debugging sessions. When running in a Q2 environment we recommend to deploy a sysmon service in order to regularly view the NameRegistrar's content. 4.3. Configuration Configuration, Configurable, ReConfigurable org.jpos.core.Configuration is a general purpose property container extensively used by jPOS components. The Configuration interface looks like this: package org.jpos.core; public interface Configuration { public void put (String name, Object value); public String get (String propertyName); public String get (String propertyName, String defaultValue); public String[] getAll (String propertyName); jPOS supporting classes 1.6.1 39 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) public int[] getInts (String propertyName); public long[] getLongs (String propertyName); public double[] getDoubles (String propertyName); public boolean[] getBooleans (String propertyName); public int getInt (String propertyName); public int getInt (String propertyName, int defaultValue); public long getLong (String propertyName); public long getLong (String propertyName, long defaultValue); public double getDouble (String propertyName); public double getDouble (String propertyName, double defaultValue); public boolean getBoolean (String propertyName); public boolean getBoolean (String propertyName, boolean defaultValue); } Having our own Configuration interface lets us implement it in different ways. We have a very little class called SimpleConfiguration backed by a java.util.Properties, but nothing prevents us from creating a more sophisticated Configuration object capable of providing dynamic data (such as an SQLConfiguration, JavaS- pacesConfiguration and the like). We also have a very simple interface called Configurable: package org.jpos.core; public interface Configuration { public void setConfiguration (Configuration cfg) throws ConfigurationException; } Later, while looking at the Q2 application we'll see that Q2 pushes a configuration object in [Re]Configurable objects by calling its setConfiguration method. Should com.mycompany.MyObject“ implement Configurable, Q2 would call its setConfiguration() method providing access to the underlying myProperty property. It's interesting to note that Q2 provides the ability to have array of properties under the same name, i.e: where one can call handy methods like String[] getAll(String). setConfiguration(Configuration cfg) can check the Configuration object and might throw a Configura- tionException in case a required property is not present or is invalid. 4.4. SystemMonitor org.jpos.util.SystemMonitor is a very simple class that periodically logs useful information such as the num- ber of running threads, memory usage, etc. jPOS supporting classes 1.6.1 40 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) Its constructor looks like this: public SystemMonitor (int sleepTime, Logger logger, String realm) Note See javadocs [http://jpos.org/doc/javadoc/org/jpos/util/SystemMonitor.html] for details. Using SystemMonitor is very easy. You simply have to instantiate it with code like this: ... ... new SystemMonitor (60*60*1000, yourLogger, “system-monitor“); // dumps every hour ... ... and it will dump info to your log every hour (60*60*1000 milliseconds). The output looks like this: freeMemory=8634840 totalMemory=19652608 inUseMemory=11017768 delay=0 ms threads=29 Thread[Reference Handler,10,system] Thread[Finalizer,8,system] Thread[Signal Dispatcher,10,system] Thread[main,5,main] Thread[Timer-0,5,main] Thread[txnmgr-0,5,main] Thread[txnmgr-1,5,main] Thread[Thread-5,5,main] Thread[channel-sender-selftest-send,5,main] Thread[channel-receiver-selftest-receive,5,main] Thread[Thread-8,5,main] Thread[scanner,5,main] Thread[btpool0-0,5,main] Thread[btpool0-1,5,main] Thread[btpool0-2,5,main] Thread[btpool0-3,5,main] Thread[btpool0-4,5,main] Thread[btpool0-5,5,main] Thread[btpool0-6,5,main] Thread[btpool0-7,5,main] Thread[btpool0-8,5,main] Thread[btpool0-9 - Invalidator - /jposee,5,main] Thread[Store default Spool Thread,2,main] Thread[Store default Expiry Thread,1,main] Thread[Timer-1,5,main] Thread[com.mchange.v2.async.ThreadPoolAsynchronousRunner$PoolThread-#0,5,main] Thread[com.mchange.v2.async.ThreadPoolAsynchronousRunner$PoolThread-#1,5,main] Thread[com.mchange.v2.async.ThreadPoolAsynchronousRunner$PoolThread-#2,5,main] Thread[Thread-23,5,main] Thread[Rollover,5,main] Thread[SystemMonitor,5,main] Thread[PooledThread-0-running,5,ThreadPool-0] jPOS supporting classes 1.6.1 41 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) --- name-registrar --- tspace:org.jpos.transaction.TransactionManager@12a3793: org.jpos.space.TSpace $TAIL 1 mux.selftest-mux: org.jpos.q2.iso.QMUX tx=17, rx=16, tx_expired=0, tx_pending=0, rx_expired=0, rx_pending=1, rx_unhandled=0, rx_forwarded=0, connected=true server.jcard-server: org.jpos.iso.ISOServer connected=1, rx=17, tx=16 127.0.0.1:33170: rx=17, tx=16 logger.: org.jpos.util.Logger tspace:default: org.jpos.space.TSpace selftest-send.21000000000029110001000000000014.req $TAILLOCK.19543955 2 jdbm:simulator:log/simulator: org.jpos.space.JDBMSpace txnmgr: org.jpos.transaction.TransactionManager logger.Q2: org.jpos.util.Logger capture-date: org.jpos.ee.CaptureDate channel.selftest-adaptor: org.jpos.iso.channel.CSChannel selftest-adaptor: org.jpos.q2.iso.ChannelAdaptor tx=16, rx=16, connects=1 Tip If you're using Q2 (revision greater than r2581), the default configuration deploys a SystemMonitor for you. See deploy/99_sysmon.xml. 4.5. Profiler org.jpos.util.Profiler is a very simple and easy to use user-space Profiler. It leverages the Logger subsystem to provide accurate information about processing times. These are Profiler's public methods: public void reset(); public void checkPoint (String detail); public long getElapsed(); public long getParcial(); See javadocs [http://www.jpos.org/doc/javadoc/org/jpos/util/Profiler.html] for details. Profiler implements Loggeable, so you can easily add a Profiler Object to a LogEvent to produce convenient profiling information. Example 4.1. Profiler Profiler prof = new Profiler(); LogEvent evt = new LogEvent (this, “any-transaction“, prof); // initialize message ISOMsg m = new ISOMsg (); m.setMTI (“1200“); ... ... prof.checkPoint (“initialization“); jPOS supporting classes 1.6.1 42 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) // send message to remote host ... ... ISORequest req = new ISORequest (m); mux.queue (req); ISOMsg response = req.getResponse (60000); prof.checkPoint (“authorization“); // capture data in local database ... ... prof.checkPoint (“capture“); ... ... Logger.log (evt); The previous example would produce output like this: initialization [2] authorization [167] capture [39] end [213] The values inside the square brackets are expressed in milliseconds; with this info in hand, we can easily find out where the time is spent in a given transaction. Tip The “end“ checkPoint is automatically computed at output time (that's when Logger calls its log listen- ers). 4.6. DirPoll Many jPOS-based applications have to interact with third-party legacy software (e.g., retail applications). Most of the time one can be lucky enough to deal with legacy applications capable of sending transactions over de- cent protocols (such as native TCP/IP, RMI, SOAP, XML-RPC, etc.), but sometimes you are not that lucky and the best thing you can get is a disk-based interchange, i.e., they place a request in a given directory, you process that request and provide a response. org.jpos.util.DirPoll uses the following directory structure: ..../request ..../response ..../tmp ..../run ..../bad and defines the following inner interfaces: jPOS supporting classes 1.6.1 43 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) public interface Processor { public byte[] process(String name, byte[] request) throws DirPollException; } public interface FileProcessor { public void process (File name) throws DirPollException; } You can either create a Processor or a FileProcessor to handle incoming traffic. Whenever a legacy application places a file in the request directory, your Processor (or FileProcessor) gets called, giving you a chance to process the given request and provide a response (if you're using a Processor, the response will be placed in the response directory). Example 4.2. DirPoll Processor public class DirPollProcessor implements DirPoll.Processor { DirPollProcessor () { super (); DirPoll dp = new DirPoll (); dp.setLogger (logger, “dir-poll“); db.setPath (“/tmp/dirpoll“); db.createDirs (); db.setProcessor (this); new Thread (dp).start (); } public byte[] process (String name, byte[] b) { return (“request: “ + name + “ content=“+ new String (b)).getBytes(); } } DirPoll has provisions to handle different kind of messages with different priority based on its file extension, so you can call: ... ... dp.addPriority (“.A“); dp.addPriority (“.B“); dp.addPriority (“.C“); ... ... in order to raise “.A“ priority over “.B“ and “.C“ requests (you can use any extension name). Before processing a given request, DirPoll moves it to the run directory, and then either to the response dir- ectory or to the bad directory (in case something goes wrong and a DirPollException has been thrown). Note If your application crashes, you have to take care of possible requests left sitting in the run directory. It is very important that your application writes the requests in the tmp directory (or any other tempor- ary directory in the same filesystem) and then moves them (after a proper operating system close opera- tion) to the request directory in order to guarantee that once a request is present in the request direct- ory, it is ready for DirPoll to process. jPOS supporting classes 1.6.1 44 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) Tip Don't trust your legacy application programmer. Please double check that the previous note has been taken into account. 4.7. ThreadPool org.jpos.util.ThreadPool, as its name implies, takes care of managing a pool of threads. Its constructor looks like this: public ThreadPool (int initialPoolSize, int maxPoolSize) (See javadocs [http://jpos.org/doc/javadoc/org/jpos/util/ThreadPool.html] for details). It's very useful to process short-lived threads, such as processing an authorization transaction. Instead of creat- ing a new thread per transaction, you can create a ThreadPool at initialization time and then call its ex- ecute(Runnable r) method. The thread will be returned to the pool when your run() method ends, so it is not a good idea to have long- running items (e.g., a for (;;) { ... } loop) in your Runnable. There's an inner interface called ThreadPool.Supervised that your Runnable can optionally implement: public class ThreadPool { public interface Supervised { public boolean expired (); } } In this case, ThreadPool will call your expired() method, and - if true - will attempt to interrupt the expired thread. Note that while this does not guarantee that your thread will gracefully end, it gives you a chance to re- pair a possible problem. Tip You can write some 'self-healing' code in your expired() implementation, but please make sure your code won't block for too long. Use only if you know what you're doing. ThreadPool implements ThreadPoolMBean, which exposes the following read-only properties: public int getJobCount (); public int getPoolSize (); public int getMaxPoolSize (); public int getIdleCount(); public int getPendingCount (); jPOS supporting classes 1.6.1 45 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) Chapter 5. Implementing Custom Packagers jPOS comes with several ISOPackager and ISOFieldPackager implementations which can be used either out- of-the-box or as a reference to encode (pack) and decode (unpack) messages that are built on the ISO-8583 standard. Tip For a list of out-of-the-box packagers you may want to have a look at the following directories: • modules/jpos/src/org/jpos/iso/packager (Java based packagers) • src/config/packager (GenericPackager configurations, see Section 5.1, “GenericPackager”) Although not required, most ISOPackager implementations extend the supporting class ISOBasePackager. This approach makes writting a custom packager a very simple task. It's basically just a matter of calling its public void setFieldPackager (ISOFieldPackager[] fld) method with a suitable array of ISOFieldPackagers. Let's look at a sample implementation: Example 5.1. ISO-8583 version 1993 packager implementation public class ISO93BPackager extends ISOBasePackager { private static final boolean pad = false; protected ISOFieldPackager fld[] = { /*000*/ new IFB_NUMERIC ( 4, “Message Type Indicator“, pad), /*001*/ new IFB_BITMAP ( 16, “Bitmap“), /*002*/ new IFB_LLNUM ( 19, “Primary Account number“, pad), /*003*/ new IFB_NUMERIC ( 6, “Processing Code“, pad), /*004*/ new IFB_NUMERIC ( 12, “Amount, Transaction“, pad), /*005*/ new IFB_NUMERIC ( 12, “Amount, Reconciliation“, pad), /*006*/ new IFB_NUMERIC ( 12, “Amount, Cardholder billing“, pad), /*007*/ new IFB_NUMERIC ( 10, “Date and time, transmission“, pad), /*008*/ new IFB_NUMERIC ( 8, “Amount, Cardholder billing fee“, pad), /*009*/ new IFB_NUMERIC ( 8, “Conversion rate, Reconciliation“, pad), /*010*/ new IFB_NUMERIC ( 8, “Conversion rate, Cardholder billing“, pad), ... ... ... /*123*/ new IFB_LLLCHAR (999, “Reserved for private use“), /*124*/ new IFB_LLLCHAR (999, “Reserved for private use“), /*125*/ new IFB_LLLCHAR (999, “Reserved for private use“), /*126*/ new IFB_LLLCHAR (999, “Reserved for private use“), /*127*/ new IFB_LLLCHAR (999, “Reserved for private use“), /*128*/ new IFB_BINARY ( 8, “Message authentication code field“) }; public ISO93BPackager() { super(); setFieldPackager(fld); } } Note [For a complete listing please see modules/jpos/src/org/jpos/iso/packager/ISO93BPackager.java] 1.6.1 46 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) 1See appendix B (ISOFieldPackagers) We hope you see the key idea: writing a custom packager involves diving into your interchange specification and setting up a suitable kind of field packager for every possible field. 1 5.1. GenericPackager After writing multiple ISOFieldPackager implementations, jPOS developer Eoin Flood came up with a nice idea: writing a GenericPackager that would read an XML configuration file and instantiate an ISOFieldPack- ager on-the-fly. Using this approach, the same packager we've seen in the previous example can be easily configured using GenericPackager and a simple XML file like this: Example 5.2. ISO-8583 version 1993 packager configuration - 07/2009 (r2966) length=“12“ name=“Amount, Cardholder billing“ pad=“false“ class=“org.jpos.iso.IFB_NUMERIC“/> ... ... ... Note [For a complete listing please see modules/jpos/cfg/packager/iso93binary.xml] Implementing Custom Packagers 1.6.1 48 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) GenericPackager uses a DTD defined in modules/jpos/cfg/packager/genericpackager.dtd that looks like this: GenericPackager's DTD eases the configuration of nested messages (an ISO-8583 field that is a full ISO-8583 message itself), e.g.: ... ... ... ... Implementing Custom Packagers 1.6.1 49 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) ... Implementing Custom Packagers 1.6.1 50 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) Chapter 6. Channel Implementations jPOS comes with several channel implementations, most of which are available in the modules/ jpos/src/org/jpos/iso/channel directory. 6.1. TCP/IP Socket-based channels Most TCP/IP-based channel implementations extend org.jpos.iso.BaseChannel and just override the sendMessageLength and getMessageLength methods. Let's have a look at org.jpos.iso.channel.CSChannel: it uses a two-byte message length header sent in net- work byte order (nbo) plus two bytes reserved for future use: public class CSChannel extends BaseChannel { ... ... protected void sendMessageLength(int len) throws IOException { serverOut.write (len >> 8); serverOut.write (len); serverOut.write (0); serverOut.write (0); } ... ... protected int getMessageLength() throws IOException, ISOException { int l = 0; byte[] b = new byte[4]; while (l == 0) { serverIn.readFully(b,0,4); l = ((((int)b[0])&0xFF) << 8) | (((int)b[1])&0xFF); if (l == 0) { (1) serverOut.write(b); serverOut.flush(); } } return l; } } (1) If message length is “0“, CSChannel bounces it immediately. This procedure is used to keep firewalls from timing out Here is a partial list of current channel implementations (for a complete list, have a look at modules/ jpos/src/org/jpos/iso/channel): Table 6.1. Class name Wire protocol CSChannel LL LL 00 00 [header] ISO-DATA LL LL represents the [header+] ISO-DATA length in network byte order 00 00 reserved for future use 1.6.1 51 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) Class name Wire protocol Header is optional ISO-DATA: ISO-8583 image NACChannel LL LL [TPDU] ISO-DATA LL LL represents the TPDU+ISO-DATA length in network byte order Optional TPDU (transport protocol data unit) ISO-DATA: ISO-8583 image NCCChannel LL LL [TPDU] ISO-DATA LL LL represents the TPDU+ISO-DATA length in BCD (binary coded decimal) Optional TPDU (transport protocol data unit) ISO-DATA: ISO-8583 image ASCIIChannel LLLL [header] ISO-DATA LLLL four bytes ASCII [header+] ISO-DATA length Optional header ISO-DATA: ISO-8583 image RawChannel LL LL LL LL [header] ISO-DATA LL LL LL LL is [header+] ISO-DATA length in network byte order ISO-DATA: ISO-8583 image VAPChannel LL LL 00 00 header ISO-DATA LL LL represents the header+ISO-DATA length in network byte order 00 00 reserved for future use VAP-specific header ISO-DATA: ISO-8583 image PADChannel [header] ISO-DATA Stream-based channel reads messages on-the-fly without using any kind of message boundary indicator. X25Channel X25 is similar to PADChannel but uses a slightly different strategy. Instead of pulling an ISO-8583 from a stream, unpacking it on the fly, X25Channel attempts to read full TCP/IP packets by specifying a small timeout value. Whenever possible, PADChannel seems like a better solution; however, cer- tain X.25 packet assembler/disassemblers sometimes send garbage over the wire (i.e. ETXs) which might confuse PADChannel. Channel Implementations 1.6.1 52 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) Class name Wire protocol XMLChannel Send/Receive messages in jPOS's internal XML message representation LogChannel Similar to XMLChannel, but you can feed it a jPOS Log, which is suitable to replay sessions 6.2. LoopbackChannel Loopback channel bounces all received messages using a blocking queue. It can be used for simulation pur- poses. When using in combination with a suitable ISOFilter, you can modify the outgoing message so it can easily simulate a response from an dummy remote host. package loopback; import java.io.IOException; import org.jpos.iso.ISOMsg; import org.jpos.iso.ISOFilter; import org.jpos.iso.ISOChannel; import org.jpos.iso.ISOException; import org.jpos.iso.channel.LoopbackChannel; import org.jpos.util.LogEvent; public class Test implements ISOFilter { public static void main (String[] args) { try { new Test().run(); } catch (Exception e) { e.printStackTrace(); } } public void run () throws ISOException, IOException { LoopbackChannel channel = new LoopbackChannel (); channel.addIncomingFilter (this); (1) ISOMsg request = createRequest(); request.dump (System.out, “request> “); channel.send (request); ISOMsg response = channel.receive(); response.dump (System.out, “response> “); } private ISOMsg createRequest () throws ISOException { ISOMsg m = new ISOMsg (“0800“); m.set (11, “000001“); m.set (41, “29110001“); m.set (70, “301“); return m; } public ISOMsg filter (ISOChannel channel, ISOMsg m, LogEvent evt) { (2) try { m.setResponseMTI (); m.set (39, “00“); } catch (ISOException e) { e.printStackTrace(); } return m; } } (1) All incoming messages will go through our custom filter (2) Filter implementation Channel Implementations 1.6.1 53 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) The previous program produces the following output: request> request> request> request> request> request> response> response> response> response> response> response> response> Tip For a better way to simulate a remote host, you can have a look at the serversimulator module in the jPOS-EE distribution. 6.3. ChannelPool ChannelPool is an ISOChannel implementation that delegates channel operations to its children channels. It can handle several children channels, making it suitable to implement transparent failover. By using its addChannel and removeChannel methods, you can react to network problems on-the-fly without affecting higher-level layers of your application. Tip As an alternative to the ChannelPool, Q2 applications can use multiple ChannelAdaptors configured with the same set of Space queues (in/out). In addition, there's a MUXPool that provides failover as well as round-robin load balancing. 6.4. SSL SocketFactories ISOServer, as well as most channels that inherit from BaseChannel can delegate socket creation to an optional socket factory. We have two kinds of socket factories: • ISOClientSocketFactory • ISOServerSocketFactory public interface ISOClientSocketFactory { public Socket createSocket(String host, int port) throws IOException, ISOException; Channel Implementations 1.6.1 54 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) } public interface ISOServerSocketFactory { public ServerSocket createServerSocket(int port) throws IOException, ISOException; } ...as well as a provider that implements both of them: org.jpos.iso.SunJSSESocketFactory Note SunJSSESocketFactory is available under the 'src/ext' module. Q2 (actually the ChannelAdaptor and QServer qbeans), accepts an optional 'socketFactory' property in the channel configuration, e.g.: Example 6.1. SocketFactory configuration sslsend sslreceive 10000 Tip While SunJSSESocketFactory can be used to demonstrate SSL support in jPOS, production-grade in- stallations should consider it just a reference/sample implementation. It uses ${user.home}/.keystore with a default password, so at the very least you want to override its getPassword() method. Channel Implementations 1.6.1 55 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) 1 See http://www.cs.yale.edu/Linda/linda-lang.html Chapter 7. jPOS Space 7.1. Overview jPOS's Space is a general-purpose coordination component inspired after The Linda Coordination Language. 1 While jPOS's Space is not a Linda implementation, we highly recommend learning about Linda in order to better understand our Space component. You can think about jPOS's Space component as being like a Map where its entries are lists of objects and its operations are fully synchronized. There are three basic operations: • void out (Object key, Object value) Put an object into the space. If an object under the given key already exists, the object is queued at the end of a list. • Object rd (Object key) Reads an object from the space. Block until one is present under the given key. • Object in (Object key) Take the object off the queue. Block until the object under the given key is present. In addition to those three basic operations, org.jpos.space.Space adds a few handy methods: • void out (Object key, Object value, long timeout) Place an object into the space using an expiration timeout. The entry is automatically removed upon expira- tion. • Object rd (Object key, long timeout) Wait a maximum of timeout milliseconds for a given entry; otherwise, return null. • Object in (Object key, long timeout) Wait a maximum of timeout milliseconds for a given entry; otherwise, return null. • Object rdp (Object key) Read an entry if it exists. • Object inp (Object key) Take an entry if it exists. 1.6.1 56 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) 2JDBM project: See http://sf.net/projects/jdbm 3See Chapter 8, Q2 - second generation QSP for details • void push (Object key, Object value) Same as out but the entry is placed at the head of the queue (like a Stack). • void push (Object key, Object value, long timeout) Same as the previous push operation with a timeout. jPOS Space interface: public interface Space { public void out (Object key, Object value); public void out (Object key, Object value, long timeout); public Object in (Object key); public Object rd (Object key); public Object in (Object key, long timeout); public Object rd (Object key, long timeout); public Object inp (Object key); public Object rdp (Object key); public void push (Object key, Object value); public void push (Object key, Object value, long timeout); } jPOS provides several Space implementations: • TSpace in-memory space implementation. • JDBMSpace Persistent space implementation - uses JDBM 2 for persistence. • SpaceProxy An RMI-based space proxy • SpaceInterceptor SpaceInterceptor can be used to intercept calls to the Space without having to extend a particular space im- plementation • SpaceLets A SpaceLet is a QBean that combines a space factory and a space interceptor with scripting capabilities. 3 Note VERY IMPORTANT: All Space operations may throw an 'SpaceError' unchecked exception. 7.2. SpaceFactory Although most Space implementations have either public constructors or factory methods that can be used to jPOS Space 1.6.1 57 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) create instances of their respective classes, we highly recommend using the SpaceFactory as the entry point for space creation or to obtain references to spaces that were previously created. Example 7.1. Using the SpaceFactory import org.jpos.space.Space; import org.jpos.space.SpaceFactory; Space sp = SpaceFactory.getSpace(); The previous example returns a reference to the default space, which happens to be a TSpace implementation registered with the name default. It's the same as calling: Space sp = SpaceFactory.getSpace(“tspace“); ...which is also the same as calling: Space sp = SpaceFactory.getSpace(“tspace:default“); SpaceFactory decodes a space URI based on the space implementation type, followed by an optional name and optional parameter(s): spacetype[:spacename[:spaceparam}} Table 7.1. Space URI Type Implementation tspace Creates or returns a reference to a previously-created instance of TSpace jdbm Creates or returns a reference to a previously-created instance of JDBMSpace. This URI accepts an optional parameter (after the Space name) which is a path to the persistent store, e.g., jd- bm:myspace:/tmp/myspace. spacelet Returns a reference to a previously-created instance of SpaceLet 7.3. Using the space jPOS's Space can be used to coordinate processes and to manage access to resources. We present here a couple of examples: Example 7.2. Controlling system throughput Imagine a situation where you want no more than 10 transactions to be processed simultaneously at any given jPOS Space 1.6.1 58 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) 4 currently available in the jPOS-EE distribution. time. There are several ways to do this using standard programming techniques, but let's look at how we'd im- plement this feature using jPOS's Space. We'll also discuss the advantages of the Space approach. At startup time: import org.jpos.space.Space; import org.jpos.space.SpaceFactory; public static final String PROCESS_TICKET = “PROCESS_TICKET“; Space sp = SpaceFactory.getSpace(); for (int i=0; i<10; i++) { sp.out (PROCESS_TICKET, new Object ()); } Then, at process time: Object ticket = null; try { ticket = sp.in (PROCESS_TICKET); // would block until a ticket is available // // process transaction // } finally { sp.out (PROCESS_TICKET, ticket); } The above example could be easily written using standard programming techniques, but using the Space ap- proach has some advantages; for example, you can use a SpaceProxy to implement a centralized throughput controller, or you could use the ReplicatedSpace 4 to implement a cluster-wide controller. Example 7.3. Coordinating clients and servers Coordinating clients and servers using the Space is very simple: the client just places a request object in the space under a given queue name and the server pulls these objects out of it. If you have multiple clients and multiple servers, you have to agree on a naming convention so the server can ensure it is responding to the correct client thread. In order to connect a given instance of a client with a given instance of a server, we have found it very useful to use 'little spaces' to wrap the real request objects so that responses are sent back by the server using the same little space. TinySpace is a lightweight space implementation suitable for just this kind of usage. We can have client code like this: public ISOMsg process (ISOMsg m, long timeout) { Space sp = SpaceFactory.getSpace (“tspace:myspace“); TinySpace ts = new TinySpace (); ts.out (“Request“, m); sp.out (“QUEUE“, ts); return (ISOMsg) ts.in (“Response“, timeout); } jPOS Space 1.6.1 59 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) ...with server code like this: Space sp = SpaceFactory.getSpace (“tspace:myspace“); for (;;) { Space ts = (Space) sp.in (“QUEUE“); ISOMsg m = (ISOMsg) ts.in (“Request“); ... ... process request, prepare response ... ts.out (“Response“, m); } Note jPOS does extensive use of the Spaces in order to provide inter-component communications. Objects such as ISOMsgs are moved along the system from one component to another one using Spaces. 7.4. SpaceTap The SpaceTap is a SpaceListener that can be used to monitor a given LocalSpace for new entries under a given key. Once a SpaceTap is created, it register itself as a listener in the source LocalSpace and copies all new entries to a destination space. Figure 7.1. Space Tap If we have a source LocalSpace ssp and a destination LocalSpace dsp and we want to monitor an entry called “ERRORS“, we can use code like this: jPOS Space 1.6.1 60 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) SpaceTap spt = new SpaceTap (ssp, dsp, “ERRORS“, “ERRORS.COPY“, 5000L); Tip If your “source“ space and “destination“ space are the same, you can use the shorter constructor: SpaceTap (LocalSpace ssp, Object key, Object tapKey, long timeout); A SpaceTap can be used for system monitoring purposes as it provides a non-intrusive way to “tap“ any given space queue. jPOS Space 1.6.1 61 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) Chapter 8. Q2 - second generation QSP 8.1. About Q2 After deploying QSP (see Chapter 13, Legacy components) in several mission-critical installations, we found it inconvenient to include all the components in a single [huge] configuration file for many reasons: • Although several QSP components support some limited ReConfiguration, many others don't. As a result, major changes usually involve restarting the application (a very costly operation in a 24/7 system) • If for some reason, the changes involved go beyond just tweaking a configuration file and require additional changes in a supporting jar file, the application has to be restarted (QSP doesn't support dynamic classload- ing). • Having a single big configuration file has proven to be error-prone. Although initially intended to be access- ible to system operators, changing QSP files on critical systems became an art reserved for experienced COOs ... Therefore, we've decided to use a simpler approach: a file per component and a very simple lifecycle to ease the implementation of such components, called QBeans (Q2 Beans). Note We use the terms QBeans and Q2 service interchangeable. QBeans are MBeans (see JMX specs) that implement the Q2's lifecycle (init/start/stop/destroy) set of opera- tions. Q2 takes care of registering them with an MBeanServer. 8.2. Building Q2 Q2 is just another jPOS module (see modules/q2) so unless you explicitly ignore it (by adding q2/** to the modules/ignore.list file) it will get compiled as part of the standard build. See Section 1.5, “Building jPOS” for details. 8.3. Building Q2 Modules The former q2Modules directory (used in versions prior to jPOS 6) with its own build system now sits in the modules/q2mod_jpos so unless you explicitly ignore it (by adding q2mod_jpos/** to the modules/ignore.list file) it will get compiled as part of the standard build. See Section 1.5, “Building jPOS” for details. 8.4. Q2 Primer 1. Running Q2 As of jPOS 6 and newer versions, running Q2 is basically running jPOS. Please review Section 2, “Running jPOS” for details. 1.6.1 62 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) If you add the '--help' parameter when starting jPOS (e.g: java -jar jpos.jar --help), you should see an output like this: usage: Q2 -c,--command Command to execute (1) -C,--config Configuration bundle (2) -d,--deploydir Deployment directory (3) -e,--encrypt Encrypt configuration bundle (4) -h,--help Usage information (5) -i,--cli Command Line Interface (6) -r,--recursive Deploy subdirectories recursively (7) -v,--version Q2's version (8) (1) a CLI command to execute (CLI is Q2's Command Line Interface). (2) Uses a QSP-like single file configuration for a group of QBeans (3) Lets you define an alternate deploy directory. The default is deploy (4) It is possible to encrypt an MBean deployment descriptor in order to protect it from the ocasional lurker (5) This help. (6) Enables Command Line Interface (CLI). Type “help“ for a list of installed commands. (7) Recurse over directories inside the deploy directory (can be used to group a set of related QBeans). (8) Displays Q2 version We'll revisit these individual options, but for now, let's start Q2 by using the following command: java -jar jpos.jar You should see an output like this: Q2 started, deployDir=/home/jpos/q2/deploy At this point, your Q2 is running and checking the deploy directory for new QBean descriptors (XML files) as well as the deploy/lib directory for new jars. Now, let's write a test QBean. The following QBean does very little but it is very useful to learn how Q2 works. QTest is a very simple QBean that just outputs a “tick“ every second. It is presented here in small fragments of code (with some parts omitted for clarity's sake). You can see a com- plete listing in Appendix E, QTest and QTestMBean source code. package org.jpos.qtest; public class QTest extends QBeanSupport implements Runnable, QTestMBean { long tickInterval = 1000; QTest extends QBeanSupport which is a general purpose QBean implementation that provides handy function- ality such as Logging, implementing the Configurable interface, etc. but you are actually not required to ex- tend QBeanSupport as long as you implement the org.jpos.q2.QBean interface. QBeanSupport is located in the q2 module in the modules/q2/src/org/jpos/q2 directory. In order to implement a QBean you basically need to provide four methods that implements the Q2 services li- Q2 - second generation QSP 1.6.1 63 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) fecycle: • init • start • stop • destroy QBeanSupport implements those methods for you providing a default implementation that in turn call four pro- tected methods: • initService • startService • stopService • destroyService as well as some handy methods that provide access to the QBean configuration (including its XML content), logging and even some helpers to write back the XML configuration. In addition, the initService, startService, stopService and destroyService set of protected methods take care of catching and logging any possible exception that your implementation may throw and keeping track of the QBean's “state“ (STARTING / STARTED / STOPPING / STOPPED / DESTROYED / FAILED) for you. By extending QBeanSupport you don't have to implement all the QBean lifecycle callbacks, just the ones you need. In our QTest case, we want our QBean to be a Runnable, so we just need to do something at “start“ time, there- fore we only have to override startService: public void startService() { new Thread(this).start(); } startService creates a new Thread passing an instance of your QTest class (which happens to be a Runnable) and starts it. public void run () { for (int tickCount=0; running (); tickCount++) { log.info (“tick “ + tickCount); ISOUtil.sleep (tickInterval); } } The run method is very simple, two things worth noting: • We can use “log.info“ here because QBeanSupport creates a Log (org.jpos.util.Log) object for you. Q2 - second generation QSP 1.6.1 64 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) 1Inversion of Control • the running() method is also provided by QBeanSupport and returns true if the service is either in the STARTING or STARTED state. In order to demonstrate the lifecycle of a QBean, we override some methods such as its constructor as well as the init(), start(), stop() and destroy() methods just to log an info message, but you obviously don't need to do that in your application. public QTest () { super(); log.info (“constructor“); } public void init () { log.info (“init“); super.init (); } public void start() { log.info (“start“); super.start (); } public void stop () { log.info (“stop“); super.stop (); } public void destroy () { log.info (“destroy“); log = null; } QBeans support two kind of configuration: the so-called IoC 1 where the container “pushes“ the configuration onto the QBean and a regular “pull“ method where you can query the Configuration object. (technically the second method is still a “push“ scheme as Q2 will “push“ a Configuration Object onto the component at “init“ time and the component can either hold a reference to the Configuration object or hold references to a subset of its properties) If you configure your QBean using properties, the configuration would look like this: So you can use code like this: String a = cfg.get (“mypropA“); String b = cfg.get (“mypropB“); or you can override void setConfiguration(Configuration) and store your a and b Strings as instance vari- ables. On the other hand, if you want to use attributes, you have to provide setters and getters and you need to expose those setters and getters as part of your MBean interface. In our QTest example we want to expose the “tickInterval“ as an attribute so we have to provide the following methods: Q2 - second generation QSP 1.6.1 65 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) public void setTickInterval (long tickInterval) { this.tickInterval = tickInterval; } public long getTickInterval () { return tickInterval; } In addition we have to define an interface called QTestMBean that extends QBeanSupportMBean and expose those two methods as well: public interface QTestMBean extends org.jpos.q2.QBeanSupportMBean { public void setTickInterval(long tickInterval) ; public long getTickInterval() ; } Note JMX requires these *MBean interfaces in order to know which attributes and operations should be managed by JMX and available through the MBeanServer. Covering JMX is beyond the scope of this guide, it's a small yet powerful API that we strongly recommend you to read. When using attributes, the QBean descriptor looks like this: 5000 Note If you look at Appendix E, QTest and QTestMBean source code you'll see that we also provide a void setPersist(Element) and Element getPersist() operations. These are extensions to the QBean provided by QBeanSupport that let a QBean return its own XML configuration so QBeanSupport can update the XML file in order to persist changed properties. You are free to ignore these feature unless you have a strong use case for it. See org.jpos.q2.QPersist for further details. Compiling QTest You can compile QTest and QTestMBean using whatever way you like, however, we'll tell you here how we'd do it in a jPOSsy way, by means of a little module. Let's call our module qtest, you want to create the following directory structure: modules/qtest/src/org/jpos/test/QTest.java modules/qtest/src/org/jpos/test/QTestMBean.java modules/qtest/deploy/10_qtest.xml Once you call “ant“, your *.java classes will be compiled into build/jpos.jar and your deployment descriptor will be copied to build/deploy/10_qtest.xml so the next time you start Q2, your QBean is going to be Q2 - second generation QSP 1.6.1 66 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) launched. Once you start jPOS/Q2 again you'd see: deploy:qtest.xml constructor setName qtest setPersist init start tick 0 tick 1 tick 2 tick 3 ... ... QTest is a verbose QBean, so it logs an output message when its constructor is called as well as when other li- fecycle methods, such as init, start, stop and destroy are called. In our example, we also implement QPersist so we see the calls to setPersist that you are free to ignore. Try to move QTest (build/deploy/10_qtest.xml) away from your deploy directory and it should stop run- ning. Q2 - second generation QSP 1.6.1 67 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) undeploying:qtest.xml stop destroy undeployed:qtest.xml Tip Please note the realm of these log events: QTest-originated messages use the realm 'qtest' while Q2 uses 'Q2.system'. You can use this facility to write a custom LogListener that can react to specific realms. Our QTest bean supports a tickInterval attribute. So, while Q2 is still running, try to modify qtest.xml so it looks like this: 5000 Now, the tick shows every five seconds instead of every second. QBeans are registered into an JMX MBeanServer, so we can change their parameters either by modifying the deployment descriptors, or by using a JMX to communicate with them. You can use jconsole in order to con- nect to the running JVM and modify the tickInterval on the fly. Figure 8.1. QTest in jConsole Q2 - second generation QSP 1.6.1 68 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) Tip In order to run jconsole you want to start Q2 using -Dcom.sun.management.jmxremote. See the bin/ q2 Unix script for details. From there, you can change the tickInterval. Let's put a value of 20000 in there and check the results. As you can see, QTest now 'ticks' every 20 seconds, but what's more important is that deploy/10_qtest.xml now looks like this: 20000 It has been automatically persisted back to disk by Q2 (see QPersist javadocs for details) 2. QBean descriptors The XML used to configure a QBean is extremely flexible and uses sensible defaults whenever possible. The minimum QBean configuration has an element name with a class attribute: Your org.project.MyClass class is supposed to implement the interface org.jpos.q2.QBean which can be easily achieved by extending org.jpos.q2.QBeanSupport as previously described. In the previous sample QBean configuration, Q2 would attempt to instantiate your class and register it with the MBeanServer (see JMX specs) using the name “Q2:type=qbean,service=myqbean“. In addition, if your class happens to have a method with a signature void setName (String), then Q2 would register the instance with the local NameRegistrar (see Section 4.2, “NameRegistrar”). The “name“ used both to register the QBean with the MBeanServer and eventually with the NameRegistrar can be taken either from the element name or from an explicit name attribute, e.g: If we don't specify a classname, Q2 tries to get a suitable class based on the element name using an internal re- source bundle called QFactory.properties (located in the modules/q2/src/org/jpos/q2 directory). As of the release r2584 (Dec/2007) QFactory.properties looks like this: logger=org.jpos.q2.qbean.LoggerAdaptor shutdown=org.jpos.q2.qbean.Shutdown script=org.jpos.q2.qbean.BSH jython=org.jpos.q2.qbean.Jython spacelet=org.jpos.q2.qbean.SpaceLet sysmon=org.jpos.q2.qbean.SystemMonitor txnmgr=org.jpos.transaction.TransactionManager transaction-manager=org.jpos.transaction.TransactionManager qmux=org.jpos.q2.iso.QMUX Q2 - second generation QSP 1.6.1 69 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) channel-adaptor=org.jpos.q2.iso.ChannelAdaptor This means that when you configure a QBean using one of the aforementioned element names, you don't need (you still can if you want) to specify its class name, e.g.: ... ... would be basically the same as ... ... Note At this point you might see why we need a special name attribute, if we have many loggers and we don't want to specify its class name, we want to use the shortcut , but we still need a way to differentiate them by using different names. The Shutdown QBean Based on the previous QFactory.properties based mapping we could deploy a file shutdown.xml like this: Note The name shutdown.xml could be any other name you want. The shutdown QBean is implemented like this: package org.jpos.q2.qbean; import org.jpos.q2.QBeanSupport; public class Shutdown extends QBeanSupport { public void startService() { getServer().shutdown (); } } By deploying the shutdown QBean you have a clean way to stop a given Q2 instance. Tip Q2 - second generation QSP 1.6.1 70 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) If you are a using jPOS-EE, you'd see that bin/stop deploys a shutdown QBean while the bin/q2 script makes sure it is not present in the deploy directory at startup time. In addition to the class attribute, Q2 supports two additional attributes: • logger If the the QBean is an instance of the LogSource interface (which is the case for all QBean implementations that extend QBeanSupport), a logger object is pushed to the QBean by calling its void setLogger (Logger logger, String realm) method. • realm If a realm attribute is present, then it will get passed in the call to setLogger, otherwise the QBean's name would be used as the realm. The XML configuration file can have any format up to the point that a QBean can implement the org.jpos.core.XmlConfigurable interface (in this case, Q2 would push a JDom Element to the QBean so it can deal with the XML fragment in any way it needs). But most of the time the QBean implementation just requires some configuration properties, so it suffice to im- plement org.jpos.core.Configurable which requires a single method void setConfiguration (Configuration) throws ConfigurationException (which is provided by QBeanSupport). 8.5. Q2 Dynamic ClassLoading Q2 scans the deploy directory looking for new QBean descriptors, and also scans the deploy/lib directory looking for new jars, which become automatically available to QBeans. If you have to add a new feature to a running system, you can isolate its functionality in a new jar, place that jar in deploy/lib and then place the XML descriptor in the deploy directory so that Q2 can take care of starting your QBean. In addition to local dynamic classloading, Q2 can pick the required jars from a remote repository or location withing the filesystem. In order to take advantage of this feature, your QBean descriptor should look like this: http://com.example/q2/Your.jar http://com.example/q2/YourOther.jar ... ... Tip Using a remote repository can be very useful when running a cluster of Q2 nodes. Note Dynamic Classloading brings a subtle problem into play when dealing with Singletons such as the NameRegistrar as you end up having one instance per classloader. If you intend to use this feature, you have to be aware of this problem and make sure that you have a single jPOS.jar available in your main lib directory. Q2 - second generation QSP 1.6.1 71 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) 8.6. Q2 Scripts Q2 supports a very easy way to run BSH or Jython-based scripts: ...or: print (“Hello Jython“); If you look at q2/src/main/org/jpos/q2/QFactory.properties, you'll notice that the special names script and jython are mapped to the QBean implementations: script=org.jpos.q2.qbean.BSH jython=org.jpos.q2.qbean.Jython The BSH script support places a few useful references to Q2 objects that can be used by your script implement- ation, namely: Table 8.1. BSH script context variables Variable name Description qbean a reference to this qbean, that can be used for ex- ample to check for qbean.running() log the log object associated with this qbean, you can use call log.info (“BSH is running“); Example 8.1. BSH based Q2 script Tip BSH supports a powerful (yet dangerous, security-wise) debugging tool that can be deployed using a single-line script (see below), and then telneting to your host (in the example below, by using port 6666), in order to get an interactive bsh shell that runs on your live Q2 JVM. This tool can be used to debug a running application, query the NameRegistrar contents, check memory usage and take correct- ive actions. 8.7. Q2 SpaceLet A SpaceLet (org.jpos.q2.qbean.SpaceLet) is a Space interceptor with scripting capabilities. It lets the user define a special space that has the opportunity to react to certain space operations using BSH scripts. Example 8.2. SpaceLet example jdbm:test String cntKey = key + “.count“; Integer counter = sp.inp (cntKey); int cnt = counter != null ? counter.intValue() : 0; sp.out (cntKey, new Integer (++cnt)); if (sp.rdp (key) != null) { String cntKey = key + “.count“; Integer counter = sp.inp (cntKey); int cnt = counter != null ? counter.intValue() : 0; sp.out (cntKey, new Integer (--cnt)); } In the previous example, whenever you out an entry into the Space spacelet:mytest, another entry with the same key and a “.count“ suffix is automatically maintained by the SpaceLet. In addition to and , the SpaceLet QBean supports a element as well as an script that can be used to perform initial housekeeping of the Space, as well as multiple script(s) that can be used to perform additional Space-related tasks (such as space monitoring, house-keeping, etc). , and accept a “source“ attribute, so you can have an external bsh source file instead of the internal script. Q2 - second generation QSP 1.6.1 73 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) If you specify a name in the spacelet element, then that name is used in the space URI (used by SpaceFactory) instead of “default“, e.g.,: would be registered as “spacelet:mySpace“. In the out operation, if the script returns a true boolean value, it is considered that it already has taken care of the operation so that SpaceLet won't out that entry into the space. On the other hand, if the script returns false (or it doesn't return anything), the SpaceLet implementation would honor the operation. In a rd or in operation, if the script returns true, then the “value“ script variable is returned instead of querying the underlying space. Tip While this feature sounds very powerful, you should use it with care, as SpaceLet operations are not as fast as direct space operations or compiled SpaceInterceptors. SpaceLets can be used for simple tasks such as triggering the garbage collector in a JDBM space, e.g.: jdbm:test:/tmp/test import org.jpos.space.JDBMSpace; if (sp instanceof JDBMSpace) { while (spacelet.running()) { Thread.sleep (60000); ((JDBMSpace)sp).gc (); } } In the previous example, there's no performance impact as the application would use jdbm:test directly and not the intercepted space spacelet:test. Q2 - second generation QSP 1.6.1 74 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) Chapter 9. Q2Mod jPOS Q2Mod jPOS (located at modules/q2mod_jpos) gives Q2 most of its jPOS functionality. It is required to run jPOS inside the Q2 container. It contains adaptors which provide the Q2 lifecycle (init/start/stop/destroy) to existing jPOS and legacy QSP components. It also adds a few components unique to Q2. The adaptors live in the following packages: • org.jpos.q2.iso • org.jpos.q2.security • org.jpos.q2.ui Table 9.1. Q2 Modules Package Name Description iso ChannelAdaptor Can be used to configure existing ISOChannel implementations iso QMUX A new MUX implementation that replaces ISOMUX iso QServer Can be used to configure existing ISOServer iso DirPollAdaptor Can be used to configure DirPoll iso TaskAdaptor Adaptor for legacy QSP Tasks iso DailyTaskAdaptor Adaptor for legacy QSP Daily Tasks iso CardAgentAdaptor Used to configure CardAgents security KeyStoreAdaptor Used to configure a KeyStore security SMAdaptor Used to configure a Security Mod- ule provider ui UI Can be used to configure UI com- ponents 9.1. ChannelAdaptor The ChannelAdaptor is an unique component not available in previous versions of jPOS/QSP. It is used to con- figure a standard client ISOChannel and it takes care of keeping it connected to a remote host. In addition to that, ChannelAdaptor's communication with other components is performed through a Space- based interface, which brings into play the ability to have more than one channel connected to a given host, as well as the ability to distribute channels accross multiple Q2 machines in a cluster. 1.6.1 75 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) Let's have a look at a sample configuration: sample-send sample-receive 5000 tspace:default Tip in and out are seen from the ChannelAdaptor's standpoint. Input to the ChannelAdaptor means something that has to be actually sent throught the underlying ISOChannel. When you have to send a message to a channel using the ChannelAdaptor, you can use code like this: import org.jpos.space.Space; import org.jpos.space.SpaceFactory; import org.jpos.iso.ISOMsg; ... ... Space sp = SpaceFactory.getSpace(); ISOMsg m = new ISOMsg (“0800“); m.set (3, “000001“); m.set (70, “301“); sp.out (“sample-send“, m); // “sample-send“ is ChannelAdaptor's input ISOMsg m = (ISOMsg) sp.in (“sample-receive“, 60000); ... ... The ChannelAdaptor places a special entry in the Space in order to inform other components about the underly- ing channel status. That entry has a special arbitrary name, which is composed by the adaptor's name plus the literal .ready. So in our previous example, the adaptor was called “sample-channel-adaptor“. If we want to know if the channel is ready, we can easily check: if (sp.rdp (“sample-channel-adaptor.ready“ != null)) { ... ... } ChannelAdaptor implements the org.jpos.iso.Channel interface, so you can locate the channel by its name via the NameRegistrar and use traditional methods like: public void send (ISOMsg m); public ISOMsg receive (); public ISOMsg receive (long timeout); Q2Mod jPOS 1.6.1 76 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) In addition to those methods, ChannelAdaptor provides: public void send (ISOMsg m, long timeout); public boolean isConnected(); Note ChannelAdaptor was designed to operate with QMUX, the new-generation space-based multiplexer in- tended to replace ISOMUX (see Section 9.2, “QMUX”). The 'channel' element of the channel adaptor supports the following attributes and properties: Table 9.2. ChannelAdaptor attributes Attribute Description class ISOChannel implementation classname packager ISOPackager implementation classname logger an optional logger realm optional logger's realm header optional channel header socketFactory optional socketFactory implementation Table 9.3. Channel properties Property Description host remote host name or IP address port remote port max-packet-length optional parameter, defaults to 100000 local-iface optional parameter, local interface name used in multi-homed setup local-port optional local port used in multi-homed setup alternate-host if main host/port can't be reached, use list of alternate hosts/ports alternate-port see previous property, alternate-host. The number of alternate-port properties must match the number of al- ternate-host properties override-header optional parameter, defaults to false. If true, the chan- nel's header overrides the ISOMsg's header timeout socket level timeout in millis. The channel is con- sidered invalid if no messages are received after the given timeout. Defaults to no timeout but it's strongly Q2Mod jPOS 1.6.1 77 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) Property Description recommended to set one connect-timeout timeout used at connect time in millis 9.2. QMUX QMUX is a modern, very simple yet powerful Q2 component designed to replace ISOMUX. It closely adheres to the Q2 framework design where components can be added and removed on the fly. The main difference between ISOMUX and QMUX is that the latter uses the Space in order to communicate with the underlying channels; this strategy brings into play a whole new set of deployment options, including the ability to multiplex several channels for redundancy/load-balancing. These channels doesn't even have to run on the same machine. They can use distributed/remote space implementations. A QMUX configuration looks like this: sample-receive sample-send If you recall our ChannelAdaptor configuration in the previous section, you'll notice that the “in“ and “out“ queue names are interchanged. in and out are seen from the component's perspective. QMUX is registered in the NameRegistrar under the name provided in the qbean configuration file (“mymux“ in our example). So that other components can get a reference, cast it to MUX and use its: public ISOMsg request (ISOMsg m, long timeout) throws ISOException; method. If you remember QSP and ISOMUX, you might wonder how to handle incoming messages that don't match an outgoing request (i.e. late responses, network management messages originated by a remote server, etc.). The old ISOMUX had a request listener for that, but in Q2, with clusters, redundancy and scalability in mind, we use the space instead. You can configure an optional unhandled queue, and QMUX will place all arriving messages that don't match any pending request in that queue. The configuration looks like this: sample-receive sample-send mymux.unhandled A separate process can be used to pull messages out of the mymux.unhandled queue for further processing. In addition to the unhandled queue, QMUX supports multiple request listeners, e.g: Q2Mod jPOS 1.6.1 78 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) sample-receive sample-send mymux.unhandled In order to match responses with their original requests, QMUX uses a constructed key which defaults to the matching response MTI plus data element 41 (terminal ID) and data element 11 (Serial Trace Audit Number). The user can override the fields used to compose a key by using the 'key' element, e.g: ... 11, 37, 41, 42 9.3. QServer QServer is a simple service that wraps an ISOServer. The configuration looks like this: 8000 QServer uses a ThreadPool to handle incoming connections. The default limits for the ThreadPool are 1 (minSessions) and 100 (maxSessions), but you can easily change that by adding the following attributes to your configuration : 10 250 Q2Mod jPOS 1.6.1 79 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) QServer supports access control based on the peer's IP address, e.g.: ... ... Note jPOS/ISOServer uses a ThreadPool in order to accept simultaneous connections. Every thread in that pool then handles incoming messages, and forwards them to the registered ISORequestListeners in a synchronous way (single-thread). It is the responsibility of the ISORequestListener to use another ThreadPool in order to handle simultaneous, long-lived transactions. Tip In order to avoid having to use a ThreadPool to handle long-lived sessions, the user may choose to use the space in order to hand off the processing of incoming requests to another component, such as the TransactionManager. 9.4. DirPoll org.jpos.q2.iso.DirPollAdaptor is used to instantiate a DirPoll component, which can be used to poll a dir- ectory for new requests or files to process (i.e. interchange files, settlement files, or simple disk-based authoriz- ation requests) (See Section 4.6, “DirPoll”). The DirPoll configuration looks like this: spool .txt 1 5000 my.dirpoll.Processor Table 9.4. DirPoll attributes Attribute Description path Directory path where DirPoll should poll for requests poolSize Indicates the maximum number of concurrent re- quests that can be delegated simultaneously to Dir- Poll's processor. pollInterval Poll time expressed in milliseconds. priorities An optional attribute consisting of a space-separated list of file extensions indicating poll priority. processor A class implementing either the org.jpos.util.DirPoll.Processor or org.jpos.util.DirPoll.FileProcessor interface. Q2Mod jPOS 1.6.1 80 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) 9.5. TaskAdaptor QSP has a handy way of integrating POJOs by using a configuration fragment. Q2 has an adaptor for these simple tasks that you can use with a configuration like this: com.mycompany.jpos.MyTask If your task happens to implement org.jpos.core.Configurable, then TaskAdaptor will take care of pushing a Configuration object. In case your task implements Runnable, TaskAdaptor will start a new thread for it. At stop time, if your task happens to implement org.jpos.util.Destroyable, its destroy() method would get called. TaskAdaptor also attempts to set a Logger - provided that the managed class implements org.jpos.util.LogSource. Note This component is intended to be used with “legacy“ task implementations. In the case of new code, we strongly recommend you implement a QBean, ideally by extending QBeanSupport. 9.6. DailyTaskAdaptor DailyTaskAdaptor can be used to run jobs at a specific time. It is configured in a similarly to the previously de- scribed TaskAdaptor with an additional start property. com.mycompany.jpos.MyDailyTask 9.7. SMAdaptor org.jpos.q2.security.SMAdaptor takes care of instantiating a security module adaptor and registering it with the NameRegistrar: Example 9.1. SMAdaptor Configuration org.jpos.security.jceadapter.JCESecurityModule Q2Mod jPOS 1.6.1 81 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) The impl attribute is used to define the SMAdaptor provider implementation. 9.8. KeyStoreAdaptor This QBean takes care of instantiating a KeyStore and registering it with the NameRegistrar: Example 9.2. KeyStoreAdaptor configuration org.jpos.security.SimpleKeyFile 9.9. QExec QExec lets you run external applications at start and stop time. It can be used to notify other applications (such as system status monitoring, database servers, etc.) about Q2 starting or stopping. Example 9.3. QExec configuration path/to/start_program; path/to/stop_program 9.10. Jetty Integration Q2 can be wrapped inside a 'war' or 'sar' and deployed inside a webcontainer or application server, such as Tomcat or JBoss. Or it can be used the other way around, starting an embedded web container, such as Jetty. Running Jetty inside Q2 is as simple as deploying the following QBean: path/to/jetty.xml Note Describing the Jetty configuration (path/to/jetty.xml) is beyond the scope of this programmer's guide; detailed information can be obtained in Jetty's website [http://www.mortbay.com/]. Q2Mod jPOS 1.6.1 82 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) Chapter 10. TransactionManager 10.1. Overview jPOS is typically used to implement mission-critical applications that have to deal carefully with error condi- tions. When you access a web page and a transient network error occurs, you just hit the reload button on your browser. By contrast, a complex financial transaction involves a lot of activities such as contacting remote hosts, perform PIN-based validations and pinblock transactions, database logging, etc. So, if something goes wrong or your machine just dies due to a power failure, its more complicated than simply hitting the reload but- ton: you have to reverse the impact of whatever actions had been committed until the failure point. The org.jpos.transaction package - along with the Q2-based TransactionManager implementation - provides the necessary framework and components required to deal with the previous scenerio. This combina- tion also fosters code reuse and 'componentization.' The key class is the TransactionParticipant ht- tp://jpos.org/doc/javadoc/org/jpos/transaction/TransactionParticipant.html, which exposes the following the fol- lowing interface: public interface TransactionParticipant extends TransactionConstants { public int prepare (long id, Serializable context); public void commit (long id, Serializable context); public void abort (long id, Serializable context); } (for the records, TransactionConstants provides the following constants) public interface TransactionConstants { public static final int ABORTED = 0; public static final int PREPARED = 1; public static final int NO_JOIN = 0x40; public static final int READONLY = 0x80; } The TransactionManager implementation takes care of 'driving' the transaction by calling all of the participants' prepare methods. If all of them return 'PREPARED', then the transaction moves to the committing phase, at which point the TransactionManager will call all of the participants' commit methods. If one of the participants' prepare methods were to return ABORTED, then the transaction would move into an aborting phase, and all the participants' abort methods would get called. 10.2. Transaction Constants Table 10.1. Return values Name Value Description ABORTED 0 The participant is not prepared. The transaction must be aborted. 1.6.1 83 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) Name Value Description PREPARED 1 This participant is prepared to commit the transaction, provided all other participants down the list return PREPARED, too. Modifiers NO_JOIN 0x40 This modifier is a hint to the TransactionManager to let it know that it is not required to call this participant's commit or abort methods once the committing or aborting phase of the transaction is reached. READONLY 0x80 This modifier is a hint to the TransactionManager to let it know that this participant has not modi- fied any persistent information in the context, so saving a snapshot of the context is not required. Note Despite the fact that a partipant may indicate that it doesn't want to JOIN a given transaction (by using the NO_JOIN run-time modifier), under certain recovery situations the TransactionManager may still call its commit or abort method, so the participant developer should be prepared to receive a commit or abort call for an unknown transaction. The code should check the long id and / or Serializable context in order to figure out what to do. 10.3. Transaction Context The only constraint imposed on a Context implementation is that it has to implement the java.io.Serializable interface. That's because the TransactionManager has to write snapshots of it at differ- ent points. You can use any Serializable object, either a custom object such as an application-specific Bean, or a gener- al-purpose object such as a java.util.Map implementation (e.g., a Hashmap). We found it very useful to use a general-purpose context holding two maps, a regular (persistent) map and a transient map, e.g.: public class Context implements Serializable, Loggeable { private transient Map tmap; // transient map private Map map; // persistent (serializable) map public Context () { super (); tmap = Collections.synchronizedMap (new LinkedHashMap ()); map = Collections.synchronizedMap (new LinkedHashMap ()); } } TransactionManager 1.6.1 84 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) ...with methods such as: public void put (Object key, Object value) { map.put (key, value); } public Object get (Object key) { return map.get (key); } public Object remove (Object key) { return map.remove (key); } ...which operate in the standard (persistent/serializable) map... and a set of t* methods: public void tput (Object key, Object value) { tmap.put (key, value); } public Object tget (Object key) { return tmap.get (key); } public Object tremove (Object key) { return tmap.remove (key); } ...which operate in the transient map. The transient map gives you the ability to place non-Serializable objects in the context, for example: a live ob- ject such as a socket connection; a jdbc connection; a thread, managed object, etc. 10.4. Context Recovery interface In the previous section, we described a Transaction Context holding two maps: a transient map and a persistent map. In a situation where the TransactionManager dies (e.g., during a power failure), a transaction may be in its pre- paring, committing or aborting phase. Either the commit or abort methods will be called on all participants, but before that happens, the Transaction- Manager gives the developer the posibility to let the participant know that we are not dealing with a normal situation but a recovery situation. The developer can also implement the ContextRecovery interface: public interface ContextRecovery { public Serializable recover (long id, Serializable context, boolean commit); (1) } (1) commit = true (if the transaction is going to commit); false (if it's going to abort). The TransactionManager provides the opportunity to build up the transient part of the Context (e.g., re- stablishing a JDBC connection, re-fetching a database record based on some persistent ID number, etc.). 10.5. AbortParticipant TransactionManager 1.6.1 85 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) Imagine you have a list of participants that conform a transaction, for example: • ValidateMessage (sanity checks) • FetchData (i.e. get Terminal/Merchant info from database) • QueryRemoteHost • LogTransaction • SendResponse If everything goes okay and all participants return PREPARED, then you'll have no problem reaching the last set of participants. By contrast, if for some reason a given participant fails (e.g., suppose FetchData fails), then the succeeding participants down the list (in our example, FetchData through SendRepsonse) won't get called be- cause the transaction manager will initiate the aborting procedure (which will call abort(id,context) only on the previously-called participants, i.e., only on ValidateMessage in our example). In the previous example, while it's okay to ignore a call to the QueryRemoteHost participant, you may still want to send a response to the client, or even log the transaction. So the developer can implement the optional AbortParticipant interface: public interface AbortParticipant extends TransactionParticipant { public int prepareForAbort (long id, Serializable context); } ...in order to let the TransactionManager know that you still want to join the transaction, even if it's known to have failed. 10.6. TransactionManager Reference Implementation org.jpos.q2.transaction.TransactionManager provides a Q2-compatible reference implementation of the previous contracts. Let's have a look at a sample configuration: Table 10.2. TransactionManager configuration TransactionManager 1.6.1 86 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) Property Description space Space used to inject transactions into this transaction manager. queue Queue name where the transaction manager pulls for new messages to be processed (sp.in(queue)). sessions Number of transactions that can be processed simul- taneously. persistent-space This TransactionManager reference implementation uses a persistent space to store a snapshot of ongoing transactions for recovery procedures. This persistent space has to be unique (see note). Note The transaction manager requires exclusive access to its persistent space. If you plan to deploy more than one transaction manager, you have to use different persistent spaces. 10.7. Integration with ISORequestListener It is extremely easy to integrate this TransactionManager framework with our ISORequestListeners. Here is how a process can be implemented: // Constants defined elsewhere ... public static final String REQUEST = “REQUEST“; public static final String ISOSOURCE = “ISOSOURCE“; public boolean process (ISOSource source, ISOMsg m) { Context ctx = new Context (); ctx.put (REQUEST, m); ctx.tput (ISOSOURCE, source); (1) sp.out (cfg.get “queue“, ctx, cfg.getLong (“timeout“)); (2) return true; } (1) Please note ISOSource is a live object. Therefore, it makes no sense to store it in our persistent snap- shot for recovery. Instead, we use the transient map of our custom context (as described above). (2) Hands off the request to the TransactionManager. ISORequestListener is free to return. The transaction manager then pulls that context out of the queue, processes it, and uses the ISOSource reference in order to send a response back to the client. 10.8. GroupSelector Having a configuration like this: TransactionManager 1.6.1 87 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) ... ...may be good for some applications, but you risk ending up having to configure multiple transaction managers for different classes of transactions (e.g., network management, authorization, draft capture, etc.). In order to simplify the TransactionManager configuration, we've added a very simple interface called GroupSelector: public interface GroupSelector extends TransactionParticipant { public String select (long id, Serializable context); } A participant implementing the GroupSelector interface can modify the flow of this transaction by returning a space-separated list of group names (or can specify 'null' to signify no action). Our Q2-based TransactionManager reference implementation supports this interface and lets you design your own configuration file with a structure like this: ... ... ... ... Example 10.1. GroupSelector public class Switch implements GroupSelector { public int prepare (long id, Serializable context) { return PREPARED | READONLY | NO_JOIN; } public void commit (long id, Serializable context) { } public void abort (long id, Serializable context) { } public String select (long id, Serializable context) { try { ISOMsg m = getRequest ((Context) context); String groups = cfg.get (m.getMTI(), null); return groups; } catch (Exception e) { warn (e); TransactionManager 1.6.1 88 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) return null; } } } By using the Switch presented in the previous example, you can write a TransactionManager configuration file like this: ... ... ... ... ... ... ... ... ... ... Using the previous approach, the application can be designed using small reusable participants. We have found it very useful to have very small participants to peform tasks like: Debug the context; introduce Delays (during testing); Open and Close O/R mapping sessions, etc. 10.9. Context implementation Example 10.2. General Purpose context implementation package org.jpos.transaction; import java.util.Map; import java.util.LinkedHashMap; import java.util.Iterator; import java.util.Collections; import java.io.IOException; import java.io.Serializable; import java.io.PrintStream; import org.jdom.Element; import org.jdom.output.Format; TransactionManager 1.6.1 89 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) import org.jdom.output.XMLOutputter; import org.jpos.util.Loggeable; public class Context implements Serializable, Loggeable { private transient Map tmap; // transient map private Map map; // persistent (serializable) map public Context () { super (); tmap = Collections.synchronizedMap (new LinkedHashMap ()); map = Collections.synchronizedMap (new LinkedHashMap ()); } /** * Transient Put */ public void tput (Object key, Object value) { tmap.put (key, value); } /** * Transient Get */ public Object tget (Object key) { return tmap.get (key); } /** * Transient remove */ public Object tremove (Object key) { return tmap.remove (key); } /** * Transient get with default */ public Object tget (Object key, Object defaultValue) { Object value = tmap.get (key); if (value == null) value = defaultValue; return value; } /** * Persistent put */ public void put (Object key, Object value) { map.put (key, value); } /** * Persistent get */ public Object get (Object key) { return map.get (key); } /** * Persistent remove */ public Object remove (Object key) { return map.remove (key); } /** * Persistent get with default */ public Object get (Object key, Object defaultValue) { Object value = map.get (key); if (value == null) value = defaultValue; return value; } public long getLong (Object key, long defaultValue) { Long L = (Long) map.get (key); return L == null ? defaultValue : L.longValue(); } public long getLong (Object key) { TransactionManager 1.6.1 90 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) return getLong (key, 0L); } public long getTLong (Object key, long defaultValue) { Long L = (Long) tmap.get (key); return L == null ? defaultValue : L.longValue(); } public long getTLong (Object key) { return getTLong (key, 0L); } /** * @return transient map */ public Map getTransient() { return tmap; } /** * @return persistent map */ public Map getPersistent() { return map; } /** * Persistent get String */ public String getString (Object key) { return (String) map.get (key); } public void dump (PrintStream p, String indent) { String inner = indent + “ “; p.println (indent + ““); p.println (indent + “ “); dumpMap (tmap, p, inner); p.println (indent + “ “); p.println (indent + “ “); dumpMap (map, p, inner); p.println (indent + “ “); p.println (indent + ““); } private void dumpMap (Map map, PrintStream p, String indent) { if (map == null) return; Iterator iter = map.entrySet().iterator(); while (iter.hasNext()) { Map.Entry entry = (Map.Entry) iter.next (); p.print (indent + ““); Object value = entry.getValue(); if (value instanceof Loggeable) { p.println (““); ((Loggeable) value).dump (p, indent + “ “); p.print (indent); } else if (value instanceof Element) { p.println (““); p.println (indent+ ““); } else if (value != null) { p.print (value.toString ()); } else { p.print (“nil“); TransactionManager 1.6.1 91 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) } p.println (““); } } private static final long serialVersionUID = 1L; } TransactionManager 1.6.1 92 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) Chapter 11. UI Framework 11.1. Overview jPOS UI is a very simple and lightweight UI layer that enables you to write reusable UI components and easily assemble them by using a QBean configuration file. It can be embedded in standalone applications (see src/examples/ui/Test.java) or used inside Q2. The UI framework is based in two core interfaces: UIFactory and ActionListeners (java.awt.event.ActionListener). The components are created by UI factories that honor the following contract: public interface UIFactory { public JComponent create (UI ui, Element config); } The overall structure of the UI configuration looks like this: ... ... ... ... ... ... ... ... We provide a set of ready-to-deploy UIFactories that can be used to create and assemble many Swing compon- ents, such as Labels, Buttons, Trees as well as higher-level components such as LogListeners, ISOMeters, etc. Let's have a look at a simple example: jPOS UI Test 1.6.1 93 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966)
http://jpos.org
Figure 11.1. Hello UI 11.2. UI configuration The configuration element supports the following optional attributes: UI Framework 1.6.1 94 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) Table 11.1. UI configuration attributes Attribute Description width Width in pixels height Height in pixels look-and-feel Optional Look and Feel undecorated true or false, defaults to false. close true or false, defaults to true. If you run the UI inside Q2, you have to 'wrap' the .. configuration inside a QBean configuration, e.g.: ... ... Please note the optional provider attribute. This is a 'hook' intended to give the developer the ability to extend the UI framework. It defaults to org.jpos.ui.UI. jPOS comes with another provider called org.jpos.bsh.BSHUI which enables BSH script-based customization for components. If you use the BSHUI provider, you can write configuration blocks like this: ... ... ... ... 11.3. menubar The menubar can have menus which in turn can have menuitems as described in the following example: UI Framework 1.6.1 95 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) UI Framework 1.6.1 96 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) Figure 11.2. Menubar The optional id attribute can be used by the rest of your application to reference instances of these components (i.e., by using the UI object as an instance registrar) in order to modify them on-the-fly (e.g., enable/disable, changing state, etc.). Most components can be associated with an ActionListener by using the action attribute. The optional attribute command can be used as a 'hand back' object in order to reuse ActionListener for different components. 11.4. UI Factories jPOS UI provides several general-purpose factories, but the main idea is that this is a framework where you can also create your own factories. Let's have a look at a very simple factory, JLabelFactory (src/main/org/jpos/ui/factory/JLabelFactory.java): public class JLabelFactory implements UIFactory { public JComponent create (UI ui, Element e) { JLabel label = new JLabel (e.getText()); String font = e.getAttributeValue (“font“); if (font != null) label.setFont (Font.decode (font)); label.setHorizontalAlignment(JLabel.CENTER); label.setBorder(new EmptyBorder(3, 3, 3, 3)); return label; } } Factories just return JComponents, which can be as simple as a label, a button, or any complex, custom com- ponent that you may wish to create. When you define components, you can use any XML element name, but you have to specify which class imple- ments that component, e.g.: My Label In order to make XML configuration files more readable, we provide a mapping file called UI.properties (located in src/main/org/jpos/ui directory) which currently has the following mappings (at the time of this writing): panel=org.jpos.ui.factory.PanelFactory label=org.jpos.ui.factory.JLabelFactory button=org.jpos.ui.factory.JButtonFactory border-layout=org.jpos.ui.factory.BorderLayoutFactory grid=org.jpos.ui.factory.GridLayoutFactory hsplit=org.jpos.ui.factory.HSplitFactory vsplit=org.jpos.ui.factory.VSplitFactory tree=org.jpos.ui.factory.JTreeFactory tabbed-pane=org.jpos.ui.factory.JTabbedPaneFactory html=org.jpos.ui.factory.HtmlFactory text=org.jpos.ui.factory.TextFactory log-listener=org.jpos.ui.factory.LogListenerFactory iso-meter=org.jpos.ui.factory.ISOMeterFactory UI Framework 1.6.1 97 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) # # The following element names depend on EXTension modules (see ext/README) # swixml=org.jpos.ui.factory.SwiXMLFactory By using these mappings, you don't have to specify a class attribute when configuring commonly-used com- ponents. As a result, you don't have to write: Instead, you simply write: The definitive documentation for these factories, is actually the source code, specifically the create (UI ui, Element e) method. Even complex factories such as the log-listener have very easy-to-read create methods, e.g.: public JComponent create (UI ui, Element e) { JTextArea textArea = new JTextArea (25, 80); String font = e.getAttributeValue (“font“); if (font != null) textArea.setFont (Font.decode (font)); String logId = e.getAttributeValue (“logger“, “Q2“); try { int maxEvents = Integer.parseInt ( e.getAttributeValue (“max-events“, “100“) ); int maxLines = Integer.parseInt ( e.getAttributeValue (“max-lines“, “1000“) ); Logger logger = (Logger) NameRegistrar.get (“logger.“ + logId); logger.addListener ( new Listener (logger, ui, textArea, maxEvents, maxLines) ); } catch (NameRegistrar.NotFoundException ex) { textArea.setText (ex.toString ()); } return textArea; } You can easily identify which attributes are supported by this component: Table 11.2. LogListenerFactory configuration Attribute Description font You can specify an optional font attribute, e.g., font=“helvetica-bold-16“ logger The logger name (defaults to Q2) max-events Maximum number of events that you want to display max-lines Maximum number of lines to display UI Framework 1.6.1 98 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) Let's have a look at another example: jPOS UI Layouts
Links Link A Link B Nodes Node A Node B Node C The quick brown fox jumped over the lazy dog ]]>
UI Framework 1.6.1 99 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) Figure 11.3. Layout example The configuration file is self-explanatory: if you look at the button, it has an action, defined outside the ... block. In this particular case, we use a special kind of action that lets you run an external script (here, test.bsh). You can create a simple test.bsh file, e.g.: print (“Hello jPOS UI Actions“); ...in order to give it a try. 11.4.1. JLabelFactory Description Can be used to create and configure JLabel objects. Usage ... ... ... ... UI Framework 1.6.1 100 The book exclusively prepared for Courtesy Edgardo Regodon Jr - 07/2009 (r2966) Table 11.3. Attributes Attribute Description id An optional id; can be used to get a reference to this component at a later time font Optional font scrollable if true, UI wraps the component with a JScrollPane width Width in pixels height Height in pixels 11.4.2. JButtonFactory Description Create and configure JButton objects. Usage