/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can obtain
* a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
* or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
* Sun designates this particular file as subject to the "Classpath" exception
* as provided by Sun in the GPL Version 2 section of the License file that
* accompanied this code. If applicable, add the following below the License
* Header, with the fields enclosed by brackets [] replaced by your own
* identifying information: "Portions Copyrighted [year]
* [name of copyright owner]"
*
* Contributor(s):
*
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
/*
* RMServerPipe.java
*
* @author Mike Grogan
* @author Bhakti Mehta
* Created on February 7, 2006, 2:10 PM
*
*/
package com.sun.xml.ws.rm.jaxws.runtime.server;
import com.sun.xml.ws.api.BindingID;
import com.sun.xml.ws.api.SOAPVersion;
import com.sun.xml.ws.api.WSBinding;
import com.sun.xml.ws.api.addressing.AddressingVersion;
import com.sun.xml.ws.api.addressing.WSEndpointReference;
import com.sun.xml.ws.api.message.Header;
import com.sun.xml.ws.api.message.*;
import com.sun.xml.ws.api.message.Message;
import com.sun.xml.ws.api.model.wsdl.WSDLPort;
import com.sun.xml.ws.api.pipe.Pipe;
import com.sun.xml.ws.api.pipe.PipeCloner;
import com.sun.xml.ws.api.server.WSEndpoint;
import com.sun.xml.ws.api.message.Headers;
import com.sun.xml.ws.rm.*;
import com.sun.xml.ws.rm.jaxws.runtime.InboundSequence;
import com.sun.xml.ws.rm.jaxws.runtime.OutboundSequence;
import com.sun.xml.ws.rm.jaxws.runtime.PipeBase;
import com.sun.xml.ws.rm.jaxws.runtime.SequenceConfig;
import com.sun.xml.ws.rm.jaxws.runtime.client.ProtocolMessageReceiver;
import com.sun.xml.ws.rm.jaxws.util.LoggingHelper;
import com.sun.xml.ws.rm.protocol.*;
import com.sun.xml.ws.runtime.util.Session;
import com.sun.xml.ws.runtime.util.SessionManager;
import com.sun.xml.ws.security.SecurityContextToken;
import com.sun.xml.ws.security.trust.WSTrustElementFactory;
import com.sun.xml.ws.security.trust.elements.str.DirectReference;
import com.sun.xml.ws.security.trust.elements.str.SecurityTokenReference;
import com.sun.xml.wss.impl.MessageConstants;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.namespace.QName;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPFactory;
import javax.xml.soap.SOAPFault;
import javax.xml.ws.WebServiceException;
import javax.xml.ws.wsaddressing.W3CEndpointReference;
import javax.xml.ws.soap.SOAPBinding;
import java.net.URI;
import java.util.HashMap;
import java.util.ResourceBundle;
import java.util.logging.Logger;
import java.util.logging.Level;
//import com.sun.xml.ws.rm.jaxws.util.LoggingHelper;
/**
* Server-side RM Pipe implementation
*/
public class RMServerPipe extends PipeBase<RMDestination,
ServerOutboundSequence,
ServerInboundSequence> {
public static final Logger logger =
Logger.getLogger(LoggingHelper.getLoggerName(RMServerPipe.class),
Messages.class.getName());
public static final LoggingHelper loggingHelper = new LoggingHelper(logger);
private static HashMap<String, ActionHandler> actionMap =
new HashMap<String, ActionHandler>();
private RMConstants constants;
protected WSDLPort wsdlModel;
protected WSEndpoint owner;
protected SequenceConfig config;
private boolean secureReliableMessaging = false;
protected WSBinding binding;
private SessionManager sessionManager =
SessionManager.getSessionManager();
//populate map if wsa:Action values for protocol messages to handlers used to process
//messages with those headers
static {
initActionMap();
}
/**
* Constructor is passed everything available in PipelineAssembler.
*
* @param wsdlModel The WSDLPort
* @param owner The WSEndpoint.
* @param nextPipe The next Pipe in the pipeline.
*
*/
public RMServerPipe(WSDLPort wsdlModel,
WSEndpoint owner,
Pipe nextPipe) {
super(RMDestination.getRMDestination(), nextPipe);
this.wsdlModel = wsdlModel;
this.owner = owner;
this.binding = this.owner.getBinding();
this.config = getSequenceConfig();
this.constants = config.getRMConstants();
this.unmarshaller = constants.createUnmarshaller();
this.marshaller = constants.createMarshaller();
}
/**
* Copy constructor used by <code>copy</code> method.
*
* @param toCopy to be copied.
* @param cloner passed as an argument to copy.
*/
private RMServerPipe( RMServerPipe toCopy, PipeCloner cloner) {
super(RMDestination.getRMDestination(), null);
cloner.add(toCopy, this);
nextPipe = cloner.copy(toCopy.nextPipe);
wsdlModel = toCopy.wsdlModel;
owner = toCopy.owner;
config = toCopy.config;
binding = owner.getBinding();
this.constants = RMConstants.getRMConstants(binding.getAddressingVersion());
this.unmarshaller = constants.createUnmarshaller();
this.marshaller = constants.createMarshaller();
//RMConstants.setAddressingVersion(binding.getAddressingVersion());
}
public Packet process(Packet packet) {
com.sun.xml.ws.rm.Message message = null;
ServerInboundSequence inboundSequence ;
SOAPFault soapFault = null;
try {
//handle special protocol messages
Packet ret = null;
try {
ret = handleProtocolMessage(packet);
} catch (CreateSequenceException e) {
soapFault = newCreateSequenceRefusedFault(e);
} catch (TerminateSequenceException e ) {
soapFault = newSequenceTerminatedFault(e);
} catch (InvalidSequenceException e){
soapFault = newUnknownSequenceFault(e);
}
if (ret != null) {
//the contract of handleProtocolMessage is to return null if messager is non-protocol
//message or protocol message is piggybacked on an application message.
return ret;
}
//If we got here, this is an application message
//do inbound bookkeeping
try {
message = handleInboundMessage(packet);
} catch (MessageNumberRolloverException e) {
soapFault = newMessageNumberRolloverFault(e);
} catch (InvalidSequenceException e){
soapFault = newUnknownSequenceFault(e);
}
Packet retPacket = null;
if (soapFault != null){
Message m = com.sun.xml.ws.api.message.Messages.create(soapFault);
//SequenceFault is to be added for only SOAP 1.1
if (binding.getSOAPVersion() == SOAPVersion.SOAP_11) {
//FIXME - need JAXBRIContext that can marshall SequenceFaultElement
Header header = Headers.create(constants.getJAXBContext(), new SequenceFaultElement());
m.getHeaders().add(header);
}
retPacket = packet.createServerResponse(
m,constants.getAddressingVersion() ,
binding.getSOAPVersion(),
constants.getAddressingVersion().getDefaultFaultAction());
retPacket.setMessage(m);
return retPacket;
}
//allow diagnostic access to message if ProcessingFilter has been specified
if (filter != null) {
filter.handleEndpointRequestMessage(message);
}
//If Message is a duplicate, handleInboundMessage threw a DuplicateMessageException,
//Therefore, this is the first time processing this message.
//use sequence id to initialize inboundSequence and outboundSequence
//local variables by doing a lookup in RMDestination
inboundSequence =
(ServerInboundSequence)message.getSequence();
if (inboundSequence == null ) {
logger.log(Level.SEVERE, Messages.NOT_RELIABLE_SEQ_OR_PROTOCOL_MESSAGE.format()
);
throw new RMException(Messages.NOT_RELIABLE_SEQ_OR_PROTOCOL_MESSAGE.format());
}
//reset inactivity timer
inboundSequence.resetLastActivityTime();
ServerOutboundSequence outboundSequence =
(ServerOutboundSequence)inboundSequence.getOutboundSequence();
//determine whether the correct STR has been used to sign message
if (secureReliableMessaging)
checkSTR(packet, inboundSequence);
//set com.sun.xml.ws.session and com.sun.xml.ws.sessionid
//invocationProperties if they have not already been set
//by SC pipe.
setSessionData(packet,
inboundSequence);
//If ordered deliver is configured,
//Block here if InboundSequence reports gaps before this message.
inboundSequence.holdIfUndeliverable(message);
//clear packet.transporBackChannel so downstream pipes do not prevent
//empty one-way response bodies to be sent back when we need to use the
//bodies for RM SequenceAcknowledgemnts.
packet.transportBackChannel = null;
//make these available in an injected WebServiceContext
packet.invocationProperties.put(Constants.sequenceProperty,
inboundSequence);
packet.invocationProperties.put(Constants.messageNumberProperty,
message.getMessageNumber());
ret = nextPipe.process(packet);
// FIXME
// This shouldn't be necessary, but havint messageNumberProperty
// set has side-effects here due to the fact that RMClientPipe
// and RMServerPipe share an implementation of handleOutboundMessage
packet.invocationProperties.put(Constants.sequenceProperty,
null);
packet.invocationProperties.put(Constants.messageNumberProperty,
null);
Message responseMessage = ret.getMessage();
Message emptyMessage ;
if (responseMessage == null) {
//This a one-way response. handleOutboundMessage
//might need a message to write sequenceAcknowledgent headers to.
//Give it one.
emptyMessage = com.sun.xml.ws.api.message.Messages.createEmpty(config.getSoapVersion());
ret.setMessage(emptyMessage);
//propogate this information so handleOutboundMessage will not
//add this to the outbound sequence, if any.
ret.invocationProperties.put("onewayresponse",true);
}
//If ordered delivery is configured, unblock the next message in the sequence
//if it is waiting for this one to be delivered.
inboundSequence.releaseNextMessage(message);
//Let Outbound sequence do its bookkeeping work, which consists of writing
//outbound RM headers.
//need to handle any error caused by
//handleOutboundMessage.. Request has already been processed
//by the endpoint.
com.sun.xml.ws.rm.Message om =
handleOutboundMessage(outboundSequence, ret);
//allow diagnostic access to outbound message if ProcessingFilter is
//specified
if (filter != null) {
filter.handleEndpointResponseMessage(om);
}
//If we populated
//ret with an empty message to be used by RM protocol, and it
//was not used, get rid of the empty message.
if (responseMessage == null &&
ret.getMessage() != null &&
!ret.getMessage().hasHeaders()) {
ret.setMessage(null);
} else {
//Fill in relatedMessage field in request message for use in case request is resent.
//the com.sun.xml.ws.api.message.Message referenced will be a copy of the one
//contained in the returned packet. See implementation of message.setRelatedMessage.
message.setRelatedMessage(om);
// MS client expects SequenceAcknowledgement action incase of oneway messages
if (responseMessage == null && ret.getMessage() != null) {
HeaderList headerList = ret.getMessage().getHeaders();
headerList.add(Headers.create(constants.getAddressingVersion().actionTag,
Constants.SEQUENCE_ACKNOWLEDGEMENT_ACTION));
}
}
return ret;
} catch (BufferFullException e) {
//need to return message with empty body and SequenceAcknowledgement
//header for inboundSequence. This is similar to handleAckRequestedAction, which
//should do much the same thing. The only difference is that handleAckRequestedAction
//must also have logic to get the inboundSequence from the AckRequested header in the
//packet
if (packet.getMessage().isOneWay(wsdlModel)) {
//refuse to process the request. Client will retry
Packet ret = new Packet();
ret.invocationProperties.putAll(packet.invocationProperties);
return ret;
}
//handleInboundMessage shouldn't let inboundSequence be null.
try {
ServerInboundSequence seq = (ServerInboundSequence)e.getSequence();
if (seq != null) {
return generateAckMessage(packet, seq,
constants.getSequenceAcknowledgementAction());
} else {
//unreachable
return null;
}
} catch (RMException ee) {
logger.severe(Messages.ACKNOWLEDGEMENT_MESSAGE_EXCEPTION.format() +e);
throw new WebServiceException(Messages.ACKNOWLEDGEMENT_MESSAGE_EXCEPTION.format() +e);
}
} catch (DuplicateMessageException e) {
// 1. If one-way return empty message without invoking process on next pipe
// 2. If two-way, formulate response according to secret Microsoft protocol.
// a. If original was processed and response is still available in OutboundSequence,
// return it again.
// b. Otherwise (original not yet processed or response already discarded, return
// ack message.
if (packet.getMessage().isOneWay(wsdlModel)) {
//ignore the message.
Packet ret = new Packet();
ret.invocationProperties.putAll(packet.invocationProperties);
return ret;
} else {
//check whether original response is available.
com.sun.xml.ws.api.message.Message response ;
com.sun.xml.ws.rm.Message original = e.getRMMessage();
com.sun.xml.ws.rm.Message origresp = original.getRelatedMessage();
if (origresp != null) {
response = origresp.getCopy();
if (response != null) {
//original response is available, resend it.
Packet ret = new Packet(response);
ret.invocationProperties.putAll(packet.invocationProperties);
return ret;
}
}
//either the original request is waiting to be processing, or the response has been
//acked and thrown away. All we can do is return a SequenceAcknowledgement.
try {
ServerInboundSequence seq =
(ServerInboundSequence)original.getSequence();
return generateAckMessage(packet, seq,
constants.getSequenceAcknowledgementAction());
} catch (RMException ee) {
throw new WebServiceException(ee);
}
}
} catch (RMException e) {
//see if a RM Fault type has been assigned to this exception type. If so, let
//the exception generate a fault message. Otherwise, wrap as WebServiceException and
//rethrow.
Message m = e.getFaultMessage();
if (m != null) {
return new Packet(m);
} else {
throw new WebServiceException(e);
}
} catch (RuntimeException e) {
throw new WebServiceException(e);
}
}
public void preDestroy() {
//nothing to do here so far
nextPipe.preDestroy();
}
public Pipe copy(PipeCloner cloner) {
return new RMServerPipe(this, cloner);
}
/**
* Handle a non-Application message. Look at the wsa:Action header and if it is
* one mapped to a handler in our actionHandlers dispatch table, invoke the handler.
*
* @param packet The Packet containing the incoming message
* @return The Packet returned by invocation of the handler
* null if no handler is registered
* @throws RMException - if any thrown by the handler.
*/
public Packet handleProtocolMessage(Packet packet) throws RMException {
ActionHandler handler ;
String actionValue ;
actionValue = packet.getMessage()
.getHeaders().getAction(constants.getAddressingVersion(),
config.getSoapVersion());
if (actionValue == null || actionValue.equals("")) {
logger.severe(Messages.NON_RM_REQUEST_OR_MISSING_WSA_ACTION_HEADER.format());
throw new RMException(Messages.NON_RM_REQUEST_OR_MISSING_WSA_ACTION_HEADER.format() )
;
}
handler = actionMap.get(actionValue);
if (handler != null) {
return handler.process(this, packet);
} else {
return null;
}
}
/**********************************/
/* Handlers for wsa:Action values */
/**********************************/
public Packet handleCreateSequenceAction(Packet packet) throws RMException{
CreateSequenceElement csrElement;
Identifier id ;
String offeredId = null;
Message message = packet.getMessage();
try {
csrElement = message.readPayloadAsJAXB(unmarshaller);
} catch (JAXBException e) {
logger.severe(Messages.CREATESEQUENCE_HEADER_PROBLEM.format() + e);
throw new RMException(Messages.CREATESEQUENCE_HEADER_PROBLEM.format() + e);
}
/**ADDRESSING_FIXME
* Assume for now that AcksTo is anonymous.
*/
URI acksTo = constants.getAnonymousURI();
/*String acksToString = acksTo.toString();*/
/*String replyToString = acksToString;*/
/*
EndpointReference replyTo = inboundAddressingProperties.getReplyTo();
if (replyTo == null) {
replyTo = addressingBuilder.newEndpointReference( ac.getAnonymousURI());
inboundAddressingProperties.setReplyTo(replyTo);
}
EndpointReference acksTo = csrElement.getAcksTo();
String ackstoUri = acksTo.getAddress().getURI().toString();
String replytoUri = replyTo.getAddress().getURI().toString();
if (!ackstoUri.equals(replytoUri)){
throw new CreateSequenceException(Messages.ACKSTO_NOT_EQUAL_REPLYTO.format(ackstoUri,replytoUri) );
}
*/
OfferType offer = csrElement.getOffer();
if (offer != null) {
id = offer.getIdentifier();
if (id != null) {
offeredId = id.getValue();
}
}
//create server-side data structures.
ServerInboundSequence inboundSequence =
provider.createSequence(acksTo,
null, //assign random id
offeredId,
config);
//start the inactivity timer
inboundSequence.resetLastActivityTime();
//TODO.. Read STR element in csrElement if any
this.secureReliableMessaging = csrElement.getSecurityTokenReference()!=null?true:false;
if (this.secureReliableMessaging) {
com.sun.xml.ws.security.secext10.SecurityTokenReferenceType strType= csrElement.getSecurityTokenReference();
SecurityContextToken sct = (SecurityContextToken)packet.invocationProperties.get(MessageConstants.INCOMING_SCT);
if (sct != null){
String strId = sct.getIdentifier().toString();
WSTrustElementFactory wsTrustElemFactory = WSTrustElementFactory.newInstance();
JAXBElement jaxbElem = new com.sun.xml.ws.security.secext10.ObjectFactory().createSecurityTokenReference(strType);
SecurityTokenReference str = wsTrustElemFactory.createSecurityTokenReference(jaxbElem);
com.sun.xml.ws.security.trust.elements.str.Reference ref = str.getReference();
if (ref instanceof com.sun.xml.ws.security.trust.elements.str.DirectReference) {
DirectReference directRef = (DirectReference)ref;
String gotId = directRef.getURIAttr().toString();
if (gotId.equals(strId)){
inboundSequence.setStrId(strId);
} else {
throw new RMSecurityException(Messages.SECURITY_TOKEN_AUTHORIZATION_ERROR.format(gotId ,strId ));
}
} else throw new RMSecurityException(Messages.SECURITY_REFERENCE_ERROR.format( ref.getClass().getName()));
} else throw new RMSecurityException(Messages.NULL_SECURITY_TOKEN.format());
}
startSession(inboundSequence);
if (offeredId == null) {
inboundSequence.getOutboundSequence().saveMessages = false;
}
//initialize CreateSequenceResponseElement
CreateSequenceResponseElement crsElement = new CreateSequenceResponseElement();
AcceptType accept ;
Identifier id2 = new Identifier();
id2.setValue(inboundSequence.getId());
crsElement.setIdentifier(id2);
URI dest;
if (offeredId != null) {
String destString = message.getHeaders()
.getTo(constants.getAddressingVersion(),
config.getSoapVersion());
try {
dest = new URI(destString);
} catch (Exception e) {
logger.severe(Messages.INVALID_OR_MISSING_TO_ON_CS_MESSAGE.format()) ;
throw new RMException(Messages.INVALID_OR_MISSING_TO_ON_CS_MESSAGE.format()
);
}
accept = new AcceptType();
W3CEndpointReference endpointReference ;
WSEndpointReference wsepr = new WSEndpointReference(dest,constants.getAddressingVersion());
if ( constants.getAddressingVersion()== AddressingVersion.W3C){
endpointReference = (W3CEndpointReference)wsepr.toSpec();
accept.setAcksTo(endpointReference);
} /*else {
//TODO support MemberSubmissionEndpointReference when issue 131 of JAXB is resolved
//endpointReference = (MemberSubmissionEndpointReference)wsepr.toSpec() ;
}*/
crsElement.setAccept(accept);
}
Message response = com.sun.xml.ws.api.message.Messages.create(constants.getJAXBContext(),
crsElement,
config.getSoapVersion());
message.assertOneWay(false);
/*ADDRESSING_FIXME
* This will probably be broken with MS client if they still send CS with
* missing reply-to.
*/
Packet ret = packet.createServerResponse(response, constants.getAddressingVersion(),
config.getSoapVersion(),
Constants.CREATE_SEQUENCE_RESPONSE_ACTION);
/*
ret.setEndPointAddressString(acksToString);
ret.proxy = packet.proxy;
//there are some invocation properties. Outgoing addressing headers at least
ret.invocationProperties.putAll(packet.invocationProperties);
//Set addressing headers
AddressingProperties outboundAddressingProperties =
addressingBuilder.newAddressingProperties();
//outboundAddressingProperties.initializeAsReply(inboundAddressingProperties, false);
outboundAddressingProperties.initializeAsReply(inboundAddressingProperties);
//AddressingProperties.initializeAsResponse does not know how to set outbound Action
//property.
outboundAddressingProperties.setAction(addressingBuilder.newURI(
constants.getCreateSequenceResponseAction()));
ret.invocationProperties.put(JAXWSAConstants.SERVER_ADDRESSING_PROPERTIES_OUTBOUND,
outboundAddressingProperties);
*/
return ret;
}
public Packet handleTerminateSequenceAction(Packet packet)
throws RMException {
TerminateSequenceElement tsElement ;
Message message = packet.getMessage();
try {
tsElement = message.readPayloadAsJAXB(unmarshaller);
} catch (JAXBException e) {
logger.severe(Messages.TERMINATE_SEQUENCE_EXCEPTION.format() + e);
throw new TerminateSequenceException(Messages.TERMINATE_SEQUENCE_EXCEPTION.format() + e);
}
String id = tsElement.getIdentifier().getValue();
ServerInboundSequence seq = provider.getInboundSequence(id);
if (seq == null) {
logger.severe(String.format(Constants.UNKNOWN_SEQUENCE_TEXT + id));
throw new InvalidSequenceException(String.format(Constants.UNKNOWN_SEQUENCE_TEXT,id),id);
}
//end the session if we own its lifetime..i.e. SC is not
//present
endSession(seq);
provider.terminateSequence(id);
//formulate response if required
Packet ret ;
OutboundSequence outboundSequence = seq.getOutboundSequence();
//If there is an "real" outbound sequence, client expects us to terminate it.
if (outboundSequence.saveMessages) {
TerminateSequenceElement terminateSeqResponse = new TerminateSequenceElement();
Identifier id2 = new Identifier();
id2.setValue(outboundSequence.getId());
terminateSeqResponse.setIdentifier(id2);
Message response = com.sun.xml.ws.api.message.Messages.create(constants.getJAXBContext(),
terminateSeqResponse,
config.getSoapVersion());
//ret = packet.createServerResponse(response, wsdlModel, binding);
ret = packet.createServerResponse(response,
constants.getAddressingVersion(),
config.getSoapVersion(), Constants.TERMINATE_SEQUENCE_ACTION);
SequenceAcknowledgementElement element = seq.generateSequenceAcknowledgement(null, marshaller);
//Header header = Headers.create(config.getSoapVersion(),marshaller,element);
//Header actionHeader = Headers.create(constants.getAddressingVersion().actionTag,
// Constants.TERMINATE_SEQUENCE_ACTION);
Header header = createHeader(element);
response.getHeaders().add(header);
//response.getHeaders().add(actionHeader);
} else {
packet.transportBackChannel.close();
ret = new Packet(null);
}
return ret;
}
public Packet handleLastMessageAction(Packet inbound) throws RMException {
try {
Message message = inbound.getMessage();
Header header = message.getHeaders().get(constants.getSequenceQName(), true);
if (header == null) {
logger.severe(Messages.INVALID_LAST_MESSAGE.format());
throw new RMException(Messages.INVALID_LAST_MESSAGE.format());
}
SequenceElement el = (SequenceElement)header.readAsJAXB(unmarshaller);
String id = el.getId();
ServerInboundSequence seq = provider.getInboundSequence(id);
if (seq == null) {
logger.severe(String.format(Constants.UNKNOWN_SEQUENCE_TEXT,id +id));
throw new InvalidSequenceException(String.format(Constants.UNKNOWN_SEQUENCE_TEXT,id),id);
}
//add message to ClientInboundSequence so that this message
//number appears in sequence acknowledgement
int messageNumber = el.getNumber();
seq.set(messageNumber, new com.sun.xml.ws.rm.Message(message));
return generateAckMessage(inbound, seq, constants.getLastAction());
} catch (JAXBException e) {
logger.severe(Messages.LAST_MESSAGE_EXCEPTION.format() +e);
throw new RMException(Messages.LAST_MESSAGE_EXCEPTION.format() +e);
}
}
public Packet handleAckRequestedAction(Packet inbound) throws RMException {
try {
Message message = inbound.getMessage();
Header header = message.getHeaders().get(constants.getAckRequestedQName(), true);
if (header == null) {
logger.severe(Messages.INVALID_ACK_REQUESTED.format());
throw new RMException(Messages.INVALID_ACK_REQUESTED.format());
}
AckRequestedElement el = (AckRequestedElement)header
.readAsJAXB(unmarshaller);
String id = el.getId();
ServerInboundSequence seq = provider.getInboundSequence(id);
if (seq == null) {
logger.severe(Constants.UNKNOWN_SEQUENCE_TEXT + id);
throw new InvalidSequenceException(String.format(Constants.UNKNOWN_SEQUENCE_TEXT,id),id);
}
seq.resetLastActivityTime();
return generateAckMessage(inbound, seq,
constants.getSequenceAcknowledgementAction());
} catch (JAXBException e) {
logger.severe(Messages.ACK_REQUESTED_EXCEPTION.format());
throw new RMException(Messages.ACK_REQUESTED_EXCEPTION.format() +e);
}
}
/**
* Handles a raw SequenceAcknowledgement
*/
public Packet handleSequenceAcknowledgementAction(Packet inbound) throws RMException {
try {
Message message = inbound.getMessage();
Header header = message.getHeaders().get(constants.getSequenceAcknowledgementQName(),false);
if (header == null) {
logger.severe(Messages.INVALID_SEQ_ACKNOWLEDGEMENT.format());
throw new RMException(Messages.INVALID_SEQ_ACKNOWLEDGEMENT.format());
}
SequenceAcknowledgementElement el = (SequenceAcknowledgementElement)header
.readAsJAXB(unmarshaller);
String id = el.getId();
ServerInboundSequence seq = provider.getInboundSequence(id);
//reset inactivity timer
//Fixed redundant null check bug found by Findbugs
//seq.resetLastActivityTime();
//reset inactivity timer
seq.resetLastActivityTime();
handleInboundMessage(inbound);
inbound.transportBackChannel.close();
Packet ret = new Packet(null);
ret.invocationProperties.putAll(inbound.invocationProperties);
return ret;
} catch (JAXBException e) {
logger.severe(Messages.SEQ_ACKNOWLEDGEMENT_EXCEPTION.format());
throw new RMException(Messages.SEQ_ACKNOWLEDGEMENT_EXCEPTION.format() +e);
}
}
/**
* This part of a Plugfest hack. We are trying to support "non-addressable client"
* scenarios wherein protocol responses are received on separate HTTP connections.
* A request containing a CreateSequenceElement has been sent to the endpoint and
* we are hosting an endpoint in ProtocolMessageReceiver and this handler will be
* called in the Pipeline for that endpoint. ProtocolMessageReceiver will correlate
* the response culled from the message here, with the request.
*/
public Packet handleCreateSequenceResponseAction(Packet inbound) throws RMException {
/*
* ADDRESSING_FIXME
* Fix this when we need to support non-anonymous acksTo
*/
return null;
/*
CreateSequenceResponseElement csrElement ;
Message message = inbound.getMessage();
AddressingProperties inboundAddressingProperties =
(AddressingProperties)(inbound.invocationProperties
.get(JAXWSAConstants.SERVER_ADDRESSING_PROPERTIES_INBOUND));
try {
csrElement = message.readPayloadAsJAXB(unmarshaller);
} catch (JAXBException e) {
throw new RMException(Messages.INVALID_CREATE_SEQUENCE_RESPONSE.format() +e);
}
Relationship[] relatesTo = inboundAddressingProperties.getRelatesTo();
Relationship relationship ;
if (relatesTo == null || null == (relationship = relatesTo[0])){
throw new RMException(Messages.CREATE_SEQUENCE_CORRELATION_ERROR.format());
}
String messageId = relationship.getID().toString();
ProtocolMessageReceiver.setCreateSequenceResponse(messageId, csrElement);
inbound.transportBackChannel.close();
Packet ret = new Packet(null);
ret.invocationProperties.putAll(inbound.invocationProperties);
return ret;
*/
}
/***********************************************************************************/
/* Wiring for dispatch Map mapping wsa:Action values to handlers. We are jumping */
/* through some hoops here to create a Map that only needs to be initialized once. */
/***********************************************************************************/
private interface ActionHandler {
public Packet process(RMServerPipe pipe, Packet packet)
throws RMException ;
}
private static void initActionMap(){
actionMap.put(Constants.CREATE_SEQUENCE_ACTION,
new ActionHandler() {
public Packet process(RMServerPipe pipe, Packet packet)
throws RMException {
return pipe.handleCreateSequenceAction(packet);
}
});
actionMap.put(Constants.TERMINATE_SEQUENCE_ACTION,
new ActionHandler() {
public Packet process(RMServerPipe pipe, Packet packet)
throws RMException {
return pipe.handleTerminateSequenceAction(packet);
}
});
actionMap.put(Constants.ACK_REQUESTED_ACTION,
new ActionHandler() {
public Packet process(RMServerPipe pipe, Packet packet)
throws RMException {
return pipe.handleAckRequestedAction(packet);
}
});
actionMap.put(Constants.LAST_MESSAGE_ACTION,
new ActionHandler() {
public Packet process(RMServerPipe pipe, Packet packet)
throws RMException {
return pipe.handleLastMessageAction(packet);
}
});
actionMap.put(Constants.CREATE_SEQUENCE_RESPONSE_ACTION,
new ActionHandler() {
public Packet process(RMServerPipe pipe, Packet packet)
throws RMException {
return pipe.handleCreateSequenceResponseAction(packet);
}
});
actionMap.put(Constants.SEQUENCE_ACKNOWLEDGEMENT_ACTION,
new ActionHandler() {
public Packet process(RMServerPipe pipe, Packet packet)
throws RMException {
return pipe.handleSequenceAcknowledgementAction(packet);
}
});
}
/*
* Private helper functions.
*/
/**
* Returns a message containing a fault defined by the WS-RM spec.
*
* @param
* e An Exception mapped to a WS-RM defined fault.
* @return
* The mapped fault
* @throws
* Exception - Exceptions not mapped to well-known fault types are
* rethrown.
*/
/**
* Initialize a <code>SequenceConfig</code> using the metadata passed in the
* ctor.
*/
SequenceConfig getSequenceConfig() {
SequenceConfig ret;
if (wsdlModel != null) {
/*
If there is a WSDL, use the SequenceConfig ctor taking a WSDLPort and
initialize SOAPVersion according to the value obtained from the binding.
*/
ret = new SequenceConfig(wsdlModel,this.binding);
BindingID bindingid = wsdlModel.getBinding().getBindingId();
if (bindingid.equals(BindingID.parse(SOAPBinding.SOAP11HTTP_BINDING))) {
ret.setSoapVersion(SOAPVersion.SOAP_11);
} else {
ret.setSoapVersion(SOAPVersion.SOAP_12);
}
} else {
/*
Use SequenceConfig initialized with default values.
*/
ret = new SequenceConfig();
}
return ret;
}
/**
* Determine whether the STR used to secure request message is the one passed in
* the CreateSequence message for the sequence.
*
* @param packet The inbound Packet containing the STR in a property
* @param seq The InboundSequence
*
* @throws RMSecurityException if STR is missing or incorrect.
*/
private void checkSTR(Packet packet, InboundSequence seq)throws RMSecurityException {
SecurityContextToken sct = (SecurityContextToken)packet.invocationProperties.get(MessageConstants.INCOMING_SCT);
URI uri = sct.getIdentifier();
if(!uri.toString().equals(seq.getStrId())){
logger.severe(Messages.SECURITY_TOKEN_MISMATCH.format());
throw new RMSecurityException(Messages.SECURITY_TOKEN_MISMATCH.format());
}
}
/**
* Create a Packet containing a message with empty body and a single
* SequenceAcknowledgement header reflecting the current status of
* the specified inbound sequence.
*
* @param inbound Packet in request for which this method is being used
* to build a response.
* @param seq The specified InboundSequence
* @throws RMException
*/
private Packet generateAckMessage(Packet inbound, ServerInboundSequence seq)
throws RMException {
return generateAckMessage(inbound, seq, null);
}
/**
* Create a Packet containing a message with empty body and a single
* SequenceAcknowledgement header reflecting the current status of
* the specified inbound sequence.
*
* @param inbound Packet in request for which this method is being used
* to build a response.
* @param seq The specified InboundSequence
* @param action If null, add as value of wsa:Action
* @throws RMException
*/
private Packet generateAckMessage(Packet inbound, ServerInboundSequence seq, String action)
throws RMException {
//construct empty non-application message to be used as a conduit for
//this SequenceAcknowledgement header.
Message message = com.sun.xml.ws.api.message.Messages.createEmpty(config.getSoapVersion());
Packet outbound = new Packet(message);
outbound.invocationProperties.putAll(inbound.invocationProperties);
//construct the SequenceAcknowledgement header and add it to thge message.
SequenceAcknowledgementElement element = seq.generateSequenceAcknowledgement(null, marshaller);
//Header header = Headers.create(config.getSoapVersion(),marshaller,element);
Header header = createHeader(element);
message.getHeaders().add(header);
if (action != null) {
Header h = Headers.create(constants.getAddressingVersion().actionTag,
action);
message.getHeaders().add(h);
}
return outbound;
}
private SOAPFault newMessageNumberRolloverFault(MessageNumberRolloverException e) throws RMException {
QName subcode = Constants.MESSAGE_NUMBER_ROLLOVER_QNAME;
String faultstring = String.format(Constants.MESSAGE_NUMBER_ROLLOVER_TEXT, e.getMessageNumber());
try {
SOAPFactory factory;
SOAPFault fault;
if (binding.getSOAPVersion() == SOAPVersion.SOAP_12) {
factory = SOAPVersion.SOAP_12.saajSoapFactory;
fault = factory.createFault();
fault.setFaultCode(Constants.SOAP12_SENDER_QNAME);
fault.appendFaultSubcode(subcode);
// not sure what more to put in detail element
} else {
factory = SOAPVersion.SOAP_11.saajSoapFactory;
fault = factory.createFault();
fault.setFaultCode(subcode);
}
fault.setFaultString(faultstring);
return fault;
} catch (SOAPException se) {
throw new RMException(se);
}
}
private SOAPFault newUnknownSequenceFault(InvalidSequenceException e) throws RMException {
QName subcode = Constants.UNKNOWN_SEQUENCE_QNAME;
String faultstring = String.format(Constants.UNKNOWN_SEQUENCE_TEXT,e.getSequenceId());
try {
SOAPFactory factory;
SOAPFault fault;
if (binding.getSOAPVersion() == SOAPVersion.SOAP_12) {
factory = SOAPVersion.SOAP_12.saajSoapFactory;
fault = factory.createFault();
fault.setFaultCode(Constants.SOAP12_SENDER_QNAME);
fault.appendFaultSubcode(subcode);
// not sure what more to put in detail element
} else {
factory = SOAPVersion.SOAP_11.saajSoapFactory;
fault = factory.createFault();
fault.setFaultCode(subcode);
}
fault.setFaultString(faultstring);
return fault;
} catch (SOAPException se) {
throw new RMException(se);
}
}
private SOAPFault newSequenceTerminatedFault(TerminateSequenceException e) throws RMException {
QName subcode = Constants.SEQUENCE_TERMINATED_QNAME;
String faultstring = String.format(Constants.SEQUENCE_TERMINATED_TEXT,e.getMessage());
try {
SOAPFactory factory;
SOAPFault fault;
if (binding.getSOAPVersion() == SOAPVersion.SOAP_12) {
factory = SOAPVersion.SOAP_12.saajSoapFactory;
fault = factory.createFault();
fault.setFaultCode(Constants.SOAP12_SENDER_QNAME);
fault.appendFaultSubcode(subcode);
// detail empty
} else {
factory = SOAPVersion.SOAP_11.saajSoapFactory;
fault = factory.createFault();
fault.setFaultCode(subcode);
}
fault.setFaultString(faultstring);
return fault;
} catch (SOAPException se) {
throw new RMException(se);
}
}
private SOAPFault newCreateSequenceRefusedFault(CreateSequenceException e) throws RMException {
QName subcode = Constants.CREATE_SEQUENCE_REFUSED_QNAME;
String faultstring = String.format(Constants.CREATE_SEQUENCE_REFUSED_TEXT,e.getMessage());
try {
SOAPFactory factory;
SOAPFault fault;
if (binding.getSOAPVersion() == SOAPVersion.SOAP_12) {
factory = SOAPVersion.SOAP_12.saajSoapFactory;
fault = factory.createFault();
fault.setFaultCode(Constants.SOAP12_SENDER_QNAME);
fault.appendFaultSubcode(subcode);
// detail empty
} else {
factory = SOAPVersion.SOAP_11.saajSoapFactory;
fault = factory.createFault();
fault.setFaultCode(subcode);
}
fault.setFaultString(faultstring);
return fault;
} catch (SOAPException se) {
throw new RMException(se);
}
}
private Header createHeader(Object obj) {
return Headers.create(constants.getJAXBRIContextHeaders(), obj);
}
/**
* Either creates a new <code>Session</code> for the
* <code>InboundSequence</code> or returns one that has
* already been created by the SC Pipe.
*
* @param sequence The InboundSequence
* @return The Session
*/
public Session startSession(InboundSequence sequence) {
String id = sequence.getSessionId();
Session sess = sessionManager.getSession(id);
if (sess == null) {
sess = sessionManager.createSession(id);
}
sess.setSequence(sequence);
return sess;
}
/**
* Terminates the session associated with the sequence if
* RM owns the lifetime of the session.. i.e. If SC is not present.
*
* @param sequence The InboundSequence
*/
public void endSession(InboundSequence sequence) {
String sessionId = sequence.getSessionId();
if (sessionId.equals(sequence.getId())) {
//we own the session
sessionManager.terminateSession(sessionId);
}
}
/**
* Sets the session and session id properties in a request packet
* if necessary. This will be the case if SC has not already done
* so.
*
* @param packet The packet.
* @param seq The sequence to which the request message belongs.
*/
public void setSessionData(Packet packet,
InboundSequence seq) {
if (null == packet.invocationProperties
.get(Session.SESSION_ID_KEY)) {
packet.invocationProperties
.put(Session.SESSION_ID_KEY, seq.getSessionId());
}
if (null == packet.invocationProperties
.get(Session.SESSION_KEY)) {
Session sess = sessionManager.getSession(seq.getSessionId());
packet.invocationProperties
.put(Session.SESSION_KEY, sess.getUserData());
}
}
}
|