JCo Tutorial
JCo Tutorial
Schuessler,
www.ARAsoft.de:
Developing Applications
with the
"SAP Java Connector"
(JCo)
Sponsor programme and close down.
James Joyce: Finnegans Wake, p. 531.27
The more complex the system and the more expert the users,
the more their technical conversation sounds like the plot of a soap opera.
Steven Pinker: How the Mind Works, p. 78
1.2. Disclaimer
This text is a preliminary, incomplete version of a book that I will publish on
programming with JCo. The complete book will have much more information. No
guarantee is given for the technical accuracy of any information in this text. We are not
responsible for any damage resulting from the use of the presented information or
running the associated sample programs.
1
Unless specifically stated otherwise.
2
We now offer a nice proxy generator integrated into JBuilder, see Appendix A-7.
5
Not all BAPIs follow all the rules, though. This can lead to a lot of confusion and frustration for
developers. This texts points out some of the deviations and their consequences.
2.4.1. Conversions
BAPIs mostly use the internal representation of codes. One of the more famous examples
is units of measurement: A SAPGUI user logged on in English will type PC (piece) as a
unit while the BAPIs use the internal (German) representation ST. Another example is
customer numbers. A customer number can contain letters, but if it consists of digits
only, it is internally stored with leading zeroes. The user wants to neither see nor have to
enter these leading zeroes.
SAPGUI automatically performs the necessary conversions so that its users always see
the external representation. This is possible since for each Domain a conversion routine
(sometimes called a conversion exit) can be defined if appropriate. This exit is called
both inbound (the user enters data to be passed to the application) and outbound (the
application returns data to be displayed in SAPGUI). Data originating in the application
is converted from the internal to the external format; data entered by the user is first
validated and then converted from the external to the internal format.
The fact that, in SAPGUI, the conversion exits are always called automatically has been a
source of confusion for many developers who wanted to try out a BAPI in SAP's
Function Builder (transaction code SE37) in order to learn more about the BAPI's
parameters. If you run the SalesOrder.CreateFromDat2 (RFM
BAPI_SALESORDER_CREATEFROMDAT2) BAPI inside SAP, for example, and enter
the document (sales order) type as OR (for a standard order), all works well. If, on the
other hand, you use the same code from outside of SAP, you will receive an error
message telling you that you have used an invalid code.
This is due to the fact that even the very technical test environment of the Function
Builder uses the conversion exits. But the BAPI itself does not invoke them. Many
developers in this situation end up hard-coding the German equivalent of OR (TA) in their
programs. That may be acceptable for a quick-and-dirty demo program, but software for
production use should avoid hard-coding constants that are subject to SAP customization.
Also, conversions are required for many other parameters, and it would definitely be
better to have a generic solution. The ARAsoft JCo Extension Library (see below)
automates the necessary conversions for you, but you can use the conversion BAPIs
2.4.2. Helpvalues
The BAPIs return lots of codes. When you invoke Customer.GetDetail, for example, you
get back, amongst other information, the code of the country in which this customer
resides. Users do not really remember what all the codes stand for, though. They want
text (the code's description) in addition to, or even instead of, the code. Along the same
lines, when a user has to enter a code (e.g., a country code when adding a new customer),
it would be nice if we offered a drop-down combo box with all available choices
(showing the text by itself or the code and the text) instead of a plain text box where the
user has to guess the correct code.
In some cases, SAP offers suitable BAPIs to retrieve the required information.
CompanyCode.GetList and GetDetail offer a list of all company codes with associated
descriptions and detailed information for one company code, respectively.
In most cases, though, we are not that lucky. There is no Country object type, for
instance. The Helpvalues object type provides BAPIs that can help us to deal with those
entities for which there is no object type in SAP. To figure out whether there is
Helpvalues support for a given entity, you must verify whether a check table or a fixed
values list is defined for the field (DDIF_FIELDINFO_GET allows you to do this at
runtime, but you can also look it up in the DD when you build your application).
Unfortunately, the Helpvalues BAPIs are hard to use and, to make matters worse, they
return different results in different SAP releases. The BAPIs are also relatively slow, so it
is imperative that we make as few calls to them as possible and cache the results. To
complicate matters further, some of the entities form hierarchies. Countries, for example,
contain regions (states, provinces, Kantone, Bundesländer). There are even multi-level
hierarchies (e.g., Sales Organizations containing Distribution Channels containing
Divisions containing Sales Offices). You clearly need a component to deal with all these
issues. If you want to avoid having to build the required component, take a look at the
ARAsoft JCo Extension Library (see below) which takes care of all the necessary
processing.
6
Some BAPIs throw exceptions nevertheless (cf. the footnote above about BAPIs not following the rules).
Always check in the SAP Function Builder whether a given BAPI has defined exceptions.
7
Read: must.
2.4.5. Delegation
The Business Object Repository supports a concept called Delegation. This is used when
you subclass an object type and overwrite one or more of the BAPIs to enforce your own
business rules. If an SAP object type is delegated to one of its subclasses, you should
always call the RFM defined in the subclass instead of the standard SAP one.
If your company uses Delegation, or you want to sell your product to a customer who
does, you should always determine the name of the correct RFM by using some kind of
properties file or looking up the correct name dynamically using metadata access
services. In order to avoid having to build your own metadata component, you could use
the ARAsoft Java BAPI ObjectFactory that comes with the ARAsoft JCo Extension
Library.
8
Details about how to combine connection pools and direct connections in the same application are
discussed in Appendix A-1.
9
SAP abbreviates the SAP Java Connector as JCo. The main class of the product is called JCO. The name
of the jar file in JCo 1.1.x is jCO.jar. Don't ask.
10
If you must use a router string to access your SAP system, the router string is specified together with the
host name in the following format: "/H/<saprouter>/H/<hostname>"
11
Note that the SAP Basis System (now known as the SAP Web Application Server) automatically issues a
DB commit statement at the end of each dialog step. Each RFM call is a separate dialog step.
12
A fairly boring BAPI, but easy to use.
The JCO.Structure class contains type-specific getter methods, like getString() for
strings. Normally an application will use the appropriate getter method, but JCo will try
to convert the contents of the field to the data type you want, if you use a different getter
method. Obviously, this conversion can fail (for example, when a string field contains
"Willem Breuker" and you invoke getDate()). In that case, an exception will be thrown
(see discussion below).
The following table lists all type-specific getter methods. In addition, getValue() can be
used to access a field's contents generically. This method returns a Java Object.
In most cases, dealing with these data types is not a big problem.13 You need to be aware
of some peculiarities of the ABAP data types for date and time, though.
SAP has two distinct data types to deal with date/time information:
• ABAP data type T is a 6-byte string with format HHMMSS.
• ABAP data type D is an 8-byte string with format YYYYMMDD.
Both data types are used in RFMs, including the BAPIs. If a BAPI deals with a
timestamp, two fields, one of type D and one of type T, will be used.
Java, on the other hand, uses one class, Date to represent both date and time information.
Thus a timestamp can be represented in one variable.
JCo automatically converts between the ABAP and Java data types. Fields of ABAP data
types D and T are represented as Java Date objects, leaving the unused portion of the
Date object at its default value. It is up to the Java developer to know (looking it up in
SAP at design time or using a metadata server at runtime) whether a given field holds an
SAP date or time value and act accordingly.
The ABAP types D and T are somewhat lenient about what kind of values they allow.
While there are no dates 00000000 or 99999999 in the real world, ABAP accepts these
values for type D fields. And some BAPIs return these values. Making them illegal in
JCo in order to conform to Java's more stringent requirements for Date objects would
have prevented developers from using BAPIs that use strange dates. Here are the details
of how JCo deals with these special values. You can assign "00000000" and "99999999"
(or "0000-00-00" and "9999-99-99", if you prefer the ISO format) to a JCO.Field object
using the setString() method. The getString() method will return "0000-00-00" or "9999-
99-99", respectively. The getDate() method will return null for "00000000" (there is no
suitable date that Java would support) and 9999-12-31 for "99999999".
A similar issue exists for time fields, "240000" is legal in ABAP, but not in Java.
JCo deals with this in the following manner:
13
But remember the discussion above about BAPIs that do not follow the rules for currency amounts.
When you run the sample program, you will see error messages for the last three
company codes since they – probably – do not exist in your SAP system.
10. Synchronization
In order to optimize performance, JCo itself synchronizes access only to JCO.Pool and
JCO.Repository objects. Everything else is not synchronized. In a multi-threaded
environment, you have to be careful when sharing objects (like JCO.Table objects)
between different threads. JCO.Client objects acquired from a pool in one thread should
never be used in a different thread. Also note that multiple concurrent SAP invocations
for the same direct connection are not possible.
11. Debugging
When debugging an application, it is often advantageous to be able to quickly check the
parameters being passed to and from SAP. JCo offers method writeHTML() to create an
HTML file based on an object of type JCO.Function, JCO.ParameterList, JCO.Structure,
or JCO.Table. For a table, by default, only the first 100 rows (and the last row) of a table
are included, which saves space (and prevents the browser from aborting due to a very
large HTML file). If you want more rows to be written out, you can set the global
property, jco.html.table_max_rows to control the maximum number of rows put
into the HTML file. Here is some sample code:
14
This requires that all involved BAPIs follow the rules regarding commit handling specified in the SAP
BAPI Programming Guide. Not all BAPIs do this. Please check the documentation of each BAPI.
import com.sap.mw.jco.*;
import de.arasoft.sap.jco.*;
import de.arasoft.sap.interfacing.*;
import sap.generated.*;
/**
*
* Sample program using the BAPI proxies
* to create a sales order.
*
* http://www.arasoft.de
* tgs@arasoft.de
* Copyright (c) 2002 ARAsoft GmbH
*
* @author Thomas G. Schuessler, ARAsoft GmbH
* @version 1.0
*/
JCO.Client mConnection;
mConnection.connect();
We are using our subclass of SAP's JCO.Repository (JCoRepository) here since it
provides very useful methods that we will need later.
mRepository = new JCoRepository(mConnection);
If we need to maintain state in SAP because we want to make multiple calls in the same
session, the behavior for direct connections and pools must be very different. Class
JCoContext hides all the differences, we just need to tell the object that from now on we
want to be stateful.
// Start an SAP transaction so that we could, if we wanted, commit
// multiple updates
// in one SAP transaction. Also guarantees that if we use a connection
// pool, we call
// commit in the same SAP session (connection) in which we called
// the update BAPI.
context.startSapTransaction();
We need an order type. In a GUI-driven program, we would first offer the user a list of
available codes and associated descriptions. This is how we would get the data:
// GenericCollection hv =
// mRepository.getHelpvalues(orderHeader.getJCoFields().getDOC_TYPE());
For now we hard-code an English code, and then convert to the internal format:
// The order type uses different codes in different languages.
// Assuming that the
// user enters the value (in English in this sample program),
// we need to convert.
String userEnteredOrderType = "OR";
// Conversion: Alternative 1
orderHeader.setDOC_TYPE(userEnteredOrderType);
orderHeader.setDOC_TYPE(
mRepository.getInternalValue(
orderHeader.getJCoFields().getDOC_TYPE()));
// Conversion: Alternative 2
// String internalOrderType =
// mRepository.getHelpvalues(orderHeader.getJCoFields().getDOC_TYPE()).
// getExternalItem(userEnteredOrderType).getInternalCode();
// orderHeader.setDOC_TYPE(internalOrderType);
But hard-coding customer numbers would be strange, so here we assume user input and
convert it to the internal format.
// The customer number must be converted into the internal format
required by the BAPI.
String userEnteredCustomerNumber = "30001";
orderPartners.setPARTN_NUMB(userEnteredCustomerNumber);
orderPartners.setPARTN_NUMB(
mRepository.getInternalValue(
orderPartners.getJCoFields().getPARTN_NUMB()));
Really strange. The only way to know this is to read the field description in SAP.
// Idiosyncrasy of the BAPI: expects three implicit decimals
orderItems.setREQ_QTY("1000"); // Really: 1.000
System.out.println(returnMessage.getFormattedMessage());
System.out.println("--- Documentation for error message: ---");
String[] documentation =
mRepository.getMessageDocumentation(returnMessage);
for (int j = 0; j < documentation.length; j++) {
System.out.println(documentation[j]);
}
} else {
The sales document number of the newly created sales order is returned as the key field
of the proxy object.