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?
Related
When I'm running a Java WebSocketStompClient, I got below error:
org.eclipse.jetty.websocket.api.MessageTooLargeException: Text message size [73728] exceeds maximum size [65536]
Sample code:
import org.apache.log4j.Logger;
import org.springframework.messaging.simp.stomp.StompFrameHandler;
import org.springframework.messaging.simp.stomp.StompHeaders;
import org.springframework.messaging.simp.stomp.StompSession;
import org.springframework.messaging.simp.stomp.StompSessionHandlerAdapter;
import org.springframework.util.concurrent.ListenableFuture;
import org.springframework.web.socket.WebSocketHttpHeaders;
import org.springframework.web.socket.client.WebSocketClient;
import org.springframework.web.socket.client.standard.StandardWebSocketClient;
import org.springframework.web.socket.messaging.WebSocketStompClient;
import org.springframework.web.socket.sockjs.client.SockJsClient;
import org.springframework.web.socket.sockjs.client.Transport;
import org.springframework.web.socket.sockjs.client.WebSocketTransport;
import org.springframework.web.socket.sockjs.frame.Jackson2SockJsMessageCodec;
import java.lang.reflect.Type;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ExecutionException;
public class HelloClient {
private static Logger logger = Logger.getLogger(HelloClient.class);
StompSession session;
private final static WebSocketHttpHeaders headers = new WebSocketHttpHeaders();
public ListenableFuture<StompSession> connect() {
Transport webSocketTransport = new WebSocketTransport(new StandardWebSocketClient());
List<Transport> transports = Collections.singletonList(webSocketTransport);
SockJsClient sockJsClient = new SockJsClient(transports);
sockJsClient.setMessageCodec(new Jackson2SockJsMessageCodec());
WebSocketStompClient stompClient = new WebSocketStompClient(sockJsClient);
long[] hb = stompClient.getDefaultHeartbeat();
boolean en = stompClient.isDefaultHeartbeatEnabled();
long timeout = stompClient.getReceiptTimeLimit();
String url = "https://www.test.com";
return stompClient.connect(url, headers, new MyHandler());
}
public void subscribeMsg(StompSession stompSession) throws ExecutionException, InterruptedException {
stompSession.subscribe("/topic/test", new StompFrameHandler() {
public Type getPayloadType(StompHeaders stompHeaders) {
return byte[].class;
}
public void handleFrame(StompHeaders stompHeaders, Object o) {
logger.info("Received message " + new String((byte[]) o));
String response = new String((byte[]) o);
}
});
}
private class MyHandler extends StompSessionHandlerAdapter {
public void afterConnected(StompSession stompSession, StompHeaders stompHeaders) {
logger.info("Now connected");
session = stompSession;
}
}
public boolean isConnected() {
try {
Thread.sleep(500);
return session != null && session.isConnected();
} catch (Exception e) {
logger.warn("Error happens when checking connection status, ", e);
return false;
}
}
public static void main(String[] args) throws Exception {
HelloClient helloClient = new HelloClient();
ListenableFuture<StompSession> f = helloClient.connect();
StompSession stompSession = f.get();
helloClient.subscribeMsg(stompSession);
while (true) {
if (!helloClient.isConnected()) {
logger.info("wss diconnected ");
logger.info("need re-create ");
}
}
}
}
How to increase the limitation for a Java stomp websocket client? I found some not related answers How can I set max buffer size for web socket client(Jetty) in Java which are not suitable for stomp websocket client.
Also tried stompClient.setInboundMessageSizeLimit(Integer.MAX_VALUE); which doesn't work.
I am looking for reference where I can get simple program to send a SNMP trap to Apache Kafka topic using Apache Camel.
Please help me if someone can explain the it using simple java program.
My RouteBuilder configuration
import org.apache.camel.builder.RouteBuilder;
public class SimpleRouteBuilder extends RouteBuilder{
#Override
public void configure() throws Exception {
String topicName = "topic=first_topic";
String kafkaServer = "kafka:localhost:9092";
String zooKeeperHost = "zookeeperHost=localhost&zookeeperPort=2181";
String serializerClass = "serializerClass=kafka.serializer.StringEncoder";
String toKafka = new StringBuilder().append(kafkaServer).append("?").append(topicName).append("&")
.append(zooKeeperHost).append("&").append(serializerClass).toString();
System.out.println(toKafka);
from("snmp:127.0.0.1:161?protocol=udp&type=POLL&oids=1.3.6.1.2.1.1.5.0").split().tokenize("\n").to(toKafka);
}
}
Main Method
import org.apache.camel.CamelContext;
import org.apache.camel.impl.DefaultCamelContext;
import org.snmp4j.Snmp;
public class MainApp {
public static void main(String[] args) {
SimpleRouteBuilder routeBuilder = new SimpleRouteBuilder();
CamelContext ctx = new DefaultCamelContext();
try {
ctx.addRoutes(routeBuilder);
ctx.start();
Thread.sleep(5 * 60 * 1000);
ctx.stop();
}
catch (Exception e) {
e.printStackTrace();
}
}
}
I was in wrong direction. The write direction is as below -
Create a Trap sender program.
Create Trap receiver/listener program.
Inside Trap receiver or listener, receive trap and send it to Apache Kafka topic through Apache camel.
POM.XML
add below dependencies -
camel-core
snmp4j
camel-kafka
Trap Sender Program
package <>;
import org.apache.camel.CamelContext;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.impl.DefaultCamelContext;
import org.snmp4j.*;
import org.snmp4j.event.ResponseEvent;
import org.snmp4j.mp.MPv2c;
import org.snmp4j.mp.MPv3;
import org.snmp4j.mp.SnmpConstants;
import org.snmp4j.security.*;
import org.snmp4j.smi.*;
import org.snmp4j.transport.DefaultUdpTransportMapping;
import java.util.Date;
public class Trapsender {
public static final String community = "public";
public static final String Oid = ".1.3.6.1.2.1.1.8";
public static final String ipAddress = "127.0.0.1";
public static final int port = 162;
public static void main(String[] args) {
Trapsender trapv3 = new Trapsender();
trapv3.sendTrap_Version3();
}
public void sendTrap_Version3() {
try {
// Create Transport Mapping
TransportMapping transport = new DefaultUdpTransportMapping();
transport.listen();
// Create Target
CommunityTarget cTarget = new CommunityTarget();
cTarget.setCommunity(new OctetString(community));
cTarget.setVersion(SnmpConstants.version2c);
cTarget.setAddress(new UdpAddress(ipAddress + "/" + port));
cTarget.setRetries(2);
cTarget.setTimeout(10000);
// Create PDU for V3
PDU pdu = new PDU();
pdu.setType(PDU.TRAP);
// need to specify the system up time
pdu.add(new VariableBinding(SnmpConstants.sysUpTime, new OctetString(new Date().toString())));
pdu.add(new VariableBinding(SnmpConstants.snmpTrapOID, new OID(Oid)));
pdu.add(new VariableBinding(new OID(Oid), new OctetString("Major")));
// Send the PDU
Snmp snmp = new Snmp(transport);
System.out.println("Sending V2 Trap... Check Wheather NMS is Listening or not? ");
ResponseEvent send = snmp.send(pdu, cTarget);
snmp.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
Receiver Trap with Apache Camel
package <>;
import org.apache.camel.CamelContext;
import org.apache.camel.ProducerTemplate;
import org.apache.camel.RoutesBuilder;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.impl.DefaultCamelContext;
import org.snmp4j.*;
import org.snmp4j.mp.MPv1;
import org.snmp4j.mp.MPv2c;
import org.snmp4j.security.Priv3DES;
import org.snmp4j.security.SecurityProtocols;
import org.snmp4j.smi.OctetString;
import org.snmp4j.smi.TcpAddress;
import org.snmp4j.smi.TransportIpAddress;
import org.snmp4j.smi.UdpAddress;
import org.snmp4j.transport.AbstractTransportMapping;
import org.snmp4j.transport.DefaultTcpTransportMapping;
import org.snmp4j.transport.DefaultUdpTransportMapping;
import org.snmp4j.util.MultiThreadedMessageDispatcher;
import org.snmp4j.util.ThreadPool;
import java.io.IOException;
public class Trapreceiver implements CommandResponder {
public static CamelContext ctx=null;
public static ProducerTemplate producer=null;
public static void main(String[] args) {
Trapreceiver snmp4jTrapReceiver = new Trapreceiver();
SimpleRouteBuilder routeBuilder = new SimpleRouteBuilder();
ctx = new DefaultCamelContext();
producer = ctx.createProducerTemplate();
try {
ctx.addRoutes(routeBuilder);
ctx.start();
}
catch (Exception e) {
e.printStackTrace();
}
// producer.sendBody("direct:start", snmp);
try {
snmp4jTrapReceiver.listen(new UdpAddress("localhost/162"), producer);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* Trap Listner
*/
public synchronized void listen(TransportIpAddress address, ProducerTemplate producer)
throws IOException {
AbstractTransportMapping transport;
if (address instanceof TcpAddress) {
transport = new DefaultTcpTransportMapping((TcpAddress) address);
} else {
transport = new DefaultUdpTransportMapping((UdpAddress) address);
}
ThreadPool threadPool = ThreadPool.create("DispatcherPool", 10);
MessageDispatcher mDispathcher = new MultiThreadedMessageDispatcher(
threadPool, new MessageDispatcherImpl());
// add message processing models
mDispathcher.addMessageProcessingModel(new MPv1());
mDispathcher.addMessageProcessingModel(new MPv2c());
// add all security protocols
SecurityProtocols.getInstance().addDefaultProtocols();
SecurityProtocols.getInstance().addPrivacyProtocol(new Priv3DES());
// Create Target
CommunityTarget target = new CommunityTarget();
target.setCommunity(new OctetString("public"));
Snmp snmp = new Snmp(mDispathcher, transport);
snmp.addCommandResponder(this);
transport.listen();
System.out.println("Listening on " + address);
try {
this.wait();
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
}
/**
* This method will be called whenever a pdu is received on the given port
* specified in the listen() method
*/
public synchronized void processPdu(CommandResponderEvent cmdRespEvent) {
System.out.println("Received PDU...");
PDU pdu = cmdRespEvent.getPDU();
if (pdu != null) {
System.out.println("Trap Type = " + pdu.getType());
System.out.println("Variables = " + pdu.getVariableBindings());
producer.sendBody("direct:start","Variables = " + pdu.getVariableBindings() );
}
}
}
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'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 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>