I'm working with an organization's payment API. The API automatically posts a soap request to our server when a customer makes payment and I response with an acknowledgement message in xml. (Check out the screenshots show a simple demonstration in SOAP UI)
SOAP UI Test Response
SOAP UI Test Raw XML
I made this code in Java to receive the soap request and send a response.
`public class testsoap extends HttpServlet {
protected void processRequest(HttpServletRequest request,
HttpServletResponse response)throws ServletException, IOException {
response.setContentType("text/xml;charset=UTF-8");
ServletInputStream out = request.getInputStream();
String xmlrpc = "";
int c = 0;
while((c = out.read()) != -1 ){ xmlrpc += (char)c; }
int startTag = xmlrpc.indexOf("<TransID>");
int endTag = xmlrpc.indexOf("</TransID>");
String parameter = xmlrpc.substring(startTag,endTag).replaceAll("<TransID>","");
String result="";
//result +="<?xml version=\"1.0\"?>\n";
result +="<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:c2b=\"http://cps.huawei.com/cpsinterface/c2bpayment\">\n";
result +="<soapenv:Header/>\n";
result +="<soapenv:Body>\n";
result +="<c2b:C2BPaymentConfirmationResult>C2B Payment Transaction "+parameter+" result received.</c2b:C2BPaymentConfirmationResult>\n";
result +="</soapenv:Body>\n";
result +="</soapenv:Envelope>\n";
response.getWriter().println(result);
}
}`
Now I need to add the location of my keystore and truststore.
Should I add this code just before I start preparing a response?
` System.setProperty("javax.net.ssl.keyStore",path_to_keystore);
System.setProperty("javax.net.ssl.keyStorePassword",akeystorepassword);
System.setProperty("javax.net.ssl.trustStore",path_to_your_cacerts_file);
System.setProperty("javax.net.ssl.trustStorePassword",atrustsorepassword)`
Or do I need to make a snippet that makes secure connection using the keystore and truststore rather than just setting a system property?
Create a Java class and write all the functionalities that you need to publish as a methods. Then you need to publish those functionalities as a WSDL to be consumed by your clients. See the following tutorial that will take you in step by step to publish a web services:
Step by Step JAX-WS Web Services with Eclipse, TomEE, and Apache CXF
Building a Simple Web Service ? A Tutorial
Implementing a simple web service
Further based on your requirements you can have complex object as an input parameter like C2BPaumentConfirmationRequest and KYCInfo in your case
Related
I am working on a web-service which is asynchronous. In my client code, I am using a boto3 session client to call a GET API of my Jetty Server which is S3 alike service. GET API fetched original data from S3 and modifies the request so as to be able to forward the request to flask server. Python flask then get the request processed (where data transformation is done) and calls the POST API of the Jetty Server.
Now I am stuck at figuring out how can I respond to the original caller? Because I am not sure if a API request can have a session-id to identify the original caller?
How can my POST API respond back to client? Following is the overall conceptualization of what I am trying to achieve. How can I do it?
Since I am using embedded Jetty, I used the built-in org.eclipse.jetty.server.HttpChannel.Listener.
I now have access to the raw internal Jetty org.eclipse.jetty.server.Request object that has the HTTP Fields for that request.
To use it, I'll create an instance of that HttpChannel.Listener, and add it as a bean to my connectors.
public class RequestChannelListener implements HttpChannel.Listener {
#Override
public void onRequestBegin(Request request) {
HttpFields.Mutable replacement = HttpFields.build(request.getHttpFields())
.put("X-Request-ID", UUID.randomUUID().toString().toUpperCase());
request.setHttpFields(replacement);
}
}
Add as a bean in the connector -
RequestChannelListener channelListener = new RequestChannelListener();
connector.addBean(channelListener);
Then all other access of that request, be it internal components of Jetty, a webapp, a specific servlet, filters, forwarding, includes, error handling in the servlet spec, error handling outside of a servlet context, etc can all see it.
To check if the custom header got added into the request or not -
Enumeration<String> headerNames = request.getHeaderNames();
while(headerNames.hasMoreElements()) {
String headerName = headerNames.nextElement();
System.out.println("Header Name - " + headerName + ", Value - " + request.getHeader(headerName));
}
We're working on a new web application to replace an old one that is commonly being used by our company. They both will ultimately have the exact same API. What we'd like to do initially is test out the new functionality by having clients send requests to the new web app, but then have our new web app propagate their requests to the old web app and send the old web app's http responses back to our clients (so that from client point of view, nothing has changed).
What I'd like to do is get the exact HttpServletResponse object that we're getting back from the old web app and send that back to clients of the new web app. What is the best way of performing this? I know that once I can retrieve the HttpServletResponse, I can just set it equal to the one we have as a parameter to our functions (API handlers) in our new web app's REST controller, but I'm having trouble retrieving it.
Is there any way of retrieving the HttpServlet response via Spring's RestTemplate?
You can use org.apache.commons.httpclient.HttpClient
It has public int executeMethod(HttpMethod method) where you can define request and after checking status is success
you can use all the HttpMethod methods.
InputStream getResponseBodyAsStream()
Header[] getResponseHeaders();
and pass to your response. Like this
public static void main(String[] args) throws Exception {
HttpClient client = new HttpClient();
GetMethod method = new GetMethod("http://The old service url");
method.addRequestHeader("X-Username", "user");
method.addRequestHeader("X-Password", "password");
// Execute the HTTP GET request
int status = client.executeMethod(method);
if(status == 200){
InputStream in = method.getResponseBodyAsStream();
OutputStream out=the out of your response
IOUtils.copy(in, out);
in.close();
out.flush();
out.close();
}
}
I am trying to implement a web application using play framework as a replacement for old http server implementation that is interfacing the old non-browser legacy http client.
That client is written in Delphi and it is posting data directly in the body of a request with some header information about it.
I thought I would get something in
request.body /* In the play controller */
but nothing is there.
See the code below:
public static void uploadPicture() {
InputStream data = request.body;
String fx = Play.getFile("").getAbsolutePath()+File.separator+"uploads"+File.separator+"test.jpg";
File f = new File(fx);
FileOutputStream moveTo = new FileOutputStream(fx);
try {
byte[] b = new byte[4096];
for (int x = 0; (data.read(b)) != -1;){
moveTo.write(b, 0, x);
}
} finally{
moveTo.close();
}
}
EDIT:
To clarify my point : I went and I created a simple Dynamic Web Project in eclipse HttpServlet and in doPost() method when I get the request.getInputStream() it contains the file that is sent from the legacy client.
Play is doing something to the body of the request!?
What are my options?
Thanks.
Irfan
Ok, it was a bug in Play 1.2.4. I installed latest version 1.2.5 and everything works out of the box.
You can access raw body of a request in request.body in the controller.
I use Netbeans to generate web service client code, client-style JAX-WS, so i can invoke a web service API.
However, when I invoke the web service API, I get the exception:
com.sun.xml.internal.ws.client.ClientTransportException: The server sent HTTP status code 307: Temporary Redirect
Why do I get this? What is the workaround? I know the problem isn't with the web service itself, because I can get responses fine via soapUI and .Net.
Faced the same problem about a month ago.
Web service client classes were generated using Apache CXF and web service returned HTTP
status 307, which led to the same exception.
Invocation of the same web service method using soapUI with property Follow Redirects set to true was successful and returned needed data.
After googling awhile, it looked like there is no property to enable following redirects in the JAX-WS for this.
So, below is the code which is currently working, though I'm not sure it is compliant with any standards:
Supposing generated client classes looks like:
// generated service class
public class MyWebServiceClient extends javax.xml.ws.Service {
// ...
private final QName portName = "...";
// ...
public RetrieveMyObjects getRetrieveMyObjects() {
return super.getPort(portName, RetrieveMyObject.class);
}
// ...
}
// generated port interface
// annotations here
public interface RetrieveMyObjects {
// annotations here
List<MyObject> getAll();
}
Now, upon executing following code:
MyWebServiceClient wsClient = new MyWebServiceClient("wsdl/location/url/here.wsdl");
RetrieveMyObjectsPort retrieveMyObjectsPort = wsClient.getRetrieveMyObjects();
wsClient should return instance which is both instance of RetrieveMyObjects & javax.xml.ws.BindingProvider interfaces. It is not stated anywhere on the surface of JAX-WS, but it seems that a lot of code is based on that fact. One can re-assure him\herself by executing something like:
if(!(retrieveMyObjectsPort instanceof javax.xml.ws.BindingProvider)) {
throw new RuntimeException("retrieveMyObjectsPort is not instance of " + BindingProvider.class + ". Redirect following as well as authentication is not possible");
}
Now, when we are sure that retrieveMyObjectsPort is instance of javax.xml.ws.BindingProvider we can send plain HTTP POST request to it, simulating SOAP request (though it looks incredibly incorrect & ugly, but this works in my case and I didn't find anything better while googling) and check whether web service will send redirect status as a response:
// defined somewhere before
private static void checkRedirect(final Logger logger, final BindingProvider bindingProvider) {
try {
final URL url = new URL((String) bindingProvider.getRequestContext().get(ENDPOINT_ADDRESS_PROPERTY));
logger.trace("Checking WS redirect: sending plain POST request to {}", url);
final HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setInstanceFollowRedirects(true);
connection.setRequestMethod("POST");
connection.setRequestProperty("Content-Type", "text/html; charset='UTF-8'");
connection.setDoOutput(true);
if(connection.getResponseCode() == 307) {
final String redirectToUrl = connection.getHeaderField("location");
logger.trace("Checking WS redirect: setting new endpoint url, plain POST request was redirected with status {} to {}", connection.getResponseCode(), redirectToUrl);
bindingProvider.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, redirectToUrl);
}
} catch(final Exception e) {
logger.warn("Checking WS redirect: failed", e);
}
}
// somewhere at the application start
checkRedirect(logger, (BindingProvider) retrieveMyObjectsPort);
Now, what this method does is: it takes BindingProvider.ENDPOINT_ACCESS_PROPERTY of retrieveMyObjectsPort i.e. the url to which this port method will be sending SOAP requests and sends plain HTTP POST request as described above. Then it checks whether response status is 307 - Temporary Redirect (other statuses like 302 or 301 may also be included) and if it is, gets the URL to which web service is redirecting and sets new endpoint for the specified port.
In my case this checkRedirect method is called once for each web service port interface and then everything seems to work fine:
Redirect is checked on url like http://example.com:50678/restOfUrl
Web service redirects to url like https://example.com:43578/restOfUrl (please note that web service client authentication is present) - endpoint of a port is set to that url
Next web service requests executed via that port are successful
Disclaimer: I'm quite new to webservices and this is what I managed to achieve due to the lack of solutions for this questions, so please correct me if something is wrong here.
Hope this helps
Yes I know this post is old, but I've had similar errors, and thought maybe somebody would benefit from my solution.
the one that plagued me the most was:
com.sun.xml.ws.client.ClientTransportException: The server sent HTTP status code 200: OK
Which turns out to mean an incomplete response header. Apparently jax-ws does some kind of validation that includes validating the HTTP headers as well. And the server I was using was just sending an empty header.
It worked like a charm after adding 'application/soap+xml' to the Content-Type header.
I've created a Java web service using the #WebService annotation and deployed the service to a Glassfish server that lives behind a proxy server.
The problem is when someone accesses our WSDL and looks at the service endpoint address location they see
http://app server url/service...
instead of
http://proxy server url/service...
I'd like to have the proxy server URL returned in the service endpoint address location of the WSDL instead of the app server url.
What I'm wondering is, can I write a Filter or a Listener to listen for requests to the WSDL, then modify the WSDL service endpoint address with the proxy server URL? What might be the recommended way to do this - I was thinking a Filter is the best choice, but wasn't 100% sure?
BTW - I thought there might be a simple setting in Glassfish to do this, but I haven't been able to locate one that works.
I took the filter approach and here's what I came up with. It seems to work.
I've included the code for my Filter's doFilter method. I also wrote a custom HttpServletResponseWrapper, but that implementation was pretty straightforward.
the Apache reverse-proxy adds the x-forwarded-host value to the HTTP header (and is the name I use to replace the app servers name). Another alternative I thought of was to place the proxy server's address as a property on the app server and grab that. Ultimately, I thought this was a cleaner way to go.
/*
* Replace the server URL with the proxy server URL when called from a proxy server
*/
#Override
public void doFilter(
ServletRequest request,
ServletResponse response,
FilterChain filterChain) throws IOException, ServletException
{
WsdlResponseWrapper myResponse = new WsdlResponseWrapper((HttpServletResponse) response);
filterChain.doFilter(request, myResponse);
boolean isResponseOutputStream = myResponse.getOutputStreamContent().length > 0;
/*
* The servlet response sent into this method only allows access to
* getOutputStream or getWriter, not both. It will throw an
* exception if an attempt is made to to access both.
*
* If this reason, I'm checking the output stream first and writing
* that content if it exists, then printing to the writer only if
* the output stream is empty.
*/
StringBuffer responseBuffer;
if (isResponseOutputStream) {
responseBuffer = new StringBuffer(new String(myResponse.getOutputStreamContent()));
} else {
responseBuffer = new StringBuffer(myResponse.getWriterContent());
}
// Change the URL when called from a proxy server
if (request instanceof HttpServletRequest) {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
String requestedHostname = httpServletRequest.getHeader("x-forwarded-host");
if ((null != requestedHostname) && !requestedHostname.isEmpty()) {
String myHostname = httpServletRequest.getHeader("host");
int myPort = httpServletRequest.getLocalPort();
// change the hostname
int index = responseBuffer.indexOf(myHostname);
int length = myHostname.length();
while (index > -1) {
responseBuffer.replace(index, index+length, requestedHostname);
index = responseBuffer.indexOf(myHostname);
}
// remove the port
String portString = ":" + myPort;
length = portString.length();
index = responseBuffer.indexOf(portString);
while (index > -1) {
responseBuffer.replace(index, index+length, "");
index = responseBuffer.indexOf(portString);
}
}
}
// forward the response
if (isResponseOutputStream) {
response.getOutputStream().write(responseBuffer.toString().getBytes());
} else {
response.getWriter().print(responseBuffer);
}
}
From Javaboutique article on servlet-filters
public final class TimerFilter implements Filter
{
public void doFilter(ServletRequest request,
ServletResponse response,
FilterChain chain)
throws IOException, ServletException
{
long startTime = System.currentTimeMillis();
chain.doFilter(request, response);
long stopTime = System.currentTimeMillis();
System.out.println("Time to execute request: " + (stopTime - startTime) +
" milliseconds");
}
...
the call to chain.doFilter will call the normal servlet in your case (I'm assuming the #WebService is provided through a servlet API), and you can then read the result from the original web service, modify it, and pass it back to your own client.
Did you consider using a web service registry for that?
The glassfish doc about web service registry says:
If you are using a load balancer,
enter the Load Balancer host name,
port number, and the SSL port number.
If you are publishing the web service
to an external registry, where the
WSDL can be found over the internet,
these options will replace the
hostname and port name specified in
the WSDL to the one of the load
balancer.
So that should work for your proxy as well.
Here are two resources that might be interesting: the glassfish ws management page, an article aobut how to set up the registry.
I never used the registry myself, and agree that it's a bit heavy-weighted for what you want to achieve, but it seems to be a possible way to publish web service endpoint information for consumers.
If Apache is your proxy, you can try doing what I did, which is really simple. Apache causes the problem (sort-of), so use Apache to fix it:
https://stackoverflow.com/a/18136594/2666055