I am using NACChannel of JPOS v2.1.0. I am using GenericPackager for packing my message. I am able to successfully send message to my ISO application. But while receiving incoming message my NACChannel.receive() throws parsing error.
My incoming message has a custom Header. I suspect this is causing the parsing error. So my questions are:
How can I handle this ISOMsg header in my incoming response?
Is there any way I can disable the Parsing Step and Receive the byte[] response from the channel?
My JPOS client code I am using:
public class BuildISOMessage {
public static void main(String[] args) throws IOException, ISOException {
createISOMessage();
}
public static byte[] createISOMessage() throws ISOException {
String HOST = "localhost";
int PORT = 40021;
// Create Packager based on XML that contain DE type
ISOBasePackager packager = new GenericPackager("800_fields.xml");
NACChannel testchannel = new NACChannel();
testchannel.setHost(HOST, PORT);
ISOMsg isoMsg = build200ISOMessage(packager);
// print the DE list
logISOMsg(isoMsg);
// Get and print the output result
byte[] data = isoMsg.pack();
try {
testchannel.setPackager(isoMsg.getPackager());
testchannel.setHeader(("ISO01" + String.format("%04d", data.length)).getBytes());
testchannel.connect();
testchannel.send(isoMsg);
if (testchannel.isConnected()) {
ISOMsg response = testchannel.receive();
}
testchannel.disconnect();
} catch (Exception e) {
e.printStackTrace();
}
return data;
}
private static ISOMsg build200ISOMessage(ISOBasePackager packager)
throws ISOException {
// Create ISO Message
ISOMsg isoMsg = new ISOMsg();
isoMsg.setPackager(packager);
isoMsg.setMTI("200");
isoMsg.set(2, "4622871000060891");
isoMsg.set(3, "300000");
isoMsg.set(4, "100");
isoMsg.set(7, "1026043633");
isoMsg.set(11, "999901");
isoMsg.set(12, "113633");
isoMsg.set(13, "1026");
isoMsg.set(15, "1116");
isoMsg.set(16, "1116");
isoMsg.set(18, "6011");
isoMsg.set(22, "21");
isoMsg.set(32, "0000004");
isoMsg.set(33, "0000004");
isoMsg.set(35, "4622871000060891=22082211963393100000");
isoMsg.set(37, "829911364035");
isoMsg.set(43, "TBNKTAS2B065B999P999300501000050 TH");
isoMsg.set(48, "00000000040000000002000000013000000000005000000000007000TYRIONLANNISER ARYA STARK000000003334000000000202 00000000000000000000");
isoMsg.set(49, "764");
isoMsg.set(52, "FFFFFFFFFFFFFFFF");
isoMsg.set(62, "221000000000");
return isoMsg;
}
private static void logISOMsg(ISOMsg msg) {
System.out.println("----ISO MESSAGE-----");
try {
System.out.println(" MTI : " + msg.getMTI());
for (int i = 1; i <= msg.getMaxField(); i++) {
if (msg.hasField(i)) {
System.out.println(" Field-" + i + " : "
+ msg.getString(i));
}
}
} catch (ISOException e) {
e.printStackTrace();
} finally {
System.out.println("--------------------");
}
}
}
Thanks all for your help.
While looking for an answer for debug purpose, I created a subclass of NACChannel and put some debug statements. By doing that I realized the issue was with my field definition and nothing to do with JPOS framework.
I was setting a header(e.g. ISO010200 where message length is 200) of length '9' by the below code.
testchannel.setHeader(("ISO01" + String.format("%04d", data.length)).getBytes());
My response also had a similar header of length '9'.
So the NACChanel receive() method was able to extract the 9 digit Header correctly.
But failed to parse the response message, my field definition was not correct.
Once that was fixed, JPOS was able to parse the response message coorectly.
Related
I'm trying to save pdf in wildfly, I'm using RestEasy MultipartFormDataInput provided with wildfly 20.0.1,
but it doesn't work.
This is what I have:
public static Response uploadPdfFile(MultipartFormDataInput multipartFormDataInput) {
// local variables
MultivaluedMap<String, String> multivaluedMap = null;
String fileName = null;
InputStream inputStream = null;
String uploadFilePath = null;
try {
Map<String, List<InputPart>> map = multipartFormDataInput.getFormDataMap();
List<InputPart> lstInputPart = map.get("poc");
for(InputPart inputPart : lstInputPart){
// get filename to be uploaded
multivaluedMap = inputPart.getHeaders();
fileName = getFileName(multivaluedMap);
if(null != fileName && !"".equalsIgnoreCase(fileName)){
try {
// write & upload file to UPLOAD_FILE_SERVER
//here I have the error: Unable to find a MessageBodyReader for media type:
//application/pdf
inputStream = inputPart.getBody(InputStream.class,InputStream.class);
uploadFilePath = writeToFileServer(inputStream, fileName);
}catch (Exception e) {
e.printStackTrace();
}
// close the stream
inputStream.close();
}
}
}
catch(IOException ioe){
ioe.printStackTrace();
}
finally{
// release resources, if any
}
return Response.ok("File uploaded successfully at " + uploadFilePath).build();
}
I'm using postman for test, http POST method, in the body I send: form-data - file and selected the file.pdf.
When I sent the request, I have the next RunTimeException when I try:
inputStream = inputPart.getBody(InputStream.class,null);
I get:
java.lang.RuntimeException: RESTEASY007545: Unable to find a MessageBodyReader for media type: application/pdf and class type org.jboss.resteasy.util.Base64$InputStream
At the moment I am saving the file receiving it in Base64, but I think that with MultipartFormDataInput it is the correct way.
This is what I have when debug:
Thanks for your support.
I solved this changing the InputStream from "org.jboss.resteasy.util.Base64.InputStream"
to "java.io.InputStream"
I am trying to upload a file to OneDrive special folder using the java sdk but I am getting the following error
Error code: BadRequest Error message: Multiple action overloads were found with the same binding parameter for 'microsoft.graph.createUploadSession'.
Here is a snippet of the java code I am using
private void uploadFile(IGraphServiceClient graphClient, File localFile, String onedriveFileName) throws IOException, InterruptedException {
// Get an input stream for the file
InputStream fileStream = new FileInputStream(localFile);
long streamSize = localFile.length();
// Create a callback used by the upload provider
IProgressCallback<DriveItem> callback = new IProgressCallback<DriveItem>() {
#Override
// Called after each slice of the file is uploaded
public void progress(final long current, final long max) {
LOGGER.trace(String.format("Uploaded %d bytes of %d total bytes", current, max));
}
#Override
public void success(final DriveItem result) {
LOGGER.info(String.format("Uploaded file with ID: %s", result.id));
}
public void failure(final ClientException ex) {
LOGGER.error(String.format("Error uploading file: %s", ex.getMessage()));
}
};
try {
// Create an upload session
UploadSession uploadSession = graphClient
.me()
.drive().special("approot")
.itemWithPath(onedriveFileName)
.createUploadSession(new DriveItemUploadableProperties())
.buildRequest()
.post();
ChunkedUploadProvider<DriveItem> chunkedUploadProvider =
new ChunkedUploadProvider<DriveItem>
(uploadSession, graphClient, fileStream, streamSize, DriveItem.class);
// Config parameter is an array of integers
// customConfig[0] indicates the max slice size
// Max slice size must be a multiple of 320 KiB
int[] customConfig = {320 * 1024};
// Do the upload
chunkedUploadProvider.upload(callback, customConfig);
} catch(IOException ex){
throw ex;
} catch (Exception ex) {
throw ex;
}
}
Any help appreciated.
Thanks
My issue is that I had special characters in the filename, removing them fixed it for me.
This is my first time ask question through this platform. I am sorry. I am not good in English. I will try my best to let you understand my questions.
I am totally beginner in Netty. I would like to implement a program to send commands to a telnet server and receive response message. I modified the sample telnet program to connect and get response from the serve when there is no authentication of serve.
The question is that
When the authentication processes are setup in server. (Require login name and password)
How to implement the client side program?
How can I receive the serve login request and response it?
Should I implement another handler to handle the authentication?
below shows how i send the commands to the server
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap();
b.group(group)
.channel(NioSocketChannel.class)
.handler(new TelnetClientInitializer(sslCtx));
// Start the connection attempt.
ChannelFuture lastWriteFuture = null;
lastWriteFuture = b.connect(HOST, PORT).sync();
Channel ch = lastWriteFuture.channel();
lastWriteFuture = ch.writeAndFlush("ls" + "\r\n", ch.newPromise());
lastWriteFuture = ch.writeAndFlush("status" + "\r\n");
lastWriteFuture = ch.writeAndFlush("ls" + "\r\n");
lastWriteFuture = ch.writeAndFlush("exit" + "\r\n");
// Wait until the connection is closed.
lastWriteFuture.channel().closeFuture().sync();
} finally {
// Shut down the event loop to terminate all threads.
group.shutdownGracefully();
}
but what should i do before send the above commands to login into the serve?
The following picture shows what i want to do in the program
Thank you very much!!!
If we talk about TELNET as a protocol you should know that Telnet client from Netty examples does not support TELNET protocol. His name is just confusing and you can't connect to any standard telnet servers. You can read more about TELNET protocol here - THE TELNET PROTOCOL .
I see 2 ways:
write your implementation for TELNET on Netty
use another implementation for examples Apache Commons Net
Example for the first way - modified netty client, i tested him on Linux servers. He has several dirty hacks like a timer but he works.
Example for the second - Java – Writing An Automated Telnet Client:
import org.apache.commons.net.telnet.*;
import java.io.InputStream;
import java.io.PrintStream;
public class AutomatedTelnetClient {
private TelnetClient telnet = new TelnetClient();
private InputStream in;
private PrintStream out;
private String prompt = "~>";
public AutomatedTelnetClient(String server) {
try {
// Connect to the specified server
telnet.connect(server, 8023);
TerminalTypeOptionHandler ttopt = new TerminalTypeOptionHandler("VT100", false, false, true, false);
EchoOptionHandler echoopt = new EchoOptionHandler(true, false, true, false);
SuppressGAOptionHandler gaopt = new SuppressGAOptionHandler(true, true, true, true);
try {
telnet.addOptionHandler(ttopt);
telnet.addOptionHandler(echoopt);
telnet.addOptionHandler(gaopt);
} catch (InvalidTelnetOptionException e) {
System.err.println("Error registering option handlers: " + e.getMessage());
}
// Get input and output stream references
in = telnet.getInputStream();
out = new PrintStream(telnet.getOutputStream());
} catch (Exception e) {
e.printStackTrace();
}
}
// public void su(String password) {
// try {
// write(“su”);
// readUntil(“Password: “);
// write(password);
// prompt = “#”;
// readUntil(prompt + ” “);
// } catch (Exception e) {
// e.printStackTrace();
// }
// }
public String readUntil(String pattern) {
try {
char lastChar = pattern.charAt(pattern.length() - 1);
StringBuffer sb = new StringBuffer();
boolean found = false;
char ch = (char) in.read();
while (true) {
System.out.print(ch);
sb.append(ch);
if (ch == lastChar) {
if (sb.toString().endsWith(pattern)) {
return sb.toString();
}
}
ch = (char) in.read();
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public void write(String value) {
try {
out.println(value);
out.flush();
System.out.println(value);
} catch (Exception e) {
e.printStackTrace();
}
}
public String sendCommand(String command) {
try {
write(command);
return readUntil(prompt + " ");
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public void disconnect() {
try {
telnet.disconnect();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
String user = "test";
String password = "test";
AutomatedTelnetClient telnet = new AutomatedTelnetClient("localhost");
// Log the user on
telnet.readUntil("login:");
telnet.write(user);
telnet.readUntil("Password:");
telnet.write(password);
// Advance to a prompt
telnet.readUntil(telnet.prompt + " ");
telnet.sendCommand("ps -ef");
telnet.sendCommand("ls");
telnet.sendCommand("w");
telnet.disconnect();
}
}
Telnet has no real concept of a password packet, a password prompt is just like any normal text output. This means that you can just send the username and password when connection as separate lines, and the telnet server will use them correctly.
ch.writeAndFlush("administrator" + "\r\n");
ch.writeAndFlush("LetMeIn4!!" + "\r\n");
If you require connecting to server that don't always require the password, then you should read the output from the server, check if it contains "username", send the username, then keep reading if it contains "password" and send the password. This is prone to breaking as servers are not required to send those strings, and legit output may also contain those. This is the downside of the telnet protocol.
I hope this my article is helpful to someone.
Netty | Implement Telnet Automated Authentication
I had to use Telnet to control the sub-equipment while developing the space ground station software. Except for the authentication, Telnet is quite similar to regular TCP server communication. So, I implemented a Handler that automatically handles Telnet authentication to communicate with the Telnet server. When connecting to the Telnet server, the following introductory message, “Username: “, “Passwrod: “ messages are displayed in sequence, and user authentication is requested. Handler automatically handles the authentication process as if a human would input account information. Below is a brief description of the implementation.
c:\> telnet 192.168.0.1 12345
Power On Self Test (POST) Passed.
Integrated Control Unit (ICU) Build xxx (Build:xxxxxx) - Feb 7 2022, 17:57:16 (Network/TCP)
Date and Time: 2022-02-16 20:01:19 (GMT)
MAC Address : [00:xx:xx:xx:C6:8F]
Username: User
Password: 1234
>
Handler
TelnetAuthenticator Handler simply works as follows.
If the message contains the string “Username: “, send the username.
If the message contains the string “Password: “, the password is sent.
If the message contains the string “>” waiting for input, delete the authentication handler from the Pipeline. After authentication, TelnetAuthenticator Handler is unnecessary.
If the account is not registered on the Telnet server or the password does not match, the string “Username: “ or “Password: “ is repeatedly received. The authentication failure error is unrecoverable, notifying the user of a failed authentication process and forcing them to disconnect.
#Slf4j
#RequiredArgsConstructor
public class TelnetAuthenticator extends SimpleChannelInboundHandler<String> {
private final ChannelSpec channelSpec;
private boolean alreadyUserTried = false;
private boolean alreadyPasswordTried = false;
#Override
protected void channelRead0(ChannelHandlerContext ctx, String msg) {
// If the message contains the string “Username: “, send the username.
if (msg.contains(channelSpec.getReqUserTag())) {
if (alreadyUserTried) {
processFail(ctx);
}
ctx.channel().writeAndFlush(channelSpec.getAccount().getUser() + channelSpec.getEndLine());
alreadyUserTried = true;
return;
}
// If the message contains the string “Password: “, the password is sent.
if (msg.contains(channelSpec.getReqPasswordTag())) {
if (alreadyPasswordTried) {
processFail(ctx);
}
ctx.channel().writeAndFlush(channelSpec.getAccount().getPassword() + channelSpec.getEndLine());
alreadyPasswordTried = true;
return;
}
// If the incoming message contains an input waiting message, the Pipeline deletes the current handler.
if (msg.contains(channelSpec.getStandByTag())) {
ctx.pipeline().remove(this.getClass());
}
}
private void processFail(ChannelHandlerContext ctx) {
ctx.fireUserEventTriggered(ErrorMessage.AUTHENTICATE_FAIL);
ctx.close();
}
}
Initialize ChannelPipeline
A ChannelPipeline configuration with a TelnetAuthenticator Handler can be: First, register InboundHandlers as follows.
First, add DelimiterBasedFrameDecoder with “Username: “, “Password: “, “>” strings as delimiters. The stripDelimiter option is set to false because all delimiters must be received to recognize the authentication process.
Add StringDecoder.
Add the implemented TelnetAuthenticator Handler.
Add other necessary business logic.
Simply add StringEncoder to Outbound. You can add other Handlers as needed.
public class PipelineInitializer extends ChannelInitializer<SocketChannel> {
private ChannelSpec channelSpec;
public void init(ChannelSpec channelSpec) {
this.channelSpec = channelSpec;
}
#Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline()
// Inbound
.addLast(new DelimiterBasedFrameDecoder(1024, false,
channelSpec.getDelimiter().reqUserTag(),
channelSpec.getDelimiter().reqPasswordTag(),
channelSpec.getDelimiter().standByTag()))
.addLast(new StringDecoder())
.addLast(new TelnetAuthenticator(channelSpec))
.addLast(new BusinessLogic())
// Outbound
.addLast(new StringEncoder());
}
}
ChannelSpec
ChannelSpec defines specifications required for communication with Telnet server. Manage server IP, port, account information, separator, etc.
#Getter
public class ChannelSpec {
private final String serverIp = "192.168.0.1";
private final int serverPort = 12345;
private final String endLine = "\r\n";
private final String standByTag = ">";
private final String reqUserTag = "Username: ";
private final String reqPasswordTag = "Password: ";
private final Account account = new Account("User", "1234");
private final Delimiter delimiter = new Delimiter();
public class Delimiter {
public ByteBuf standByTag() {
return toByteBuf(standByTag);
}
public ByteBuf reqUserTag() {
return toByteBuf(reqUserTag);
}
public ByteBuf reqPasswordTag() {
return toByteBuf(reqPasswordTag);
}
private ByteBuf toByteBuf(String input) {
ByteBuf delimiterBuf = Unpooled.buffer();
delimiterBuf.writeCharSequence(input, StandardCharsets.UTF_8);
return delimiterBuf;
}
}
}
#RequiredArgsConstructor
#Getter
public class Account {
private final String user;
private final String password;
}
Folks,
I'm using Apache CXF (JAX-RS)'s LoggingInInterceptor and LoggingOutInterceptor to log the request and response objects to my web service and also to log the response time.
For this, I have extended both these classes and done relevant configuration in the appropriate XML files. Doing this, I was able to log the request and response object.
However, I also want to log the request URL in both these interceptors. I was able to get the HttpServletRequest object (Inside the LoggingInInterceptor) using the following:
HttpServletRequest request = (HttpServletRequest)message.get(AbstractHTTPDestination.HTTP_REQUEST);
Then, from the request object I was able to get the request URL (REST URL in my case). I was however, not able to get the request object in the LoggingOutInterceptor using this code (or by any other means).
Here is a summary of the issue:
I need to access the reqeuest URI inside the LoggingOutInterceptor (using HttpServletRequest object perhaps?).
Would appreciate any help on this.
Update: Adding the interceptor code.
public class StorefrontRestInboundInterceptor extends LoggingInInterceptor {
/**
* constructor.
*/
public StorefrontRestInboundInterceptor() {
super();
}
#Override
public void handleMessage(final Message message) throws Fault {
HttpServletRequest httpRequest = (HttpServletRequest) message.get(AbstractHTTPDestination.HTTP_REQUEST);
if (isLoggingRequired()) {
String requestUrl = (String) message.getExchange().get("requestUrl");
Date requestTime = timeService.getCurrentTime();
LOG.info("Performance Monitor started for session id:" + customerSession.getGuid());
LOG.info(httpRequest.getRequestURI() + " Start time for SessionID " + customerSession.getGuid() + ": "
+ requestTime.toString());
}
try {
InputStream inputStream = message.getContent(InputStream.class);
CachedOutputStream outputStream = new CachedOutputStream();
IOUtils.copy(inputStream, outputStream);
outputStream.flush();
message.setContent(InputStream.class, outputStream.getInputStream());
LOG.info("Request object for " + httpRequest.getRequestURI() + " :" + outputStream.getInputStream());
inputStream.close();
outputStream.close();
} catch (Exception ex) {
LOG.info("Error occured reading the input stream for " + httpRequest.getRequestURI());
}
}
public class StorefrontRestOutboundInterceptor extends LoggingOutInterceptor {
/**
* logger implementation.
*/
protected static final Logger LOG = Logger.getLogger(StorefrontRestOutboundInterceptor.class);
/**
* constructor.
*/
public StorefrontRestOutboundInterceptor() {
super(Phase.PRE_STREAM);
}
#Override
public void handleMessage(final Message message) throws Fault {
if (isLoggingRequired()) {
LOG.info(requestUrl + " End time for SessionID " + customerGuid + ": " + (timeService.getCurrentTime().getTime() - requestTime)
+ " milliseconds taken.");
LOG.info("Performance Monitor ends for session id:" + customerGuid);
}
OutputStream out = message.getContent(OutputStream.class);
final CacheAndWriteOutputStream newOut = new CacheAndWriteOutputStream(out);
message.setContent(OutputStream.class, newOut);
newOut.registerCallback(new LoggingCallback(requestUrl));
}
public class LoggingCallback implements CachedOutputStreamCallback {
private final String requestUrl;
/**
*
* #param requestUrl requestUrl.
*/
public LoggingCallback(final String requestUrl) {
this.requestUrl = requestUrl;
}
/**
* #param cos CachedOutputStream.
*/
public void onFlush(final CachedOutputStream cos) { //NOPMD
}
/**
* #param cos CachedOutputStream.
*/
public void onClose(final CachedOutputStream cos) {
try {
StringBuilder builder = new StringBuilder();
cos.writeCacheTo(builder, limit);
LOG.info("Request object for " + requestUrl + " :" + builder.toString());
} catch (Exception e) {
LOG.info("Error occured writing the response object for " + requestUrl);
}
}
}
Update:Since you are in Out chain you may need to get the In message from where you can get the request URI since the Request URI may null for out going response message.
You may try like this to get the Incoming message:
Message incoming = message.getExchange().getInMessage();
Then I think you should be able to get the Request URI using:
String requestURI = (String) incoming.get(Message.REQUEST_URI);
or
String endpointURI = (String) incoming.get(Message.ENDPOINT_ADDRESS);
If this is still not working, try to run the interceptor in PRE_STREAM phase like Phase.PRE_STREAM in your constructor.
You can also try to get the message from Interceptor Chain like this:
PhaseInterceptorChain chain = message.getInterceptorChain();
Message currentMessage = chain.getCurrentMessage();
HttpServletRequest req = (HttpServletRequest) currentMessage.get("HTTP.REQUEST");
When testing the Web Agent sample in Java, I am getting an error reply
<?xml version="1.0" encoding="utf-8"?>
<response xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" type="error">
<Error>
<returnCode>706</returnCode>
<errorMessage>Value cannot be null.
Parameter name: s</errorMessage>
</Error>
</response>
I followed the Ruby example in the CoSign Web Agent samples and the documentation
I have used the demo.pdf file provided in the sample.
This is the XML (from test app) sent in the POST request (the <content></content> has the Base64 encoded PDF, but omitted because of length).
<?xml version="1.0" encoding="utf-8" ?>
<request>
<Logic>
<allowAdHoc>true</allowAdHoc>
<workingMode>pull</workingMode>
<enforceReason>false</enforceReason>
</Logic>
<Url>
<finishURL>http://localhost:64956/retrieveSignedFile.aspx</finishURL>
</Url>
<Document>
<fileID>1234567890</fileID>
<contentType>pdf</contentType>
<content>{BASE64 encoded pdf content}</content>
</Document>
</request>
The following is the java code I have used:
public class CoSignTest {
private static final String INPUT = "D:\\tmp\\demo.pdf";
private static final String PRECONTENT = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n" +
"<request>\n" +
" <Logic>\n" +
" <allowAdHoc>true</allowAdHoc>\n" +
" <workingMode>pull</workingMode>\n" +
" <enforceReason>false</enforceReason>\n" +
" </Logic>\n" +
" <Url>\n" +
" <finishURL>http://localhost:64956/retrieveSignedFile.aspx</finishURL>\n" +
" </Url>\n" +
" <Document>\n" +
" <fileID>1234567890</fileID>\n" +
" <contentType>pdf</contentType>\n" +
" <content>";
private static final String POSTCONTENT = "</content>\n" +
" </Document>\n" +
"</request>";
private static final String POST_URL = "https://webagentdev.arx.com/Sign/UploadFileToSign";
private static final String PULL_URL = "https://webagentdev.arx.com/Sign/DownloadSignedFileG";
public static final int TIMEOUT = 300000;
public static void main(String[] args) throws Exception {
InputStream is = new FileInputStream(INPUT);
String content = PRECONTENT + new String(Base64.encodeBase64(loadResource(is)), "UTF-8") + POSTCONTENT;
System.out.println(content);
String reply = new String(sendDocForProcessing(URLEncoder.encode(content, "UTF-8")));
System.out.println(reply);
System.out.println("DONE");
}
private static String sendDocForProcessing(String content) throws Exception {
HttpClient client = null;
HttpMethodBase method = null;
SimpleHttpConnectionManager mgr = new SimpleHttpConnectionManager();
String reply = "";
try {
mgr.getParams().setConnectionTimeout(TIMEOUT);
mgr.getParams().setSoTimeout(TIMEOUT);
client = new HttpClient(mgr);
method = new PostMethod(POST_URL);
method.getParams().setParameter(HttpMethodParams.RETRY_HANDLER, new DefaultHttpMethodRetryHandler(1, false));
method.getParams().setParameter("http.socket.timeout", TIMEOUT);
client.getHttpConnectionManager().getParams().setConnectionTimeout(TIMEOUT);
client.getParams().setCookiePolicy(CookiePolicy.BROWSER_COMPATIBILITY);
method.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
method.getParams().setParameter("inputXML", content);
client.executeMethod(method);
reply = new String(method.getResponseBody());
} catch (Exception e) {
e.printStackTrace();
} finally {
if(method != null) {
method.releaseConnection();
}
client = null;
mgr.shutdown();
}
if (isSigningSuccessful(reply)) {
return reply;
} else {
throw new Exception("Failed in signing the document. Error: " + reply);
}
}
private static boolean isSigningSuccessful(String reply) throws ParserConfigurationException, IOException, SAXException {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.parse(new ByteArrayInputStream(reply.getBytes()));
Element elem = doc.getDocumentElement();
String type = elem.getAttribute("type");
return !"error".equals(type);
}
public static byte[] loadResource(InputStream in) {
if (in == null) {
return new byte[0];
}
try {
int indice, tempIndice;
byte[] tempArr;
byte[] mainArr = new byte[0];
byte[] byteArr = new byte[65535];
for (indice = 0; (indice = in.read(byteArr)) > 0;) {
tempIndice = mainArr.length + indice;
tempArr = new byte[tempIndice];
System.arraycopy(mainArr, 0, tempArr, 0, mainArr.length);
System.arraycopy(byteArr, 0, tempArr, mainArr.length, indice);
mainArr = tempArr;
}
in.close();
return mainArr;
} catch (Exception e) {
e.printStackTrace();
}
return new byte[0];
}
}
The XML elements are case sensitive and must be passed as shown in the documentation (e.g. Document instead of document, Auth instead of auth and so on). In addition, your XML request is missing the finishURL parameter which is mandatory.
Also note that some parameters in your XML request are obsolete. See the updated request parameter list in the link above. A sample XML is available here.
Thanks for adding your Java code. Note that the HttpClient instance is configured incorrectly and as a result the http-post request is sent empty. Take a look at the modifications I did in your sendDocForProcessing function in order to properly post the XML content:
private static String sendDocForProcessing(String content) throws Exception {
HttpClient client = null;
PostMethod method = null;
String reply = "";
try {
client = new HttpClient();
method = new PostMethod(POST_URL);
method.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
NameValuePair[] data = { new NameValuePair("inputXML", content) };
method.setRequestBody(data);
client.executeMethod(method);
reply = method.getResponseBodyAsString();
} catch (Exception e) {
e.printStackTrace();
} finally {
if(method != null) {
method.releaseConnection();
}
}
if (isSigningSuccessful(reply)) {
return reply;
} else {
throw new Exception("Failed in signing the document. Error: " + reply);
}
}
The content passed to the above function should not be URL-encoded as it is already done by the HttpClient library.
In addition, when analyzing the response, I suggest you to check the value of the returnCode element rather than the type property. The response is always of type 'error'.
Also note that the function name isSigningSuccessful is misleading as this stage is still prior to the act of signing.