Directory API Development – Part 3 of 3
-
Introduction:
This is the 3rd part of Directory API development. The 1st part gave a high-level overview of directory API development and the pre-requisites of using the Directory API.
In part 2, we explored the details involving the development preparation required in NWDS before actual coding can begin.
In this blog, part 3, we will dive into the actual coding using the Directory API.
To use as an example for this blog, I will use the Directory API to create SOAP receiver communication channels. The steps of creating other types of communication channels are similar. Primarily, the adapter type and the parameter names will need to be changed. To get the parameter names to use with the Directory API, it is recommended that a listing of an existing communication channel be done first using the “/wsnavigator”.
-
Pre-Requisite of using the Directory API:
As mentioned in part 1, to use the Directory API, the userid must be assigned specific roles. These roles must be assigned in the java server. The roles are not available using the ABAP transaction code SU01. It is critically important that step III in part 1 is carried out before proceeding with using the Directory API.
-
Development Process:
Please note, for this blog, NWDS 7.1 is used with PI 7.1.
Since a Communication Channel will have to be created in a Business Component or Business System, for this blog, I created two business components in the Integration Directory: BusComponent1 and BusComponent2
Overview:
The code development process consists of the following steps:
- Create new package and classes to contain our custom code. In this blog, I created the following:
Package: com.test.commchan
Classes: CreateSoapChannel, ChangeList and Parameters
(The ChangeList class is used to create and activate a changelist. The Parameters class is used to store commonly used values, e.g. userid and password. I created these 2 classes, instead of including them in the CreateSoapChannel class, is for re-usability purposes, in case you decide to develop other directory API functions. You can also split these 2 classes into separate packages.) - The Parameters class is quite simple. It containts the following code:
The userid must be authorized with the right roles to use the Directory API.
public static String getUserid() {
return USERID;
}public static String getPassword() {
return PASSWORD;
}
} - The ChangeList class contains 3 methods:
createChangeList: This method creates a new change list and returns its changelist id.
activate: This method activates the changelist, using the changelist id.
setURL: This method sets the URL on where the webservice is to be executed. By default, the webservice will executed on the server as set in the binding and endpoint of the WSDL configuration in part 2. We can change this configuration by using this method. Consequently, this code can be used to execute the webservice on any server.It containts the following code:
public class ChangeList {
private static String apiURL = “/ChangeListService/HTTPBasicAuth?wsdl&style=document&mode=standard”;
private String url = new String();public String[] activate(String chgList) {
Vector<String> vMsg = new Vector<String>();
vMsg.add(new String(chgList).concat(” – Activate”));
try {
ChangeListService service = new ChangeListServiceLocator();
ChangeListServiceVi document = null;
// check for server to execute the webservice
if (this.url.length() == 0)
document = service.getHTTPBasicAuthPort();
else {
URL docUrl = new URL(this.url);
document = service.getHTTPBasicAuthPort(docUrl);
}
HTTPBasicAuthBindingStub stub = (HTTPBasicAuthBindingStub)document;
stub.setUsername(Parameters.getUserid());
stub.setPassword(Parameters.getPassword());
LogMessageCollection cLogMsg = document.activate(chgList);
LogMessageCommunicationChannel[] logMsg = cLogMsg.getLogMessageCommunicationChannel();
if (logMsg != null && logMsg.length > 0) {
for (int i=0; i<logMsg.length; i++) {
LogMessageItem msgItem = logMsg[i].getLogMessageItem();
Text msgTxt = msgItem.getMessage();
vMsg.add(msgTxt.getValue());
System.out.println(” msg: ” + msgTxt.getValue());
}
}
cLogMsg = document.revert(chgList);
}
catch (Exception e) {
vMsg.add(e.getMessage());
e.printStackTrace();
}
return (String[])vMsg.toArray((String[])new String[1]);
}public void setURL(String serverPort) {
if (serverPort == null) return;
this.url = this.url.concat(“http://“).concat(serverPort).concat(apiURL);
}public String createChangeList(String name) {
Vector<String> vMsg = new Vector<String>();
String changeListID = “”;
String changeListName = “”;
try {
ChangeListService service = new ChangeListServiceLocator();
ChangeListServiceVi document = null;
if (this.url.length() == 0)
document = service.getHTTPBasicAuthPort();
else {
URL docUrl = new URL(this.url);
document = service.getHTTPBasicAuthPort(docUrl);
}
HTTPBasicAuthBindingStub stub = (HTTPBasicAuthBindingStub)document;
stub.setUsername(Parameters.getUserid());
stub.setPassword(Parameters.getPassword());
ChangeListIDRestricted changeListCreateRequest = new ChangeListIDRestricted();
LONG_Description description = new LONG_Description();
description.setLanguageCode(“EN”);
description.setValue(name);
changeListCreateRequest.setDescription(description);
changeListCreateRequest.setName(name);
ChangeListCreateOut changeListCreateOut = document.create(changeListCreateRequest);
ChangeListID changeList = changeListCreateOut.getChangeListID();
changeListID = changeList.getChangeListID();
changeListName = changeList.getName();
vMsg.add(new String(changeListID).concat(“(“).concat(changeListName).concat(“)”).concat(” – Create ChangeList”));
}
catch (Exception e) {
vMsg.add(e.getMessage());
e.printStackTrace();
}
return changeListID;
}public static void main(String[] args) {
ChangeList changeList = new ChangeList();
changeList.setURL(“nspah227:52700”);
String name = “ChangeList Demo”;
String changeID = changeList.createChangeList(name);
System.out.println(changeID);
System.out.println(“Done”);
}
} - The CreateSoapChannel contains the following methods:
createSoapChannelActivate: This method is the main method to create and actiate the SOAP communication channel configurations.
createChangeList: This method creates a changelist containing the SOAP communication channel configurations.
createSoapChan: This method creates all the SOAP communication channel configurations. build1SoapChan: This method creates one SOAP communication channel configuration.
setURL: This method sets the URL on where the webservice is to be executed. It serves the same purpose as in the ChangeList class.
setActivate: This method sets the flag on whether the configuration is to be activated. I included this option on whether to activate the changelist. If this is set to “false”, then we can see the changelist list in the “Change List” tab of the Integration Directory. At that point, we can manually activate it or reject it. If set to “true”, then the configuration will be activated by the program.The class containts the following code:
import java.net.URL;
import java.util.Vector;public class CreateSoapChannel {
private static String apiURL = “/CommunicationChannelService/HTTPBasicAuth?wsdl&style=document&mode=standard”;
private String url = new String();
private String serverPort = null;
private boolean bActivate = true;
private String docChangeID = null;public CreateSoapChannel() {}
public CreateSoapChannel(String changeID) {
this.docChangeID = changeID;
this.setActivate(false);
}public String[] createSoapChannelActivate(String[] party, String[] component, String[] channel, String[] url, String[] action) {
Vector<String> vMsg = new Vector<String>();
CommunicationChannelRestricted[] chans = this.createSoapChan(party, component, channel, url, action);
String[] msg = this.createChangeList(chans);
for (int i=0; i<msg.length; i++) vMsg.add(msg[i]);
if (bActivate) {
ChangeList act = new ChangeList();
act.setURL(serverPort);
String changeID = (String)vMsg.get(0).substring(0, 36);
String[] msgs = act.activate(changeID);
for (int i=0; i<msgs.length; i++) vMsg.add(msgs[i]);
}
return (String[])vMsg.toArray((String[])new String[1]);
}public String[] createChangeList (CommunicationChannelRestricted[] chans) {
String changeID = null;
Vector<String> vMsg = new Vector<String>();
try {
CommunicationChannelCreateChangeIn crtChan = new CommunicationChannelCreateChangeIn();
crtChan.setCommunicationChannel(chans);// Changelist
if (this.docChangeID != null) crtChan.setChangeListID(this.docChangeID);HTTPService service = new HTTPServiceLocator();
CommunicationChannelServiceVi_Document document = null;
if (url.length() == 0)
document = service.getHTTPPort();
else {
URL docUrl = new URL(url);
document = service.getHTTPPort(docUrl);}
// Provide authorized userid
HTTPBindingStub stub = (HTTPBindingStub)document;
stub.setUsername(Parameters.getUserid());
stub.setPassword(Parameters.getPassword());// Execute request
ConfigurationObjectModifyOut resp = document.create(crtChan);
ChangeListID chgID = resp.getChangeListID();
changeID = chgID.getChangeListID();
vMsg.add(new String(changeID).concat(” – Create Comm Chan”));
LogMessageCollection cLogMsg = resp.getLogMessageCollection();
LogMessageCommunicationChannel[] logMsg = cLogMsg.getLogMessageCommunicationChannel();
if (logMsg != null && logMsg.length > 0) {
for (int i=0; i<logMsg.length; i++) {
LogMessageItem msgItem = logMsg[i].getLogMessageItem();
Text msgTxt = msgItem.getMessage();
vMsg.add(msgTxt.getValue());
}
}
}
catch (Exception ex){
vMsg.add(ex.getMessage());
ex.printStackTrace();
}
return (String[])vMsg.toArray((String[])new String[1]);
}public CommunicationChannelRestricted[] createSoapChan(String[] party, String[] component, String[] channel, String[] url, String[] action) {
Vector<CommunicationChannelRestricted> vChan = new Vector<CommunicationChannelRestricted>();
for (int i=0; i<channel.length; i++) {
CommunicationChannelRestricted commChan = this.build1SoapChan(party[i], component[i], channel[i], url[i], action[i]);
vChan.add(commChan);
}
CommunicationChannelRestricted[] chans = (CommunicationChannelRestricted[])vChan.toArray(new CommunicationChannelRestricted[1]);
return chans;
}private CommunicationChannelRestricted build1SoapChan(String party, String component, String channel, String url, String action) {
CommunicationChannelRestricted commChan = new CommunicationChannelRestricted();
try {commChan.setMasterLanguage(“EN”);
// Communication Channel ID info
CommunicationChannelID commID = new CommunicationChannelID();
commID.setChannelID(channel);
commID.setComponentID(component);
commID.setPartyID(party);
commChan.setCommunicationChannelID(commID);// Adapter Type
DesignObjectID adapter = new DesignObjectID();
adapter.setName(“SOAP”);
adapter.setNamespace(“http://sap.com/xi/XI/System“);
adapter.setSoftwareComponentVersionID(“1879eed0-7b4e-11d9-87c6-c81c0a114c15”);
commChan.setAdapterMetadata(adapter);// Adapter Configuration
commChan.setDirection(“Receiver”);
commChan.setTransportProtocol(“HTTP”);
commChan.setTransportProtocolVersion(“”);
commChan.setMessageProtocol(“SOAP”);
commChan.setMessageProtocolVersion(“”);// Adapter Properties
Vector<GenericProperty> vProp = new Vector<GenericProperty>();
GenericProperty prop = new GenericProperty(“XMBWS.TargetURL”, url);
vProp.add(prop);
prop = new GenericProperty(“XMBWS.DefaultSOAPAction”, action);
vProp.add(prop);
prop = new GenericProperty(“useAuth”, “1”);
vProp.add(prop);
prop = new GenericProperty(“XMBWS.User”, Parameters.getUserid());
vProp.add(prop);
prop = new GenericProperty(“XMBWS.Password”, Parameters.getPassword());
vProp.add(prop);
GenericProperty[] props = (GenericProperty[])vProp.toArray(new GenericProperty[1]);
commChan.setAdapterSpecificAttribute(props);
}
catch (Exception ex){
ex.printStackTrace();
}
return commChan;
}public void setURL(String serverPort) {
if (serverPort == null) return;
this.serverPort = serverPort;
this.url = this.url.concat(“http://“).concat(serverPort).concat(apiURL);
}public void setActivate(boolean yesno) {
this.bActivate = yesno;
}public static void main(String[] args) {
String[] recParty = new String[]{“*”, “*”};
String[] recComponent = new String[]{“BusComponent1″,”BusComponent2”};
String[] url = new String[]{“http://webservice1“,”http://webservice2“};
String[] action = new String[]{“http://action1“,”http://action2“};
String[] recChannel = new String[]{“SOAP_1_receiver”,”SOAP_2_receiver”};String serverPort = “nspah227:52700”;
ChangeList chg = new ChangeList();
chg.setURL(serverPort);
String chgID = chg.createChangeList(“My Test Blog ChangeList”);CreateSoapChannel test = new CreateSoapChannel(chgID);
test.setActivate(false);
test.setURL(serverPort);
String[] msg = test.createSoapChannelActivate(recParty, recComponent, recChannel, url, action);
for (int i=0; i<msg.length; i++) {
System.out.println(msg[i]);
}System.out.println(“Done”);
}}
- Please note in the “main” method, the data used to create the communication channel are set into arrays. This should be self-explanatory.
- When we execute the CreateSoapChannel, with “setActivate(false)”:
We will see in the Integration Directory the changelist: (make sure you log on using the same userid as the one used in the Directory API program…you may also need to refresh the screen)
andIf we used “setActivate(true)”, then there will be no changelist shown and the configuration would have been activated automatically.
- Create new package and classes to contain our custom code. In this blog, I created the following:
This 3 part blog series is simply excellent.
Even though I haven't tried using this option yet, I don't think there should be any problems what so ever considering that you have described all that is needed in the blogs.
Thank You for the effort and time.
Regards
Bhavesh
We are trying to follow the steps in your blog "Directory API Development - Part 2 of 3" and we are facing the following problems:
We are working on PI 7.1.
In the Integration Repository Menu :
SAP Basis --->SAP Basis 7.10 ->http://sap.com/xi/XI/System ->
"Service Interfaces",
"Message Types"
"Fault Message Types",
"Data Types",
"Adapter Metadata"
only are visible.
We need the "External Definitions" in order to export WSDL. Is it not available in the standard SAP PI 7.1 ?
Do we have to import it explicitly?
Kindly advice.
Also, we are facing a problem After the
"proxy generation".
After the new packages are created, we are getting errors related to ".jar" files.
1.The constructor ArraySerializerFactory(QName, QName) is undefined PubSub/src/BusinessComponentServiceWsd HTTPBasicAuthBindingStub.java
2.The constructor TypeDesc(Class, boolean) is undefined
PubSub/src/types/api/server/ib/aii/sap/com ChangeListID.java
3.The method setItemQName(QName) is undefined for the type ParameterDesc PubSub/src/BusinessComponentServiceWsd HTTPBasicAuthBindingStub.java
4.The method setNillable(boolean) is undefined for the type ElementDesc
PubSub/src/types/api/server/ib/aii/sap/com ChangeListID.java
Thanks in advance
~ Suraj
In PI 7.1, you will need to connect to the ESR from your NWDS to import the directory API. The alternative is to import the 7.0 external definitions into 7.1. However, this will limit you to the features of PI 7.0.
As for the jar errors, you will need the jars as specified in Part 2 of the blog.
Regards,
Bill
Mr Li, I'm using NWDS 7.3 SP04, and the HTTPBindingStub class does not seem to be available? Has the method of setting basic auth user name and password changed?
The call is made, but fails with an HTTP status of 401 because of the missing credentials.
Regards,
Morten Wittrock
Hi,
This blog is based on API available for PI 7.0, which can also be used in PI 7.1. It is possible the WSDL API is still available for PI 7.3. A detail how-to in using this API can be found on http://scn.sap.com/docs/DOC-3509.
PI 7.3 introduced another set of API which are more robust and able to handle new configuration objects introduced with PI 7.1 & 7.3. Below is a sample program on using the new API. You have to import the API from the ESR when you are in NWDS.
package com.test.dirapi;
import java.util.List;
import javax.xml.ws.BindingProvider;
import com.sap.xi.basis.IntegratedConfigurationIn;
import com.sap.xi.basis.IntegratedConfigurationInService;
import com.sap.xi.basis.IntegratedConfigurationQueryIn;
import com.sap.xi.basis.IntegratedConfigurationQueryOut;
import com.sap.xi.basis.MessageHeaderID;
public class IntegratedConfiguration {
private static String apiURL = "/IntegratedConfigurationInService/IntegratedConfigurationInImplBean?wsdl=binding&mode=ws_policy";
private String serverPort = "usphlvm1426:50000";
private String user = "demo";
private String password = "pswd";
private String url = new String();
private IntegratedConfigurationIn port;
public IntegratedConfiguration() {
setURL(serverPort);
try {
port = getPort();
}
catch (Exception e) {
e.printStackTrace();
}
}
public List<MessageHeaderID> query() {
IntegratedConfigurationQueryIn queryIn = new IntegratedConfigurationQueryIn();
MessageHeaderID msgHdr = new MessageHeaderID();
queryIn.setIntegratedConfigurationID(msgHdr);
IntegratedConfigurationQueryOut queryOut = port.query(queryIn);
List<MessageHeaderID> lMsgHdr = queryOut.getIntegratedConfigurationID();
return lMsgHdr;
}
private void setURL(String serverPort) {
if (serverPort == null)
return;
else
this.url = this.url.concat("http://").concat(serverPort).concat(apiURL);
}
private IntegratedConfigurationIn getPort() throws Exception{
IntegratedConfigurationIn port = null;
try {
IntegratedConfigurationInService service = null;
service = new IntegratedConfigurationInService();
port = (IntegratedConfigurationIn) service.getIntegratedConfigurationIn_Port();
BindingProvider bp = (BindingProvider)port;
bp.getRequestContext().put(BindingProvider.USERNAME_PROPERTY, user);
bp.getRequestContext().put(BindingProvider.PASSWORD_PROPERTY, password);
if (url.length() != 0)
bp.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, url);
}
catch (Exception ex){
ex.printStackTrace();
}
return port;
}
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
IntegratedConfiguration test = new IntegratedConfiguration();
List<MessageHeaderID> listMsgHdr = test.query();
System.out.println("Done - number of configurations = " + listMsgHdr.size());
}
}
Great, thank you very much.
Hi William,
Thanks for the blog.
As you mentioned, above is the program code(Sample for the new API) for SAP PI 7.3. Is this can be applicable/suitable for PI 7.31 SP14.
We would like to develop this API in PI 7.31 for auto creation of objects and configurations in ID part and as well as to extract end point details in to excel sheet.
Please confirm and suggest.
Regards,
Sandhya.
Hi Wiilam,
I am facing an issue invoking the read method for Communication Channel wsdl. I get the below error.
org.apache.axis.encoding.ser.BeanPropertyTarget set
SEVERE: Could not convert [Ltypes.api.server.ibdir.aii.sap.com.GenericTableRowTableCell; to bean field 'valueTableRow', type [Ltypes.api.server.ibdir.aii.sap.com.GenericTableRowTableCell;
The error comes only when I try to read channels which have FCC or some kind of table structure. I tried using different axis versions however without any luck. Can you kindly help?
Regards,
Sathya
Hi William,
Sorry for the trouble. I was able to resolve the issue 🙂 Some changes were needed in the NWDS config during generation of clients.
Thank you for the wonderful blog and keep them coming !! 🙂
Regards,
Sathya
Hi Bill,
I am getting below error , how to resolve below error?
test.ToolException: No operation found using soap keys [urn:ChangeListServiceVi], [create], []. InterfaceMapping Object class: com.sap.engine.services.webservices.espbase.mappings.InterfaceMapping mappings: {InterfaceMappingID=sap.com/com.sap.xi.directory.webservices640_CommunicationChannelService_HTTPBasicAuthPort, BindingType=Soap}.
at test.WebService.createChangeList(WebService.java:78)
at test.ConnectionTest.main(ConnectionTest.java:16)
Thanks
Hari.