on 01-27-2020 2:17 PM
Hello everyone,
I'm running into an issue regarding HTTP Adapter in CPI. The scenario is to pick up a file in a SFTP Server, create form data with the zip file attached and send it through HTTP Adapter to Ariba.
The problem is I keep running into error status code 411: Content-Length is required.
I've tried to define this header manually, but with no success. Sending the message to another host confirms that this header is created automatically when the message is sent by CPI, so it must be another thing unknown to me.
Exploring the blogs I've found another post talking about this exact same issue. The link is down below:
Nick Yang proposed a solution that revolves around opening a conection to the destination directly into the script, discarding the necessity of using HTTP Adapter. But I tried to replicate it, but keep getting the error:
java.lang.NoSuchMethodException: No signature of method: sun.net.www.http.PosterOutputStream.write() is applicable for argument types: (org.apache.camel.converter.stream.InputStreamCache) values: [org.apache.camel.converter.stream.InputStreamCache@144c18fd] Possible solutions: write([B), write(int), write(int), write(int), wait(), wait(long)
I assume this is because I'm creating the form data before in another script, but I don't convert the body of the message to byte array afterwards.
Can someone help me on this?
This is my current script:
def Message postZipContentToAriba(Message message){
def messageLog = messageLogFactory.getMessageLog(message)
def propertiesMap = message.getProperties()
def headersMap = message.getHeaders()
// POST
def post = new URL("Ariba-Network-URL").openConnection();
def zipContent = message.getBody()
post.setRequestMethod("POST")
post.setDoOutput(true)
post.setRequestProperty("Content-Type", headersMap.get("Content-Type") as String)
post.getOutputStream().write(zipContent);
def postRC = post.getResponseCode();
if(postRC.equals(200)) {
messageLog.addAttachmentAsString("Post result:", post.getInputStream().getText() as String, "text/plain")
}else{
def exceptionMsg = "HTTP" + postRC.toString() + ", " + post.getInputStream().getText()
throw new Exception(exceptionMsg as String)
}
return message
}
Message processData(Message message) {
return postZipContentToAriba(message)
}
The script used before this one is to create multi-part form-data:
Message processData(Message message) {
// Set multipart into body
ByteArrayOutputStream outputStream = new ByteArrayOutputStream()
try{
byte[] bytes = message.getBody(byte[])
// Construct Multipart
MimeBodyPart bodyPartHead1 = new MimeBodyPart()
bodyPartHead1.setText('true')
bodyPartHead1.setDisposition('form-data; name="fullload"')
MimeBodyPart bodyPartHead2 = new MimeBodyPart()
bodyPartHead2.setText('Import External System Master Data')
bodyPartHead2.setDisposition('form-data; name="event"')
MimeBodyPart bodyPartHead3 = new MimeBodyPart()
bodyPartHead3.setText(secret)
bodyPartHead3.setDisposition('form-data; name="sharedsecret"')
MimeBodyPart bodyPartHead4 = new MimeBodyPart()
bodyPartHead4.setText(systemId)
bodyPartHead4.setDisposition('form-data; name="systemId"')
MimeBodyPart bodyPartHead5 = new MimeBodyPart()
bodyPartHead5.setText('Load And Delete')
bodyPartHead5.setDisposition('form-data; name="Operation"')
MimeBodyPart bodyPartHead6 = new MimeBodyPart()
bodyPartHead6.setText('Cloud')
bodyPartHead6.setDisposition('form-data; name="clienttype"')
MimeBodyPart bodyPartHead7 = new MimeBodyPart()
bodyPartHead7.setText('CPI')
bodyPartHead7.setDisposition('form-data; name="clientinfo"')
MimeBodyPart bodyPartHead8 = new MimeBodyPart()
bodyPartHead8.setText('1.0.0')
bodyPartHead8.setDisposition('form-data; name="clientversion"')
MimeBodyPart bodyPart = new MimeBodyPart()
ByteArrayDataSource dataSource = new ByteArrayDataSource(bytes, 'application/zip')
DataHandler byteDataHandler = new DataHandler(dataSource)
bodyPart.setDataHandler(byteDataHandler)
bodyPart.setFileName('content.zip')
bodyPart.setDisposition('form-data; name="content"')
MimeMultipart multipart = new MimeMultipart()
multipart.addBodyPart(bodyPartHead1)
multipart.addBodyPart(bodyPartHead2)
multipart.addBodyPart(bodyPartHead3)
multipart.addBodyPart(bodyPartHead4)
multipart.addBodyPart(bodyPartHead5)
multipart.addBodyPart(bodyPartHead6)
multipart.addBodyPart(bodyPartHead7)
multipart.addBodyPart(bodyPartHead8)
multipart.addBodyPart(bodyPart)
multipart.updateHeaders()
multipart.writeTo(outputStream)
message.setBody(outputStream)
// Set Content type with boundary
String boundary = (new ContentType(multipart.contentType)).getParameter('boundary');
message.setHeader('Content-Type', "multipart/form-data; boundary=\"${boundary}\"")
// Calculate message size
//message.setHeader('content-length', message.getBodySize())
}catch(Exception e){
}finally{
outputStream.close()
}
return message
}
Thank you and kind regards,
João Gomes
Hello everyone,
With some search and fine tunning, I was able to use client certificate authentication using a mix between java and groovy, changing the already used script for shared secret authentication.
Here it follows:
import com.sap.gateway.ip.core.customdev.util.Message
import javax.activation.DataHandler
import javax.mail.internet.ContentType
import javax.mail.internet.MimeBodyPart
import javax.mail.internet.MimeMultipart
import javax.mail.util.ByteArrayDataSource
import java.nio.charset.StandardCharsets
import java.util.Base64;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.HttpsURLConnection;
import java.net.URL;
import java.net.URLConnection;
def Message postZipContentToAriba(Message message){
// We build a SSLContext with both our trust/key managers
SSLContext sslContext = SSLContext.getInstance("TLS");
com.sap.it.api.keystore.KeystoreService ks = com.sap.it.api.ITApiFactory.getApi(com.sap.it.api.keystore.KeystoreService.class, null)
if (ks != null){
// Get relevant objects for SSLContext
KeyManager[] km = ks.getKeyManagers();
TrustManager[] tm = ks.getTrustManagers();
// Initialize Context
sslContext.init(km, tm, null);
}
SSLSocketFactory sslSf = sslContext.getSocketFactory();
// Prepare URL to connect
def messageLog = messageLogFactory.getMessageLog(message)
def propertiesMap = message.getProperties()
def headersMap = message.getHeaders()
String aribaAddress = propertiesMap.get("aribaAddress")
String queryParameters = propertiesMap.get("queryParameters")
// We prepare a URLConnection
URL url = new URL(aribaAddress + "?" + queryParameters);
URLConnection urlConnection = url.openConnection();
// Before actually opening the sockets, we affect the SSLSocketFactory
HttpsURLConnection httpsUrlConnection = (HttpsURLConnection) urlConnection;
httpsUrlConnection.setSSLSocketFactory(sslSf);
// POST
def zipContent = message.getBody(byte[])
httpsUrlConnection.setRequestMethod("POST")
httpsUrlConnection.setDoOutput(true)
httpsUrlConnection.setRequestProperty("Content-Type", headersMap.get("Content-Type") as String)
httpsUrlConnection.getOutputStream().write(zipContent);
def postRC = httpsUrlConnection.getResponseCode();
if(postRC.equals(200)) {
messageLog.addAttachmentAsString("Post result:", httpsUrlConnection.getInputStream().getText() as String, "text/plain")
}else{
def exceptionMsg = "HTTP" + postRC.toString() + ", " + httpsUrlConnection.getInputStream().getText()
throw new Exception(exceptionMsg as String)
}
return message
}
Message processData(Message message) {
return postZipContentToAriba(message)
}
I hope it helps anyone trying this approach!
Best regards,
João Gomes
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
User | Count |
---|---|
75 | |
9 | |
7 | |
7 | |
6 | |
6 | |
6 | |
6 | |
5 | |
4 |
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.