This is the first post in a new series about our open source framework Hibersap. You can use Hibersap in your Java applications to implement interfaces to SAP systems. Under the hood, Hibersap uses the SAP Java Connector version 3 or a JCA compatible resource adapter to connect to SAP. It makes use of Java annotations to map Java classes and fields to ABAP function modules and their parameters and offers a Hibernate-like API to remotely execute those function modules.
In this post we will develop a small sample application that shows Hibersap’s basic concepts. The following posts in this series will successively demonstrate other concepts like transactions, data type conversion, using resource adapters or testing Hibersap applications.
Preparations
- Have a SAP ABAP system for testing purposes up and running. If you do not have access to one, you may download and install a trial/developer version from http://www.sdn.sap.com/irj/scn/nw-downloads.
- Make sure Maven2 is installed on your computer.
- Download SAP Java Connector 3 (http://service.sap.com/connectors) and extract the sapjco3.jar and the sapjco3 native library.
- Install sapjco3 jar to your local Maven repository from the commandline: “mvn install:install -file -DgroupId=com.sap -DartifactId=sap-jco -Dversion=3.0.3 -Dpackaging=jar -Dfile= sapjco3.jar” (assuming you have version 3.0.3, otherwise change the version number to the actual value).
- Create a Maven project with the repositories and dependencies elements as described in the listings below.
- Put the JCo native library into your library path or into the folder your application will be started from (e. g. the root folder of your IDE project that contains your main class).
- Put the Java classes that are described in this chapter into the folder $project_home/src/main/java.
- If you prefer not to write your own code, you may use the example project. You will find more information at the end of this post.
pom.xml – Defining the Hibersap Maven repository:
<repositories> <repository> <id>repository.hibersap</id> <name>Hibersap Repository for Maven</name> <url>http://hibersap.svn.sourceforge.net/viewvc/hibersap/m2repo </url> </repository> </repositories>
pom.xml – Hibersap dependencies:
<dependencies> <dependency> <groupId>org.hibersap</groupId> <artifactId>hibersap-core</artifactId> <version>1.0</version> </dependency> <dependency> <groupId>org.hibersap</groupId> <artifactId>hibersap-jco</artifactId> <version>1.0</version> </dependency> <dependency> <groupId>com.sap</groupId> <artifactId>sap-jco</artifactId> <version>[3.0,4.0)</version> </dependency> </dependencies>
The ABAP function module
We are going to write a small example application that will call the SAP function BAPI_FLCUST_GETLIST which returns a list of customers. This function is part of a demo application in SAP that offers a simplified flight-booking system. (If not already done, it might be necessary to initialize the application in order to create some test and configuration data. To do this, execute the program SAPBC_DATA_GENERATOR in transaction SE38.)
This is the function module’s interface in SAP:
FUNCTION BAPI_FLCUST_GETLIST. IMPORTING VALUE(CUSTOMER_NAME) LIKE BAPISCUDAT-CUSTNAME OPTIONAL VALUE(WEB_USER) LIKE BAPISCUAUX-WEBUSER OPTIONAL VALUE(MAX_ROWS) LIKE BAPISCUAUX-BAPIMAXROW OPTIONAL TABLES CUSTOMER_RANGE STRUCTURE BAPISCUCRA OPTIONAL EXTENSION_IN STRUCTURE BAPIPAREX OPTIONAL CUSTOMER_LIST STRUCTURE BAPISCUDAT OPTIONAL EXTENSION_OUT STRUCTURE BAPIPAREX OPTIONAL RETURN STRUCTURE BAPIRET2 OPTIONAL
This function module has import parameters that represent search criteria to look up flight customers in SAP’s database. The matching customers are returned in the CUSTOMER_LIST table, which contains information such as the customer name and address. The RETURN structure may be filled by SAP with extra messages like errors, warnings, etc.
The import parameters are simple types, whereas the table parameters represent complex data types (ABAP structures). The RETURN table parameter is of type BAPIRET2, which is a common data structure that can be found in many function modules’ interfaces and that is not specific to this BAPI.
To keep the example simple we will only make use of the import parameters CUSTOMER_NAME and MAX_ROWS and the table parameters CUSTOMER_LIST and RETURN. The CUSTOMER_NAME may contain a name pattern with wildcards (‘*’ for any number of characters, ‘+’ for exactly one character), while MAX_ROWS limits the number of returned customers to the specified value. Each CUSTOMER_LIST table entry contains the following fields:
Field Name | Type | Description |
---|---|---|
CUSTOMERID | Numeric character | Customer Number |
CUSTNAME | Character | Customer name |
FORM | Character | Form of address |
STREET | Character | Street |
POSTCODE | Character | Postal Code |
CITY | Character | City |
COUNTR_ISO | Character | ISO country code |
PHONE | Character | Telephone number of flight customer |
Character | Customer e-mail address |
Additionally, we will use the following fields of the RETURN table:
Field Name | Type | Description |
---|---|---|
TYPE | Character | Message type: S Success, E Error, W Warning, I Info, A Abort |
ID | Character | Messages, message class |
NUMBER | Numeric character | Messages, message number |
MESSAGE | Character | Message text |
The BAPI class
To call a function module from a Java application using Hibersap, we have to write a Java class that acts as an adapter to JCo. The BAPI class is a plain Java class with a number of fields representing the BAPI’s import, export, and table parameters. In case the BAPI parameter being a scalar parameter, the Java field itself is of a simple Java type. In the case of a structure parameter, the Java field’s type has to be a complex type, too. A table parameter maps to a Collection of a complex type.
All setup related to the function module’s interface is done via Java annotations. A BAPI class is defined using the Hibersap class annotation @Bapi, which has an argument specifying the name of the SAP function module we want to call (all Hibersap annotations can be found in the package org.hibersap.annotations).
package org.hibersap.examples.model; import org.hibersap.annotations.*; @Bapi("BAPI_FLCUST_GETLIST") public class GetCustomerList { ... }
The fields that are mapped to the function module’s parameters are annotated with either the @Import, @Export, or @Table annotations. Additionally, we have to specify the function module’s field name to which it relates, using the @Parameter annotation.
@Import @Parameter("MAXROWS") private int maxRows; @Import @Parameter("CUSTOMER_NAME") private String namePattern; @Table @Parameter("RETURN") private List<BapiRet2> returnValues = new ArrayList<BapiRet2>(); @Table @Parameter("CUSTOMER_LIST") private List<Customer> customers = new ArrayList<Customer>();
The Java type of each simple field is related to the SAP field’s data type. Hibersap relies on the Java Connector’s conversion scheme which works correctly in most cases. (In cases where it doesn’t, Hibersap offers the use of Converters which are introduced in a following part of this blog series.)
To conclude the example, we write a constructor which has the import parameters as arguments, initializing the corresponding fields.
public GetCustomerList(int maxRows, String namePattern) { this.maxRows = maxRows; this.namePattern = namePattern; }
Finally, we should add a getter method for the customer list and the list of return values. Hibersap itself does not need setter methods, because all fields are set using reflection. We could of course add additional fields and methods if needed.
public List<BapiRet2> getReturnValues() { return returnValues; } public List<Customer> getCustomers() { return customers; }
The structure classes
In the above code, there are two classes that are yet unknown. The ABAP structure BAPIRET2 is used in many ABAP function modules. Therefore Hibersap already provides an implementation in package org.hibersap.bapi. The class Customer is rather application specific, thus we will implement it on our own.
Each Structure class has to be annotated with @BapiStructure to tell Hibersap that it maps to a complex parameter in a BAPI. Each particular field is annotated with the already known @Parameter annotation that defines the name of the corresponding structure field. Our implementation of the Customer consists of the individual fields as well as a getter method for each field.
@BapiStructure public class Customer { @Parameter("CUSTOMERID") private String number; @Parameter("CUSTNAME") private String name; @Parameter("FORM") private String formOfAddress; @Parameter("COUNTR_ISO") private String countryCode; @Parameter("CITY") private String city; @Parameter("POSTCODE") private String postalCode; @Parameter("STREET") private String street; @Parameter("PHONE") private String telephoneNumber; @Parameter("EMAIL") private String email; public String getNumber() { return number; } // add getters for the other fields }
Configuration
To configure Hibersap, we specify some information needed by Hibersap, plus properties for the Java Connector. To accomplish this, we create an XML file named hibersap.xml in $project_home/src/main/resources/META-INF (Hibersap expects the configuration file to be in the classpath under “/META-INF/hibersap.xml”).
In the example, we use a minimum set of JCo properties to connect to the back-end SAP system. All valid JCo properties are specified in the JCo library interface com.sap.conn.jco.ext.DestinationDataProvider (see javadoc provided with JCo). The example assumes that SAP runs on the local machine, the system number is ’00’ and that we connect to client ‘001’ with user ‘sapuser’ and password ‘password’. If you connect to your own SAP system, you have to adapt these properties to your specific environment.
<?xml version="1.0" encoding="UTF-8"?> <hibersap xmlns="urn:hibersap:hibersap-configuration:1.0"> <session-manager name="NSP"> <context>org.hibersap.execution.jco.JCoContext</context> <properties> <property name="jco.client.client" value="001" /> <property name="jco.client.user" value="sapuser" /> <property name="jco.client.passwd" value="password" /> <property name="jco.client.lang" value="en" /> <property name="jco.client.ashost" value="127.0.0.1" /> <property name="jco.client.sysnr" value="00" /> <property name="jco.destination.pool_capacity" value="1" /> </properties> <annotatedClasses> <class>org.hibersap.examples.model.GetCustomerList</class> </annotatedClasses> </session-manager> </hibersap>
Calling the ABAP function
In Hibersap, a SessionManager is responsible for creating Sessions. A Session represents a connection to the SAP system. The first time we call a function module on a Session, Hibersap gets a connection from the underlying connection pool (that is managed by JCo). When closing a session, the connection is returned to the pool. Therefore you have to take care to always close the session, preferably in a finally block, otherwise the connection pool may get exhausted sooner or later.
The following code configures the Hibersap SessionManager named “NSP”. Note that the name has to match the one defined in the name attribute of the session-manager element in hibersap.xml. In a real application the creation of the SessionManager should be done once, reusing the SessionManager throughout the application’s lifetime, because it is rather expensive to create.
public SessionManager createSessionManager() { AnnotationConfiguration configuration = new AnnotationConfiguration( "NSP" ); return configuration.buildSessionManager(); }
Now it is time to call the function module in SAP. After creating the SessionManager and opening a new Session, we create an instance of our BAPI class, passing all parameters needed to execute the function as Constructor arguments. Then we simply call the execute() method on the Session, passing the BAPI class, which actually performs the call to SAP. Now the bapi object is enriched with all the values which the function module returned and which we have mapped in our BAPI Class.
public void showCustomerList() { SessionManager sessionManager = createSessionManager(); Session session = sessionManager.openSession(); try { GetCustomerList bapi = new GetCustomerList( 50, "M*" ); session.execute( bapi ); printCustomerData( bapi ); } finally { session.close(); } }
To see the result of the function call, we simply print the table fields to the console in the printCustomerData() method:
private void printCustomerData( GetCustomerList bapi ) { System.out.println( "Customers:" ); List<Customer> customers = bapi.getCustomers(); for ( Customer customer : customers ) { System.out.print( customer.getNumber() ); System.out.print( "\t" + customer.getName() ); [...] System.out.println( "\t" + customer.getEmail() ); } System.out.println( "Return:" ); List<BapiRet2> returnValues = bapi.getReturnValues(); for ( BapiRet2 value : returnValues ) { System.out.print( value.getMessage() ); System.out.print( "\t" + value.getNumber() ); System.out.print( "\t" + value.getType() ); System.out.println( "\t" + value.getId() ); } }
Finally, we create a main method that calls showCustomerList(), build the project with maven on the command-line using “mvn compile” and run the main class.
Example project
There is an example web application which you can check out from the Hibersap Subversion repository using the following URL:
https://hibersap.svn.sourceforge.net/svnroot/hibersap/tags/hibersap-example-webapp-1.0
See the README.txt on instructions how to set up the project (basically this means setting up the SAP Java Connector), build and run the application with Maven. Then you can use your web browser to find and display flight customers from SAP.
With each of the following posts we will iteratively extend the application.
Conclusion
In this post we showed how to elegantly and efficiently implement SAP interfaces with Hibersap. Making use of Java Annotations and a Hibernate-/JPA-like API, Hibersap fits well into the range of state-of-the-art Java EE technologies that make writing enterprise applications much more fun, compared to the rather verbose and technical API of the SAP Java Connector.
For more information see the Hibersap web site (http://hibersap.org), in particular the Reference Documentation if you like to take a deeper look into the functionality provided by the framework.
If you like Hibersap, please do not hesitate to get involved with the project. Hibersap is open source software and relies on community feedback for improvement. Hibersap is hosted on Sourceforge.net. On its project site (http://sf.net/projects/hibersap) you have the possibility to open tracker items for feature requests or errors, to take part in forum discussions if you are searching for help or want to give any kind of feedback or to subscribe to the mailing lists to get the latest announcements or to discuss the development of Hibersap applications.