1. Introduction
Hibersap helps developers of Java applications to call business logic in SAP ERP. Hibersap defines a set of Java annotations to map SAP function modules to Java classes as well as a small, clean API to execute these function modules and handle transaction and security aspects. Hibersap’s programming model is quite similar to those of modern O/R mappers, significantly speeding up the development of SAP interfaces and making it much more fun to write the integration code.
Under the hood, Hibersap either uses the SAP Java Connector (JCo) or a JCA compatible resource adapter to communicate with the SAP backend. While retaining the benefits of JCo and JCA like transactions, security, connection pooling, etc., developers can focus on writing business logic because the need for boilerplate code is largely reduced.
Hibersap can either be configured programmatically or by providing an XML file. Switching between JCo and JCA is a sole matter of configuration, the program code remains unchanged. This makes it possible to execute integration tests via JCo while using a resource adapter in the production environment.
Regarding data type conversion from ABAP to Java types, Hibersap per default uses the conversion as is done by JCo resp. the JCA Resource Adapter. Custom converters may be used to implement special conversion logic. Hibersap will then call the conversion code on-the-fly, before and after calling the function module in SAP.
Hibersap may be configured to use Bean Validation (JSR 303) to validate field values according to the standard Bean Validation annotations.
If the function module defines a standard Return structure or table, Hibersap is able to automatically detect an error state and throw a SapException which includes the information returned by SAP.
For Java EE applications it is recommended to use a resource adapter since it integrates seamlessly with Java EE containers. Using the Hibersap EJB tools makes it very easy to call SAP from EJB methods making use of Container Managed Transactions and Container Managed Security. In a managed environment calls to SAP functions may even be part of distributed transactions.
For advanced use cases, there are two types of interceptors that work on different levels of the call stack.
In the next chapters you will see out how easy it is to use Hibersap.
2. Installation
2.1. The Hibersap libraries
The Hibersap framework is divided into the following libraries:
-
hibersap-core
-
hibersap-jco
-
hibersap-jca
-
hibersap-ejb
When using Hibersap in conjunction with a JCA compatible resource adapter, both the hibersap-core and the hibersap-jca jars must be on the application’s classpath. When using Hibersap with the SAP Java Connector (JCo), the hibersap-core and the hibersap-jco libraries are needed.
When using Hibersap with EJBs, you may consider using the EJB tools provided by the hibersap-ejb library. The EJB tools provide an EJB3 interceptor which takes care of opening and closing sessions as well as injecting them into your EJBs at a special injection point denoted by a @HibersapSession annotation. See section Hibersap EJB Tools for more information.
2.2. Third Party Libraries
Hibersap has a few dependencies on third-party libraries, which you need to add to your application’s classpath. The easiest way to achieve this is to build your projects with Maven. See section Building Hibersap Applications with Maven.
Depending on which interfacing technology is actually used, you also need the SAP Java Connector (JCo) or a JCA compatible resource adapter.
2.2.1. Hibersap Dependencies
When using Maven, it will download the dependencies automatically during the build. Otherwise, you need to add dependencies manually. The following sections show Hibersap’s runtime dependencies and their version as Maven coordinates, so they can easily be downloaded from the Maven central repository.
hibersap-core Dependencies
-
Apache Commons Lang: commons-lang:commons-lang:2.6
-
Apache Commons Logging: commons-logging:commons-logging:1.2
hibersap-jco Dependencies
-
hibersap-core dependencies, plus:
-
SAP Java Connector (JCo) version 3.0.x (see section The SAP Java Connector (JCo))
2.2.2. The SAP Java Connector (JCo)
The SAP JCo library is provided by SAP to their customers and comes with its own (commercial) license. Thus it can not be provided by public Maven repositories or download sites. Please check the following URL for more information and downloads: https://service.sap.com/connectors. You need to have an account for this URL, which may be obtained by SAP customers. Valuable help regarding SAP technologies can also be found on the SAP Community Network (SCN): https://scn.sap.com.
After downloading SAP Java Connector 3.0, extract the sapjco3.jar and the sapjco3 native library. You then need to install the sapjco3.jar into your local Maven repository or deploy it to your project’s or company’s remote repository.
To install it to your local Maven repository issue the following Maven command on the command line. Since JCo is defined as an optional dependency in Hibersap, you can choose any values for groupId, artifactId and version. You make sure your project’s pom.xml defines the dependency with the matching coordinates.
Note: The artifactId com.sap.conn.jco.sapjco3
is a trick to work around a runtime check in the latest versions of
the SAP JCo library which enforces the name of the loaded jar file to either equal "sapjco3.jar" (or to start with
"com.sap.conn.jco").
Without this, Maven would rename the jar file to something like "sapjco3.jar-3.0.15.jar" when copying it
from the repository which would lead to runtime errors.
mvn install:install-file -DgroupId=org.hibersap \\ -DartifactId=com.sap.conn.jco.sapjco3 \\ -Dversion=3.0.15 \\ -Dpackaging=jar -Dfile=sapjco.jar
The native library must be on your application’s library path.
You may copy it to a global library path, to a special directory such as C:/WINDOWS/SYSTEM32
on Windows systems or to the execution directory of the application (e.g. the bin folder of your application server or the folder from which you run your application).
Additionally, the JCo jar file must be on the application’s classpath.
You may set the Java library path programmatically within your application by calling
System.setProperty("java.library.path", "/the/actual/path/to/sapjco3-native-lib/");
or by passing it to the Java runtime when executing your Java application using
java [...] -Djava.library.path="/the/actual/path/to/sapjco3-native-lib/"
2.3. The Cuckoo Resource Adapter for SAP
If you are using Hibersap with a resource adapter in a managed environment, the preferred choice is the Cuckoo Resource Adapter for SAP. The project’s web site can be found under https://sourceforge.net/p/cuckoo-ra/home.
You need to deploy the resource adapter to your application server. Since it uses the SAP Java Connector internally, the previous section also applies to this scenario, i.e. the SAP JCo has to be installed for use within the application server.
2.4. Other JCA compatible Resource Adapters
Being the only open source resource adapters for SAP, Hibersap was tested with Cuckoo as well as the SAP BAPI JCA Adapter (see https://sourceforge.net/projects/sapbapijcaadapt). Nevertheless, commercial alternatives do exist. We would be glad to get your feedback when using one of these. Note that a resource adapter must implement the JCA Common Client Interface (CCI) to be supported by Hibersap.
Integrating another Resource Adapter in combination with application-managed authentication includes writing a Resource Adapter specific implementation of the interface org.hibersap.execution.jca.cci.ConnectionSpecFactory
.
This is due to a limitation in the JCA specification which specifies the javax.resource.cci.ConnectionSpec
as an empty interface, letting it up to the vendor-specific implementation to define which properties it contains.
Hibersap’s aim to support different resource adapters means that there must be a layer on top of the ConnectionSpec interface which makes it possible to adapt to different JCA implementations without having explicit dependencies to each of them.
Hibersap implements ConnectionSpecFactories for Cuckoo as well as the SAP BAPI JCA Adapter that use reflection for setting and getting the ConnectionSpec’s parameters in order to reduce (compile-time) dependencies to the individual resource adapters.
However, an implementation of the ConnectionSpecFactory
is not needed if the application uses only container-managed authentication, because in this case Hibersap does not need to use a ConnectionSpec.
2.5. Building Hibersap Applications with Maven
The Hibersap libraries are deployed to the Maven Central repository, so the dependencies are downloaded by Maven when you specify them in your project.
The following listing shows how to specify the dependencies to the Hibersap libraries in your pom.xml.
If you use Hibersap directly with JCo you need to declare the following dependencies in your project:
<dependency>
<groupId>org.hibersap</groupId>
<artifactId>hibersap-core</artifactId>
<version>1.3.0</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>org.hibersap</groupId>
<artifactId>hibersap-jco</artifactId>
<version>1.3.0</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>org.hibersap</groupId>
<artifactId>com.sap.conn.jco.sapjco3</artifactId>
<version>3.0.15</version>
<type>jar</type>
</dependency>
When using Hibersap with JCA you need to declare the following dependencies:
<dependency>
<groupId>org.hibersap</groupId>
<artifactId>hibersap-core</artifactId>
<version>1.3.0</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>org.hibersap</groupId>
<artifactId>hibersap-jca</artifactId>
<version>1.3.0</version>
<type>jar</type>
</dependency>
Due to Maven’s dependency management capabilities, all transient dependencies are automatically resolved so that there is no need to specify any of the above mentioned third-party libraries. One exception to this is the dependency to the SAP Java Connector (JCo) which you need when using Hibersap directly with JCo. This dependency in declared as optional in the pom.xml of the hibersap-jco module. Because of this it is explicitly declared in the above example.
3. Configuration
Hibersap configuration consists of a set of properties. There are two possibilities to configure Hibersap:
-
XML file configuration
-
Programmatic configuration
While bootstrapping Hibersap first tries to configure itself looking for the /META-INF/hibersap.xml
file in the classpath.
When creating a SessionManager, configuration can be set or overwritten using programmatic configuration.
Using XML file configuration is usually the easiest way to configure Hibersap.
3.1. XML file configuration
The format of the XML configuration file is inspired by JPA’s persistence.xml. If you have to access different SAP systems, you can define more than one SessionManager by multiplying the session-manager XML element.
The following example shows XML file configuration when using Hibersap with JCo. Note that you may use the Hibersap XML schema definition as shown in the hibersap element to get validation and auto-completion in your IDE or XML editor.
<?xml version="1.0" encoding="UTF-8"?>
<hibersap xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://hibersap.org/xml/ns/hibersap-config"
xsi:schemaLocation="http://hibersap.org/xml/ns/hibersap-config
https://hibersap.sourceforge.net/xml/hibersap_1_1.xsd">
<session-manager name="A12">
<context>org.hibersap.execution.jco.JCoContext</context>
<properties>
<property name="jco.client.client" value="800" />
<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="10.20.30.40" />
<property name="jco.client.sysnr" value="00" />
</properties>
<annotated-classes>
<annotated-class>org.hibersap.examples.flightlist.FlightListBapi</annotated-class>
<annotated-class>org.hibersap.examples.flightdetail.FlightDetailBapi</annotated-class>
</annotated-classes>
</session-manager>
</hibersap>
The following example shows the XML file configuration when using a JCA compatible resource adapter.
<?xml version="1.0" encoding="UTF-8"?>
<hibersap xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://hibersap.org/xml/ns/hibersap-config"
xsi:schemaLocation="http://hibersap.org/xml/ns/hibersap-config
http://hibersap.sourceforge.net/xml/hibersap_1_1.xsd">
<session-manager name="A12">
<context>org.hibersap.execution.jca.JCAContext</context>
<jca-connection-factory>java:/eis/sap/A12</jca-connection-factory>
<jca-connectionspec-factory>
org.hibersap.execution.jca.cci.SapBapiJcaAdapterConnectionSpecFactory
</jca-connectionspec-factory>
<annotated-classes>
<annotated-class>org.hibersap.examples.flightlist.FlightListBapi</annotated-class>
<annotated-class>org.hibersap.examples.flightdetail.FlightDetailBapi</annotated-class>
</annotated-classes>
</session-manager>
</hibersap>
Following is a complete list of Hibersap’s configuration parameters.
- context
-
The fully qualified class name of the Context class. This class must implement
org.hibersap.session.Context
and acts as a facade to the interfacing technology that is actually used. Existing implementations areorg.hibersap.execution.jco.JCoContext
for the SAP Java Connector (JCo) ororg.hibersap.execution.jca.JCAContext
for a JCA compatible Resource Adapter (default: JCoContext). - jca-connection-factory
-
The JNDI name of the JCA Connection Factory. This parameter has to be specified if the application uses a JCA compatible resource adapter. The resource adapter has to be deployed on the application server independently from Hibersap and the application, defining a JNDI name for each configured
ConnectionFactory
. Hibersap will use this name to look up the resource adapter’sConnectionFactory
. - jca-connectionspec-factory
-
The fully qualified class name of the
ConnectionSpecFactory
implementation used to get aConnectionSpec
object. AConnectionSpec
is used to provide data such as a user name and password for the current session. The existing implementations areCuckooJcaAdapterConnectionSpecFactory
andSapBapiJcaAdapterConnectionSpecFactory
. Others may be added by implementingorg.hibersap.execution.jca.cci.ConnectionSpecFactory
interface. - properties
-
Zero or more additional properties. These depend on the interfacing technology in use. For the SAP JCo, all the JCo-specific properties must be defined here. See javadoc for the class
com.sap.conn.jco.ext.DestinationDataProvider
in the JCo distribution to get a complete list of possible properties. For use with JCA, this element is not needed. - annotated-classes
-
All annotated BAPI classes which are used with the
SessionManager
must be listed here. - validation-mode
-
Specifies if Bean Validation (JSR-303) shall be used. Possible values are
NONE
to deactivate Bean Validation,AUTO
to activate Bean Validation only if a provider is available on the classpath, andCALLBACK
to make sure Bean Validation is used. WithCALLBACK
, Hibersap will throw an exception if no Bean Validation provider is present at runtime. - bapi-interceptor-classes
-
A list of the interceptor classes. These must implement
org.hibersap.interceptor.BapiInterceptor
and are called before and after a SAP function module gets executed. Each interceptor must be defined with the fully qualified class name in a seperate bapi-interceptor-class element. - execution-interceptor-classes
-
A list of the interceptor classes. These must implement
org.hibersap.interceptor.ExecutionInterceptor
and are called before and after a SAP function module gets executed. Each interceptor must be defined with the fully qualified class name in a seperate execution-interceptor-class element.
To build a SessionManager
using XML file configuration, you simply have to create an object of class org.hibersap.configuration.AnnotationConfiguration
, specifying the SessionManager
name as an argument.
Note that there is also a default constructor for AnnotationConfiguration
which can be used if there
is only one configured SessionManager
. Hibersap will issue a warning when there are more than one `SessionManager`s
configured, but the no-args constructor is used.
AnnotationConfiguration configuration= new AnnotationConfiguration("A12");
SessionManager sessionManager = configuration.buildSessionManager();
3.2. Programmatic configuration
After creating a Configuration
object which will be used to build the SessionManager
, configuration can be set or overwritten programmatically.
The information from the XML file is internally converted into a Java data structure reflecting the structure of the XML document. All configuration classes have Java Bean style accessor methods (get.. and set..) for their fields.
When using programmatic configuration you have to change or create SessionManagerConfig
for each SessionManager
you want to use.
You can use method chaining to build the object.
The following example creates a SessionManagerConfig
object that is equivalent to the one created internally when using XML file configuration.
SessionManagerConfig cfg = new SessionManagerConfig( "A12" )
.setContext( JCoContext.class.getName() )
.setProperty( "jco.client.client", "800" )
.setProperty( "jco.client.user", "sapuser" )
.setProperty( "jco.client.passwd", "password" )
.setProperty( "jco.client.lang", "en" )
.setProperty( "jco.client.ashost", "10.20.80.76" )
.setProperty( "jco.client.sysnr", "00" );
AnnotationConfiguration configuration = new AnnotationConfiguration(cfg);
configuration.addBapiClasses( FlightListBapi.class, FlightDetailBapi.class );
SessionManager sessionManager = configuration.buildSessionManager();
4. Parameter Mapping
The main task of Hibersap is to map Remote Function Modules (RFMs) — which are defined in SAP ERP — to Java classes. To accomplish this, Hibersap provides a set of Java annotations that map Java classes and their fields to the elements of the function module’s interface. This section provides detailed information on the Hibersap annotations and on how data types are converted.
For a complete example of how to define Hibersap classes and mappings, see the Hibersap Get Started Guide or checkout the Hibersap examples code from GitHub.
4.1. Hibersap Annotations
Since Hibersap maps SAP Remote Function Modules to plain Java classes, an application must define one class for each function module that acts as a kind of proxy.
This class is called a BAPI class and has to be annotated with the @Bapi
annotation.
(a BAPI, Business Application Programming Interface, is actually a subset of all Remote Function Modules in a SAP ERP system but provides a more handy term).
The annotation’s value
property defines the name of the corresponding function module:
@Bapi("BAPI_SFLIGHT_GETLIST")
public class FlightListBapi {
...
}
A function module’s interface usually has a set of parameters which are either of a scalar data type (such as a number, a text or a date) or of a complex data type (in ABAP a complex type is called structure, and provides a set of attributes of scalar or complex data type). In Hibersap, scalar parameters can be mapped directly to a field of the BAPI Java class, whereas structures are mapped to complex Java types, i.e. classes with a set of fields. The classes representing an ABAP structure may be annotated with the @BapiStructure annotation, but this is not mandatory. Since Structure class instances need to be created by Hibersap, they must have a default constructor.
@BapiStructure
public class BapiRet2 {
...
}
To map parameters of a function module to fields of Java classes, those fields have to be annotated with the @Parameter
annotation.
Here, Hibersap needs to know the name of the function module’s parameter as well as its type (scalar or complex) using the annotation’s value and type properties respectively.
The Enumeration ParameterType
defines the types ParameterType.STRUCTURE
and ParameterType.SIMPLE
for parameters of a complex or scalar type:
@Export
@Parameter(value = "RETURN", type = ParameterType.STRUCTURE)
private BapiRet2 returnData;
Since the interface parameters of ABAP functions are divided into import, export and table parameters, Hibersap has to know where to find the corresponding parameters. The @Import, @Export and @Table annotations provide this information.
@BapiStructure
public class BapiRet2 {
@Parameter("MESSAGE")
private String message;
...
}
ABAP tables are lists of complex parameters. As such they are mapped to a Java array or a java.util.Collection
field.
Hibersap again needs to know the name of the ABAP parameter as provided by the @Parameter
annotation and its type as provided by the @Table
annotation.
It is a special characteristic of table parameters in ABAP that they import and export data in both directions.
An import or export parameter can also be a table, in which case the parameter needs to be annotated with @Import
or @Export
and @Parameter(…, type = ParameterType.TABLE_STRUCTURE)
.
Tables that import data to the function module are filled by the Java application, Hibersap will use the application-provided array or collection to populate the table. Tables that export data from the function module will be read by Hibersap and copied to the corresponding array or collection. Hibersap detects the type of its elements by the array type if it is declared as an array, or by its generic type if it is declared as a collection. When using a collection, it is crucial to declare it with a generic type (the class Flight in the example), otherwise Hibersap will throw an error because it cannot determine the element type:
@Bapi("BAPI_SFLIGHT_GETLIST")
public class FlightListBapi {
@Table
@Parameter("FLIGHTLIST")
private List<Flight> flightList;
}
The field representing a table does not need to be defined as a concrete implementation of the java.util.Collection
interface.
When Hibersap creates a collection, it instantiates a java.util.HashSet
for fields defined as java.util.Set
and a java.util.ArrayList
for fields defined as java.util.Collection
or java.util.List
.
The following list shows an overview of the Hibersap annotations.
@Bapi
|
Maps a Java class to a Remote Function Module in SAP.
|
@BapiStructure
|
Maps the Java class to a structure in an RFM’s interface. |
@Parameter
|
Maps a field to a parameter of the RFM’s interface.
|
@Import
|
Defines a field annotated with |
@Export
|
Defines a field annotated with |
@Table
|
Defines a field annotated with |
@Convert
|
The parameter’s data type will be converted by the given converter, i.e. the Java field’s data type may differ from the JCo data type conversion pattern.
|
@ThrowExceptionOnError
|
Tells Hibersap to throw a
|
4.2. Type Conversion
4.2.1. Default Conversion scheme
The Java type of each simple field is closely related to the ABAP field’s data type. When using Hibersap with JCo, it relies on the Java Connector’s conversion scheme as shown in the following table.
ABAP type | Description | Java type |
---|---|---|
C |
Character |
java.lang.String |
N |
Numerical character |
java.lang.String |
D |
Date |
java.lang.Date |
T |
Time |
java.lang.Date |
X |
Byte field |
byte[] |
P |
Packed number |
java.lang.BigDecimal |
I |
4-byte integer |
int |
F |
Floating point number |
double |
STRING |
Variable-length character |
java.lang.String |
XSTRING |
Variable-length byte field |
byte[] |
When using Hibersap with JCA, it relies on the data types returned by the Resource Adapter. Most Resource Adapters for SAP use JCo, so the above type conversion scheme will apply here, too.
4.2.2. Custom Converters
Custom converters allow for converting a parameter’s data type to any Java type and vice versa.
A common example for a custom converter is one that converts boolean values.
ABAP does not have a boolean data type, a boolean in ABAP is usually represented by a character field of length 1.
It evaluates to true if the parameter’s value it equals to 'X', to false if the parameter’s value is empty.
With a Hibersap converter it is possible to map an ABAP "boolean" parameter to a boolean
field in Java.
You can use Hibersap converters to do any kind of data type conversion. There are a few Converters defined in package org.hibersap.conversion
. However, it is easy to write your own converter by implementing the org.hibersap.conversion.Converter
interface:
public interface Converter<J, S> extends Serializable
{
/**
* Convert the SAP value, as it is returned by the underlying
* interfacing technology (e.g. the SAP Java Connector, JCo)
* to the Java data type of the corresponding BAPI class field.
* Hibersap will call this method after calling the SAP function
* and before setting the field in the Java class.
*
* @param sapValue The object which is returned by the SAP interface
* @return The converted value
* @throws ConversionException if the value can not be converted
*/
J convertToJava( S sapValue ) throws ConversionException;
/**
* Convert the Java value of the corresponding BAPI class field to
* the data type as it is expected by the underlying interfacing
* technology (e.g. the SAP Java Connector, JCo).
* Hibersap will call this method before calling the SAP function.
*
* @param javaValue The value of the BAPI class field
* @return The converted value
* @throws ConversionException if the value can not be converted
*/
S convertToSap( J javaValue ) throws ConversionException;
}
To use a converter, you simply annotate the field in the BAPI or Structure class with Hibersap’s @Convert
annotation, specifying the converter that should be called:
@Import
@Parameter ( "SHOW_DETAILS" )
@Convert( converter = BooleanConverter.class )
private final boolean showDetails;
You can use converters not only with simple parameters, but also with structure and table parameters. In case of a structure parameter, the object passed to Converter.convertToJava()
will be a java.util.Map
with the structure parameter names as keys and the parameter values as values. Converter.convertToSap()
must return a Map like this.
When using a converter with a table parameter, the object passed to Converter.convertToJava()
is a java.util.List
with java.util.Map
instances as list elements. Each of these maps has the structure parameter name as the map’s key and the parameter’s value as the map’s value. Converter.convertToSap()
must return a List
of `Map`s of the same structure.
4.3. Bean Validation
Java Bean Validation (JSR 303) is a Java EE standard which defines an API and metadata model (in the form of Java annotations) to validate Java Beans and their attributes. If a Bean Validation provider is on the classpath of your application, Hibersap will validate the BAPI and Structure classes each time before a function gets executed.
To configure Bean Validation for your Hibersap application you may specify the validation element in the hibersap XML configuration file:
<hibersap>
<session-manager name="...">
...
<validation-mode>AUTO</validation-mode>
...
</session-manager>
</hibersap>
If using programmatic configuration, just set the validationMode
property of the SessionManagerConfig
:
sessionManagerConfig.setValidationMode( ValidationMode.AUTO );
The validation element may contain any of the values defined in org.hibersap.configuration.xml.ValidationMode
:
AUTO |
Use Bean Validation if a provider is found on the classpath (default). |
CALLBACK |
Force the use of Bean Validation. Hibersap will throw an exception if no provider is found on the classpath. |
NONE |
Do not use Bean Validation, even if a provider is present. |
5. Calling SAP Functions
5.1. Building a SessionManager
Chapter Configuration explains in detail how to configure Hibersap.
The following code snippet assumes that there is the /META-INF/hibersap.xml
configuration file defining a session manager named "A12" in the application’s classpath and shows how to build a SessionManager
instance:
AnnotationConfiguration configuration= new AnnotationConfiguration( "A12" );
SessionManager sessionManager = configuration.buildSessionManager();
The SessionManager
should be created only once in an application’s lifetime since its creation is
It depends on the kind of application how to store the SessionManager
instances. In a web application they may be created and closed in a ServletContextListener
when starting and stopping the application, putting them into the servlet context.
In an EJB application this may be done using a Singleton EJB and its life-cycle methods and binding the individual SessionManager instances to JNDI.
In a stand-alone application the SessionManager might be simply passed around or managed by some dependency injection framework.
5.2. Calling a function in SAP
Calling a remote function module in SAP is as easy as opening a new Session
with the SessionManager
, creating an instance of the BAPI class with the required parameters and passing it to the Session.execute()
method.
Session session = sessionManager.openSession();
try {
FlightListBapi flightList = new FlightListBapi( "DE", "Frankfurt",
"DE", "Berlin",
null, false, 10 );
session.execute( flightList );
showResult( flightList );
}
finally
{
session.close();
}
When calling Session.execute()
, Hibersap populates the given Bapi object with the SAP function’s return parameters as defined in the parameter mappings.
A physical connection to the SAP system is created during the first call of Session.execute()
.
It is crucial to close the Session when it is no longer needed.
If sessions are not closed, the connection pool (as managed by JCo or the resource adapter) may get exhausted.
Therefore it is strongly recommended to close the Session
in a finally
block like in the above example.
This makes sure the session gets closed even if an exception is thrown during execution of the try
block.
Keep in mind that, if the application keeps sessions open for a long time, connections may also be shut down by the SAP system after some timeout period or there is some possibility that they may get broken due to network problems. Thus, a session should have a short lifetime (just like a database session) and should only be used for a single unit of work.
Hibersap sessions are inexpensive to create, the Java Connector’s or Resource Adapter’s connection pool takes care of an efficient connection management (if properly configured), and Hibersap will not akquire a SAP connection before it is needed.
5.3. Interceptors
There are two kind of interceptors that intercept calls to SAP function on different levels. Both interceptor types make it possible for applications to apply additional logic which is related to cross-cutting concerns like e.g. logging. Both interceptors are registered with a session manager in the Hibersap configuration and apply to all function calls using this session manager.
5.3.1. Bapi Interceptors
Bapi Interceptors implement the org.hibersap.interceptor.BapiInterceptor
interface and are called before and after a function call has been executed.
The interface defines the methods beforeExecution( java.lang.Object )
and afterExecution( java.lang.Object )
. As an argument, the Bapi object is passed to these methods.
E.g., Hibersap uses a Bapi Interceptor internally to apply Bean Validation (see class org.hibersap.validation.BeanValidationInterceptor
).
Applications may of course add their own Bapi Interceptors.
5.3.2. Execution Interceptors
Execution interceptors implement the org.hibersap.interceptor.ExecutionInterceptor
interface and are also called before and after a function call has been executed.
The interface defines the methods beforeExecution( org.hibersap.mapping.model.BapiMapping, java.util.Map<String, Object> )
and afterExecution( org.hibersap.mapping.model.BapiMapping, java.util.Map<String, Object> )
.
In comparison to Bapi Interceptors, they are working on a lower level of the execution stack and provide information about the mapping of the Bapi class currently being executed as well as Hibersap’s internal representation of the parameters passed to and received from SAP.
The parameters are provided to the interceptor methods in terms of a java.util.Map
with keys of type java.util.String
specifying the parameter names and values of different types specifying the parameter values.
The map’s root elements have the keys "IMPORT", "EXPORT" and "TABLE" with a java.util.Map
each containing the lists of the function’s import, export and table parameters respectively.
All other keys are the names of the SAP function’s parameters.
For scalar parameters values are of a simple Java type like numbers, dates or strings. For complex parameters values are of type java.util.Map
with the type elements as key/value pairs. For table parameters the values are of type java.util.List
with maps for each row.
An example structure:
java.util.Map functionMap
"IMPORT": java.util.Map
<name import param simple>: <value import param1 simple>
<name import param complex>: java.util.Map
<name structure param 1>: <value structure param1>
<name structure param 2>: <value structure param2>
"TABLE": java.util.Map
<name table param>: java.util.List
<list element 1>
java.util.Map
<name structure param1 row 1>: <value structure param1 row 1>
<name structure param2 row 1>: <value structure param2 row 1>
<list element 2>
java.util.Map
<name structure param1 row 2>: <value structure param1 row 2>
<name structure param2 row 2>: <value structure param2 row 2>
...
As an example, Hibersap internally uses an Execution Interceptor to detect errors when using the @ThrowExceptionOnError annotation (see class org.hibersap.interceptor.impl.SapErrorInterceptor
). This way, the function’s parameter containing the error information does not need to be mapped to Java fields in the Bapi class to allow Hibersap to detect errors.
For use in applications Bapi Interceptors may suffice in most cases, are much easier to implement and should therefore be favored if sufficient for the use case. Execution Interceptors should only be used when the additional flexibility is needed.
6. Transactions
SAP ERP allows external callers of its function modules to control logical units of work (i.e. transactions).
An application may call a number of function modules and commit or rollback all changes made during the function calls.
This is done by calling special function modules named BAPI_TRANSACTION_COMMIT
and BAPI_TRANSACTION_ROLLBACK
.
NOTE: Please note that not all Remote Function Modules and even not all standard BAPIs in SAP R/3 support this feature. Special care has to be taken in the ABAP code which should not call a COMMIT WORK itself. See SAP note 0131838, "Collective Note for BAPIs with the Commit Work Command" for a list of BAPIs that are not transaction enabled.
Using Hibersap, it is very easy to start and end transactions, you do not have to map and call these function modules yourself. Depending on the chosen interfacing technology, you can either handle your transactions manually (JCo and JCA) or use Container Managed Transactions (JCA only).
6.1. Manual transaction handling
The Hibersap Session has a method beginTransaction()
which must be called when starting the transaction.
beginTransaction()
returns a Transaction
object on which you can later call the methods commit()
or rollback()
to tell SAP to commit or rollback all changes made during the transaction.
Session session = sessionManager.openSession();
Transaction transaction = null;
try
{
transaction = session.beginTransaction();
session.execute( bapiObject1 );
...
session.execute( bapiObjectN );
transaction.commit();
}
catch (Exception e)
{
if (transaction != null) {
transaction.rollback();
}
}
finally
{
session.close();
}
In Java EE applications that make use of EJBs and Bean Managed Transactions (BMT) it is also necessary to explicitly start, commit and rollback transactions like in the code example.
6.2. Automatic transaction handling
Applications running in an application server and using EJBs and a Java EE compatible Resource Adapter can profit from the application server’s capability to automatically handle transactions. This is called Container Managed Transactions (CMT).
In case of using CMT, the application code does not have to take care of starting, committing and rolling back transactions.
See section Java EE Integration for a detailed discussion on Container Managed Transactions.
7. Authentification
7.1. Configured User
The most common way to interact with a SAP system in enterprise applications is to use a central SAP user that is shared by all users of the application.
Using this kind of authentication is a matter of configuration.
When using Hibersap with JCo, the user credentials are provided through Hibersap configuration, namely the JCo properties defined for a SessionManager
.
When using Hibersap with JCA, the credentials are usually defined in the resource adapter configuration.
7.2. Per-session authentication
If the application has to provide user credentials, e.g. when each application user logs on to the SAP system using his own SAP account, you can specify logon credentials for each Hibersap session you create.
In this case, when obtaining a Session
from the SessionManager
, the method openSession( org.hibersap.session.Credentials )
must be used. The authentication information is specified in the Credentials
object.
Credentials credentials = new Credentials()
.setUser("archibald_tuttle")
.setPassword("myPassw0rd")
.setLanguage("EN");
Session session = sessionManager.openSession( credentials );
session.execute( bapiClass );
...
Hibersap simply passes the attributes defined in the credentials object to SAP.
The following table lists the available attributes of the Credentials
class including the corresponding JCo parameter as defined in interface com.sap.conn.jco.ext.DestinationDataProvider
of the SAP Java Connector version 3.
Credentials field | Description | JCo parameter |
---|---|---|
|
SAP logon user |
|
|
SAP logon user alias, can be used instead of logon user |
|
|
SAP client |
|
|
Logon language ISO code |
|
|
Logon password |
|
|
SAP Cookie Version 2 as logon ticket for SSO based authentication |
|
|
X509 certificate for certificate based authentication |
|
Using this method with JCo mode, Hibersap will internally create a custom JCo destination setting the specified credential attributes. Note that in this case, every user needs to have the proper permissions in SAP to use the function modules that are called via Hibersap.
In addition to calling the function modules itself, JCo makes calls to the SAP system to obtain meta data on function modules, the so-called repository calls.
Those calls depend on different sets of permissions which the specific user might or might not have.
Therefore, JCo uses a configured user to do repository calls instead of the current user supplied with the Hibersap credentials object when creating custom destinations.
This user may either be configured via the JCo parameter jco.client.user
, or with a separate user for repository calls using the parameter jco.destination.repository.user
.
The latter is recommended for applications which make use of per-session authentication, for this makes sure the functions are always called with the session user and the repository calls are done with the repository user.
In this case, the property jco.client.user
should not be set.
When using Hibersap with JCA, the configuration should be done accordingly, but is resource adapter specific. The Cuckoo Resource Adapter defines almost all possible JCo properties and passes them on to the Java Connector.
7.3. Single sign-on
A special case of per-session authentication is the use of single sign-on.
In a SAP environment it is common to use SAP Logon Tickets issued by SAP systems.
In a web application / portal scenario the ticket is stored in a cookie named MYSAPSSO2
in the HTTP user session.
After obtaining this ticket it can be passed to the Hibersap session by setting the `Credentials.ssoTicket' field object like described in the previous section.
8. Java EE Integration
When developing applications which run inside a Java EE application server and make use of Enterprise Java Beans (EJB), it is recommended using Hibersap in combination with a JCA compatible resource adapter. A resource adapter offers some important features which will be treated in this section.
8.1. Transaction Management
In an application server the JTA transaction manager handles transactions. In combination with EJBs and container managed transactions (CMT), the application code does not need to care about transaction demarcation because the container sets the transaction boundaries implicitly. This simplifies development and reduces the probability of errors.
If using CMT, all the function calls to SAP which take place during a call to an EJB business method are running in one transaction.
When the EJB method returns normally, the transaction manager commits the transaction while in case of an error the transaction is automatically rolled back.
In this context, error means that either a RuntimeException
is thrown or a Java EE application exception configured with the rollback=true
attribute. (Note that Hibersap itself only throws unchecked exceptions.)
In EJB3, container managed transaction is the default transaction management demarcation.
However, can be explicitly declared using the @TransactionManagement
annotation with a transaction management type of CONTAINER
or BEAN
.
The transaction semantics are further defined by the @TransactionAttribute
annotation on the EJB class or the individual EJB methods where TransactionAttributeType.REQUIRED
is a sensible default value.
8.1.1. Distributed Transactions
In an application it might be necessary or wanted to access SAP and other resources like a database and/or a JMS message queue in one unit of work making sure either every resource is committed in the case of success, or every resource is rolled back in case of an error. To accomplish this, Java EE supports distributed transactions through the JTA transaction manager.
Unfortunately, SAP R/3 does not provide the two-phase commit protocol (2PC) which is necessary for a resource to participate in a distributed transaction. The effect is that resource adapters for SAP R/3 support only local transactions, which is no problem if there is only one resource (the SAP R/3 system) enlisting in a transaction. If there are multiple resources participating in a distributed transaction, the transaction manager starts a distributed transaction which requires 2PC aware resources as its participants.
The good news is that almost all of the modern application servers (including JBoss/Wildfly, Glassfish, Bea Weblogic and SAP WebAS) support a strategy called Last Resource Commit Optimization which makes it possible for a single non-2PC-aware resource (in our case SAP) to enlist in a distributed transaction. This is transparent for the programmer. However, as soon as there are two non-2PC-resources enlisting in the distributed transaction, the transaction manager will throw an error.
8.2. Security Management
With a resource adapter there are two different methods to authenticate with the SAP R/3 system: container managed and component managed authentication. Hibersap supports both authentication methods.
With component managed authentication, an application passes credentials each time a connection is obtained from the resource adapter.
This method may be used when each user of the application signs in using its own SAP user, e.g. by entering the user name and password in the application or when using single sign-on with logon tickets.
In JCA, the credentials are passed with a javax.resource.cci.ConnectionSpec
instance.
Internally, Hibersap will create this instance and fill it with information passed with the credentials object when a new session is opened. (The code is the same as in Per-session authentication.)
Credentials credentials = new Credentials()
.setUser("archibald_tuttle")
.setPassword("myPassw0rd");
Session session = sessionManager.openSession( credentials );
session.execute( bapiClass );
[...]
With container managed authentication the sing-on information is either obtained from a JAAS login module or — in the simplest case — a central user and password is taken from the resource adapter’s configuration.
Using a login module, the sign-on information is passed by the container to the resource adapter with a javax.security.auth.Security
object. Login module configuration is container specific.
8.3. JCA Configuration
Another advantage of using a resource adapter over using JCo directly is that a resource adapter can be deployed and configured independently of the applications that use it. Thus it is not necessary to reconfigure and redeploy all applications that connect to a SAP system whenever connection parameters (e.g. the SAP system’s host name) change. Instead, only the resource adapter has to be reconfigured. Since the configuration of the resource adapter specifies all the necessary properties to access the SAP system, in the Hibersap configuration you just need to specify the JNDI name that is used to look up the resource adapter’s connection factory.
8.4. Hibersap EJB tools
If using Hibersap in a Java EE environment with Enterprise Java Beans, consider using the Hibersap EJB tools. The EJB tools help in automatically creating Hibersap Sessions, injecting them into your EJBs and closing them.
The following code implements a Stateless Session EJB with a business method to search for Customers in SAP.
The EJB’s business methods are intercepted with the HibersapSessionInterceptor
.
It has a field named session
which is annotated with @HibersapSession
to tell the interceptor which SessionManager
to inject into the EJB.
The annotation’s value
parameter specifies a JNDI name to which the SessionManager
is bound.
@Stateless
@Interceptors( HibersapSessionInterceptor.class )
public class CustomerServiceBean implements CustomerService
{
@HibersapSession( "java:global/eis/hibersap/A12" )
private Session session;
public CustomerSearch search( String nameSearchPattern )
{
CustomerSearch customerSearch = new CustomerSearch( nameSearchPattern );
session.execute( customerSearch );
return customerSearch;
}
}
CustomerSearch
is a Hibersap BAPI class. As can be seen, the code can directly use the injected Hibersap session to call functions in the SAP backend.
There is no need to close the Session, this is done by the interceptor.
The example uses container managed transactions and the method is called with transaction attribute REQUIRED
(the default behaviour).
This makes sure the SAP call is transactional and will be committed or rolled back by the transaction manager after the EJB business method was executed.
Internally, the interceptor adds each created Hibersap session to the EJB’s session context to make sure that subsequent calls to other EJBs use the same session. When the HibersapSessionInterceptor is executed, it first checks if a session of the specified session manager is already in the session context. If so, it uses the existing session, else a new session is created.
On returning from the business method for which the session was opened, the interceptor will close the session, thus making sure the underlying JCA connection will be returned to the connection pool.
To make the example complete we need a way to create a SessionManager
and bind it to JNDI where it can be looked up by the interceptor.
The following code implements a singleton EJB with the @Startup
annotation that is created when the application gets deployed.
The container will automatically call the lifecycle method rebindSessionManager()
which is annotated with @PostConstruct
when the EJB is started.
The method creates a session manager from a Hibersap XML configuration file and binds it to JNDI. When the application gets undeployed, the method unbindSessionManager()
is called which removes the session manager from JNDI.
To actually bind and unbind the session manager to and from JNDI, the helper methods in class JndiUtil
are used which is a part of the hibersap-ejb module.
@Singleton
@Startup
public class HibersapBootstrapBean
{
// The JNDI name we will bind the Hibersap SessionManager to
// (not the name of the JCA connection factory!)
public static final String JNDI_NAME = "java:jboss/eis/hibersap/SM_A12";
@PostConstruct
public void rebindSessionManager()
{
SessionManager sessionManager = new AnnotationConfiguration("A12").buildSessionManager();
JndiUtil.rebindSessionManager( sessionManager, JNDI_NAME );
}
@PreDestroy
public void unbindSessionManager()
{
JndiUtil.unbindSessionManager( JNDI_NAME )
}
}
When using Maven add the hibersap-ejb module to your application by defining the following dependency:
<dependency>
<groupId>org.hibersap</groupId>
<artifactId>hibersap-ejb</artifactId>
<version>1.3.0</version>
</dependency>