Wednesday, November 24, 2010

Sapping up your Java Code - Part 1: The connection

SAP is an Enterprise Resource Planning system which controls a large market share. In many big companies SAP is the backbone that provides and relates information from most, if not all, departments. Having so much data, being able to access that information from outside the system becomes imperative. At the company I work for, we use Adobe LiveCycle to create paperless workflows that send and retrieve information from the SAP system. This enables our users to have a clear, concise... pretty... interface into SAP for common tasks. We are currently supporting 1000 users with this method. (Also, it only uses up one license on the SAP side). So, how does it work...


Let's talk about setting up a connection to SAP. Well, the first thing that you need is the SAP JCo. This is not freely available to everyone, you need an account at the SAP Service Marketplace. This site very poorly laid out, and it might be hard to find... I'd include a link right to the page, but I'm not sure that it would be reliable. I recommend doing a search for "JCo Download" and then when that search fails to return anything, click on the "Documents" section. You will have multiple options available for download, Windows/Linux/etc... pick whichever one applies to you. Make sure you add the jar and dll/so to your classpath properly. This post only talks about JCo3, I had worked with JCo2 previously but it wasn't very reliable under large amounts of processing, so I'd recommend going straight to JCo3.

Wahoo... we have the SAP JCo, now, how do we connect? The easiest way is not very efficient at all. In fact, it's a pretty dumb way to do it. The first thing you'll do is create a Properties object in your java code. You'll then populate it with some data, and then you'll write it to a file with the extension ".jcoDestination." You then tell JCoDestinationManager to get the destination by providing it the file name without the extension, here, let me show you some code:

//Other Class definition up here
...

//Don't just throw the exception, do actual error handling, this is just for
//demo sake

private JCoDestination makeConnection() throws Exception {
//Make the properties object
Properties p = new Properties();
p.setProperty(DestinationDataProvider.JCO_ASHOST, host);
p.setProperty(DestinationDataProvider.JCO_SYSNR, systemNumber);
p.setProperty(DestinationDataProvider.JCO_CLIENT, client);
p.setProperty(DestinationDataProvider.JCO_USER, userName);
p.setProperty(DestinationDataProvider.JCO_PASSWD, password);
p.setProperty(DestinationDataProvider.JCO_LANG, language);
p.setProperty(DestinationDataProvider.JCO_POOL_CAPACITY, poolSize);
p.setProperty(DestinationDataProvider.JCO_PEAK_LIMIT, poolPeakSize);

//Write it out to a file... because file I/O is so nice
FileOutputStream fos = new FileOutputStream("sapconn.jcoConnection");
toProp().store(fos, "SAP System Connection");
fos.close();

//And read it back in...
JCoDestination destination = JCoDestinationManager
.getDestination("sapconn");
return destination;
}


As you can see it's a lot of pointless work to get the connection object. you could circumvent much of this by saving the first jcoDestination file that you create and re-using for every time you need to make your connection. Nonetheless, I think SAP made a very poor design choice when they limited the creation of "Destinations" to file I/O. There is an alternative to this method which is probably much more efficient, but also much more work. It involves implementing the DestinationDataProvider, registering it with the SAP JCo, and then creating the Destination.

The JCoDestination object is not quite a connection to SAP, but it's the closest thing you'll get. The JCoDestination is essentially a pointer to the connection pool. When you execute anything that needs to contact SAP it uses a JCoDestination to see exactly where it should be going. Now, let's talk about those properties we were just setting.

Table 1: SAP Configuration Properties


Property NameDescription
JCO_ASHOSTThis is the host, or server, that is running your SAP instance.
JCO_SYSNRThe is the System Number for your SAP system. If you're strictly a Java programmer, you're probably best to just ask what this is from someone in your SAP team. As far as I can tell, this is generally used to segregate Development and Production systems.
JCO_CLIENTAgain, this is something you'll have to ask for. The client seems to be a way to segregate some functionality of the system. Such as, functionality for different plants.
JCO_USERThis is the user that your Java code will use to connect to the SAP system.
JCO_PASSWDAnd this is the password related to that user.
JCO_LANGThe Language you want to use for the connection. I use EN, for English.
JCO_POOL_CAPACITYThis setting is for performance. It determines how many connections to SAP it will open at a time. You'll have to play with this setting, I'd start at 50, or 100 for moderate user load.
JCO_PEAK_LIMITOn top of the capacity limit, there is a peak limit. The capacity is for normal operation, whereas the peak, is for those times when your server is being pounded by user requests. The peak limit will allow for extra connections to be created as needed, over the initial capacity. These connections will be destroyed as soon as they are not in use, where as the connections specified in the capacity setting, will remain. This setting should be something larger than the CAPACITY, but it is up to you to determine what value is best for your application.

And there you go, you're connected to SAP. From here you're going to need access to an RFC (Remote Function Call) if you really want to make the system do something. Coming soon will be a discussion on the JCoFunction object and how to use it.

Glassfish Startup Script for Linux Systems

I found a better resource for this than mine:
Run GlassFish V3 as a non-root Service

The above link covers Fedora/Ubuntu, but works for Red Hat as well.


Recently I was setting up a server that was to run Glassfish V3 on Red Hat EL5. The problem here is that Glassfish does not provide a start-up script for unix based systems. Also, the asadmin tool is unable to add Glassfish as a service like it does in Windows and Solaris, so where does that leave us, writing our own init script.

I will be honest, I'm not really a hardcore LINUX user, so there may be a better way to do this, but here's what I did.

In the {gfinstall}/glassfish/bin directory I created a new file, simply named glassfish. In this file I added the following script:
#!/bin/sh
### BEGIN INIT INFO
# Provides:          glassfish
# Required-Start:    $network
# Required-Stop:
# Default-Start:     3 5
# Default-Stop:      0 1 2 6
# Short-Description: Glassfish Web Server
# Description:       Glassfish Web Server
### END INIT INFO


gfbin=/path/to/your/gfinstall/glassfish/bin


function usage
{
   echo "Must pass command line param start or stop"
}

while [ "$1" != "" ]; do
    case $1 in
        start )
               nohup $gfbin/startserv > /dev/null &
                                ;;
        stop )
               $gfbin/stopserv
                                ;;
        * )                     usage
                                exit 1
    esac
    shift
done

This script accepts the parameters start and stop, and that is all. No restart, no help, just start and stop. I did this because, it's really the only functionality you need, and it would have been a pain to try to do the restart (the stopserv command exits before the server has shut down).

Next, I linked this script into the /etc/init.d folder (using 'ln -s'). From that point, we can do a 'chkconfig glassfish on' and everything will be good to go. The information in the '### BEGIN INIT INFO' is what enables chkconfig to work. So don't remove it, you might want to change it though, depending on what you need.