/*
* JBoss, Home of Professional Open Source.
* Copyright 2012, Red Hat Middleware LLC, and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.as.test.integration.ws.wsrm;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Iterator;
import java.util.Set;
import javax.xml.namespace.QName;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPMessage;
import javax.xml.ws.WebServiceException;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.handler.soap.SOAPHandler;
import javax.xml.ws.handler.soap.SOAPMessageContext;
import org.jboss.logging.Logger;
import org.jboss.logging.Logger.Level;
/**
* @author <a href="mailto:rsvoboda@redhat.com">Rostislav Svoboda</a>
*/
public class ReliableCheckHandler implements SOAPHandler<SOAPMessageContext> {
/*
* 1 -- Body - CreateSequence
* 2 -- Body - CreateSequenceResponse
* 3 -- Header - wsrm:Sequence
* 4 -- Header - wsrm:SequenceAcknowledgement
*/
int status = 0;
private static Logger log = Logger.getLogger(ReliableCheckHandler.class);
public Set<QName> getHeaders() {
return null;
}
public boolean handleFault(SOAPMessageContext context) {
handleContext(context);
return true;
}
public boolean handleMessage(SOAPMessageContext context) {
handleContext(context);
return true;
}
private synchronized void handleContext(SOAPMessageContext smc) {
Boolean outboundProperty = (Boolean) smc.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
SOAPMessage message = smc.getMessage();
if (outboundProperty.booleanValue()) {
log.debug("Outgoing message:");
} else {
log.debug("Incoming message:");
}
log.debug("-----------");
try {
JBossLoggingOutputStream os = new JBossLoggingOutputStream(log, Level.DEBUG);
message.writeTo(os);
os.flush();
log.debug("");
} catch (Exception e) {
log.debug("Exception in handler: " + e);
}
log.debug("-----------");
SOAPElement firstBodyElement;
try {
switch (status % 4) {
case 0:
@SuppressWarnings("unchecked")
Iterator<SOAPElement> it = (Iterator<SOAPElement>) message.getSOAPBody().getChildElements();
if (it.hasNext()) {
firstBodyElement = it.next();
final QName createSequenceQName = new QName("http://schemas.xmlsoap.org/ws/2005/02/rm", "CreateSequence");
if (!createSequenceQName.equals(firstBodyElement.getElementQName())) {
throw new WebServiceException("CreateSequence in soap body was expected, but it contains '"
+ firstBodyElement.getElementQName() + "'");
}
status++;
} else {
//we could get multiple acknowledments
verifySequenceAcknowledgement(message);
}
break;
case 1:
firstBodyElement = (SOAPElement) message.getSOAPBody().getChildElements().next();
final QName createSequenceResponseQName = new QName("http://schemas.xmlsoap.org/ws/2005/02/rm", "CreateSequenceResponse");
if (!createSequenceResponseQName.equals(firstBodyElement.getElementQName())) {
throw new WebServiceException("CreateSequenceResponse in soap body was expected, but it contains '"
+ firstBodyElement.getElementQName() + "'");
}
status++;
break;
case 2:
Iterator headerElements = message.getSOAPHeader().getChildElements();
boolean found = false;
final QName sequenceQName = new QName("http://schemas.xmlsoap.org/ws/2005/02/rm", "Sequence");
while (headerElements.hasNext()) {
SOAPElement soapElement = (SOAPElement) headerElements.next();
if (sequenceQName.equals(soapElement.getElementQName())) {
found = true;
}
}
if (!found) {
throw new WebServiceException("wsrm:Sequence is not present in soap header");
}
status++;
break;
case 3:
if (verifySequenceAcknowledgement(message)) {
status++;
}
break;
}
} catch (SOAPException ex) {
throw new WebServiceException(ex.getMessage(), ex);
}
}
private boolean verifySequenceAcknowledgement(SOAPMessage message) throws SOAPException {
Iterator headerElements = message.getSOAPHeader().getChildElements();
boolean found = false;
boolean otherRMHeadersFound = false;
final QName sequenceAckQName = new QName("http://schemas.xmlsoap.org/ws/2005/02/rm", "SequenceAcknowledgement");
while (headerElements.hasNext()) {
SOAPElement soapElement = (SOAPElement) headerElements.next();
if (sequenceAckQName.equals(soapElement.getElementQName())) {
found = true;
} else if ("http://schemas.xmlsoap.org/ws/2005/02/rm".equals(soapElement.getNamespaceURI())) {
otherRMHeadersFound = true;
}
}
//fail if we did not find the sequence ack and the message has other WS-RM headers (hence out of order) or has body contents (hence a non-reliable message is being processed)
if (!found && (otherRMHeadersFound || message.getSOAPBody().getChildElements().hasNext())) {
throw new WebServiceException("wsrm:SequenceAcknowledgement is not present in soap header");
}
return found;
}
public void close(MessageContext context) {
}
private class JBossLoggingOutputStream extends OutputStream {
public static final int DEFAULT_BUFFER_LENGTH = 2048;
protected boolean hasBeenClosed = false;
protected byte[] buf;
protected int count;
private int bufLength;
protected Logger logger;
protected Level level;
JBossLoggingOutputStream(Logger log, Level level) throws IllegalArgumentException {
if (log == null) {
throw new IllegalArgumentException("Null category!");
}
if (level == null) {
throw new IllegalArgumentException("Null priority!");
}
this.level = level;
logger = log;
bufLength = DEFAULT_BUFFER_LENGTH;
buf = new byte[DEFAULT_BUFFER_LENGTH];
count = 0;
}
public void close() {
flush();
hasBeenClosed = true;
}
public void write(final int b) throws IOException {
if (hasBeenClosed) {
throw new IOException("The stream has been closed.");
}
// would this be writing past the buffer?
if (count == bufLength) {
// grow the buffer
final int newBufLength = bufLength + DEFAULT_BUFFER_LENGTH;
final byte[] newBuf = new byte[newBufLength];
System.arraycopy(buf, 0, newBuf, 0, bufLength);
buf = newBuf;
bufLength = newBufLength;
}
buf[count] = (byte) b;
count++;
}
public void flush() {
if (count == 0) {
return;
}
// don't print out blank lines; flushing from PrintStream puts
// out these
// For linux system
if (count == 1 && ((char) buf[0]) == '\n') {
reset();
return;
}
// For mac system
if (count == 1 && ((char) buf[0]) == '\r') {
reset();
return;
}
// On windows system
if (count == 2 && (char) buf[0] == '\r' && (char) buf[1] == '\n') {
reset();
return;
}
final byte[] theBytes = new byte[count];
System.arraycopy(buf, 0, theBytes, 0, count);
logger.log(level, new String(theBytes));
reset();
}
private void reset() {
// not resetting the buffer -- assuming that if it grew then it
// will likely grow similarly again
count = 0;
}
}
}