I try to invoke HTTPS SOAP web service through java code:
URL url = new URL("https://somehost:8181/services/"SomeService?wsdl");
QName qname = new QName("http://services.somehost.com/", "SomeService");
Service service = Service.create(url, qname);
SomeService port = service.getPort(SomeService .class);
port.doSomething();
But get exception:
threw an unexpected exception: javax.xml.ws.soap.SOAPFaultException: Security Requirements not met - No Security header in message
When I analized correct request sample I determined it have to contain header:
<S:Header>
<To xmlns="http://www.w3.org/2005/08/addressing">http://somehost:8181/services/SomeService</To>
<Action xmlns="http://www.w3.org/2005/08/addressing">https://somehost:8181/services/"SomeService/doSomethingRequest</Action>
<ReplyTo xmlns="http://www.w3.org/2005/08/addressing">
<Address>http://www.w3.org/2005/08/addressing/anonymous</Address>
</ReplyTo>
<MessageID xmlns="http://www.w3.org/2005/08/addressing">uuid:3428539e-d645-72ae-adc0-5423c1e68942</MessageID>
<wsse:Security S:mustUnderstand="true">
<wsu:Timestamp wsu:Id="_1" xmlns:ns14="http://docs.oasis-open.org/ws-sx/ws-secureconversation/200512" xmlns:ns13="http://schemas.xmlsoap.org/soap/envelope/">
<wsu:Created>2013-01-15T16:36:30Z</wsu:Created>
<wsu:Expires>2014-01-15T14:06:30Z</wsu:Expires>
</wsu:Timestamp>
</wsse:Security>
So how to add this header to my SOAP request?
I personally add two classes: HeaderHandler and HeaderHandlerResolver:
import java.util.HashSet;
import java.util.Set;
import javax.xml.namespace.QName;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPEnvelope;
import javax.xml.soap.SOAPHeader;
import javax.xml.soap.SOAPMessage;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.handler.soap.SOAPHandler;
import javax.xml.ws.handler.soap.SOAPMessageContext;
public class HeaderHandler implements SOAPHandler<SOAPMessageContext> {
public boolean handleMessage(SOAPMessageContext smc) {
Boolean outboundProperty = (Boolean) smc.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
if (outboundProperty.booleanValue()) {
SOAPMessage message = smc.getMessage();
try {
SOAPEnvelope envelope = smc.getMessage().getSOAPPart().getEnvelope();
SOAPHeader header = envelope.addHeader();
SOAPElement security =
header.addChildElement("Security", "wsse", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd");
SOAPElement usernameToken =
security.addChildElement("UsernameToken", "wsse");
usernameToken.addAttribute(new QName("xmlns:wsu"), "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd");
SOAPElement username =
usernameToken.addChildElement("Username", "wsse");
username.addTextNode("test");
SOAPElement password =
usernameToken.addChildElement("Password", "wsse");
password.setAttribute("Type", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText");
password.addTextNode("test321");
//Print out the outbound SOAP message to System.out
message.writeTo(System.out);
System.out.println("");
} catch (Exception e) {
e.printStackTrace();
}
} else {
try {
//This handler does nothing with the response from the Web Service so
//we just print out the SOAP message.
SOAPMessage message = smc.getMessage();
message.writeTo(System.out);
System.out.println("");
} catch (Exception ex) {
ex.printStackTrace();
}
}
return outboundProperty;
}
public Set getHeaders() {
// The code below is added on order to invoke Spring secured WS.
// Otherwise,
// http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd
// won't be recognised
final QName securityHeader = new QName(
"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd",
"Security", "wsse");
final HashSet headers = new HashSet();
headers.add(securityHeader);
return headers;
}
public boolean handleFault(SOAPMessageContext context) {
//throw new UnsupportedOperationException("Not supported yet.");
return true;
}
public void close(MessageContext context) {
//throw new UnsupportedOperationException("Not supported yet.");
}
}
And
import java.util.ArrayList;
import java.util.List;
import javax.xml.ws.handler.Handler;
import javax.xml.ws.handler.HandlerResolver;
import javax.xml.ws.handler.PortInfo;
public class HeaderHandlerResolver implements HandlerResolver {
public List<Handler> getHandlerChain(PortInfo portInfo) {
List<Handler> handlerChain = new ArrayList<Handler>();
HeaderHandler hh = new HeaderHandler();
handlerChain.add(hh);
return handlerChain;
}
}
In the HeaderHandler class, you can add needed credentials.
To use them finally:
HeaderHandlerResolver handlerResolver = new HeaderHandlerResolver();
service.setHandlerResolver(handlerResolver);
I have followed the steps mentioned by #LaabidiRaissi. The code works fine but it never appends the security element under the header. I have confirmed it by printing out the outbound SOAP message to System.out. After a deep research, I have found that the SOAPMessage needs to be explicitly saved for reflecting the updated message header.
soapMessage.saveChanges();
For more reference -
Check this link
Sample main Class:
package test;
import java.util.ArrayList;
import java.util.List;
import javax.xml.ws.BindingProvider;
import javax.xml.ws.handler.Handler;
// next headers is generated from "NetBeans New Webservice Client"
import sk.firma.wstest.definitions.*;
import sk.firma.wstest.schemas.*;
/**
*
* #author Jan
*/
public class TestWSService {
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
try {
WsService service = new WsService();
Ws port = service.getWsKsSoap11();
// This is the block that apply the Ws Security to the request
BindingProvider bindingProvider = (BindingProvider) port;
#SuppressWarnings("rawtypes")
List<Handler> handlerChain = new ArrayList<Handler>();
handlerChain.add(new WSSecurityHeaderSOAPHandler("username", "password"));
bindingProvider.getBinding().setHandlerChain(handlerChain);
// Initialize and Run Service
InVal inVal = new InVal();
ReturnValue retVal = port.test(inVal);
} catch (Exception e) {
e.printStackTrace();
}
}
}
And now WS-Security Header SOAP Handler:
package test;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import java.util.TimeZone;
import javax.xml.namespace.QName;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPEnvelope;
import javax.xml.soap.SOAPHeader;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.handler.soap.SOAPHandler;
import javax.xml.ws.handler.soap.SOAPMessageContext;
public class WSSecurityHeaderSOAPHandler implements SOAPHandler<SOAPMessageContext> {
private static final String URL_WSSE_NAMESPACE = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd";
private static final String URL_WSU_NAMESPACE = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd";
private final String usernameText;
private final String passwordText;
public WSSecurityHeaderSOAPHandler(String usernameText, String passwordText) {
this.usernameText = usernameText;
this.passwordText = passwordText;
}
public String getCurrentDateTime() {
/* e.g. 2001-10-13T09:00:00Z */
final SimpleDateFormat FORMATTER_DATETIME_NO_MS = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
DateFormat dfETC = FORMATTER_DATETIME_NO_MS;
dfETC.setTimeZone(TimeZone.getTimeZone("CET"));
StringBuffer dateETC = new StringBuffer(dfETC.format(new Date()));
dateETC.append('Z');
return dateETC.toString();
}
public String getCurrentDateTimePlusDelay(long delayInSeconds) {
/* e.g. 2001-10-13T09:00:00Z */
final SimpleDateFormat FORMATTER_DATETIME_NO_MS = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
DateFormat dfETC = FORMATTER_DATETIME_NO_MS;
dfETC.setTimeZone(TimeZone.getTimeZone("CET"));
Date date = new Date();
long timeInMsecs = date.getTime();
date.setTime(timeInMsecs + delayInSeconds*1000L);
StringBuffer dateETC = new StringBuffer(dfETC.format(date));
dateETC.append('Z');
return dateETC.toString();
}
#Override
public boolean handleMessage(SOAPMessageContext soapMessageContext) {
Boolean outboundProperty = (Boolean) soapMessageContext.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
if (outboundProperty) {
try {
SOAPEnvelope soapEnvelope = soapMessageContext.getMessage().getSOAPPart().getEnvelope();
SOAPHeader header = soapEnvelope.getHeader();
if (header == null) {
header = soapEnvelope.addHeader();
}
SOAPElement securityHeaderElement = header.addChildElement("Security", "wsse", URL_WSSE_NAMESPACE);
securityHeaderElement.addAttribute(soapEnvelope.createName("S:mustUnderstand"), "1");
// Add Timestamp element to "Security" soapHeaderElement
// Sample: <u:Timestamp>
// <u:Created>2011-10-13T08:20:01.183Z</u:Created>
// <u:Expires>2011-10-13T17:25:01.183Z</u:Expires>
// </u:Timestamp>
javax.xml.soap.Name timestampElementName = soapEnvelope.createName("Timestamp", "wsu", URL_WSU_NAMESPACE);
SOAPElement timestampSOAPElement = securityHeaderElement.addChildElement(timestampElementName);
String created = getCurrentDateTime();
String expires = getCurrentDateTimePlusDelay(60L*60L); /* 60 minutes delay */
// Add Created to Timestamp
SOAPElement createdSOAPElement = timestampSOAPElement
.addChildElement("Created"/* local name */, "wsu" /* prefix */, URL_WSU_NAMESPACE);
createdSOAPElement.addTextNode(created);
// Add Expires to Timestamp
SOAPElement expiresSOAPElement = timestampSOAPElement
.addChildElement("Expires"/* local name */, "wsu" /* prefix */,URL_WSU_NAMESPACE);
expiresSOAPElement.addTextNode(expires);
// Add usernameToken to "Security" soapHeaderElement
javax.xml.soap.Name usernameTokenElementName = soapEnvelope.createName("UsernameToken", "wsse",
URL_WSSE_NAMESPACE);
SOAPElement usernameTokenSOAPElement = securityHeaderElement.addChildElement(usernameTokenElementName);
// Add Username to usernameToken
SOAPElement userNameSOAPElement = usernameTokenSOAPElement
.addChildElement("Username"/* local name */, "wsse" /* prefix */, URL_WSSE_NAMESPACE);
userNameSOAPElement.addTextNode(this.usernameText);
// Add password to UsernameToken
javax.xml.soap.Name passwordElementName = soapEnvelope.createName("Password", "wsse", URL_WSSE_NAMESPACE);
SOAPElement passwordSOAPElement = usernameTokenSOAPElement.addChildElement(passwordElementName);
/* Add "Type" attribute to <Password> header element */
//passwordSOAPElement.addAttribute(soapEnvelope.createName("Type", "", ""),
// "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText");
passwordSOAPElement.addTextNode(this.passwordText);
} catch (Exception e) {
throw new RuntimeException("Error on wsSecurityHandler: " + e.getMessage());
}
}
return true;
}
#Override
public void close(MessageContext context) {
// TODO Auto-generated method stub
}
#Override
public boolean handleFault(SOAPMessageContext context) {
// TODO Auto-generated method stub
return true;
}
#Override
public Set<QName> getHeaders() {
// throw new UnsupportedOperationException("Not supported yet.");
final QName securityHeader = new QName("http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd",
"Security", "wsse");
final HashSet headers = new HashSet();
headers.add(securityHeader);
return headers;
}
}
You can also use Apache wss4j to easily add the header and also encrypt you password.
import org.apache.ws.security.WSConstants;
import org.apache.ws.security.message.WSSecHeader;
import org.apache.ws.security.message.WSSecUsernameToken;
import javax.xml.namespace.QName;
import javax.xml.soap.SOAPMessage;
import javax.xml.soap.SOAPPart;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.handler.soap.SOAPHandler;
import javax.xml.ws.handler.soap.SOAPMessageContext;
import java.io.ByteArrayOutputStream;
import java.util.Set;
public class WSSecurityHeaderSOAPHandler implements SOAPHandler<SOAPMessageContext> {
private final String usernameText;
private final String passwordText;
public WSSecurityHeaderSOAPHandler(String usernameText, String passwordText) {
this.usernameText = usernameText;
this.passwordText = passwordText;
}
#Override
public boolean handleMessage(SOAPMessageContext context) {
Boolean outboundProperty = (Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
if (outboundProperty.booleanValue()) {
try {
SOAPMessage soapMessage = context.getMessage();
soapMessage.removeAllAttachments();
SOAPPart soappart = soapMessage.getSOAPPart();
WSSecHeader wsSecHeader = new WSSecHeader();
wsSecHeader.insertSecurityHeader(soappart);
WSSecUsernameToken token = new WSSecUsernameToken();
token.setPasswordType(WSConstants.PASSWORD_DIGEST);
token.setUserInfo(usernameText, passwordText);
token.build(soappart, wsSecHeader);
soapMessage.saveChanges();
} catch (Exception e) {
throw new RuntimeException("Error on wsSecurityHandler: " + e.getMessage());
}
}
return true;
}
#Override
public boolean handleFault(SOAPMessageContext context) {
return false;
}
#Override
public void close(MessageContext context) {
}
#Override
public Set<QName> getHeaders() {
return null;
}
}
And you need to update your request like this:
// This is the block that apply the Ws Security to the request
BindingProvider bindingProvider = (BindingProvider) portType;
List<Handler> handlerChain = new ArrayList<>();
handlerChain.add(new WSSecurityHeaderSOAPHandler("username", "password"));
bindingProvider.getBinding().setHandlerChain(handlerChain);
Maven Dependency:
<dependency>
<groupId>org.apache.ws.security</groupId>
<artifactId>wss4j</artifactId>
<version>1.6.19</version>
</dependency>
Related
Hi i have created a handler in java for getting the events from dynamo DB
Here is my code
package com.Lambda.dynamodb;
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.LambdaLogger;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import com.amazonaws.services.lambda.runtime.events.DynamodbEvent;
import com.amazonaws.services.lambda.runtime.events.DynamodbEvent.DynamodbStreamRecord;
public class DDBEventProcessor implements
RequestHandler<DynamodbEvent, String> {
public String handleRequest(DynamodbEvent ddbEvent, Context context) {
for (DynamodbStreamRecord record : ddbEvent.getRecords()){
System.out.println(record.getEventID());
System.out.println(record.getEventName());
System.out.println(record.getDynamodb().toString());
}
return "Successfully processed " + ddbEvent.getRecords().size() + " records.";
}
}
Lambda function able to write the events in cloudwatch but the challenge is i have to index all the streamed records to the AWS elasticsearch service endpoint and index it.
while search through blogs i got few code samples in python and node.js but my requirement is i have to build this lambda function in java
Could anyone please suggest how to achieve this in java lambda function?
Hi i have included the code below may helpful to some one. Dynamo DB streams to index the document in elasticsearch both inside AWS and outside AWS
package com.Firstlambda;
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import com.amazonaws.auth.AWS4Signer;
import com.amazonaws.auth.AWSCredentialsProvider;
import com.amazonaws.auth.DefaultAWSCredentialsProviderChain;
import com.amazonaws.services.dynamodbv2.document.Item;
import com.amazonaws.services.dynamodbv2.document.ItemUtils;
import com.amazonaws.services.dynamodbv2.model.AttributeValue;
import com.amazonaws.services.lambda.runtime.events.DynamodbEvent;
import com.amazonaws.services.lambda.runtime.events.DynamodbEvent.DynamodbStreamRecord;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequestInterceptor;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.xcontent.XContentType;
import org.json.JSONObject;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class HelloWorld implements RequestHandler<DynamodbEvent, String> {
private static String serviceName = "es";
private static String region = "us-east-1";
private static String aesEndpoint = ""
private static String index = "";
private static String type = "_doc";
static final AWSCredentialsProvider credentialsProvider = new DefaultAWSCredentialsProviderChain();
public String handleRequest(DynamodbEvent ddbEvent, Context context) {
for (DynamodbStreamRecord record : ddbEvent.getRecords()) {
System.out.println("EventName : " + record.getEventName());
System.out.println("EventName : " + record.getDynamodb());
//AWS outside
RestHighLevelClient esClient = esClient();
//AWS outside
//AWS Inside
//RestHighLevelClient esClient = esClient(serviceName, region);
//AWS Inside
if (record.getEventName().toLowerCase().equals("insert")) {
String JsonString = getJsonstring(record.getDynamodb().getNewImage());
String JsonUniqueId = GetIdfromJsonString(JsonString);
IndexRequest indexRequest = new IndexRequest(index, type, JsonUniqueId);
indexRequest.source(JsonString, XContentType.JSON);
try {
IndexResponse indexResponse = esClient.index(indexRequest, RequestOptions.DEFAULT);
System.out.println(indexResponse.toString());
return "Successfully processed " + ddbEvent.getRecords().size() + " records.";
} catch (IOException e) {
System.out.println(e.getMessage());
}
} else if (record.getEventName().toLowerCase().equals("modify")) {
String JsonString = getJsonstring(record.getDynamodb().getNewImage());
String JsonUniqueId = GetIdfromJsonString(JsonString);
UpdateRequest request = new UpdateRequest(index, type, JsonUniqueId);
String jsonString = JsonString;
request.doc(jsonString, XContentType.JSON);
try {
UpdateResponse updateResponse = esClient.update(
request, RequestOptions.DEFAULT);
System.out.println(updateResponse.toString());
return "Successfully processed " + ddbEvent.getRecords().size() + " records.";
} catch (IOException e) {
System.out.println(e.getMessage());
}
} else {
System.out.println("remove");
System.out.println("KEYID : " + record.getDynamodb().getKeys().get("ID").getN());
String deletedId = record.getDynamodb().getKeys().get("ID").getN();
DeleteRequest request = new DeleteRequest(index, type, deletedId);
try {
DeleteResponse deleteResponse = esClient.delete(
request, RequestOptions.DEFAULT);
} catch (IOException e) {
System.out.println(e.getMessage());
}
}
}
return "Successfullyprocessed";
}
public String getJsonstring(Map<String, AttributeValue> newIma) {
String json = null;
Map<String, AttributeValue> newImage = newIma;
List<Map<String, AttributeValue>> listOfMaps = new ArrayList<Map<String, AttributeValue>>();
listOfMaps.add(newImage);
List<Item> itemList = ItemUtils.toItemList(listOfMaps);
for (Item item : itemList) {
json = item.toJSON();
}
return json;
}
public String GetIdfromJsonString(String Json) {
JSONObject jsonObj = new JSONObject(Json);
return String.valueOf(jsonObj.getInt("ID"));
}
// Adds the interceptor to the ES REST client
// public static RestHighLevelClient esClient(String serviceName, String region) {
// AWS4Signer signer = new AWS4Signer();
// signer.setServiceName(serviceName);
// signer.setRegionName(region);
// HttpRequestInterceptor interceptor = new AWSRequestSigningApacheInterceptor(serviceName, signer, credentialsProvider);
// return new RestHighLevelClient(RestClient.builder(HttpHost.create(aesEndpoint)).setHttpClientConfigCallback(hacb -> hacb.addInterceptorLast(interceptor)));
// }
public static RestHighLevelClient esClient() {
String host = "d9bc7cbca5ec49ea96a6ea683f70caca.eastus2.azure.elastic-cloud.com";
int port = 9200;
String userName = "elastic";
String password = "L4Nfnle3wxLmV95lffwsf$Ub46hp";
String protocol = "https";
final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(AuthScope.ANY,
new UsernamePasswordCredentials(userName, password));
RestClientBuilder builder = RestClient.builder(new HttpHost(host, port, protocol))
.setHttpClientConfigCallback(httpClientBuilder -> httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider));
RestHighLevelClient client = new RestHighLevelClient(builder);
return client;
}
}
This is just a sample code has to be modified based on our requirements
How can I add empty xmlns="" tag to header elements?
Let's imagine we have same project in two servers.
In this project I used the below SOAPHandler.
First server send request with empty xmlns="" tag<username xmlns="">test</username><password xmlns="">test</password>, but second server send request without xmlns="" tag.
<username>test</username><password>test</password>I need xmlns="" tag in every servers.
How can I solve this problem?
import java.util.Collections;
import java.util.Set;
import javax.xml.namespace.QName;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPEnvelope;
import javax.xml.soap.SOAPHeader;
import javax.xml.soap.SOAPMessage;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.handler.soap.SOAPHandler;
import javax.xml.ws.handler.soap.SOAPMessageContext;
import org.apache.log4j.Logger;
/**
*
* #author BashirovMA
*/
public class MyHandler implements SOAPHandler<SOAPMessageContext> {
static final Logger log = Logger.getLogger(MyHandler.class);
private String username;
private String password;
public MyHandler() {
this.username = "";
this.password = "";
}
public MyHandler(String username, String password) {
this.username = username;
this.password = password;
}
public MyHandler(String username, String password) {
this.username = username;
this.password = password;
}
#Override
public boolean handleMessage(SOAPMessageContext smc) {
SOAPMessage msg = smc.getMessage();
Boolean outboundProperty = (Boolean) smc.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
if (outboundProperty.booleanValue()) {
try {
SOAPEnvelope envelope = smc.getMessage().getSOAPPart().getEnvelope();
if (envelope.getHeader() != null) {
envelope.getHeader().detachNode();
}
SOAPHeader header = envelope.addHeader();
SOAPElement root = header.addHeaderElement(envelope.createName("messageHeader", "", "http://services.ws.com/"));
SOAPElement el1 = root.addChildElement(envelope.createName("username", "", ""));
el1.setValue(String.valueOf(username));
SOAPElement el2 = root.addChildElement(envelope.createName("password", "", ""));
el2.setValue(String.valueOf(password));
msg.saveChanges();
} catch (Exception e) {
e.printStackTrace();
log.error(e);
}
} else {
try {
SOAPMessage message = smc.getMessage();
message.writeTo(System.out);
} catch (Exception ex) {
ex.printStackTrace();
log.error(ex);
}
}
return true;
}
#Override
public Set<QName> getHeaders() {
return null;
}
#Override
public boolean handleFault(SOAPMessageContext messageContext) {
return true;
}
#Override
public void close(MessageContext context) {
}
}
I am new to the SOAP world.
I have coverted the wsdl file to java class using maven plugin
Below is the pom.xml configuration.
<plugin>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-codegen-plugin</artifactId>
<version>3.1.12</version>
<executions>
<execution>
<id>generate-sources</id>
<phase>generate-sources</phase>
<configuration>
<sourceRoot>${project.basedir}/src/main/java</sourceRoot>
<wsdlOptions>
<wsdlOption>
<wsdl>${project.basedir}/src/main/resources/EIAproxy.wsdl</wsdl>
<wsdlLocation>classpath:EIAproxy.wsdl</wsdlLocation>
</wsdlOption>
</wsdlOptions>
</configuration>
<goals>
<goal>wsdl2java</goal>
</goals>
</execution>
</executions>
</plugin>
below is the class files
Interface definition
#WebService(targetNamespace = "http://schema.concierge.com", name = "EaiEnvelopeSoap")
#XmlSeeAlso({com.concierge.schema.envelope.ObjectFactory.class, ObjectFactory.class})
#SOAPBinding(parameterStyle = SOAPBinding.ParameterStyle.BARE)
public interface EaiEnvelopeSoap {
#WebResult(name = "clientRequestResponse", targetNamespace = "http://schema.concierge.com", partName = "parameters")
#WebMethod(action = "http://www.openuri.org/clientRequest")
public ClientRequestResponse clientRequest(
#WebParam(partName = "parameters", name = "clientRequest", targetNamespace = "http://schema.concierge.com")
ClientRequest parameters
);
}
Here is class file that extends service
#WebServiceClient(name = "EaiEnvelope",
wsdlLocation = "classpath:EIAproxy.wsdl",
targetNamespace = "http://schema.concierge.com")
public class EaiEnvelope extends Service {
public final static URL WSDL_LOCATION;
public final static QName SERVICE = new QName("http://schema.concierge.com", "EaiEnvelope");
public final static QName EaiEnvelopeSoap = new QName("http://schema.concierge.com", "EaiEnvelopeSoap");
static {
URL url = EaiEnvelope.class.getClassLoader().getResource("EIAproxy.wsdl");
if (url == null) {
java.util.logging.Logger.getLogger(EaiEnvelope.class.getName())
.log(java.util.logging.Level.INFO,
"Can not initialize the default wsdl from {0}", "classpath:EIAproxy.wsdl");
}
WSDL_LOCATION = url;
}
public EaiEnvelope(URL wsdlLocation) {
super(wsdlLocation, SERVICE);
}
public EaiEnvelope(URL wsdlLocation, QName serviceName) {
super(wsdlLocation, serviceName);
}
public EaiEnvelope() {
super(WSDL_LOCATION, SERVICE);
}
public EaiEnvelope(WebServiceFeature ... features) {
super(WSDL_LOCATION, SERVICE, features);
}
public EaiEnvelope(URL wsdlLocation, WebServiceFeature ... features) {
super(wsdlLocation, SERVICE, features);
}
public EaiEnvelope(URL wsdlLocation, QName serviceName, WebServiceFeature ... features) {
super(wsdlLocation, serviceName, features);
}
/**
*
* #return
* returns EaiEnvelopeSoap
*/
#WebEndpoint(name = "EaiEnvelopeSoap")
public EaiEnvelopeSoap getEaiEnvelopeSoap() {
return super.getPort(EaiEnvelopeSoap, EaiEnvelopeSoap.class);
}
/**
*
* #param features
* A list of {#link javax.xml.ws.WebServiceFeature} to configure on the proxy. Supported features not in the <code>features</code> parameter will have their default values.
* #return
* returns EaiEnvelopeSoap
*/
#WebEndpoint(name = "EaiEnvelopeSoap")
public EaiEnvelopeSoap getEaiEnvelopeSoap(WebServiceFeature... features) {
return super.getPort(EaiEnvelopeSoap, EaiEnvelopeSoap.class, features);
}
}
My SoapHandler file is
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.StringWriter;
import java.util.Set;
import javax.xml.namespace.QName;
import javax.xml.soap.SOAPBody;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPEnvelope;
import javax.xml.soap.SOAPHeader;
import javax.xml.soap.SOAPMessage;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.handler.soap.SOAPHandler;
import javax.xml.ws.handler.soap.SOAPMessageContext;
import com.xxx.fdp.common.LoggerManager;
import com.xxx.fdp.constants.LoggerConstantEnum;
import com.xxx.fdp.property.config.AbilityConfig;
public class HeaderHandler implements SOAPHandler<SOAPMessageContext> {
/** The logger manager. */
LoggerManager loggerManager = new LoggerManager();
#Override
public boolean handleMessage(SOAPMessageContext smc) {
Boolean outboundProperty = (Boolean) smc.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
loggerManager.info(LoggerConstantEnum.AbilityDailySyncUp, "Entered in handleMessage with outBoundProperty : " + outboundProperty);
if (outboundProperty.booleanValue()) {
SOAPMessage message = smc.getMessage();
loggerManager.info(LoggerConstantEnum.AbilityDailySyncUp, " Message : " + message);
try {
message.writeTo(System.out);
SOAPEnvelope envelope = smc.getMessage().getSOAPPart().getEnvelope();
envelope.addNamespaceDeclaration("com", "http://schema.concierge.com");
message.getMimeHeaders().setHeader("Content-Type", "application/soap+xml; charset=utf-8");
SOAPHeader header = envelope.addHeader();
SOAPElement authentication = header.addChildElement("authentication", "auth", "http://schemas.eia.org/middleware/AuthInfo");
SOAPElement username = authentication.addChildElement("user", "auth");
username.addTextNode(AbilityConfig.getInstance().getSoapUser());
SOAPElement password = authentication.addChildElement("password", "auth");
password.addTextNode(AbilityConfig.getInstance().getSoapPassword());
SOAPElement authType = authentication.addChildElement("type", "auth");
authType.addTextNode(AbilityConfig.getInstance().getSoapAuthType());
SOAPBody body = envelope.getBody();
// Print out the outbound SOAP message to System.out
message.saveChanges();
loggerManager.info(LoggerConstantEnum.AbilityDailySyncUp, "Request Format : " + envelope.getBody() + ",Header : " + envelope.getHeader());
ByteArrayOutputStream out = new ByteArrayOutputStream();
message.writeTo(out);
String strMsg = new String(out.toByteArray());
loggerManager.info(LoggerConstantEnum.AbilityDailySyncUp, "Request Format Message: " + strMsg);
StringWriter writer = new StringWriter();
message.writeTo(new StringOutputStream(writer));
// message.writeTo(System.out);
System.out.println("");
} catch (Exception e) {
e.printStackTrace();
}
} else {
try {
// This handler does nothing with the response from the Web
// Service so
// we just print out the SOAP message.
SOAPMessage message = smc.getMessage();
loggerManager.info(LoggerConstantEnum.AbilityDailySyncUp, "Response Message : " + message);
message.writeTo(System.out);
System.out.println("");
} catch (Exception ex) {
ex.printStackTrace();
}
}
loggerManager.info(LoggerConstantEnum.AbilityDailySyncUp, "Exit in handleMessage with outBoundProperty : " + outboundProperty);
return outboundProperty;
}
private static class StringOutputStream extends OutputStream {
private StringWriter writer;
public StringOutputStream(StringWriter writer) {
this.writer = writer;
}
#Override
public void write(int b) throws IOException {
writer.write(b);
}
}
#Override
public boolean handleFault(SOAPMessageContext context) {
loggerManager.info(LoggerConstantEnum.AbilityDailySyncUp, "Entered in handleFault ");
return false;
}
#Override
public void close(MessageContext context) {
loggerManager.info(LoggerConstantEnum.AbilityDailySyncUp, "Entered in close ");
}
#Override
public Set<QName> getHeaders() {
// TODO Auto-generated method stub
return null;
}
}
My handlerResolver file is
import java.util.ArrayList;
import java.util.List;
import javax.xml.ws.handler.Handler;
import javax.xml.ws.handler.HandlerResolver;
import javax.xml.ws.handler.PortInfo;
import com.xxx.fdp.common.LoggerManager;
import com.xxx.fdp.constants.LoggerConstantEnum;
public class HeaderHandlerResolver implements HandlerResolver {
LoggerManager loggerManager = new LoggerManager();
#SuppressWarnings("rawtypes")
#Override
public List<Handler> getHandlerChain(PortInfo portInfo) {
loggerManager.info(LoggerConstantEnum.AbilityDailySyncUp,"Entered in getHandlerChain");
List<Handler> handlerChain = new ArrayList<Handler>();
loggerManager.info(LoggerConstantEnum.AbilityDailySyncUp,"Created handlerChanin object");
HeaderHandler headerHandler = new HeaderHandler();
loggerManager.info(LoggerConstantEnum.AbilityDailySyncUp,"Created HeaderHandler object");
handlerChain.add(headerHandler);
loggerManager.info(LoggerConstantEnum.AbilityDailySyncUp,"returned handlerChain");
return handlerChain;
}
}
Method which is used to call service
public void processRequest(EaiEnvelope envelope,AbilitySyncUpData abilityObject) {
loggerManager.info(LoggerConstantEnum.AbilityDailySyncUp, "| processing ability async up starts with TranactionId : "+abilityObject.getTransactionId());
try {
com.concierge.schema.EaiEnvelope service=new com.concierge.schema.EaiEnvelope();
HeaderHandlerResolver handlerResolver = new HeaderHandlerResolver();
service.setHandlerResolver(handlerResolver);
EaiEnvelopeSoap port=service.getEaiEnvelopeSoap();
ClientRequest request=new ClientRequest();
request.setEaiEnvelope(envelope);
loggerManager.info(LoggerConstantEnum.AbilityDailySyncUp,"Sending request to web service with TranactionId : "+abilityObject.getTransactionId());
ClientRequestResponse response=port.clientRequest(request);
loggerManager.info(LoggerConstantEnum.AbilityDailySyncUp,"Response received "+response+" After sending request to web service with TranactionId : "+abilityObject.getTransactionId());
} catch (Exception e) {
loggerManager.error(LoggerConstantEnum.AbilityDailySyncUp, "| Exception occured : " + e.fillInStackTrace(), e);
writeCsvFile(abilityObject);
}
loggerManager.info(LoggerConstantEnum.AbilityDailySyncUp, "| processRequest method ends here with TranactionId : "+abilityObject.getTransactionId());
I am not able to get call in soapHandler's handleMessage method
and call is going on getHeadersmethod when the service methodClientRequestResponse response=port.clientRequest(request);` is called.
I have reffered from several answers that are present on stackoverflow:
SoapHandler not called after WS operation is executed
https://stackoverflow.com/a/12712728/1569443
https://stackoverflow.com/a/14523921/1569443
http://www.javadb.com/using-a-message-handler-to-alter-the-soap-header-in-a-web-service-client/
https://soa2world.blogspot.com/2009/05/direct-web-service-client-using-java.html
I am still not able to call handleMessage method of handler as no logs gets printed. Call is made in handlerResolver class but not in HeaderHandler class.
How can I resolve this issue?
I'm trying to setup a SOAPHandler on my server to convert this incoming request
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
<S:Body>
<getMachine xmlns="http://machine.soap.webservices.product.company.at/">
<machineId>92623-15853588</machineId>
</getMachine>
</S:Body>
</S:Envelope>
to this request.
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
<S:Body>
<enns1:getMachine xmlns:enns1="http://machine.soap.webservices.product.company.at/">
<machineId>92623-15853588</machineId>
</enns1:getMachine>
</S:Body>
</S:Envelope>
My SOAPHandler looks like this.
package at.company.product.webservices.soap;
import java.io.IOException;
import java.util.Iterator;
import java.util.Set;
import javax.xml.namespace.QName;
import javax.xml.soap.SOAPBody;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPMessage;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.handler.soap.SOAPHandler;
import javax.xml.ws.handler.soap.SOAPMessageContext;
public class SOAPValidationHandler implements SOAPHandler<SOAPMessageContext> {
private static final String PREFIX = "enns1";
#Override
public boolean handleMessage(SOAPMessageContext context) {
System.out.println("Server : handleMessage()......");
Boolean isRequest = (Boolean) context
.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
// inbound
if (!isRequest) {
try {
SOAPMessage soapMsg = context.getMessage();
SOAPBody body = soapMsg.getSOAPBody();
Iterator<SOAPElement> it = body.getChildElements();
// Look for all body elements who have a "xmlns" attribute and
// add a namespace prefix to it
while (it.hasNext()) {
SOAPElement elem = it.next();
addNamespacePrefix(elem);
Iterator itChildren = elem.getChildElements();
while (itChildren.hasNext()) {
Object child = itChildren.next();
if (child instanceof SOAPElement) {
SOAPElement cElem = ((SOAPElement) child);
// TODO: Remove the namespace from the child
// cElem.removeNamespaceDeclaration(""); => Does not
// work
}
}
}
// tracking
soapMsg.writeTo(System.out);
} catch (SOAPException e) {
System.err.println(e);
} catch (IOException e) {
System.err.println(e);
}
}
return true;
}
private void addNamespacePrefix(SOAPElement elem) throws SOAPException {
Iterator<Object> it = elem.getAllAttributes();
QName name = new QName("xmlns");
String value = elem.getAttributeValue(name);
if (value != null) {
elem.addNamespaceDeclaration(PREFIX, elem.getNamespaceURI());
elem.removeNamespaceDeclaration("");
elem.setPrefix(PREFIX);
}
}
#Override
public boolean handleFault(SOAPMessageContext context) {
System.out.println("Server : handleFault()......");
return true;
}
#Override
public void close(MessageContext context) {
System.out.println("Server : close()......");
}
#Override
public Set<QName> getHeaders() {
System.out.println("Server : getHeaders()......");
return null;
}
}
After the processing of the request with the SOAPHandler the request looks like this:
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
<S:Body>
<enns1:getMachine xmlns:enns1="http://machine.soap.webservices.product.company.at/">
<machineId xmlns="http://machine.soap.webservices.product.company.at/">92623-15853588</machineId>
</enns1:getMachine>
</S:Body>
</S:Envelope>
As you can see I'm able to add the namespace prefix to the <getMachine> tag but then it automatically adds the xmlns attribute to the child element <machineId>. How can I avoid or fix this?
After toying around with the API I came up with this solution. This solves the described case.
package at.company.product.webservices.soap;
import java.io.IOException;
import java.util.Iterator;
import java.util.Set;
import javax.xml.namespace.QName;
import javax.xml.soap.SOAPBody;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPEnvelope;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPFault;
import javax.xml.soap.SOAPHeader;
import javax.xml.soap.SOAPMessage;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.handler.soap.SOAPHandler;
import javax.xml.ws.handler.soap.SOAPMessageContext;
import javax.xml.ws.soap.SOAPFaultException;
public class SOAPValidationHandler implements SOAPHandler<SOAPMessageContext> {
private static final String PREFIX = "enns1";
#Override
public boolean handleMessage(SOAPMessageContext context) {
Boolean isRequest = (Boolean) context
.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
// for response message only, true for outbound messages, false for
// inbound
if (!isRequest) {
try {
SOAPMessage soapMsg = context.getMessage();
SOAPEnvelope soapEnv = soapMsg.getSOAPPart().getEnvelope();
SOAPHeader soapHeader = soapEnv.getHeader();
// if no header, add one
if (soapHeader == null) {
soapHeader = soapEnv.addHeader();
// throw exception
generateSOAPErrMessage(soapMsg, "No SOAP header.");
}
SOAPBody body = soapMsg.getSOAPBody();
Iterator<SOAPElement> it = body.getChildElements();
while (it.hasNext()) {
SOAPElement elem = it.next();
addNamespacePrefix(elem);
Iterator itChildChildren = elem.getChildElements();
while (itChildChildren.hasNext()) {
Object obj = itChildChildren.next();
if ((obj instanceof SOAPElement)) {
SOAPElement soapElem = (SOAPElement) obj;
String name = soapElem.getElementName().getLocalName();
QName qName = new QName(name);
((SOAPElement) obj).setElementQName(qName);
}
}
}
// tracking
soapMsg.writeTo(System.out);
} catch (SOAPException e) {
System.err.println(e);
} catch (IOException e) {
System.err.println(e);
}
}
// continue other handler chain
return true;
}
private void addNamespacePrefix(SOAPElement elem) throws SOAPException {
Iterator<Object> it = elem.getAllAttributes();
QName name = new QName("xmlns");
String value = elem.getAttributeValue(name);
if (value != null) {
elem.addNamespaceDeclaration(PREFIX, elem.getNamespaceURI());
elem.removeNamespaceDeclaration("");
elem.setPrefix(PREFIX);
}
}
#Override
public boolean handleFault(SOAPMessageContext context) {
return true;
}
#Override
public void close(MessageContext context) {
}
#Override
public Set<QName> getHeaders() {
return null;
}
private void generateSOAPErrMessage(SOAPMessage msg, String reason) {
try {
SOAPBody soapBody = msg.getSOAPPart().getEnvelope().getBody();
SOAPFault soapFault = soapBody.addFault();
soapFault.setFaultString(reason);
throw new SOAPFaultException(soapFault);
} catch (SOAPException e) {
}
}
}
I need your help in order to consume the services of this wsdl file:
http://lyrics.wikia.com/server.php?wsdl
For example the service "getArtist" with parameter "artist=U2"
I developed this java code:
public class Constante {
public static final String SOAP_ACTION = "LyricWiki#getArtist";
public static final String METHOD_NAME = "getArtist";
public static final String NAMESPACE = "LyricWiki";
public static final String URL = "http://lyrics.wikia.com/server.php";
public static final String KEY_ARTIST = "artist";
}
import java.io.IOException;
import org.ksoap2.SoapEnvelope;
import org.ksoap2.serialization.PropertyInfo;
import org.ksoap2.serialization.SoapObject;
import org.ksoap2.serialization.SoapSerializationEnvelope;
import org.ksoap2.transport.HttpTransportSE;
import org.xmlpull.v1.XmlPullParserException;
public class TestWSDL {
public static void run() {
SoapObject soapclient = new SoapObject(Constante.NAMESPACE, Constante.METHOD_NAME);
// Yes you need this one in order to send the whole string or else only
// the first letter
// is going to be send
SoapObject parameters = new SoapObject(Constante.NAMESPACE, Constante.METHOD_NAME);
parameters.addProperty(Constante.KEY_ARTIST, "U2");
soapclient.addProperty(Constante.METHOD_NAME, parameters);
SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);
envelope.setOutputSoapObject(soapclient);
HttpTransportSE httpTransportSE = new HttpTransportSE(Constante.URL);
try {
httpTransportSE.call(Constante.SOAP_ACTION, envelope);
Object result = envelope.getResponse();
System.out.println(result);
} catch (IOException e) {
e.printStackTrace();
} catch (XmlPullParserException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void main(String[] args) {
run();
}
}
And I got:
org.xmlpull.v1.XmlPullParserException: unterminated entity ref (position:TEXT ?
I think that my problem is in the class "Constante" but I do not know the right format to use.
Any advice or code solution will be good for me,
Thanks in advance for your help and time
I tested using chartlyrics and I got the lyric,
Now, I share with you my code:
public class Constante {
public static final String SOAP_ACTION = "http://api.chartlyrics.com/SearchLyricDirect";
public static final String METHOD_NAME = "SearchLyricDirect";
public static final String NAMESPACE = "http://api.chartlyrics.com/";
public static final String URL = "http://api.chartlyrics.com/apiv1.asmx";
public static final String KEY_ARTIST = "artist";
public static final String KEY_SONG = "song";
}
import java.io.IOException;
import org.ksoap2.SoapEnvelope;
import org.ksoap2.SoapFault;
import org.ksoap2.serialization.SoapObject;
import org.ksoap2.serialization.SoapSerializationEnvelope;
import org.ksoap2.transport.HttpTransportSE;
import org.xmlpull.v1.XmlPullParserException;
public class TestWSDL {
public static void run2() throws SoapFault {
SoapObject request = new SoapObject(Constante.NAMESPACE,
Constante.METHOD_NAME);
request.addProperty(Constante.KEY_ARTIST, "U2");
request.addProperty(Constante.KEY_SONG, "One");
SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(
SoapEnvelope.VER11);
envelope.dotNet = true;
envelope.setOutputSoapObject(request);
HttpTransportSE androidHttpTransport = new HttpTransportSE(
Constante.URL);
try {
// call the web service method
androidHttpTransport.call(Constante.SOAP_ACTION, envelope);
} catch (Exception e) {
e.printStackTrace();
}// Next task is to get Response and format that response
SoapObject obj;
obj = (SoapObject) envelope.getResponse();
// System.out.println(obj);
System.out.println(obj.getProperty("TrackId"));
System.out.println(obj.getProperty("LyricChecksum"));
System.out.println(obj.getProperty("LyricId"));
System.out.println(obj.getProperty("LyricSong"));
System.out.println(obj.getProperty("LyricArtist"));
System.out.println(obj.getProperty("LyricUrl"));
System.out.println(obj.getProperty("LyricCovertArtUrl"));
System.out.println(obj.getProperty("LyricRank"));
System.out.println(obj.getProperty("LyricCorrectUrl"));
System.out.println(obj.getProperty("Lyric"));
}
public static void main(String[] args) {
try {
run2();
} catch (SoapFault e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
I hope to consume the LyricWiki soap api.
Regards.