Java Paypal Integration SOAP JAX-WS - SSL Handshakeexception - java

This question is related to the integration of PayPal API with Java using SOAP request/response model.
The following method consist in establish the request parameters and return the response string, in this case the token. I watch several examples of how suppose to do the request call, but those methods were created for less versions (this version is "109.0", the other examples I saw were 80, 60 even 40).
Anyway, I downloaded the .wsdl file from PayPal, created the client java classes with SOAPUI and JAX-WS web services style, saved the project and opened with MyEclipse. I imported the PayPal certificate on my Java TomCat Server aswell, using keytool import on cacerts file of java.
The following method suppose to be the request method to return the String value:
public String setExpressCheckout(String returnURL, String cancelURL) throws ErrorGeneral {
PayPalAPIInterfaceService pp = new PayPalAPIInterfaceService();
UserIdPasswordType login = new UserIdPasswordType();
login.setUsername("carlos.martinez_api1.netijam.com");
login.setPassword("1389974315");
login.setSignature("AFcWxV21C7fd0v3bYYYRCpSSRl31AI-ujedgZR8zf1CorgeJpph2tssY");
String token = "";
String ackString;
// following class is generated by wsdl2java utility Service class
final PayPalAPIAAInterface expressCheckoutPort = pp.getPayPalAPIAA();
final Binding binding = ((BindingProvider) expressCheckoutPort).getBinding();
List<Handler> handlersList = new ArrayList<Handler>();
// now, adding instance of Handler to handlersList which should do our job:
// creating header instance
CustomSecurityHeaderType headerObj = new CustomSecurityHeaderType();
UserIdPasswordType credentials = new UserIdPasswordType();
credentials.setUsername("carlos.martinez_api1.netijam.com");
credentials.setPassword("1389974315");
credentials.setSignature("AFcWxV21C7fd0v3bYYYRCpSSRl31AI-ujedgZR8zf1CorgeJpph2tssY");
headerObj.setCredentials(credentials);
ObjectFactory objectFactory = new ObjectFactory();
// creating JAXBElement from headerObj
final JAXBElement<CustomSecurityHeaderType> requesterCredentials = objectFactory.createRequesterCredentials(headerObj);
handlersList.add(new SOAPHandler<SOAPMessageContext>() {
#Override
public boolean handleMessage(final SOAPMessageContext context) {
try {
// checking whether handled message is outbound one as per Martin Strauss answer
final Boolean outbound = (Boolean) context.get("javax.xml.ws.handler.message.outbound");
if (outbound != null && outbound) {
// obtaining marshaller which should marshal instance to xml
final Marshaller marshaller = JAXBContext.newInstance(CustomSecurityHeaderType.class).createMarshaller();
// adding header because otherwise it's null
final SOAPHeader soapHeader = context.getMessage().getSOAPPart().getEnvelope().addHeader();
// marshalling instance (appending) to SOAP header's xml node
marshaller.marshal(requesterCredentials, soapHeader);
}
} catch (final Exception e) {
throw new RuntimeException(e);
}
return true;
}
#Override
public boolean handleFault(SOAPMessageContext context) {
// TODO Auto-generated method stub
return false;
}
#Override
public void close(MessageContext context) {
// TODO Auto-generated method stub
}
#Override
public Set<QName> getHeaders() {
// TODO Auto-generated method stub
return null;
}
// ... default implementations of other methods go here
});
// as per Jean-Bernard Pellerin's comment setting handlerChain list here, after all handlers were added to list
binding.setHandlerChain(handlersList);
try {
SetExpressCheckoutReq sECR = new SetExpressCheckoutReq();
SetExpressCheckoutRequestType sECRDT = new SetExpressCheckoutRequestType();
sECRDT.setVersion("109.0");
SetExpressCheckoutRequestDetailsType details = new SetExpressCheckoutRequestDetailsType();
PaymentDetailsType paymentDetails = new PaymentDetailsType();
paymentDetails.setOrderDescription("Integrating Stuff Test Order");
paymentDetails.setInvoiceID("INVOICE-" + Math.random());
BasicAmountType orderTotal = new BasicAmountType();
orderTotal.setCurrencyID(CurrencyCodeType.EUR);
orderTotal.setValue("120.00");
paymentDetails.setOrderTotal(orderTotal);
paymentDetails.setPaymentAction(PaymentActionCodeType.SALE);
List<PaymentDetailsType> listaDetallesPago = new ArrayList<PaymentDetailsType>();
listaDetallesPago.add(paymentDetails);
details.setPaymentDetails(listaDetallesPago);
details.setReturnURL(returnURL);
details.setCancelURL(cancelURL);
sECRDT.setSetExpressCheckoutRequestDetails(details);
sECR.setSetExpressCheckoutRequest(sECRDT);
SetExpressCheckoutResponseType response = expressCheckoutPort.setExpressCheckout(sECR);
ackString = response.getAck().value();
System.out.println(ackString);
token = response.getToken();
for (ErrorType msg : response.getErrors()) {
System.out.println(msg.getLongMessage());
}
} catch (Exception ex) {
System.out.println(ex.getMessage());
}
// get the token from the response
return token;
}
And this is the error that I receive:
javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
at sun.security.ssl.Alerts.getSSLException(Alerts.java:192)
at sun.security.ssl.Alerts.getSSLException(Alerts.java:154)
at sun.security.ssl.SSLSocketImpl.recvAlert(SSLSocketImpl.java:1959)
at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1077)
at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1312)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1339)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1323)
at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:563)
at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:185)
at sun.net.www.protocol.http.HttpURLConnection.getOutputStream(HttpURLConnection.java:1091)
at sun.net.www.protocol.https.HttpsURLConnectionImpl.getOutputStream(HttpsURLConnectionImpl.java:250)
at com.sun.xml.ws.transport.http.client.HttpClientTransport.getOutput(HttpClientTransport.java:92)
at com.sun.xml.ws.transport.http.client.HttpTransportPipe.process(HttpTransportPipe.java:110)
at com.sun.xml.ws.protocol.soap.ClientMUPipe.process(ClientMUPipe.java:72)
at com.sun.xml.ws.handler.HandlerPipe.process(HandlerPipe.java:134)
at com.sun.xml.ws.handler.HandlerPipe.process(HandlerPipe.java:134)
at com.sun.xml.ws.client.Stub.process(Stub.java:125)
at com.sun.xml.ws.client.sei.SEIStub.doProcess(SEIStub.java:127)
at com.sun.xml.ws.client.sei.SyncMethodHandler.invoke(SyncMethodHandler.java:238)
at com.sun.xml.ws.client.sei.SyncMethodHandler.invoke(SyncMethodHandler.java:212)
at com.sun.xml.ws.client.sei.SEIStub.invoke(SEIStub.java:110)
at com.sun.proxy.$Proxy39.setExpressCheckout(Unknown Source)
at capsula.SetExpressCheckoutService.setExpressCheckout(SetExpressCheckoutService.java:168)
at capsula.SetExpressCheckoutService.main(SetExpressCheckoutService.java:56)
I tried to search on every part of the web to find a solution but I cannot. I read a lot of examples from PayPal but It doesn't tell anything about new versions, because in old version there is an easy way to make the request including the credentials on the request and not doing that shitty part of:
ObjectFactory objectFactory = new ObjectFactory();
// creating JAXBElement from headerObj
final JAXBElement<CustomSecurityHeaderType> requesterCredentials = objectFactory.createRequesterCredentials(headerObj);
handlersList.add(new SOAPHandler<SOAPMessageContext>() {
#Override
public boolean handleMessage(final SOAPMessageContext context) {
try {
// checking whether handled message is outbound one as per Martin Strauss answer
final Boolean outbound = (Boolean) context.get("javax.xml.ws.handler.message.outbound");
if (outbound != null && outbound) {
// obtaining marshaller which should marshal instance to xml
final Marshaller marshaller = JAXBContext.newInstance(CustomSecurityHeaderType.class).createMarshaller();
// adding header because otherwise it's null
final SOAPHeader soapHeader = context.getMessage().getSOAPPart().getEnvelope().addHeader();
// marshalling instance (appending) to SOAP header's xml node
marshaller.marshal(requesterCredentials, soapHeader);
}
} catch (final Exception e) {
throw new RuntimeException(e);
}
return true;
}
#Override
public boolean handleFault(SOAPMessageContext context) {
// TODO Auto-generated method stub
return false;
}
#Override
public void close(MessageContext context) {
// TODO Auto-generated method stub
}
#Override
public Set<QName> getHeaders() {
// TODO Auto-generated method stub
return null;
}
// ... default implementations of other methods go here
});
// as per Jean-Bernard Pellerin's commententer code here setting handlerChain list here, after all handlers were added to list
binding.setHandlerChain(handlersList);
Because I'm not sure if this is settle correctly.
All help will be appreciated. Thanks! =)

The problem was in my own generated classes when I tried to call the webservice, I couldnt pass the CustomSecurityHeader and was because the class APIService wasn't generated correctly. For this case, and after an investigation I figured out what happened.
At first I was using soapUI program to autogenerate the classes sending the .wsdl file that Paypal gives to developers. But, the important thing that I didnt do it’s stablishing a custom argument necessary to do a correct generated code:
-XadditionalHeaders -Xnocompile
This was extracted for an other tutorial who use “wsimport” command (for Jax-WS library generated code) and the guy who uses this command added those arguments aswell. The complete line is. I could either use a Terminal Console command like this:
wsimport -keep -XadditionalHeaders -Xnocompile -p paypal.test Desktop/PayPalSvc.wsdl/
To generate the correct classes and pass the CustomSecurityHeader.
My present code to do a correct request is:
String usuario = "carlos.martinez_api1.netijam.com";
String password = "XEDdsafXSCE4";
String firma = "A6aFJz-KhDsdf7f-668iCsdfweFplAcbDlof-vWP5wsdfsdEAy9T-d.";
//Make encapsulated object of my own class cabeceraPeticiones
CabeceraPeticiones cabeceraPeticiones = CabeceraPeticiones.getInstanceCabecera(usuario, password, firma);
//Call the method that takes the header in the format that webservice needs
Holder<CustomSecurityHeaderType> cabeceraSeguridad = cabeceraPeticiones.getCabecera();
//Set the connection params to work properly and the webservice method that I want to execute
PayPalAPIInterfaceService pp = new PayPalAPIInterfaceService();
GetExpressCheckoutDetailsReq gECR = new GetExpressCheckoutDetailsReq();
GetExpressCheckoutDetailsRequestType gECDRT = new GetExpressCheckoutDetailsRequestType();
PayPalAPIAAInterface expressCheckoutPort = pp.getPayPalAPIAA();
((BindingProvider) expressCheckoutPort).getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, "https://api-aa-3t.sandbox.paypal.com/2.0/");
//I take the response from the webservice operation that I execute, passing the header and the webservice request param
GetExpressCheckoutDetailsResponseType response = expressCheckoutPort.getExpressCheckoutDetails(gECR, cabeceraSeguridad);

Related

How to add trusted certs for org.apache.rahas.client.STSClient rather updating truststore at JVM level

I am currently working on an client program to access the soap webservice. The webservice is hit via WSO2 Identity Server (IS) Security Token Service (STS). I have coded using org.apache.rahas.client.STSClient. I am looking for a solution to add trusted certificates for STSClient rather than updating the trusted entries at JVM level, because post this webservice call, I have functionality which will need access to default cacerts. Kindly help me with the solution.
Basically I am trying to replace the below two lines
System.setProperty("javax.net.ssl.trustStore", keystorePath);
System.setProperty("javax.net.ssl.trustStorePassword", keystorePwd);
I explored setting up stsClient.setCryptoInfo() with the truststore details, but I didn't get enough help from internet to understand what it does.
I tried exploring how set custom ssl for the stsclient, but couldn't figure out that also.
public class Client {
public void initialize() {
try {
loadConfigurations();
// set the trust store as a system property for communication over
// TLS.
System.setProperty("javax.net.ssl.trustStore", keystorePath);
System.setProperty("javax.net.ssl.trustStorePassword", keystorePwd);
// create configuration context
ConfigurationContext configCtx = ConfigurationContextFactory
.createConfigurationContextFromFileSystem(repoPath);
// create STS client
STSClient stsClient = new STSClient(configCtx);
stsClient.setRstTemplate(getRSTTemplate());
String action = null;
String responseTokenID = null;
action = TrustUtil.getActionValue(RahasConstants.VERSION_05_02,
RahasConstants.RST_ACTION_ISSUE);
stsClient.setAction(action);
// request the security token from STS.
Token responseToken;
Policy stsPolicy = loadPolicy(stsPolicyPath);
// add rampart config assertion to the ws-sec policies
RampartConfig rampartConfig = buildRampartConfig();
stsPolicy.addAssertion(rampartConfig);
responseToken = stsClient.requestSecurityToken(null, stsEPR, stsPolicy, relyingPartyEPR);
// store the obtained token in token store to be used in future
// communication.
TokenStorage store = TrustUtil.getTokenStore(configCtx);
responseTokenID = responseToken.getId();
store.add(responseToken);
// print token
System.out.println(responseToken.getToken().toString());
...
//Send the token to relying party
if (enableRelyingParty) {
/* Invoke secured service using the obtained token */
OMElement responseElem = null;
// create service client
ServiceClient serClient = new ServiceClient(configCtx, null);
// engage modules
serClient.engageModule("addressing");
serClient.engageModule("rampart");
// load policy of secured service
Policy sec_policy = loadPolicy(relyingPartyPolicyPath);
// add rampart config to the ws-sec policies
sec_policy.addAssertion(rampartConfig);
// set in/out security policies in client opts
serClient.getOptions().setProperty(RampartMessageData.KEY_RAMPART_POLICY,
sec_policy);
// Set the token id as a property in the Axis2 client scope, so that
// this will be picked up when creating the secure message to invoke
// the endpoint.
serClient.getOptions().setProperty(RampartMessageData.KEY_CUSTOM_ISSUED_TOKEN,
responseTokenID);
// set action of the Hello Service to be invoked.
serClient.getOptions().setAction("urn:echoString");
serClient.getOptions().setTo(new EndpointReference(relyingPartyEPR));
// invoke the service
responseElem = serClient.sendReceive(getPayload(echoRequestMsg));
// cleanup transports
serClient.getOptions().setCallTransportCleanup(true);
System.out.println(responseElem.toString());
System.exit(0);
}
} catch (IOException e) {
e.printStackTrace();
} catch (TrustException e) {
e.printStackTrace();
} catch (XMLStreamException e) {
e.printStackTrace();
}
}
private OMElement getRSTTemplate() throws TrustException {
OMFactory omFac = OMAbstractFactory.getOMFactory();
OMElement element = omFac.createOMElement(SP11Constants.REQUEST_SECURITY_TOKEN_TEMPLATE);
if (ClientConstants.SAML_TOKEN_TYPE_20.equals(tokenType)) {
TrustUtil.createTokenTypeElement(RahasConstants.VERSION_05_02, element).setText(
RahasConstants.TOK_TYPE_SAML_20);
} else if (ClientConstants.SAML_TOKEN_TYPE_11.equals(tokenType)) {
TrustUtil.createTokenTypeElement(RahasConstants.VERSION_05_02, element).setText(
RahasConstants.TOK_TYPE_SAML_10);
}
if (ClientConstants.SUBJECT_CONFIRMATION_BEARER.equals(subjectConfirmationMethod)) {
TrustUtil.createKeyTypeElement(RahasConstants.VERSION_05_02, element,
RahasConstants.KEY_TYPE_BEARER);
} else if (ClientConstants.SUBJECT_CONFIRMATION_HOLDER_OF_KEY
.equals(subjectConfirmationMethod)) {
TrustUtil.createKeyTypeElement(RahasConstants.VERSION_05_02, element,
RahasConstants.KEY_TYPE_SYMM_KEY);
}
// request claims in the token.
OMElement claimElement = TrustUtil.createClaims(RahasConstants.VERSION_05_02, element,claimDialect);
// Populate the <Claims/> element with the <ClaimType/> elements
addClaimType(claimElement, claimUris);
return element;
}
private void addClaimType(OMElement parent, String[] claimUris) {
OMElement element = null;
// For each and every claim uri, create an <ClaimType/> elem
for (String attr : claimUris) {
element = parent.getOMFactory()
.createOMElement(
new QName("http://schemas.xmlsoap.org/ws/2005/05/identity",
"ClaimType", "wsid"), parent);
element.addAttribute(parent.getOMFactory().createOMAttribute("Uri", null, attr));
}
}
private Policy loadPolicy(String policyPath) throws XMLStreamException, FileNotFoundException {
StAXOMBuilder omBuilder = new StAXOMBuilder(policyPath);
return PolicyEngine.getPolicy(omBuilder.getDocumentElement());
}
private RampartConfig buildRampartConfig() {
RampartConfig rampartConfig = new RampartConfig();
rampartConfig.setUser(username);
rampartConfig.setEncryptionUser(encryptionUser);
rampartConfig.setUserCertAlias(userCertAlias);
rampartConfig.setPwCbClass(pwdCallbackClass);
Properties cryptoProperties = new Properties();
cryptoProperties.put("org.apache.ws.security.crypto.merlin.keystore.type", "JKS");
cryptoProperties.put("org.apache.ws.security.crypto.merlin.file", keystorePath);
cryptoProperties
.put("org.apache.ws.security.crypto.merlin.keystore.password", keystorePwd);
CryptoConfig cryptoConfig = new CryptoConfig();
cryptoConfig.setProvider("org.apache.ws.security.components.crypto.Merlin");
cryptoConfig.setProp(cryptoProperties);
rampartConfig.setEncrCryptoConfig(cryptoConfig);
rampartConfig.setSigCryptoConfig(cryptoConfig);
return rampartConfig;
}
private OMElement getPayload(String value) {
OMFactory factory = null;
OMNamespace ns = null;
OMElement elem = null;
OMElement childElem = null;
factory = OMAbstractFactory.getOMFactory();
ns = factory.createOMNamespace("http://echo.services.core.carbon.wso2.org", "ns");
elem = factory.createOMElement("echoString", ns);
childElem = factory.createOMElement("in", null);
childElem.setText(value);
elem.addChild(childElem);
return elem;
}
...
}
PasswordCBHandler.java is used by the underlying Rampart module to get the password of the key alias which is used to sign the request.
public class PasswordCBHandler implements CallbackHandler{
...
public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
readUsernamePasswordFromProperties();
WSPasswordCallback pwcb = (WSPasswordCallback) callbacks[0];
String id = pwcb.getIdentifier();
int usage = pwcb.getUsage();
if (usage == WSPasswordCallback.USERNAME_TOKEN) {
if (username.equals(id)) {
pwcb.setPassword(password);
}
} else if (usage == WSPasswordCallback.SIGNATURE || usage == WSPasswordCallback.DECRYPT) {
if (keyAlias.equals(id)) {
pwcb.setPassword(keyPassword);
}
}
}
...
}

How to send a SOAP request using WebServiceTemplate?

I am trying to send a request to a SOAP webservice. I read this tutorial and prepared the following code. However, I am going to send different requests to multiple SOAP webservices, whereas the tutorial focused on one request. How can I send SOAP request using WebserviceTemplate?
WebServiceTemplate
SoapMessage soapMsg = new SoapMessage();
soapMsg.setUsername("Requester");
soapMsg.setPassword("Pass");
soapMsg.setLanguageCode("EN");
Request request = new Request();
request.setDeparture("FDH");
request.setDestination("HAM");
Date date = new Date();
SimpleDateFormat frm2 = new SimpleDateFormat("yyyy-MM-dd");
request.setDepartureDate(frm2.parse(frm2.format(date)));
request.setNumADT(1);
request.setNumCHD(0);
request.setNumInf(0);
request.setCurrencyCode("EUR");
request.setWaitForResult(true);
request.setNearByDepartures(true);
request.setNearByDestinations(true);
request.setRronly(false);
request.setMetaSearch(false);
soapMsg.setRequest(request);
WebServiceTemplate webServiceTemplate = new WebServiceTemplate(). //how to create object and send request!
Object response = webServiceTemplate.marshalSendAndReceive(
"https://aaa5.elsyarres.net", soapMsg);
Response msg = (Response) response;
System.err.println("size of results of wogolo:"
+ msg.getFlights().getFlight().size());
You can use following code, you do not need to define anything in xml file.
try {
SaajSoapMessageFactory messageFactory = new SaajSoapMessageFactory(
MessageFactory.newInstance());
messageFactory.afterPropertiesSet();
WebServiceTemplate webServiceTemplate = new WebServiceTemplate(
messageFactory);
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
marshaller.setContextPath("PACKAGE");
marshaller.afterPropertiesSet();
webServiceTemplate.setMarshaller(marshaller);
webServiceTemplate.afterPropertiesSet();
Response response = (Response) webServiceTemplate
.marshalSendAndReceive(
"address",
searchFlights);
Response msg = (Response) response;
} catch (Exception s) {
s.printStackTrace();
}
To send different SOAP requests to different SOAP services, you just need to make your WebServiceTemplate aware of all requests and responses it will have to process.
Create a Java class for each request and response like so:
package models;
import javax.xml.bind.annotation.XmlRootElement;
import java.io.Serializable;
#XmlRootElement
public class FlyRequest implements Serializable {
private boolean nearByDeparture;
public FlyRequest() {}
public boolean isNearByDeparture() {
return nearByDeparture;
}
public void setNearByDeparture(boolean nearByDeparture) {
this.nearByDeparture = nearByDeparture;
}
}
(The #XmlRootElement is because we use JAXB marshaller below; see Jaxb reference for more info).
The setup of the template is done for example like so:
SaajSoapMessageFactory messageFactory = new SaajSoapMessageFactory(MessageFactory.newInstance());
messageFactory.afterPropertiesSet();
WebServiceTemplate webServiceTemplate = new WebServiceTemplate(messageFactory);
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
marshaller.setContextPath("models");
marshaller.afterPropertiesSet();
webServiceTemplate.setMarshaller(marshaller);
webServiceTemplate.afterPropertiesSet();
"models" is the name of the package where the Request/Responses classes are, so that jaxb can find them.
Then you just instantiate the request of the class you want to perform the call, like so:
// call fly service:
FlyRequest flyRequest = new FlyRequest();
flyRequest.setNearByDeparture(false);
Object flyResponse = webServiceTemplate.marshalSendAndReceive("https://example.net/fly", flyRequest);
// call purchase service:
PurchaseRequest purchaseRequest = new PurchaseRequest();
purchaseRequest.setPrice(100);
Object purchaseResponse = webServiceTemplate.marshalSendAndReceive("https://example.net/purchase", purchaseRequest);
Similarly, you can cast the response objects into your JAXB classes defined above.
Here is an Example what you should be looking for
Soap has a lot of restriction unlike REST, It follows some standards which have to be meet before you get Network call to work,
But unlike Rest, in Soap if you have WSDL URL you can get all the information needed to call the Soap call
private final String NAMESPACE = "http://www.w3schools.com/webservices/";
private final String URL = "http://www.w3schools.com/webservices/tempconvert.asmx?WSDL";
private final String SOAP_ACTION = "http://www.w3schools.com/webservices/CelsiusToFahrenheit";
private final String METHOD_NAME = "CelsiusToFahrenheit";
this code was written in Android so you can ignore some part of it but I still kept it in answer so someone from android background can put a good use to it
Open [WSDL][1] in browser and check for the things which matter to call a remote method on server.
1
you will see an attribute targetNamespace whose value would be Namespace which you will use in this case Namespace is http://www.w3schools.com/webservices/
2
Now you require the name of the method this WSDL has four method each of the are int attribute s:element with the value is the name of the Method in this case four methods are FahrenheitToCelsius, FahrenheitToCelsiusResponse, CelsiusToFahrenheit, CelsiusToFahrenheitResponse
3
Now you have to fure out the SOAP Action which is NAMESPACE+METHOD
but WSDL also gives information about that as well, look for the tag soap:operation and it's soapAction attribute havs the Soap action as it's value in this case which we want to call is http://www.w3schools.com/webservices/CelsiusToFahrenheit
private class MyTask extends AsyncTask<Void, Void, String> {
#Override
protected void onPreExecute() {
super.onPreExecute();
progressDialog.show();
}
#Override
protected String doInBackground(Void... params) {
try {
SoapObject soapObject = new SoapObject(NAMESPACE, METHOD_NAME);
soapObject.addProperty("Celsius","12");
SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);
envelope.dotNet = true;
envelope.setOutputSoapObject(soapObject);
HttpTransportSE httpTransportSE = new HttpTransportSE(URL);
httpTransportSE.call(SOAP_ACTION, envelope);
SoapPrimitive soapPrimitive = (SoapPrimitive)envelope.getResponse();
Log.d("TAG", "doInBackground: "+soapPrimitive.toString());
return soapObject.toString();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(String aVoid) {
super.onPostExecute(aVoid);
progressDialog.dismiss();
textView.setText(""+aVoid);
}
}
Assuming that your SoapMessage is marhsallable
To send the same message to multiple endpoints you only need to loop on the sending code and the request handler.
Something like this:
{
String endpoint = "https://aaa5.elsyarres.net"
WebServiceTemplate webServiceTemplate = new WebServiceTemplate().
webServiceTemplate.setDefaultUri(endpoint);
Object response = webServiceTemplate.marshalSendAndReceive(soapMsg);
// handle you are response as you are currently doing.
// Loop changing the endpoint as you need.
}
This code uses the Spring WebServiceTemplate
I tried many options and finally below one worked for me if you have to send soap header with authentication(Provided authentication object created by wsimport) and also need to set soapaction.
public Response callWebService(String url, Object request)
{
Response res = null;
log.info("The request object is " + request.toString());
try {
res = (Response) getWebServiceTemplate().marshalSendAndReceive(url, request,new WebServiceMessageCallback() {
#Override
public void doWithMessage(WebServiceMessage message) {
try {
// get the header from the SOAP message
SoapHeader soapHeader = ((SoapMessage) message).getSoapHeader();
// create the header element
ObjectFactory factory = new ObjectFactory();
Authentication auth =
factory.createAuthentication();
auth.setUser("****");
auth.setPassword("******");
((SoapMessage) message).setSoapAction(
"soapAction");
JAXBElement<Authentication> headers =
factory.createAuthentication(auth);
// create a marshaller
JAXBContext context = JAXBContext.newInstance(Authentication.class);
Marshaller marshaller = context.createMarshaller();
// marshal the headers into the specified result
marshaller.marshal(headers, soapHeader.getResult());
} catch (Exception e) {
log.error("error during marshalling of the SOAP headers", e);
}
}
});
} catch (Exception e) {
e.printStackTrace();
}
return res;
}

Java web service : User defined meta-data

I have a SOAP web service implementation on Jboss 4.2.3. I want to add a version number check for the service. Whenever a client makes a call, I will pass the client version number. I will write an interceptor at the server that would check the client version number. If it is a client with a different version number, I would not process the request.
What I want to know is if there is a way to pass the version number from the client in some context parameter other than adding it in the web service method signature?
In general, if I want to pass some custom META-DATA from client to server, how do I do it ?
In general, if I want to pass some custom META-DATA from client to
server, how do I do it ?
This can be achieved through SOAP Message Handlers both side (Client and Server ) in Jax-WS .
Client Side:
The custom-meta-data , like version number, UUID , Signature information can be added via SOAP Headers.
1..Write a VersionNumberHandler as shown below.
public class VersionNumberHandler implements SOAPHandler<SOAPMessageContext> {
private static final String LoggerName = "ClientSideLogger";
private Logger logger;
private final boolean log_p = true; // set to false to turn off
public VersionNumberHandler() {
logger = Logger.getLogger(LoggerName);
}
public boolean handleMessage(SOAPMessageContext ctx) {
if (log_p)
logger.info("handleMessage");
// Is this an outbound message, i.e., a request?
Boolean request_p = (Boolean) ctx
.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
// Manipulate the SOAP only if it's a request
if (request_p) {
// Get the Version Number from some property file ,
// to place in the message header.
String versionNumber = "v1.0";
try {
SOAPMessage msg = ctx.getMessage();
SOAPEnvelope env = msg.getSOAPPart().getEnvelope();
SOAPHeader hdr = env.getHeader();
// Ensure that the SOAP message has a header.
if (hdr == null)
hdr = env.addHeader();
QName qname = new QName("http://ticket.example.com/",
"versionnumber");
SOAPHeaderElement helem = hdr.addHeaderElement(qname);
// In SOAP 1.2, setting the actor is equivalent to
// setting the role.
helem.setActor(SOAPConstants.URI_SOAP_ACTOR_NEXT);
helem.setMustUnderstand(true);
helem.addTextNode(versionNumber);
msg.saveChanges();
// For tracking, write to standard output.
msg.writeTo(System.out);
} catch (SOAPException e) {
System.err.println(e);
} catch (IOException e) {
System.err.println(e);
}
}
return true; // continue down the chain
}
public boolean handleFault(SOAPMessageContext ctx) {
if (log_p)
logger.info("handleFault");
try {
ctx.getMessage().writeTo(System.out);
} catch (SOAPException e) {
System.err.println(e);
} catch (IOException e) {
System.err.println(e);
}
return true;
}
public Set<QName> getHeaders() {
if (log_p)
logger.info("getHeaders");
return null;
}
public void close(MessageContext messageContext) {
if (log_p)
logger.info("close");
}
2..Mention this class in the Handler-Chain.xml.
<javaee:handler>
<javaee:handler-class>
com.example.client.handler.VersionNumberHandler
</javaee:handler-class>
</javaee:handler>
3..Add the handler-chain in the client (Stub) also.
#WebServiceClient(name = "TicketWSImplService", targetNamespace = "http://ticket.example.com/", wsdlLocation = "http://localhost:8080/ticket?wsdl")
#HandlerChain(file = "handler-chain.xml")
public class TicketWSImplService extends Service {
#WebMethod
public void method(){
}
Here, we are adding a new header element "versionnumber" and mustunderstand=true, which means the server/intermediaries has to process this element, otherwise Jax-WS-Runtime will throw SOAP Fault exception to the client. Now we need to write a Validator(SOAP Handler) at the server side to validate this version number which is being passed by the clients.
Server Side:
1..Write a VersionNumberValidator as shown below.
public class VersionNumberValidator implements SOAPHandler<SOAPMessageContext> {
#SuppressWarnings("unused")
#Override
public boolean handleMessage(SOAPMessageContext ctx) {
// Is this an inbound message, i.e., a request?
Boolean response_p = (Boolean) ctx
.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
// Manipulate the SOAP only if it's incoming.
if (!response_p) {
try {
SOAPMessage msg = ctx.getMessage();
SOAPEnvelope env = msg.getSOAPPart().getEnvelope();
SOAPHeader hdr = env.getHeader();
// Ensure that the SOAP message has a header.
if (hdr == null) {
generateSOAPFault(msg, "No message header.");
return true;
}
Iterator mustUnderstandHeaders = msg.getSOAPHeader()
.examineMustUnderstandHeaderElements(
"http://schemas.xmlsoap.org/soap/actor/next");
String value = null;
while (mustUnderstandHeaders.hasNext()) {
Node next = (Node) mustUnderstandHeaders.next();
System.out.println("mustUnderstandHeaders name:"
+ next.getValue());
if (next.getNodeName().equalsIgnoreCase("versionnumber"))
value = next.getValue();
if (value != null && !value.equalsIgnoreCase("v1.0")) {
generateSOAPFault(msg, "Version Number Mismatch");
}
}
// For tracking, write to standard output.
msg.writeTo(System.out);
} catch (SOAPException e) {
System.err.println(e);
} catch (IOException e) {
System.err.println(e);
}
}
return true; // continue down the chain
}
#Override
public boolean handleFault(SOAPMessageContext ctx) {
return true; // do continue down the chain
}
// For now, no-ops.
#Override
public Set<QName> getHeaders() {
Set<QName> headers = new HashSet<QName>();
QName qName = new QName("http://ticket.example.com/", "versionnumber");
headers.add(qName);
return headers;
}
#Override
public void close(MessageContext messageContext) {
}
private void generateSOAPFault(SOAPMessage msg, String reason) {
try {
SOAPBody body = msg.getSOAPBody();
SOAPFault fault = body.addFault();
QName fault_name = new QName(
SOAPConstants.URI_NS_SOAP_1_2_ENVELOPE, "UltimateReceiver");
fault.setFaultCode(fault_name);
fault.setFaultRole("http://ticket.example.com/versionNumber_validator");
fault.addFaultReasonText(reason, Locale.US);
} catch (SOAPException e) {
}
}
2..Mention this class in the Handler-Chain-server.xml.
<javaee:handler>
<javaee:handler-class>
com.example.client.handler.VersionNumberValidator
</javaee:handler-class>
</javaee:handler>
3..Publish the webservices.
Now, the every client request will be having "version number =v1.0", At the server side , you will be validating this value is correct or not. If it is not correct, SOAPFaultException will be thrown.
You could add it to the http-headers but that would mean your client would need to do this which also means they can change it and give you wrong numbers causing issues on the server. It's only as reliable as the messages being sent in.
Either way, this isn't the right way to restrict access to your Web Service, you should use http basic authentication or if it's version differences then you should create multiple version endpoints giving clients access to the versions they need.
Also, JBoss 4.2.3 is so old it might not even work. See [1]
Mus
[1] https://community.jboss.org/message/534711
It's a bad idea to try to add out-of-band metadata to a web service. Just pick a new URL for each version if the data structures are incompatible. If they are compatible, put the version number inside the request.
This way you can still support interoperation with all different libraries and not require your clients to find a new hoop to jump through for each toolkit.

How to persist SOAP Messages in MySQL Database - working with Axis Client

I am writing a java axis client, how could I persist raw xml into data base, till now I have found two ways of logging raw xml, but they are for console or to a file, but I need to persist each request and response into mysql database, where I could use connection as a user parameter.
here is what I have done already.
log raw ws xml to console
log raw ws xml to a file
Well I have found a solution, First we need to use the Custom handler as I mentioned earlier(1), we can set property in the message context
like
public class FedexWsHandler extends GenericHandler {
public QName[] getHeaders() {
return null;
}
public boolean handleRequest(MessageContext context) {
try {
SOAPMessageContext smc = (SOAPMessageContext) context;
SOAPMessage reqMsg = smc.getMessage();
context.setProperty("req-msg", reqMsg);
} catch (Exception ex) {
ex.printStackTrace();
}
return true;
}
public boolean handleResponse(MessageContext context) {
try {
SOAPMessageContext smc = (SOAPMessageContext) context;
SOAPMessage reqMsg = smc.getMessage();
context.setProperty("res-msg", reqMsg);
} catch (Exception ex) {
ex.printStackTrace();
}
return true;
}
}
and then in out client we can get that property and do what ever you want to do, like
MyServiceLocator locator = new MyServiceLocator();
MyService service = locator.getMyService();
service.getResults("foo", "bar"); // call the service
// I want to get that message I have set in request and response handler methods
MessageContext ctx = locator.getCall().getMessageContext();
SOAPMessage reqMsg = (SOAPMessage) requestContext.getProperty("req-msg");
SOAPMessage resMsg = (SOAPMessage) requestContext.getProperty("res-msg");
But it is not a safe way to do that as this is not Thread Safe. as per its docs
.... so if any one can suggest me some better solution.
JAXB Marshaller : StringWriter output has a truncated tag value
Please have a look on the above post, you can convert the xml to java string and and than you can use any mysql data type which can hold your xml converted to string. you can use blob if the string in very large.

Secure a java embedded web service with javax.jws without application server

I wrote the following code to implement a Java web service that communicates with an application written in another language on the same host:
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
#WebService(name = "MyWebService")
#SOAPBinding(parameterStyle = SOAPBinding.ParameterStyle.WRAPPED)
public class MyWebService {
#WebMethod(operationName = "methodName", action = "urn:#methodName")
#WebResult(name = "result", partName = "output")
public String methodName(#WebParam(name = "param1", partName = "input") String param1,
#WebParam(name = "param2", partName = "input") String param2){
// ...do something
return "You called this service with params: " + param1 + "," + param2;
}
Since requirements are not to use an application server to expose the web service I instantiated the service from another class as follows:
Endpoint endpoint = Endpoint.create(new MyWebService());
URL url = new URL("http://localhost:7777/MyWebService");
endpoint.publish(url.toString());
Questions:
1) Which is the simplest way to secure this service with username and password considering the architecture of this project?
Any code sample would be greatly appreciated.
2) I made some research and found the use of SOAPHandler and I think it would work for me.
In the case of using the SOAPHandler class how do I add headers to the message to require authentication from the client?
Thank you in advance
thanks so much for the response that's the direction I'm following too but
when I check any of the headers for example:
SOAPHeader header = soapContext.getMessage().getSOAPPart().getEnvelope().getHeader();
Iterator<SOAPElement> iterator = header.getAllAttributes();
I get a nullpointer exception...any ideas?
I did a working program. Just to add to what you already found out, following is a way to use handler
Endpoint endpoint = Endpoint.create(new MyWebService());
Binding binding = endpoint.getBinding();
List<Handler> handlerChain = new ArrayList<Handler>(1);
handlerChain.add(new MyHandler());
binding.setHandlerChain(handlerChain);
URL url = new URL("http://localhost:7777/MyWebService");
endpoint.publish(url.toString());
MyHandler is class extending Handler interface. Alternately, you can use #HandlerChain annotation which will need an xml configuration file for handlers. Configure this for incoming messages only
public class MyHandler implements SOAPHandler{
#Override
public Set<?> getHeaders() {
// TODO Auto-generated method stub
return null;
}
#Override
public void close(MessageContext context) {
// TODO Auto-generated method stub
}
#Override
public boolean handleFault(MessageContext context) {
// TODO Auto-generated method stub
return false;
}
#Override
public boolean handleMessage(MessageContext context) {
System.out.println("Hehehe the handler");
SOAPMessageContext soapContext = (SOAPMessageContext)context;
try {
SOAPHeader header = soapContext.getMessage().getSOAPPart().getEnvelope().getHeader();
//Check there if the required data (username/password) is present in header or not and return true/false accordingly.
} catch (SOAPException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return true;
}
}
From the client side also, if your client is using JAB-WS, you will have to use client handlers. Following is a typical JAX-WS client invocation example
Dispatch<Source> dispatch = … create a Dispatch<Source>
dispatch.getBinding().setHandlerChain(chain)
Source request = … create a Source object
Source response = dispatch.invoke(request);
Here the handler in chain will add header to outgoing request. Configure this for Outgoing messages only.
What you did is fair enough.
Concerning the authentication you can just expose a method for passing user name and password as login credentials.
Once the user has provided the correct credentials the user has been authenticated.
Note: Now you must maintain session data and make sure that an incoming request is from an authenticated user. The Endpoint just deploys internally a lightweight http server. You must design you web service implementation to keep "state" among requests.
You have 2 more options.
Do the authentication at the SOAP level. I would not really recomend
it. But if you do, note that the Endpoint does not deploy a
WSDL. So you must communicate exactly to the client connecting,
the SOAP header you expect. It is possible though to write a WSDL by
yourself and "attach" it to the Endpoint.
Do the authentication at the http request level. I.e. add a token or
cookie to the http request. To be honest I do not remember if this
is easy using the Endpoint

Categories

Resources