cancel
Showing results for 
Search instead for 
Did you mean: 

SAP JCo 3 - cannot connect to repository using custom destination

Former Member
0 Kudos

I'm working on a Java application that will connect to SAP, with the destination configuration coming from a file, and the user and password being entered at run time. I'm trying to implement this using the createCustomDestination() method on the JCoDestination.

Here is some sample code of how I am trying to do this. There is also a test.jcoDestination file that contains only the connection information; no user name, password, or client.

import com.sap.conn.jco.JCoCustomDestination;
import com.sap.conn.jco.JCoDestination;
import com.sap.conn.jco.JCoDestinationManager;
import com.sap.conn.jco.JCoException;
import com.sap.conn.jco.JCoRepository;
import com.sap.conn.jco.JCoCustomDestination.UserData;


public class Test {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		try {
			JCoDestination destination = JCoDestinationManager.getDestination("test");
			JCoCustomDestination customDestination = destination.createCustomDestination();
			
			UserData userData = customDestination.getUserLogonData();
			
			System.console().printf("Enter client:");
			userData.setClient(System.console().readLine());
			
			System.console().printf("Enter user:");
			userData.setUser(System.console().readLine());
			
			System.console().printf("Enter password:");
			userData.setPassword(new String(System.console().readPassword()));
			
			System.console().printf("Enter language:");
			userData.setLanguage(System.console().readLine());
			
			// Try to connect - this works
			customDestination.ping();
			
			// Try to connect to the repository - this fails
			JCoRepository repository = customDestination.getRepository();
			
			
		} catch (JCoException e) {
			e.printStackTrace();
		}
		
	}

}

Everything works well down through the ping() call, which means the connection is being created. However, on the getRepository() call I get an exception saying that the client isn't specified. It's as if the getRepository method is ignoring the custom destination information I set and only looking at what's in the destination file.

Am I doing something wrong, or is this a bug in the JCo library?

Thanks,

Jonathan

Accepted Solutions (1)

Accepted Solutions (1)

Former Member
0 Kudos

Hi Jonathan,

The problem is that when creating a JCoCustomDestination, the reference to the parent JCoDestination is part of the object. When requesting the repository of a JCoCustomDestination via method getRepository() the class returns the repository of the parent JCoDestination (when a different one is defined - which is the case for a JCoCustomDestination ).

Funny enough, class JCoCustomDestination also provides a method setRepositoryDestination(). Per API documentation it should do the following:

Sets the destination, which will be used for metadata queries in the repository for this destination.

This is what one would expect by method name, but I'd say what it actually does is different: I.e. instead of setting the destination for repository queries, it actually sets the repository based on the passed destination by calling method getRepository(). This becomes visible when you'd add the following line to your coding:

customDestination.setRepositoryDestination(customDestination);

I'm tempted to call this a bug and not a feature, but maybe others (SAP?) have a different perspective on that...

The only solutions to this problem I found were:

1) Defining a proper destination from the start (i.e. JCoDestination contains already all required information)

2) Using reflection to mess with the field pointing to the parent repository (more a proof of concept than a real solution as this obviously shouldn't be done)

Best wishes, harald

Former Member
0 Kudos

I tried using the setRepositoryDestination method, but as you said, it didn't work as expected. I believe this to be a bug as well. As it stands, I don't see a way to set the user data for the repository destination at run time.

I would put all the connection information in the destination, but in this case I can't. In my full app what I've done is create a DestinationDataProvider that reads the saplogon.ini file, and then I present the list of configured systems to the user to select. Since saplogon.ini doesn't include any sort of log in information (a good thing), this isn't an option in this case.

The only work around I can think of at this point is adding a static method to the SAP logon destination provider I wrote that sets static client/user/password fields. I could then return these fields as properties in the getDestinationProperties method. This wouldn't be an option for a web application, but since this is a client app it should be ok...

Former Member
0 Kudos

Hhhmm, I'm not quite sure I fully understand your approach with the custom DestinationDataProvider. I'd assume that you don't need the DestinationDataProvider before the user has picked a system. Once he picked the system, he can enter the additional logon information and then you could request the JCoDestination via your registered DestinationDataProvider. At this time you should be able to simply combine the information from SAPlogon.ini and the additional information the user entered (within the DestinationDataProvider which would return all the properties).

Answers (2)

Answers (2)

Former Member
0 Kudos

I've been working with this for several hours, and I finally made it work. It's so simple, but it took a long review of SAP's source code to figure it out:

Instead of doing this:


JCoDestination destination = JCoDestinationManager.getDestination("test");
JCoCustomDestination customDestination = destination.createCustomDestination();

Do this:


JCoCustomDestination customDestination = (JCoCustomDestination)JCoDestinationManager.getDestination("test");

After which you can set the user and password parameters in your custom destination as usual. The reason this works is that the createCustomDestination() method simply creates a copy of the exact same object (of type com.sap.conn.jco.rt.RfcDestination) with the parentDestination field filled in. The parentDestination field being populated is what leads to the error described in the original post. Simply typecasting the regular destination object leaves the parentDestination field as null, bypassing the problem while still retaining all of the original logon data. I've tested this pretty thoroughly and am fairly positive about the results.

Edited by: Dan Rempe on May 11, 2010 2:33 AM

Former Member
0 Kudos

Dan,

Your solution wouldn't work. I just try it with two different users are trying to login to the same SAP destination. It will result in the following exception:

===================================================

Caused by: com.sap.conn.jco.JCoRuntimeException: (136) JCO_ERROR_ILLEGAL_STATE:

Destination instance is already connected to a backend, hence changes in the destination parameters are not allowed. If you need to change a setting, create a new instance with createCustomDestination().

===================================================

I think the exception message is pretty clear that you can't just cast it to JCoCustomDestination type.

Jian

Edited by: jian.liao on Sep 1, 2010 3:59 AM

Former Member
0 Kudos

Just for those who have not yet recognized this: The docu for JCoCustomDestination.setRepositoryDestination() really was misleading and has been improved meanwhile. Now it says:

Sets the destination, whose repository will be used for metadata queries for this destination. In case a JCoCustomDestination is passed, the destination it originates from will provide its repository.

Former Member
0 Kudos

Stefan,

Could you please explain the design consideration for this behavior of JCo. Why can't we have a partial destination setup without username and password, then feed the username/password into the it through JCoCustomDestination?

If this kind of usage is not allowed, JCo should throw out an exception at the JCoDestination retrieving time (i.e. JCoDestinationManager.getDestination("xxx") and "xxx" is the destination id that just have partial setup without username/password). It should not throw out the exception at destination.getRepository(). It is too late and confusing people.

Btw, I decompiled the code just as Dan did. Here is my understanding. This problem is caused by JCoCustomDestination is just a clone object but only difference is that the internally parentDestination field is pointed to the original destination instance. When will call the getRepository() on this new JCoCustomDestination instance, it will always delegate to its parentDestination to retrieve the repository. Then it cause the fail.

My understanding is that JCoRepository is an expensive object. For the sake of saving resource, all the JCoCustomDestination instances created from the same JCoDestination instance should share the same JCoRepository instance. I am not sure if it is really need, because I noticed that JCoRepository had been proper cached and reuse internally in JCo 3.x.

Thanks,

Jian

Edited by: jian.liao on Sep 1, 2010 4:51 PM

Former Member
0 Kudos

I don't get the point.

Just because the JCoRepository is an expensive object it should be shared amongst all users and applications.

Therefore it is not possible to set some JCoCustomDestination into a standard JCoRepository. Otherwise the custom logon credentials might be used by all other users and applications for their repository queries. Would you like to have your personal user ID being logged as the one consuming the most system resources (because it would be used by everyone for meta data queries)? And what if your personal user is lacking some authorizations for all the meta data queries? Or you just change your personal password at the SAP backend? No one will get any meta data then anymore.

On the other side, the possible alternative of creating user specific repositories would be against the goal of just having one and share the meta data. As you already said - repository queries are time expensive and resource consuming.

The JCo3 API enforces an efficient application design within a multi-user/multi-application environment.

A JCoCustomDestination is for specifying application or call specific logon credentials - not for standard JCoRepositories.

Maybe you have not recognized yet that you can create a JCoDestination and specify the jco.destination.repository* properties? You then may still configure your partial destination setup without username and password for the application RFC calls.

Former Member
0 Kudos

I think I finally found that way to work around this issue - "cannot connect to repository using custom destination".

The exception is thrown from the code statement - "dest.getRepository().getFunction(funcName)". If I create a JCoFuntionTemplate locally and add it to JCoCustomRepository instance, then I can bypass above code and avoid the exception.

But I am not sure if it worth doing it. Would JCoCustomRepository bring any burden to application developer? like code maintainability, performance etc.

Former Member
0 Kudos

I recommend not to work around the issue but to solve it.

Therefore you would have to accept that within a multi-user environment you will need an additional independent technical userID just for querying the RFC meta data.

Then you would have to set this repository user for example with the properties "jco.destination.repository.user" and "jco.destination.repository.passwd". This user should be granted the authorizations for a repository user only (please see SAP note [460089|https://service.sap.com/sap/support/notes/460089]).

Former Member
0 Kudos

I'm using JCoCustomDestination too, because I want to dynamically specify user and password. When I invoke the getRepository() method on JCoCustomDestination I get the error too.

This thread is from 2007, so after 2 years this bug hasn't been fixed?