Tips and Tricks for SAP Java Connector (JCo) Client Programming
Tips and Tricks for SAP
Java Connector (JCo)
Client Programming
Thomas G. Schuessler
This article originally appeared in the January/February 2004 issue of SAP
Professional Journal and appears here with the permission of the publisher,
Wellesley Information Services. For information about SAP Professional
Journal and other WIS publications, visit [Link].
free advice often turns out to be expensive
Terry Pratchett, The Wee Free Men, p. 50
Thomas G. Schuessler is
the founder of ARAsoft, a
company offering products,
consulting, custom
development, and training
to customers worldwide,
specializing in integration
between SAP and non-SAP
components and applications.
Thomas is the author of
SAPs BIT525 and BIT526
classes. Prior to founding
ARAsoft in 1993, he worked
with SAP AG and SAP
America for seven years.
When I teach SAP Java Connector (JCo) training classes or workshops,
some of the participants have previous JCo programming experience.
They may have tried the sample programs supplied with JCo or even
written some JCo code themselves. Invariably, they are looking for
advanced information on JCo to help them improve their applications or
speed up their development projects. If you are in the same situation,
then this article is for you. It contains an assortment of general
recommendations, performance tips, debugging tricks, and solutions
to specific client programming challenges that should be of value to
all JCo developers.
If you are a Java programmer new to JCo, I recommend that you
read my JCo tutorial (supplied with JCo) and then try out some of the
sample programs before continuing with this article.
Take Advantage of the SAP Java IDoc
Class Library
From the dawn of its time, JCo could be utilized to send and receive
IDocs (Intermediate Documents). The creation or interpretation of
an IDoc required a lot of programming effort, though. Not anymore.
With the release of the SAP Java IDoc Class Library, you can create or
interpret an IDoc with much less work. Download the product from
(complete bio appears on page 123)
For site licenses and volume subscriptions, call 1-781-751-8799.
105
SAP Professional Journal
January/February 2004
[Link] and try the sample
programs. For a great introduction to the library, read
Robert Chus articles in the September/October 2003
issue of this publication.
(RFM) in SAP, JCo needs to translate between the
ABAP and Java data types. JCo uses six different
Java data types to represent the ABAP data types.
Figure 1 shows the mapping between the ABAP and
Java data types used by JCo.
Upgrade to JCo 2.1.1
Corresponding to these six Java data types, JCo
offers six data-type-specific access methods for fields
(e.g., getBigDecimal()) plus a generic access method
(getValue()). See Figure 2 for a complete list of these
seven methods.
When you compare the quality of different SAP
products, JCo is definitely in the top 10 percent.
That notwithstanding, there have been some releases
that introduced features (a.k.a. bugs) that we could
have lived without. An automatic update to the latest
release is therefore dangerous. Release 2.1.1, I
am happy to report, is a very stable release and I
recommend that you upgrade to it sooner or later.
Sooner especially if you are still using JCo 1.x, for
which support has ended with 2003.
In addition, JCo provides convenience methods
that are useful if you need different data types in
your application (see Figure 3). Some of these
methods may be new even to experienced JCo
developers because SAP has added them
comparatively recently.
When you use an access method that does not
correspond to a fields Java data type, JCo will try to
convert the contents to the requested data type. A
[Link] will be thrown if the
conversion fails.
Data Type Mapping and
Conversions
When you invoke an RFC-enabled Function Module
Figure 1
ABAP and Java Data Types
ABAP
Description
Java
Type Constant
1-byte integer
int
JCO.TYPE_INT1
2-byte integer
int
JCO.TYPE_INT2
4-byte integer
int
JCO.TYPE_INT
Character
String
JCO.TYPE_CHAR
Numerical character
String
JCO.TYPE_NUM
Binary Coded Decimal
BigDecimal
JCO.TYPE_BCD
Date
Date
JCO.TYPE_DATE
Time
Date
JCO.TYPE_TIME
Float
double
JCO.TYPE_FLOAT
Raw data
byte[]
JCO.TYPE_BYTE
String (variable-length)
String
JCO.TYPE_STRING
Raw data (variable-length)
byte[]
JCO.TYPE_XSTRING
106
2004 SAP Professional Journal. Reproduction prohibited. All rights reserved.
Tips and Tricks for SAP Java Connector (JCo) Client Programming
Figure 2
Basic Field Access Methods
public [Link] getBigDecimal(int index/String field_name)
public byte[] getByteArray(int index/String field_name)
public [Link] getDate(int index/String field_name)
public double getDouble(int index/String field_name)
public int getInt(int index/String field_name)
public [Link] getString(int index/String field_name)
public [Link] getValue(int index/String field_name)
Figure 3
Convenience Field Access Methods
public [Link] getBigInteger(int index/String field_name)
public [Link] getBinaryStream(int index/String field_name)
public char getChar(int index/String field_name)
public [Link] getCharacterStream(int index/String field_name)
public short getShort(int index/String field_name)
public long getLong(int index/String field_name)
public [Link] getTime(int index/String field_name)
To change the value of a field, you utilize the
setValue() method. This method is overloaded for
the data types listed in Figure 4. Again, JCo will try
to convert and throw an exception if you pass an
unconvertible value.
Figure 4
For most data types, the mapping always works
without requiring any special attention. There are
two exceptions:
long
Data Types Allowed in setValue()
byte[]
char
int
double
short
Some BAPIs use unconventional date and/or
time values. Dates like 00000000 and
99999999 as well as the time 240000 are
[Link]
[Link]
2004 SAP Professional Journal. Reproduction prohibited. All rights reserved.
107
SAP Professional Journal
Figure 5
January/February 2004
Treatment of Special Date and Time Values
ABAP Data Type
Contents
getString()
getDate() or getTime()
00000000
0000-00-00
null
99999999
9999-99-99
9999-12-31
240000
[Link]
[Link]
illegal in Java. In order to support BAPIs that
use these values, JCo has special rules shown
in Figure 5. As you can see, you get different
results depending on whether you invoke
getString() or getDate()/getTime().
Some BAPIs do not follow the rules defined
for currency amounts and return incorrect
amounts if the specific currency uses more or
less than two decimals. Unfortunately, quite
famous BAPIs like [Link] are
amongst those in this category. For more
information about this problem and how to deal
with it, please read my article Currencies and
Currency Conversions in BAPI Programming
in the September/October 2001 issue of this
publication.
Figure 6
Always Use a Fresh
[Link] Object
Some customers have tried to optimize the
performance of their applications by reusing existing
[Link] objects. This is not only superfluous
because [Link] buffers the metadata for
the RFMs itself, but also dangerous. If you call a
function in SAP that fills a table parameter without
deleting existing rows first, then more and more
rows are added to this table. In other words, you get
incorrect results. In order to simplify the creation of
new [Link] objects, you can use a method like
the one shown in Figure 6. Class ARAsoftException,
which allows the chaining of exceptions, is listed in
Appendix A on page 124. Figure 7 is sample code
that uses the createFunction() method.
The createFunction Method
public [Link] createFunction(String name)
throws ARAsoftException {
try {
IFunctionTemplate ft =
[Link]([Link]());
if (ft == null)
return null;
return [Link]();
}
catch (Exception ex) {
throw new ARAsoftException(
"Problem retrieving [Link] object.", ex);
}
}
108
2004 SAP Professional Journal. Reproduction prohibited. All rights reserved.
Tips and Tricks for SAP Java Connector (JCo) Client Programming
Figure 7
Using the createFunction Method
[Link] function =
createFunction("BAPI_COMPANYCODE_GETLIST");
[Link](function);
[Link] bapiReturn = [Link]()
.getStructure("RETURN");
if ( ! ([Link]("TYPE").equals("") ||
[Link]("TYPE").equals("S")) ) {
[Link]([Link]("MESSAGE"));
[Link](0);
}
[Link] table =
[Link]().getTable("COMPANYCODE_LIST");
int records = [Link]();
for (int i = 0; i < records; i++) {
[Link](i);
function = createFunction("BAPI_COMPANYCODE_GETDETAIL");
[Link]()
.setValue([Link]("COMP_CODE"),
"COMPANYCODEID");
[Link](function);
bapiReturn = [Link]()
.getStructure("RETURN");
if ( ! ([Link]("TYPE").equals("") ||
[Link]("TYPE").equals("S")) ) {
[Link]([Link]("MESSAGE"));
}
}
Use the Request/Response
Programming Model
An RFM has three types of parameters:
Import: These parameters are passed from the
JCo client to the RFM. Import parameters are
usually scalars (simple fields) or structures
(groups of fields). In some rare cases (since 4.6C)
import parameters are tables, but this feature is
not used in BAPIs. Import parameters can be
optional or mandatory. Scalar optional import
parameters usually have a default value.
Export: These parameters are passed from the
RFM back to the JCo client. As for import
parameters, export parameters are usually
scalars or structures, and in rare cases, tables.
Tables: These parameters, which can be optional
or mandatory, represent ABAP internal tables,
i.e., smart arrays. Table parameters are
technically passed by reference. Only the
documentation (in other words, the source
code1) tells the developer whether the parameter
is semantically to be interpreted as import, export,
or both.
When an RFM is defined as a BAPI, a table
parameter can be marked as import only,
export only, or both, but this setting has only
informational value and is totally ignored when
the BAPI is invoked as an RFM. In addition,
not all BAPIs are defined correctly with regard
to this setting.
Tom Archer and Andrew Whitechapel, Inside C#, p. 145.
2004 SAP Professional Journal. Reproduction prohibited. All rights reserved.
109
SAP Professional Journal
Figure 8
January/February 2004
The createRequest Method
public [Link] createRequest(String name)
throws ARAsoftException {
try {
IFunctionTemplate ft =
[Link]([Link]());
if (ft == null)
return null;
return [Link]();
}
catch (Exception ex) {
throw new ARAsoftException(
"Problem retrieving [Link] object.", ex);
}
}
Figure 9
Using the Request/Response Programming Model
[Link] request = createRequest("BAPI_COMPANYCODE_GETLIST");
[Link] response = [Link](request);
[Link] bapiReturn = [Link]("RETURN");
if ( ! ([Link]("TYPE").equals("") ||
[Link]("TYPE").equals("S")) ) {
[Link]([Link]("MESSAGE"));
[Link](0);
}
[Link] table =
[Link]("COMPANYCODE_LIST");
int records = [Link]();
for (int i = 0; i < records; i++) {
[Link](i);
request = createRequest("BAPI_COMPANYCODE_GETDETAIL");
[Link]([Link]("COMP_CODE"),
"COMPANYCODEID");
response = [Link](request);
bapiReturn = [Link]("RETURN");
if ( ! ([Link]("TYPE").equals("") ||
[Link]("TYPE").equals("S")) ) {
[Link]([Link]("MESSAGE"));
}
}
In the standard JCo programming model, these
three parameter types are represented by three
110
objects of type [Link], accessed via the
[Link] objects getImportParameterList(),
2004 SAP Professional Journal. Reproduction prohibited. All rights reserved.
Tips and Tricks for SAP Java Connector (JCo) Client Programming
getExportParameterList(), and
getTableParameterList() methods. (See Figure 7
for sample code using these methods.)
Although most developers get used to the standard
programming model after comparatively little time,
some still feel uncomfortable. For these developers,
JCo offers an alternative, the Request/Response
programming model. Instead of explicitly creating
a [Link] object, the application creates an
object of type [Link]. This object then allows
access to all parameters defined as Import or Tables.
To invoke the RFM, you use the
execute([Link] request) method of object
type [Link]. This overloaded version of the
execute([Link] function) method returns
an object of type [Link], which allows access
to all Export and Tables parameters. Figure 8 shows
a sample method (similar to the createFunction()
method in Figure 6) that can be used to encapsulate
the creation of a [Link] object. Figure 9
contains the application code from Figure 7, rewritten
to use the Request/Response programming model.
There are no performance penalties for using the
Request/Response model, so your choice should
be based entirely on your personal preferences.
Each development project should probably define
a standard for this choice, so that developers have
no problem when maintaining other peoples code.
cache. Creating one repository object per user of
your application, or, even worse, for each session of
each user, significantly increases the number of SAP
calls your application is responsible for. This will not
only slow down your application, but also the SAP
system itself. So the importance of creating only one
repository per SAP system cannot be overestimated.
Unfortunately, JCo does not help you to
accomplish this task, so you need to write a suitable
repository manager class yourself. This class must
keep a reference to each repository object and provide
methods that allow the client program to access
existing repositories or create new ones if necessary.
For more information about [Link],
please refer to my article Repositories in the SAP
Java Connector (JCo) in the March/April 2003 issue
of this publication. An implementation of a repository
manager class, StandardRepositoryManager, is
reprinted in Appendix B on page 126 for your
convenience.
Using Connection Pools
This topic warrants its own article, but I will give you
the most important information here:
Use one pool for your [Link] and
technical support components encapsulating
Helpvalues, conversions between the internal and
external SAP data formats, metadata retrieval, etc.
Use one pool for the anonymous users (if any)
of your application.
Use a (small) pool for each named user of your
application. Using this approach instead of
explicitly created [Link] objects makes it
much easier to strike the proper balance between
freeing resources in SAP and avoiding too many
logons to SAP.
Return a [Link] (via a call to releaseClient())
that you obtained from a pool (via a call to
getClient()) early, but not too early. It makes
no sense to bracket each RFM invocation with
Use Only One Repository
The most important performance tip for applications
that are clients to an SAP server is to avoid calls to
said server. In general, that means caching readonly SAP information in your client application.
In terms of the [Link] class, it means to
create only one repository object per SAP system
that your application is connected to. The RFM
metadata that the [Link] caches is clientindependent (client here meaning the 3-digit
number that logically separates an SAP database),
so one repository per SAP system is sufficient.
[Link] dynamically retrieves RFM metadata
from SAP for each RFM that it could not find in its
2004 SAP Professional Journal. Reproduction prohibited. All rights reserved.
111
SAP Professional Journal
January/February 2004
(default: 10 minutes) is reached. This allows
you to reuse a connection without having to go
through the expensive logon process again. If
a connection that is not part of the internal array
is returned to the pool (this is only possible if
MaxConnections is larger than MaxPoolSize),
it is closed immediately.
getClient()/releaseClient() if a sequence of RFMs
is called successively without any user interaction
or other prolonged wait periods.
Also be aware that if you use BAPIs that
require an extra commit by the application,
these BAPIs and the commit BAPI
([Link], RFM
BAPI_TRANSACTION_COMMIT) must be
called in the same getClient()/releaseClient()
bracket.
Make the pool large enough so that wait situations
do not occur. In older versions of JCo, the
maximum value of connections in a pool,
specified when the pool was created, could
never be exceeded. Nowadays, there is an
additional parameter that can be set using the
setMaxConnections() method of [Link].
When the pool is created, MaxConnections
is set to the same value as MaxPoolSize.
MaxConnections controls the maximum number
of [Link] objects that can be obtained from
the pool. MaxPoolSize controls how many
[Link] objects are kept in an internal array.
If a connection that is part of the internal array
is returned to the pool, it will be kept open
(connected to SAP) until ConnectionTimeOut
Figure 10
My recommendation is to set MaxPoolSize to a
value large enough to cover any activity of your
application other than absolute peaks. Make
MaxConnections large enough so that the limit is
never reached. An exception to this would be the
small pools used for individual named users. Here
a small MaxConnections is a suitable way to ensure
that the same user does not have an inordinate
number of sessions with the SAP system.
Use Properties for Connection
Information
When you create a [Link] or [Link] object,
you have several overloaded versions of the pertinent
methods to choose from. My recommendation is to
avoid hard-coded values and use a Properties object
instead. Figure 10 contains a code snippet that
Using Properties to Create a [Link]
import [Link].*;
static final String POOL_NAME = "ARAsoft";
try {
if ([Link]().getPool(POOL_NAME) == null) {
OrderedProperties logonProperties =
[Link]("/[Link]");
[Link](POOL_NAME, 10, logonProperties);
}
[Link] pool = [Link]().getPool(POOL_NAME);
}
catch (Exception ex) {
[Link]();
}
112
2004 SAP Professional Journal. Reproduction prohibited. All rights reserved.
Tips and Tricks for SAP Java Connector (JCo) Client Programming
Figure 11
Sample Contents of File [Link]
[Link]=001
[Link]=userid
[Link]=secret
[Link]=hostname
[Link]=00
creates a pool object based on the information in a file
(Figure 11). You can initialize your Properties object
whichever way you like, but for your convenience
class OrderedProperties is provided in Appendix C
on page 130.
replace multiple calls to the appendRow() method
by one call to appendRows(int num_rows). An
alternative approach is the ensureBufferCapacity(int
required_rows) method, which was added in
JCo 2.1.1.
Inactivate Table Parameters
Collecting Structures into a Table
Some RFMs, especially BAPIs, cover a lot of
application scope and thus need to have quite a few
table parameters. A concrete application usually only
needs a subset of all these tables. You can improve
the performance of your application by inactivating
those table parameters that your application does
not utilize. This is accomplished by invoking
the setActive() method, available both for the
[Link] and [Link] object types.
Figure 12 shows sample code that inactivates a
table parameter.
A structure parameter is equivalent to a table
parameter with exactly one row. Both structures and
table rows are composed of fields. This is reflected
by the fact that both [Link] and [Link]
are subclasses of [Link]. In some applications,
we need to somehow collect the contents of multiple
structure objects. An example would be an
application that needs all the details for all company
codes. You would first call [Link]
(RFM BAPI_COMPANYCODE_GETLIST) to
produce a list of all company codes and their
names and then call [Link]
(RFM BAPI_COMPANYCODE_GETDETAIL)
once for each company code in order to retrieve
the details provided in structure parameter
COMPANYCODE_DETAIL.
Improving System Performance
When Appending Multiple Table
Rows
You can manipulate table parameters by deleting,
inserting, or appending rows. If you need to append
multiple rows, your application will run faster if you
Figure 12
JCo makes collecting all the information
contained in those structures extremely easy by
allowing you to collect them into a table. The first
step is to create a table with the same fields (columns)
Inactivating a Table Parameter
[Link]().setActive(false, "TABLE_PARAM");
2004 SAP Professional Journal. Reproduction prohibited. All rights reserved.
113
SAP Professional Journal
January/February 2004
Figure 13
Collecting Structures into a Table
import [Link].*;
import [Link];
import [Link];
import [Link];
public class CompanyCodes {
[Link] mRepository;
[Link] mConnection;
static final String POOL_NAME = "ARAsoft";
public CompanyCodes() {
try {
if ([Link]().getPool(POOL_NAME) == null) {
OrderedProperties logonProperties =
[Link]("/[Link]");
[Link](POOL_NAME, 10, logonProperties);
}
[Link] pool = [Link]().getPool(POOL_NAME);
mRepository =
[Link]()
.getRepository(pool, true);
mConnection = [Link](POOL_NAME);
[Link] function =
createFunction("BAPI_COMPANYCODE_GETLIST");
[Link](function);
BapiMessageInfo returnMessage =
new BapiMessageInfo([Link]()
.getStructure("RETURN"));
if ( ! [Link]() ) {
[Link]([Link]());
[Link](0);
}
[Link] table =
[Link]().getTable("COMPANYCODE_LIST");
function = createFunction("BAPI_COMPANYCODE_GETDETAIL");
// Create a table based on a structure
that the structure to be collected contains. This
is accomplished by invoking the constructor of
[Link], passing the structure parameter object
114
for COMPANYCODE_DETAIL as the only
parameter (see Figure 13 for sample code for
this and subsequent operations).
2004 SAP Professional Journal. Reproduction prohibited. All rights reserved.
Tips and Tricks for SAP Java Connector (JCo) Client Programming
Figure 13 (continued)
[Link] infos =
new [Link]([Link]()
.getStructure("COMPANYCODE_DETAIL"));
[Link]([Link]());
int records = [Link]();
for (int i = 0; i < records; i++) {
[Link](i);
function = createFunction("BAPI_COMPANYCODE_GETDETAIL");
[Link]()
.setValue([Link]("COMP_CODE"),
"COMPANYCODEID");
[Link](function);
returnMessage =
new BapiMessageInfo([Link]()
.getStructure("RETURN"));
if ( ! [Link](false, false,
null, "FN021") ) {
[Link]([Link]());
[Link](0);
}
// Copy the data of the structure to the table
[Link]([Link]()
.getStructure("COMPANYCODE_DETAIL"));
}
[Link]("c:\\[Link]");
}
catch (Exception ex) {
[Link]();
}
finally {
[Link](mConnection);
}
}
public static void main(String[] args) {
CompanyCodes app = new CompanyCodes();
}
}
Then, after each call of
BAPI_COMPANYCODE_GETDETAIL, we
append the structure data in the
COMPANYCODE_DETAIL parameter to the table
by invoking the copyFrom() method offered by
[Link]. It is not necessary to call appendRow()
2004 SAP Professional Journal. Reproduction prohibited. All rights reserved.
115
SAP Professional Journal
Figure 14
January/February 2004
Exception Handling
[Link] function = [Link]("DDIF_FIELDINFO_GET");
try {
[Link]()
.setValue("MARA", "TABNAME");
[Link](function);
}
catch ([Link] ex) {
if ([Link]().equalsIgnoreCase("NOT_FOUND")) {
[Link]
("Dictionary structure/table not found.");
[Link](1);
}
else {
[Link]([Link]());
[Link](1);
}
}
catch ([Link] ex) {
// Handle the exception
}
catch (Exception ex) {
// Handle the exception
}
Synchronization in JCo
before calling copyFrom(), since the latter method
appends a new row automatically.
Use Proper Exception Handling
JCo uses three types of exceptions. [Link]
is the main one, and [Link] and
[Link] are its subclasses.
[Link] was discussed earlier.
[Link] is thrown whenever the RFM
you invoke raises an exception. All three exceptions
are runtime exceptions, i.e., you are not syntactically
required to catch them or define them in the method
signature. This should not be interpreted as an
indication that proper exception handling in your
application is not necessary. Figure 14 shows an
exception handling example, in which we differentiate
between the NOT_FOUND exception and other
ABAP exceptions raised by DDIF_FIELDINFO_GET.
116
Sharing objects between threads is always somewhat
dangerous without synchronization, so it is important
to know how JCo deals with this. The rule is simple:
Access to [Link] and [Link] objects is
synchronized and nothing else! Sharing connections
([Link] objects) is specifically disallowed and
will lead to an exception. Sharing other objects like
[Link] objects is possible, but you need to take
care of the synchronization yourself.
Create HTML to Help with
Debugging
When debugging your application, you oftentimes
need to check the contents of the parameters of the
RFMs you invoke. Using the debugger contained
in your IDE, this is a very cumbersome task. JCo
2004 SAP Professional Journal. Reproduction prohibited. All rights reserved.
Tips and Tricks for SAP Java Connector (JCo) Client Programming
Figure 15
The HTML Representation of a Table
offers methods that make this exercise very simple,
by allowing you to create HTML files for [Link]
(and hence its subclasses [Link],
[Link], [Link], [Link], and
[Link]) and [Link] objects. The
pertinent method is writeHTML([Link]
html_filename). If you use it for a [Link] (as
shown towards the end of Figure 13), by default
only the first 100 rows plus the last row of the table
are written to the HTML file (see Figure 15 for a
screenshot of the beginning of the HTML file created
by running the code in Figure 13). This is because a
[Link] object could potentially contain millions of
records, and browsers (at least Internet Explorer) have
problems displaying very large HTML pages. If you
need more than these 101 rows, you can change the
[Link].table_max_rows property by calling
the setProperty() method of class JCO.
Debugging the ABAP Code
Sometimes you are quite certain that your Java code
is correct and the problem to be debugged probably
resides in the invoked ABAP code. How can you find
out whether you are right? SAP actually allows you
to debug the ABAP code if the following prerequisites
are met:
SAPGUI is installed on the machine on
which the JCo client code is running.
You invoke setAbapDebug(true)
for the relevant [Link] or [Link]
object before the connection to SAP is
established.
Then, as if by magic, the ABAP debugger will
2004 SAP Professional Journal. Reproduction prohibited. All rights reserved.
117
SAP Professional Journal
Figure 16
January/February 2004
Using the ABAP Debugger
appear. It obviously helps to be able to operate this
debugger and understand ABAP source code. You
may have to ask an ABAP expert for help here. See
Figure 16 for a screenshot of the ABAP debugger
in action.
If the RFM that you are invoking is not in
the repository cache yet, the first few calls you
will see in SAP will be to metadata retrieval
functions like DDIF_FIELDINFO_GET. Just
continue until the RFM you are interested in is
reached.
Obviously, debugging a web application this
118
way may be a bit difficult, because you may not be
allowed direct access to that server or it does not
have SAPGUI installed. This is one of several
reasons why I keep recommending that all SAPrelated application functionality be encapsulated
in classes that can be tested separately.
Combining ABAP Date
and Time Fields
ABAP uses different data types for date (D) and
time (T) information. In Java, both date and time
2004 SAP Professional Journal. Reproduction prohibited. All rights reserved.
Tips and Tricks for SAP Java Connector (JCo) Client Programming
Figure 17
Combining ABAP Date and Time Fields
import [Link];
private static final SyncDateFormat dateISO =
new SyncDateFormat("yyyy-MM-dd");
private static final SyncDateFormat timeISO =
new SyncDateFormat("HH:mm:ss");
private static final SyncDateFormat dateTimeISO
= new SyncDateFormat("yyyy-MM-ddHH:mm:ss");
public static Date combineDateAndTime(Date date, Date time) {
try {
return [Link]([Link](date) +
[Link](time));
}
catch (Exception ex) { return null; }
}
public static Date combineDateAndTime([Link] date, [Link] time) {
try {
return combineDateAndTime([Link](), [Link]());
}
catch (Exception ex) { return null; }
}
are contained in a [Link] object. How do
you combine two ABAP fields containing date and
time into one Java Date object? There are several
possible approaches to this, but my favorite one uses
the [Link] class. Unfortunately,
this class has a bug in at least some of Suns Java
releases. This bug creates incorrect Date objects
if SimpleDateFormat is invoked concurrently
by multiple threads. The JCo developers found
this bug during stress tests of JCo and decided
to solve the issue by writing their own class,
[Link]. This class
extends SimpleDateFormat and overwrites the
buggy format() and parse() methods, making
them synchronized. JCo does not provide Javadoc
for SyncDateFormat, but that is not necessary,
because it has the same features as
SimpleDateFormat.
Figure 17 contains two combineDateAndTime()
methods that use JCos SyncDateFormat class. The
first method expects two parameters of type Date,
the second one two [Link] objects. [Link]
is a convenience class that allows you to treat fields
in a uniform way, regardless of whether they are
scalar parameters, fields in a structure, or fields in
a row in a table.
Retrieving the SAP Date and Time
The date and time used in the SAP server is not
necessarily the same as that used in the JCo
client machine. SAP provides an RFM,
MSS_GET_SY_DATE_TIME, that allows you to
retrieve the current date and time of the SAP server.
2004 SAP Professional Journal. Reproduction prohibited. All rights reserved.
119
SAP Professional Journal
Figure 18
January/February 2004
Retrieving the SAP Date and Time
public static Date getSapDateAndTime([Link] connection,
IRepository repository)
throws ARAsoftException {
[Link] function = null;
try {
function = createFunction(repository, "MSS_GET_SY_DATE_TIME");
if (function == null)
throw new ARAsoftException
("MSS_GET_SY_DATE_TIME not found in the SAP system.");
[Link](function);
return
combineDateAndTime([Link]()
.getField("SAPDATE"),
[Link]()
.getField("SAPTIME"));
}
catch (ARAsoftException ax) { throw ax; }
catch (Exception ex) {
throw new ARAsoftException(
"Problem invoking MSS_GET_SY_DATE_TIME.", ex);
}
}
Figure 18 contains a utility method that invokes
MSS_GET_SY_DATE_TIME and returns a Java Date
object, making use of the combineDateAndTime()
method just discussed.
not available in other ABAP-based SAP components
like CRM.
Retrieving the SAP Date Format
Encapsulate the Checking of the
BAPI Return Parameter
For every R/3 user, the date format to be used
in SAPGUI can be individually defined. If you
want to use the same date format in your own
GUI (e.g., in a browser-based application), you
need to find out the date format defined in R/3.
This can be accomplished by invoking
RFC_GET_SAP_SYSTEM_PARAMETERS,
which in addition to the date format also returns
the users language and the decimal symbol defined
for the user. Figure 19 lists a sample function for
retrieving the date format from R/3. Note that
RFC_GET_SAP_SYSTEM_PARAMETERS is
Each BAPI is supposed to have a RETURN
parameter. This can be either a structure or a table.
In the latter case, an empty table signifies a successful
invocation of the BAPI. If the table contains rows,
you need to check each of them. If a RETURN
structure parameter or a row in a RETURN table
parameter contains something other than S or
an empty string in the TYPE field, then the BAPI
was not completely successful. To ensure that all
application programs check the RETURN parameter
in a consistent fashion, it is recommended that you use
a standard method like the one shown in Figure 20.
120
2004 SAP Professional Journal. Reproduction prohibited. All rights reserved.
Tips and Tricks for SAP Java Connector (JCo) Client Programming
Figure 19
Retrieving the SAP Date Format
public static String getSapDateFormat ([Link] connection,
IRepository repository)
throws Exception {
[Link] function =
[Link]("RFC_GET_SAP_SYSTEM_PARAMETERS")
.getFunction();
[Link](function);
return
[Link]()
.getString("DATE_FORMAT");
}
Figure 20
The First isBapiReturnCodeOkay Method
public static boolean isBapiReturnCodeOkay([Link] object) {
[Link] istructure;
[Link] itable;
try {
if (object instanceof [Link]) {
return ([Link]("TYPE").equals("") ||
[Link]("TYPE").equals("S"));
}
if (object instanceof [Link]) {
itable = ([Link])object;
int count = [Link]();
if (count == 0) return true;
boolean allOkay = true;
for (int i = 0; i < count; i++) {
[Link](i);
if ( ! ( [Link]("TYPE").equals("") ||
[Link]("TYPE").equals("S") ) ) {
allOkay = false;
break;
}
}
return (allOkay);
}
}
catch (Exception ex) {}
return false;
}
2004 SAP Professional Journal. Reproduction prohibited. All rights reserved.
121
SAP Professional Journal
Figure 21
January/February 2004
Using the First isBapiReturnCodeOkay Method
[Link] function =
createFunction("BAPI_COMPANYCODE_GETLIST");
[Link](function);
[Link] bapiReturn = [Link]()
.getStructure("RETURN");
if ( ! isBapiReturnCodeOkay(bapiReturn) ) {
[Link]([Link]("MESSAGE"));
[Link](0);
}
Figure 22
The Second isBapiReturnCodeOkay Method
public static boolean isBapiReturnCodeOkay([Link] function) {
[Link] exports = [Link]();
if (exports != null && [Link]("RETURN")) {
return isBapiReturnCodeOkay([Link]("RETURN"));
} else {
[Link] tables = [Link]();
if (tables != null && [Link]("RETURN")) {
return isBapiReturnCodeOkay([Link]("RETURN"));
} else {
return false;
}
}
}
This method defines a parameter of type
[Link], the superclass of both [Link]
and [Link].
parameter list or the table parameter list, depending
on how the BAPI has defined the RETURN
parameter.
Figure 21 shows sample code using the
isBapiReturnCodeOkay() method.
This slight inconvenience can be removed by
providing an overloaded version of the
isBapiReturnCodeOkay() method that accepts an
object of type [Link] (Figure 22). This new
version finds out for itself whether the RETURN
parameter is a structure or a table and then calls
our first isBapiReturnCodeOkay() method with the
correct parameter.
One disadvantage to the isBapiReturnCodeOkay()
method from Figure 20 is that the application program
must pass the RETURN parameter. If you use
the standard JCo programming model, that means
that the application must either access the export
122
2004 SAP Professional Journal. Reproduction prohibited. All rights reserved.
Tips and Tricks for SAP Java Connector (JCo) Client Programming
Figure 23
Using the Second isBapiReturnCodeOkay Method
[Link] function =
createFunction("BAPI_COMPANYCODE_GETLIST");
[Link](function);
if ( ! isBapiReturnCodeOkay(function) ) {
[Link](0);
}
Figure 23 shows sample code using the second
method.
And What About JCo Server
Programming?
This article has dealt exclusively with JCo client
programming, which is what most projects require.
For an introduction to JCo server programming,
please read my article Server Programming with the
SAP Java Connector (JCo) in the September/October
2003 issue of this publication.
action in an absence of information
is wasted effort
Robert Asprin, Hit or Myth, p. 156
Thomas G. Schuessler is the founder of ARAsoft
([Link]), a company offering products,
consulting, custom development, and training to
a worldwide base of customers. The company
specializes in integration between SAP and nonSAP components and applications. ARAsoft offers
various products for BAPI-enabled programs on
the Windows and Java platforms. These products
facilitate the development of desktop and Internet
applications that communicate with R/3. Thomas
is the author of SAPs BIT525 Developing BAPIenabled Web Applications with Visual Basic
and BIT526 Developing BAPI-enabled Web
Applications with Java classes, which he teaches
in Germany and in English-speaking countries.
Thomas is a regularly featured speaker at SAP
TechEd and SAPPHIRE conferences. Prior to
founding ARAsoft in 1993, he worked with SAP AG
and SAP America for seven years. Thomas can be
contacted at [Link]@[Link] or at
tgs@[Link].
2004 SAP Professional Journal. Reproduction prohibited. All rights reserved.
123
SAP Professional Journal
January/February 2004
Appendix A:
Class ARAsoftException
package [Link];
/*
* Copyright (c) 2001 ARAsoft GmbH
* All Rights Reserved.
*/
/**
* Exception class
*
* @author ARAsoft GmbH
* @version 1.0
* @since 1.0
*/
public class ARAsoftException extends Exception {
private Exception mOriginalException = null;
private final static Copyright copyright = new Copyright();
/**
* Constructor.
*/
public ARAsoftException() {
super();
}
/**
* Constructor to be used if we wrap another exception.
* @param originalException The original exception
*/
public ARAsoftException([Link] originalException) {
super([Link]());
mOriginalException = originalException;
}
/**
* Constructor with a message string.
* @param s Exception string
*/
public ARAsoftException(String s) {
super(s);
}
124
2004 SAP Professional Journal. Reproduction prohibited. All rights reserved.
Appendix A: Class ARAsoftException
/**
* Constructor to be used if we wrap another exception and have an
* additional message string.
* @param s Exception string
* @param originalException The original exception
*/
public ARAsoftException(String s, Exception originalException) {
super(s);
mOriginalException = originalException;
}
/**
* Returns the original exception.
* @return The original exception.
*/
public Exception getOriginalException() {
return mOriginalException;
}
/**
* Returns the exception message.
* @return The exception message.
*/
public String getMessage() {
if (mOriginalException == null)
return [Link]();
return [Link]() + '\n' +
"Original exception:" + '\n' +
[Link]();
}
}
2004 SAP Professional Journal. Reproduction prohibited. All rights reserved.
125
SAP Professional Journal
January/February 2004
Appendix B:
Class StandardRepositoryManager
package [Link];
import [Link];
import [Link].*;
import [Link];
/*
* Copyright (c) 2002 ARAsoft GmbH
* All Rights Reserved.
*/
/**
* A singleton object that manages [Link] objects.
*
* @author ARAsoft GmbH
* @version 2.5
* @since 2.5
*/
public class StandardRepositoryManager {
static private StandardRepositoryManager repositoryManager = null;
static private TreeMap items = null;
protected StandardRepositoryManager() {
items = new TreeMap();
}
/**
* Returns the singleton instance of this class.
* @return The singleton instance.
*/
static public synchronized StandardRepositoryManager
getSingleInstance() {
if ( repositoryManager == null )
repositoryManager = new StandardRepositoryManager();
return repositoryManager;
}
/**
* Creates a [Link] object for the SAP system to which the pool
* is connected.
* Throws an exception if a repository for this system already exists.
126
2004 SAP Professional Journal. Reproduction prohibited. All rights reserved.
Appendix B: Class StandardRepositoryManager
* @return The created repository object.
* @param pool The [Link] object.
*/
public synchronized [Link] createRepository([Link] pool)
throws ARAsoftException {
[Link] client = null;
try {
client = [Link]([Link]());
String name = [Link]().getSystemID();
[Link](client);
client = null;
if ( [Link](name) )
throw new ARAsoftException
("A repository for system '" + name + "' already exists.");
[Link] repository =
new [Link](name, [Link]());
[Link](name, repository);
return repository;
}
catch (Exception ex) {
throw new ARAsoftException(ex);
}
finally {
if ( client != null ) {
[Link](client);
}
}
}
/**
* Checks whether a [Link] object for the specified SAP system
* already exists.
* @return Does a repository for the specified SAP system exist?
* @param systemId The system ID of the SAP system.
*/
public boolean existsRepository(String systemId) {
[Link] repository = ([Link]) [Link](systemId);
return ( repository != null );
}
/**
* Checks whether a [Link] object for the SAP system to which
* the pool is connected already exists.
* @return Does a repository for this system exist?
* @param pool The [Link] object.
*/
public boolean existsRepository([Link] pool)
throws ARAsoftException {
[Link] client = null;
try {
(continued on next page)
2004 SAP Professional Journal. Reproduction prohibited. All rights reserved.
127
SAP Professional Journal
January/February 2004
(continued from previous page)
client = [Link]([Link]());
String name = [Link]().getSystemID();
[Link](client);
client = null;
return [Link](name);
}
catch (Exception ex) {
throw new ARAsoftException(ex);
}
finally {
if ( client != null ) {
[Link](client);
}
}
}
/**
* Returns the [Link] object for the SAP system to which the
* pool is connected.
* Throws an exception if no repository exists.
* @return The repository object.
* @param pool The [Link] object.
*/
public synchronized [Link] getRepository([Link] pool)
throws ARAsoftException {
return [Link](pool, false);
}
/**
* Returns the [Link] object for the SAP system to which the
* pool is connected. If no repository exists and
* <code>createIfItDoesNotExist</code>
* is <code>true</code>, a new repository is created,
* otherwise an exception is thrown.
* @return The repository object.
* @param pool The [Link] object.
* @param createIfItDoesNotExist Should a new repository be created
*
if none exists?
*/
public synchronized [Link] getRepository
([Link] pool, boolean createIfItDoesNotExist)
throws ARAsoftException {
[Link] client = null;
try {
client = [Link]([Link]());
String name = [Link]().getSystemID();
[Link](client);
client = null;
try {
return [Link](name);
128
2004 SAP Professional Journal. Reproduction prohibited. All rights reserved.
Appendix B: Class StandardRepositoryManager
}
catch (ARAsoftException ax) {
if ( createIfItDoesNotExist ) {
return [Link](pool);
} else {
throw ax;
}
}
}
catch (Exception ex) {
throw new ARAsoftException(ex);
}
finally {
if ( client != null ) {
[Link](client);
}
}
}
/**
* Returns the [Link] object for the specified SAP system.
* If no repository exists an exception is thrown.
* @return The repository object.
* @param systemId The system ID of the SAP system.
*/
public synchronized [Link] getRepository(String systemId)
throws ARAsoftException {
[Link] repository = ([Link]) [Link](systemId);
if ( repository == null )
throw new ARAsoftException
("No repository exists for system '"
+ systemId + "'.");
return repository;
}
/**
* Removes the specified [Link] object.
* @param repository The repository to be removed.
*/
public synchronized void removeRepository([Link] repository)
{
String name = [Link]();
if ( [Link](repository) ) {
[Link](name);
}
}
}
2004 SAP Professional Journal. Reproduction prohibited. All rights reserved.
129
SAP Professional Journal
January/February 2004
Appendix C:
Class OrderedProperties
import [Link].*;
import [Link].*;
public class OrderedProperties extends [Link] {
ArrayList orderedKeys = new ArrayList();
public OrderedProperties() {
super();
}
public OrderedProperties([Link] defaults) {
super(defaults);
}
public synchronized Iterator getKeysIterator() {
return [Link]();
}
public static OrderedProperties load(String name)
throws Exception {
OrderedProperties props = null;
[Link] is =
[Link](name);
props = new OrderedProperties();
if (is != null) {
[Link](is);
return props;
}
else {
throw new IOException("Properties could not be loaded.");
}
}
public synchronized Object put(Object key, Object value) {
Object obj = [Link](key, value);
[Link](key);
return obj;
}
public synchronized Object remove(Object key) {
Object obj = [Link](key);
[Link](key);
return obj;
}
}
130
2004 SAP Professional Journal. Reproduction prohibited. All rights reserved.
the leading technical journal for
SAP administrators, architects,
consultants, and developers
About SAP Professional Journal
Elevate your mastery of SAP technology with in-depth tutorials and expert guidance in SAP Professional Journal.
Top independent consultants, experienced SAP customers and the technical gurus who write the code for SAP contribute
proven best practices and their favorites tips and techniques for developers, administrators and specialists in SAP
technology. Articles cover everything from ABAP to Java programming, from performance optimization to integration.
Each issue features step-by-step instruction on the SAP projects that top your to-do list right now. You can
immediately put to use practical advice that can't be found anywhere else.
Learn Directly From the Experts
All SAP Professional Journal authors are among the best and brightest minds in the
SAP community. The unique articles they write are training courses unto themselves.
Articles for developers often feature code samples and step-by-step development
techniques, while articles for administrators feature detailed screenshots and in-depth
guidance to enhance performance. Typical articles span 20-30 pages, and overall
coverage blends operational and theoretical background with day-to-day implications of
how and why SAP technology operates as it does, and how best to incorporate that
technology into your own IT environment. In addition we provide a complete download
library of all source and sample code featured in our articles.
Published bimonthly, every issue features highly-specific articles, each focused on
a narrow technical area or particular SAP offering. Every author is uniquely qualified to
take advanced, complex concepts and present them in an easy-to-understand format, at a
level of technical detail available nowhere else, to help advance your understanding of SAP technology.
Invest in Your Team
Keeping up with advances in SAP technology is a constant endeavor that is critical to your success, as well as to
achieving your enterprise's key business goals. Now your whole team can do so with access to every single article ever
published in SAP Professional Journal through our electronic license program. This program is the most affordable way to
access the SAP Professional Journal online archive your whole team gets one-click access to new articles, plus the
ability to search instantly through more than 3,500 pages of accumulated solutions, for a fraction of the cost of multiple
subscriptions.
Your team is your strongest asset, and giving them access to SAP Professional Journal is the best investment you
can make in them -- guaranteeing your organizations SAP knowledge and skills stay on the cutting edge. To learn more
about our Electronic License Program call 781-751-8799 or send a message to licenses@[Link].
Subscribe Risk-Free
SAP Professional Journal is backed by a 100% money-back guarantee. If at any point, for any reason, you are not
completely satisfied, we will refund your subscription payment IN FULL. Don't wait any longer to benefit from the most
respected technical journal for SAP expertise. This is the most in-depth publication written specifically for SAP
professionals like you. Subscribe to SAP Professional Journal today at [Link] or call us at 781-751-8799.
SAP Professional Journal
990 Washington Street, Ste. 308
[Link]
Dedham MA 02026, USA