Java Proxy WebDAV client for SAP PI
Hello everybody. Few weeks ago I was looking how I can connect SAP XI/PI with WebDAV server. I found a lot of posts where people ask how to work with WebDAV, however I didn’t find any ready to use solutions, only suggestion that the best solution is the Java Proxy. From my work experience, I know that Java coding is not a strong side of many XI/PI consultants. That’s why I am writing this post how to make a simple WebDAV client.
First, I choose Java library for WebDAV. I choose Sardine (the library and it’s description you can find here), because this library looks pretty simple and it can do everything I need. In addition, it’s better to download other necessary libraries now:
The second step, I created Outbound Synchronous Service Interface. It is universal, so I can use it for any WebDAV actions. For Request and Response I’ve used the same structure, however I’ve used different Message Type names. My Data Type looks like that (the Message Element contains the message which should be received/sent):
The third step, open NWDS and create EJB Project. In this project you should add EAR file.
The next step is open Enterprise Service Browser Perspective (Window/Open Perspective) , log in to the SAP XI/PI system and Generate JavaBean Skeleton.
And the final step is adding libraries into the project and writing code:
The main class SardineClient in sardineclient package (it takes message and does the action which was set in the response:
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package sardineclient;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.SAXException;
import com.github.sardine.DavResource;
import com.github.sardine.Sardine;
import com.github.sardine.SardineFactory;
import com.test.webdav.DTMessage;
import com.test.webdav.DTWebDAV;
import com.test.webdav.DTWebDAVResp;
/**
* WebDav client template for SAP Java Proxy Main class for WebDAV client
*
* @author rassakhatsky
*/
public class SardineClient {
public DTWebDAVResp execute(DTWebDAV requestMessage, DTWebDAVResp responseMessage) throws IOException, com.test.webdav.FMFault_Exception, ParserConfigurationException, SAXException {
for (int i = 0; i < requestMessage.getWebDAV().size(); i++) { //take each structure in the message
DTMessage message = new DTMessage(); //result message (body)
DTWebDAVResp.WebDAV webDAW = new DTWebDAVResp.WebDAV(); //result message (header)
DTMessage.Row row = new DTMessage.Row();//result message (rows in the header of the message)
/**
* Take header data
*/
String action = requestMessage.getWebDAV().get(i).getAction(); //take action
String user = requestMessage.getWebDAV().get(i).getUser(); //take user name
String password = requestMessage.getWebDAV().get(i).getPassword(); //take password
String serverAddress = requestMessage.getWebDAV().get(i).getServerAddress(); //take server address
String serverAddressTo = requestMessage.getWebDAV().get(i).getServerAddressTo(); //take server address (target for copy/move actions)
String serverAddressFrom = requestMessage.getWebDAV().get(i).getServerAddressFrom(); //take server address (source for copy/move actions)
/**
* Create new WebDAV client and after check which action has to be
* used
*/
Sardine sardine = SardineFactory.begin(user, password);
/**
* This uses a HTTP HEAD request to see if a file exists on the
* remote server.
*/
if (action.equalsIgnoreCase("exists") || action.equalsIgnoreCase("ex")) {
if (sardine.exists(serverAddress)) {
row.getRecord().add("File/Directory exists");
} else {
row.getRecord().add("File/Directory doesn't exist");
}
message.getRow().add(row);
}
/**
* This uses HTTP PUT to delete a resource on a webdav server. Most
* likely you will want to pass in a username/password for this one
* unless the server is behind a firewall. =)
*/
if (action.equalsIgnoreCase("delete")) { // delete action
//Check if file is exist
if (sardine.exists(serverAddress)) {
//file exists
sardine.delete(serverAddress);
row.getRecord().add("File has been deleted.");
} else {
//file doesn't exist
row.getRecord().add("File not found.");
}
message.getRow().add(row);
}
/**
* This creates a directory on the remote server.
*/
if (action.equalsIgnoreCase("createDirectory") || action.equalsIgnoreCase("cd")) { // createDirectory
//Check if directory exists
if (sardine.exists(serverAddress)) {
//directory exists
row.getRecord().add("Directory exists.");
} else {
//directory doesn't exist
sardine.createDirectory(serverAddress);
row.getRecord().add("Directory has been created successfuly.");
}
message.getRow().add(row);
}
/**
* This allows you to HTTP PUT a file up on a webdav server. It
* takes an InputStream so that you don't have to buffer the entire
* file into memory first as a byte array.
*/
if (action.equalsIgnoreCase("put")) { // put action
Handler handler = new Handler();
byte[] file = handler.executePut(requestMessage.getWebDAV().get(i));
sardine.put(serverAddress, file);
row.getRecord().add("File has been created successfuly.");
message.getRow().add(row);
}
/**
* This moves a file from one location to another on the remote
* server. It assumes you want to overwrite all files.
*/
if (action.equalsIgnoreCase("move")) { // move action
//check if file exists
if (sardine.exists(serverAddressFrom)) {
//file exists
sardine.move(serverAddressFrom, serverAddressTo);
row.getRecord().add("File has been moved successfuly.");
} else {
//directory doesn't exist
row.getRecord().add("File doesn't exist.");
}
message.getRow().add(row);
}
/**
* This copies a file from one location to another on the remote
* server. It assumes you want to overwrite all files.
*/
if (action.equalsIgnoreCase("copy")) { // copy action
//check if file exists
if (sardine.exists(serverAddressFrom)) {
//file exists
sardine.copy(serverAddressFrom, serverAddressTo);
row.getRecord().add("File has been copied successfuly.");
} else {
//directory doesn't exist
row.getRecord().add("File doesn't exist.");
}
message.getRow().add(row);
}
/**
* This returns a List of DavResource objects for a directory or a
* single DavResource for a file on a remote dav server. The URL
* should be properly encoded and must end with a "/" for a
* directory. The depth is an optional parameter that defaults to 1.
*/
if (action.equalsIgnoreCase("list")) {
//Check if directory exists
if (sardine.exists(serverAddress)) {
//directory exists
row.getRecord().add("Directory exists.");
List<DavResource> resources = sardine.list(serverAddress);
for (DavResource res : resources) {
row.getRecord().add(res.toString());
}
} else {
//directory doesn't exist
row.getRecord().add("Directory doesn't exist.");
}
message.getRow().add(row);
}
/**
* This will get an InputStream reference to a remote file.
* Obviously you want to point at a file and not a directory for
* this one. The url should be properly encoded
*/
if (action.equalsIgnoreCase("get")) { // get action
//check if file exists
if (sardine.exists(serverAddress)) {
//file exists
InputStream is = sardine.get(serverAddress);
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser parcer = factory.newSAXParser();
SAXHandler_4_Get handlerXI = new SAXHandler_4_Get();
parcer.parse(is, handlerXI);
message = handlerXI.getOutput().getMessage();
} else {
//directory doesn't exist
row.getRecord().add("File/Directory doesn't exist.");
message.getRow().add(row);
}
}
webDAW.setMessage(message); //set message (body)
webDAW.setAction(action);
webDAW.setServerAddress(serverAddress);
webDAW.setServerAddressFrom(serverAddressFrom);
webDAW.setServerAddressTo(serverAddressTo);
webDAW.setUser(user);
responseMessage.getWebDAV().add(i, webDAW);
}
return responseMessage;
}
}
The Execution class in sardineclient package (it makes XML message for Put action, so you should make it for you, it’s just an example):
package sardineclient;
import com.test.webdav.DTWebDAVResp.WebDAV;
class Execution {
public StringBuffer resultMessage;
public void createFile(WebDAV source) {
String address;
String addressTo;
String password;
String addressFrom;
String user;
String action;
if (!(source.getServerAddress() == null)) {
address = source.getServerAddress();
} else {
address = "";
}
if (!(source.getServerAddressTo() == null)) {
addressTo = source.getServerAddressTo();
} else {
addressTo = "";
}
if (!(source.getServerAddressFrom() == null)) {
addressFrom = source.getServerAddressFrom();
} else {
addressFrom = "";
}
if (!(source.getUser() == null)) {
user = source.getUser();
} else {
user = "";
}
if (!(source.getPassword() == null)) {
password = source.getPassword();
} else {
password = "";
}
if (!(source.getAction() == null)) {
action = source.getAction();
} else {
action = "";
}
resultMessage = new StringBuffer(250000);
resultMessage.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
resultMessage.append("<ns0:MT_WebDAV_resp xmlns:ns0=\"http://test.com/WebDAV\">");
resultMessage.append("<WebDAV");
if ((!action.isEmpty()) || (!(action == null))) {
resultMessage.append(" Action=\"");
resultMessage.append(action);
resultMessage.append("\"");
}
if ((!user.isEmpty()) || (!(user == null))) {
resultMessage.append(" User=\"");
resultMessage.append(user);
resultMessage.append("\"");
}
if ((!(password == null)) || (!password.isEmpty())) {
resultMessage.append(" Password=\"");
resultMessage.append(password);
resultMessage.append("\"");
}
if ((!address.isEmpty()) || (!(address == null))) {
resultMessage.append(" ServerAddress=\"");
resultMessage.append(address);
resultMessage.append("\"");
}
if ((!addressFrom.isEmpty()) || (!(addressFrom == null))) {
resultMessage.append(" ServerAddressFrom=\"");
resultMessage.append(addressFrom);
resultMessage.append("\"");
}
if ((!addressTo.isEmpty()) || (!(addressTo == null))) {
resultMessage.append(" ServerAddressTo=\"");
resultMessage.append(addressTo);
resultMessage.append("\"");
}
resultMessage.append(">");
if ((!source.getMessage().getRow().isEmpty()) || (!(source.getMessage().getRow() == null))) {
resultMessage.append("<Message>");
for (int i = 0; i < source.getMessage().getRow().size(); i++) {
resultMessage.append("<Row>");
for (int j = 0; j < source.getMessage().getRow().get(i).getRecord().size(); j++) {
resultMessage.append("<Record>");
resultMessage.append(source.getMessage().getRow().get(i).getRecord().get(j));
resultMessage.append("</Record>");
}
resultMessage.append("</Row>");
}
resultMessage.append("</Message>");
}
resultMessage.append("</WebDAV>");
resultMessage.append("</ns0:MT_WebDAV_resp>");
}
public byte[] getOutput() {
return String.valueOf(resultMessage).getBytes();
}
}
The Handler class in sardineclient package (I use it for Put action too, in Production I have some additional function in this class):
package sardineclient;
import com.test.webdav.DTWebDAVResp;
import com.test.webdav.DTWebDAV.WebDAV;
public class Handler {
public byte[] executePut(WebDAV message) {
DTWebDAVResp.WebDAV response = new DTWebDAVResp.WebDAV();
response.setServerAddress(message.getServerAddress());
response.setUser(message.getUser());
response.setAction(message.getAction());
if ((!message.getMessage().getRow().isEmpty())||(!(message.getMessage().getRow()==null))) {
for (int i = 0; i < message.getMessage().getRow().size(); i++) {
response.setMessage(message.getMessage());
}
}
Execution exec = new Execution();
exec.createFile(response);
return exec.getOutput();
}
}
The SAXHandler_4_Get class in sardineclient package (I use for Get action, in other words my program reads XML from a WebDAV server using this class, so it’s just an example how to make your own class):
package sardineclient;
import com.test.webdav.DTMessage;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import com.test.webdav.DTWebDAVResp.WebDAV;
import java.util.ArrayList;
public class SAXHandler_4_Get extends DefaultHandler {
String tagContent;
WebDAV xml = new WebDAV();
DTMessage message = new DTMessage();
//Check place
boolean index_MT_WebDAV_resp, index_Message, index_Row, index_Record;
//Level
int level_Row;
int level_Record;
//temp
ArrayList<String> records = new ArrayList<String>();
ArrayList<DTMessage.Row> rows = new ArrayList<DTMessage.Row>();
DTMessage.Row row = new DTMessage.Row();
@Override
public void startDocument() throws SAXException {
level_Record = 0;
level_Row = 0;
index_MT_WebDAV_resp = false;
index_Message = false;
index_Row = false;
index_Record = false;
}
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
try {
if (qName.indexOf("MT_WebDAV_resp") != -1) {
index_MT_WebDAV_resp = true;
}
if (qName.indexOf("Message") != -1) {
index_Message = true;
}
if (qName.indexOf("Row") != -1) {
index_Row = true;
}
if (qName.indexOf("Record") != -1) {
index_Record = true;
}
} catch (Exception e) {
throw new SAXException(e);
}
}
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
try {
if (tagContent == null) {
tagContent = new String(ch, start, length);
} else {
tagContent = new String(ch, start, length);
}
} catch (Exception e) {
throw new SAXException(e);
}
}
@Override
@SuppressWarnings("empty-statement")
public void endElement(String uri, String localName, String qName) throws SAXException {
if (index_Record) {
records.add(level_Record, tagContent);
index_Record = false;
level_Record += 1;
} else {
if (index_Row) {
//row.getRecord().addAll(new ArrayList<String>(records));
rows.add(new DTMessage.Row());
rows.get(level_Row).getRecord().addAll(new ArrayList<String>(records));
records.clear();
index_Row = false;
level_Row += 1;
level_Record = 0;
} else {
if (index_Message) {
message.getRow().addAll(rows);
index_Message = false;
level_Row = 0;
} else {
if (index_MT_WebDAV_resp) {
index_MT_WebDAV_resp = false;
}
}
}
}
}
@Override
public void endDocument() throws SAXException {
xml.setMessage(message);
}
public WebDAV getOutput() {
return xml;
}
}
And final class which was generated automatically by the Generate JavaBean Skeleton step, in my case it looks like SIWebDAVSIImplBean
package com.test.webdav;
import java.io.IOException;
import javax.ejb.Stateless;
import javax.jws.WebService;
import javax.xml.parsers.ParserConfigurationException;
import org.xml.sax.SAXException;
import sardineclient.SardineClient;
import com.sap.engine.services.webservices.espbase.configuration.ann.dt.AuthenticationDT;
import com.sap.engine.services.webservices.espbase.configuration.ann.dt.AuthenticationEnumsAuthenticationLevel;
import com.sap.engine.services.webservices.espbase.configuration.ann.dt.RelMessagingNW05DTOperation;
import com.sap.engine.services.webservices.espbase.configuration.ann.dt.SessionHandlingDT;
import com.sap.engine.services.webservices.espbase.configuration.ann.dt.TransportGuaranteeDT;
import com.sap.engine.services.webservices.espbase.configuration.ann.dt.TransportGuaranteeEnumsLevel;
@SessionHandlingDT(enableSession = false)
@AuthenticationDT(authenticationLevel = AuthenticationEnumsAuthenticationLevel.BASIC)
@TransportGuaranteeDT(level = TransportGuaranteeEnumsLevel.NONE)
@WebService(portName = "SI_WebDAV_SI_Port", serviceName = "SI_WebDAV_SI_Service", endpointInterface = "com.test.webdav.SIWebDAVSI", targetNamespace = "http://test.com/WebDAV", wsdlLocation = "META-INF/wsdl/com/test/webdav/SI_WebDAV_SI/SI_WebDAV_SI.wsdl")
@Stateless
public class SIWebDAVSIImplBean {
@RelMessagingNW05DTOperation(enableWSRM = false)
public com.test.webdav.DTWebDAVResp siWebDAVSI(com.test.webdav.DTWebDAV MT_WebDAV) throws com.test.webdav.FMFault_Exception {
DTWebDAVResp response = new DTWebDAVResp();
SardineClient webDAWServer = new SardineClient();
try {
response = webDAWServer.execute(MT_WebDAV, response);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
}
return response;
}
}
That’s all. After you only should deploy and start using this Proxy. I hope this text not very chaotic.
😉 Good work, Dmitry! Let's code WebDAV adapter!