cancel
Showing results for 
Search instead for 
Did you mean: 

JCo server programming, properties and connection settings

Former Member
0 Kudos

Greetings, SAP professionals.

The reason I come to this forum is that I'm hoping to gain some insights into the use of the SAP Java Connector (JCo). I am a developer who was tasked with making a new component for a systems integration application. As is typical of integration software, our app can link together various different systems using a variety of protocols, as well as providing the means to apply business logic on messages passed from one location to another. We already have a connector acting as an SAP client which was implemented using JCo. Now, we were asked to develop a new component: a server capable of accepting RFCs from a remote SAP system acting as client. The server is to be created using the JCo classes, so basically an extension of JCo.Server, with some logic for creating function templates from configuration files.

However, while I'm understanding the structure of the Java API, it's not entirely clear to me just what the classes do. I've found the JavaDoc for JCo to be mostly descriptive of the interface of classes and methods, but not really explaining what these achieve, or how. So I'm hoping to be set straight, as I fear I'm kind of misunderstanding the functionality of JCo... Being mainly an integrations developer, I unfortunately often have to settle for gaining a superficial knowledge of a variety of systems to quickly interface with them, so I don't have any prior knowledge of SAP but still need to be able to implement something with JCo without too much delay.

The most important question I have is this: when a JCO.Server implementation is started, does it act as a fully standalone component capable of receiving calls, or does it merely act as a sort of listener for some main SAP system? I'm not talking about a reliability on the two .dll files (or .so for Linux) that are required for the use of JCo, I just wish to know if the JCo package is entirely self-sufficient for server functionality or if it is intended to be linked to some SAP system.

A second problem I have is that the parameters passed to various constructors aren't clear to me... I'm not familiar with SAP terminology, nor have I worked with any client apps that make use of an SAP system.

The meaning of client strings, gwhost, gwservice, ashost, system IDs and program IDs mostly elude me, especially when it comes to knowing what client parameters must match what server parameters.

In order to familiarize myself with the classes, I've tried playing around with them a bit trying to create a small test app that first starts a JCO.Server instance, then tries to make a remote function call to it with a JCO.Client (within the same class, for simplicity and debugging purposes). I was wondering if this actually makes sense... Would a JCo client be capable of connecting to a JCo server, all running purely in Java, or is that nonsense?

To eliminate some common troubleshooting options, I'll quicly describe the steps I've taken:

  • Both librfc32.dll and sapjcorfc.dll were placed in the Windows system32 folder. Maybe only librfd32 needs to be placed there, but I copied both anyway to make sure.

  • The directory containing the jar file and both dll files is included in my environment path variable.

  • I've added a line to the C:\Windows\system32\drivers\etc\services file as follows:

sapgw00 3300/tcp #SAP System Gateway Port

  • I've opened port 3300 in my Windows firewall. In fact, I also tested with the firewall completely turned off.

However, I do not manage to get my test class to work. I've tried ports 3300, 3200 and 3600. I've tried various permutations of the client and server properties. I've tried removing the line from the services file, which would prompt the client to state upon connecting that the service "sapgw00" is unknown. When I add it back in, the error changes to "partner not reached", so it is definitely picking something up.

The server itself starts just fine, but connecting through a client doesn't work. My class source code is posted below. Maybe what I'm trying to do doesn't make any sense, but at the moment it's my best guess.

I realize this is a pretty long post and the class, while not exactly big, also implies a bit of reading of its own. But if anyone could give me any answers that are new to me, I'd be hugely grateful. Right now I'm kind of stuck, and just setting up the service and letting our customer test on it is a somewhat slow approach that can't match developing and testing on one and the same host.

Preliminary thanks to everyone who took the effort to read this.

//***********************************************************

//Start of code

//***********************************************************

import java.util.Properties;

import com.sap.mw.jco.IFunctionTemplate;

import com.sap.mw.jco.IMetaData;

import com.sap.mw.jco.IRepository;

import com.sap.mw.jco.JCO;

public class Test {

public static void main(String[] args) {

Test test = new Test();

ServerThread serverThread = test.new ServerThread();

serverThread.start();

while(!serverThread.isReady) {

try {

Thread.sleep(5000);

} catch(final InterruptedException i) {

System.out.println("Rudely awakened");

}

}

try {

// JCO.Function func = getSampleFunction(test, "STAY");

// serverThread.server.handleRequest(func);

// System.out.println(func.getExportParameterList().toXML());

// func = getSampleFunction(test, "STOP");

// serverThread.server.handleRequest(func);

// System.out.println(func.getExportParameterList().toXML());

final Properties clientProps = getClientProps();

JCO.Client client = JCO.createClient(clientProps);

client.connect();

IRepository rep = JCO.createRepository("1", client);

IFunctionTemplate templ = rep.getFunctionTemplate("TEST_FUNC");

JCO.Function function = templ.getFunction();

function.getImportParameterList().setValue("STAY", "FIELD1");

client.execute(function);

JCO.Function function2 = templ.getFunction();

function2.getImportParameterList().setValue("STOP", "FIELD1");

client.execute(function2);

} catch(final Exception e) {

e.printStackTrace(System.out);

serverThread.requestStop();

while(serverThread.isAlive) {

try {

Thread.sleep(5000);

} catch(final InterruptedException i) {

System.out.println("Rudely awakened");

}

}

} finally {

}

}

private static Properties getClientProps() {

final Properties props = new Properties();

props.setProperty("jco.client.client", "100");

props.setProperty("jco.client.user", "");

props.setProperty("jco.client.passwd", "");

props.setProperty("jco.client.lang", "");

props.setProperty("jco.client.sysnr", "00");

props.setProperty("jco.client.ashost", "/H/localhost/S/sapgw00");

props.setProperty("jco.client.gwhost", "localhost");

props.setProperty("jco.client.gwserv", "sapgw00");

return props;

}

public class ServerThread extends Thread {

public void run() {

isAlive = true;

IRepository repos = new TestRepository("testrep");

repos.addFunctionInterfaceToCache(getFunctionInterface());

server = new TestServer(repos);

server.start();

System.out.println("Server successfully started");

isReady = true;

while(!stop) {

try {

Thread.sleep(1000);

} catch(final InterruptedException i) {

System.out.println("Wouldn't let me sleep...");

}

stop = server.stopRequested;

}

server.stop();

isAlive = false;

System.out.println("Server successfully stopped");

}

public void requestStop() {

server.requestStop();

}

public TestServer server;

public boolean isReady = false;

public boolean isAlive = false;

}

public class TestServer extends JCO.Server {

public TestServer(IRepository rep) {

super("localhost", "sapgw00", "PROGID", rep);

}

public void handleRequest(JCO.Function fct) {

try {

JCO.ParameterList importParams = fct.getImportParameterList();

final String importXML = importParams.toXML();

System.out.println("XML representation of import parameters: ");

System.out.println(importXML);

final String input = importParams.getString("FIELD1");

System.out.println("FIELD1 value: " + input);

JCO.ParameterList exportParams = fct.getExportParameterList();

if(input.equals("STOP")) {

exportParams.getField("FIELD2").setValue("OK");

stopRequested = true;

}

}

catch(JCO.AbapException ex) {

throw ex;

}

catch(Throwable t) {

throw new JCO.AbapException("SYSTEM_FAILURE", t.getMessage());

}

}

public boolean checkAuthorization(String functionName, int authorMode, String partner, byte[] key) {

System.out.println(functionName + " " + partner);

return true;

}

public void requestStop() {

stopRequested = true;

}

public boolean stopRequested = false;

}

public class TestRepository extends JCO.BasicRepository implements IRepository {

public TestRepository(String name) {

super(name);

}

}

public static IMetaData getFunctionInterface() {

JCO.MetaData metaData = new JCO.MetaData("TEST_FUNC");

metaData.addInfo("FIELD1", IMetaData.TYPE_STRING, 4);

metaData.setFlags(0, IMetaData.IMPORT_PARAMETER);

metaData.addInfo("FIELDX", IMetaData.TYPE_STRING, 8);

metaData.setFlags(1, IMetaData.IMPORT_PARAMETER & IMetaData.OPTIONAL_PARAMETER);

metaData.addInfo("FIELD2", IMetaData.TYPE_STRING, 2);

metaData.setFlags(2, IMetaData.EXPORT_PARAMETER);

return metaData;

}

public static JCO.Function getSampleFunction(Test test, String s) {

TestRepository testRep = test.new TestRepository("testrepository");

testRep.addFunctionInterfaceToCache(getFunctionInterface());

JCO.Function func = testRep.getFunctionTemplate("TEST_FUNC").getFunction();

func.getImportParameterList().setValue(s, "FIELD1");

return func;

}

private static boolean stop = false;

}

Accepted Solutions (1)

Accepted Solutions (1)

Former Member
0 Kudos

Greetings,

I'll be deploying a short Java program by means of test to see if I got the core server functionality right. Basically I'll simply start a server with a repository (build via metadata objects) that has one single function interface (let's name it "TEST_FUNC").

For the server, I'll pass the following properties:

jco.server.gwhost=localhost

jco.server.gwserv=3300

jco.server.progid=TESTID

Do these properties make sense, or am I still getting them wrong? Would anything require to be run on the local host, besides having the right port made accessible?

Considering the above server, what client settings would the partner use for connecting? I was thinking of the following:

(jco.client.)client: 001

user:

passwd:

lang:

sysnr:00

ashost: external IP address of host running server

gwhost: same as ashost?

gwserv: 3300

The server has the authentication method overridden to accept any login. Also, I'll leave the language property blank to choose the default language.

Muchos gracias for any feedback on whether or not I'm typing nonsense.

Former Member
0 Kudos

If I understood you correctly, you want to provide a "service" that can be called from SAP. To provide this service you've chosen to implement an (external) RFC server program via JCo. One common method for RFC server programs is to register in SAP on the gateway - you do this by supplying the three parameters

<ol>

<li><b>jco.server.gwhost</b> - SAP gateway host on which the server should be registered (so this would be the server name or IP address of the SAP gateway; localhost is only correct, if your RFC server program runs on the same server as the SAP gateway)</li>

<li><b>jco.server.gwserv</b> - Gateway service, i.e. the port on which a registration can be done</li>

<li><b>jco.server.progid</b> - Program ID under which your RFC server program can be reached (free, made-up case sensitive name, that should represent the service your RFC server is providing)</li>

</ol>

So essentially you're creating a listener, that is registered in SAP and waits for any invocations. Within SAP they will define a <i>RFC destination</i>, which basically represents a TCP/IP connection pointing to the SAP gateway where you registered with the given program ID. If you want more details, check the SAP help pages for <a target="_blank" href="http://help.sap.com/saphelp_nw04/helpdata/en/22/04262b488911d189490000e829fbbd/content.htm">RFC destinations</a> (you're looking for destination type <b>T</b>, see explanations <a target="_blank" href="http://help.sap.com/saphelp_nw04/helpdata/en/22/042652488911d189490000e829fbbd/content.htm">here</a>).

Usually gateway host and service (port) are given to you by the SAP basis folks and you tell them which program ID you're using. They will then enter those parameters in an RFC destination of type <b>T</b> in SAP. So no need for any of the client parameters you've mentioned. Although, I'd like to repeat, it's usually handy to also have SAP logon parameters maintained on your RFC server program, so that you can utilize the repository data from SAP (might be irrelevant in your case).

Hope this clarifies it a bit...

Former Member
0 Kudos

Here's an example, which might shed some more light on the process. Let's assume that the SAP basis team defined an RFC destination <i>TEST_JCO</i> of type <b>T</b> using registration on gateway server <i>sapappserver.example.com</i> on port <i>sapgw00</i> and program ID <i>TestJCo</i>.

An ABAP application developer defined your function TEST_FUNC as a skeleton in SAP (i.e. just interface parameters, no coding). Thus one could then use transaction <i>SE37</i> for testing remote invocation of TEST_FUNC on destination <i>TEST_JCO</i>.

As a result, SAP would look up the definition of RFC destination <i>TEST_JCO</i> and see that it points to <i>sapappserver.example.com</i> on port <i>sapgw00</i> with program ID <i>TestJCo</i>. Therefore it would now invoke a remote function call (RFC) via this application server using the registered connection from your RFC server.

Most simple RFC server programs simply provide some service, but there's no more authentication or authorization mechanisms (since that's already been taken care of in SAP).

p.s.: I had to split up my message, because it was getting too long and it started looking like your initial posting...

Former Member
0 Kudos

Hello Harald,

A most outstanding explanation that finally made the entire process clear to me. After all it really isn't very complicated (the JCo classes seem to do a fine job of taking most complexity out of the developer's hands), but I had trouble finding any documentation as clear as your posts.

The main problem was that the role of the SAP gateway wasn't clear to me... I viewed JCo as something that operates stand-alone, without any intervention of an SAP gateway. Silly, of course, but I've never been instructed in even the basics of SAP. I suppose for a more experiences SAP user, a lot of the parameters and processes involved are self-explanatory.

Thanks to your posts I believe things are gonna be fine now. At least I'm no longer doing guess work. As for the function repository, I'll offer the possibility of using either a client connection to a SAP system for retrieving function interfaces, or making the skeletons from metadata described in XML. The authentication method could similarly be implemented by choice. After all, the component might be used by more than one customer in the future and I value flexibility.

Thank you very much. If only every Java component would be so clearly explained my days would be a lot more productive!

Answers (1)

Answers (1)

Former Member
0 Kudos

Whooooaaa, where did my paragraph structure go?

Sorry folks, I'll try to clean that post up a bit.

EDIT: No editing of older posts... Too bad. Guess the Java code messed up the markup parsing?

Edited by: Geert H. on Nov 18, 2009 6:36 PM

Former Member
0 Kudos

I think it's just the length of your coding, see /thread/1319756 [original link is broken], take a look at the examples, <b>StepByStepServer.java</b>, is a good introduction.<br />

<br />

Note that an external RFC server program can basically be called via two methods: Either by direct invocation (kind of remote shell invoke) or by calling a registered listener. The latter is basically what you were trying to do via your sample program (kind of skipped over it, because it was so long).<br />

<br />

When registering in SAP (at the gateway), you need to fill at least the following three parameters:<br />

<ol>

<li><i>ServerDataProvider.JCO_GWHOST</i> - server name or IP address of the SAP gateway where you're registering</li>

<li><i>ServerDataProvider.JCO_GWSERV</i> - port name</li>

<li><i>ServerDataProvider.JCO_PROGID</i> - program ID under which your RFC server program can be reached in SAP</li>

</ol>

In principle you don't need any RFC client information (like user, password, etc.), but I'd advise to use a client for the repository connection (you can define which JCoDestination your server uses to retrieve repository information via parameter <i>ServerDataProvider.JCO_REP_DEST</i>). In most cases the RFC functions that are invoked from SAP are at least defined as skeletons in SAP, so that you can utilize ABAP code completion (and it's in general useful to also have the metadata available in SAP).

<br />

In your example for the client connection you utilize parameter <i>jco.client.ashost</i> wrong: You filled it with a connection string, but it should just contain the server name (DNS) or IP address of the SAP system. The port information is provided via parameter <i>jco.client.sysnr</i> (i.e. 3300 + system number represents the port that is called via RFC on server, named sapgw00 for system number 00).

<br />

Also, your example looks odd, because it looks like you're trying to do a connection between your own client/server program, which I think is technically impossible (anybody correct me if I'm wrong). For testing your RFC server program, you should invoke an RFC function you've implemented in your server program from SAP.<br />

<br />

Hope this helps, harald

Former Member
0 Kudos

Hello Harald,

Thank you very much for the excellent information. I hadn't heard of the 3.x releases of JCo yet. We're reliant on our customer who ordered the project for providing us with the libraries we need. I'll check if we could receive an updated version.

Mostly I'm grateful you stated that trying to connect one's own client and server programs is probably impossible, so I can stop wasting time trying to get that to work. I guess I'll make a test version of the server and let our customer test on that using their SAP system, we'll probably get much further in testing there (plus, they have experienced SAP professionals at their side).

I haven't marked the subject as answered yet since I'll probably create at least one follow-up post, to check if the combination of server properties I'll set and client properties I'll communicate for testing would make sense. But every answer will receive proper points.

This thread has helped me a good deal in delivering a proper SAP service. Thanks.