com.sun.xml.ws.rx.mc.runtime.McServerTube Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of webservices-rt Show documentation
Show all versions of webservices-rt Show documentation
This module contains the Metro runtime code.
/*
* Copyright (c) 1997, 2020 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0, which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
package com.sun.xml.ws.rx.mc.runtime;
import com.sun.istack.NotNull;
import com.sun.xml.ws.api.message.AddressingUtils;
import com.sun.xml.ws.api.message.Header;
import com.sun.xml.ws.api.message.MessageHeaders;
import com.sun.xml.ws.api.message.Headers;
import com.sun.xml.ws.api.message.Message;
import com.sun.xml.ws.api.message.Packet;
import com.sun.xml.ws.api.pipe.Fiber;
import com.sun.xml.ws.api.pipe.NextAction;
import com.sun.xml.ws.api.pipe.Tube;
import com.sun.xml.ws.api.pipe.TubeCloner;
import com.sun.xml.ws.api.pipe.helper.AbstractFilterTubeImpl;
import com.sun.xml.ws.api.pipe.helper.AbstractTubeImpl;
import com.sun.xml.ws.api.server.Container;
import com.sun.istack.logging.Logger;
import com.sun.xml.ws.api.SOAPVersion;
import com.sun.xml.ws.api.addressing.AddressingVersion;
import com.sun.xml.ws.api.ha.HighAvailabilityProvider;
import com.sun.xml.ws.api.message.Messages;
import com.sun.xml.ws.commons.ha.HaContext;
import com.sun.xml.ws.rx.RxRuntimeException;
import com.sun.xml.ws.rx.mc.dev.AdditionalResponses;
import com.sun.xml.ws.rx.mc.localization.LocalizationMessages;
import com.sun.xml.ws.rx.mc.protocol.wsmc200702.MakeConnectionElement;
import com.sun.xml.ws.rx.mc.protocol.wsmc200702.MessagePendingElement;
import com.sun.xml.ws.rx.message.jaxws.JaxwsMessage;
import com.sun.xml.ws.rx.util.Communicator;
import com.sun.xml.ws.rx.util.FiberExecutor;
import java.util.ArrayList;
import java.util.List;
import jakarta.xml.bind.JAXBException;
import javax.xml.namespace.QName;
import jakarta.xml.soap.Detail;
import jakarta.xml.soap.SOAPException;
import jakarta.xml.soap.SOAPFault;
import javax.xml.stream.XMLStreamException;
import org.w3c.dom.Node;
/**
*
* @author Marek Potociar
*/
public class McServerTube extends AbstractFilterTubeImpl {
private static final class AppRequestProcessingCallback implements Fiber.CompletionCallback {
private static final Logger LOGGER = Logger.getLogger(AppRequestProcessingCallback.class);
private final ResponseStorage responseStorage;
private final String clientUID;
private final McConfiguration configuration;
public AppRequestProcessingCallback(@NotNull ResponseStorage responseStorage, @NotNull String clientUID, @NotNull McConfiguration configuration) {
this.responseStorage = responseStorage;
this.clientUID = clientUID;
this.configuration = configuration;
}
public void onCompletion(Packet response) {
try {
LOGGER.finer(LocalizationMessages.WSMC_0105_STORING_RESPONSE(clientUID));
HaContext.initFrom(response);
storeResponse(response);
final AdditionalResponses additionalResponses = response.getSatellite(AdditionalResponses.class);
if (additionalResponses != null) {
for (Packet additionalResponse : additionalResponses.getAdditionalResponsePacketQueue()) {
storeResponse(additionalResponse);
}
} else {
LOGGER.fine("Response packet did not contain any AdditionalResponses property set.");
}
} finally {
HaContext.clear();
}
}
public void onCompletion(Throwable error) {
LOGGER.severe(LocalizationMessages.WSMC_0106_EXCEPTION_IN_REQUEST_PROCESSING(clientUID), error);
}
private void storeResponse(Packet response) {
if (response.getMessage() != null) {
final MessageHeaders headers = response.getMessage().getHeaders();
headers.remove(configuration.getAddressingVersion().toTag);
headers.add(Headers.create(configuration.getAddressingVersion().toTag, configuration.getRuntimeVersion().getAnonymousAddress(clientUID)));
JaxwsMessage responseMessage = new JaxwsMessage(response, AddressingUtils.getMessageID(headers, configuration.getAddressingVersion(), configuration.getSoapVersion()));
responseStorage.store(responseMessage, clientUID);
}
}
}
//
private static final Logger LOGGER = Logger.getLogger(McServerTube.class);
//
private final McConfiguration configuration;
private final FiberExecutor fiberExecutor;
private final ResponseStorage responseStorage;
private final Communicator communicator;
McServerTube(McConfiguration configuration, Tube tubelineHead, Container container) {
super(tubelineHead);
this.configuration = configuration;
this.fiberExecutor = new FiberExecutor("McServerTubeCommunicator", tubelineHead);
this.responseStorage = new ResponseStorage(configuration.getUniqueEndpointId());
this.communicator = Communicator.builder("mc-server-tube-communincator")
.soapVersion(configuration.getSoapVersion())
.addressingVersion(configuration.getAddressingVersion())
.tubelineHead(super.next)
.jaxbContext(configuration.getRuntimeVersion().getJaxbContext(configuration.getAddressingVersion()))
.container(container)
.build();
}
McServerTube(McServerTube original, TubeCloner cloner) {
super(original, cloner);
this.configuration = original.configuration;
this.fiberExecutor = original.fiberExecutor;
this.responseStorage = original.responseStorage;
this.communicator = original.communicator;
}
@Override
public AbstractTubeImpl copy(TubeCloner cloner) {
LOGGER.entering();
try {
return new McServerTube(this, cloner);
} finally {
LOGGER.exiting();
}
}
@Override
@SuppressWarnings("element-type-mismatch")
public NextAction processRequest(Packet request) {
try {
LOGGER.entering();
HaContext.initFrom(request);
if (HaContext.failoverDetected()) {
responseStorage.invalidateLocalCache();
}
assert request.getMessage() != null : "Unexpected [null] message in the server-side Tube.processRequest()";
String clientUID = getClientUID(request);
if (isMakeConnectionRequest(request)) {
return handleMakeConnectionRequest(request, clientUID);
}
if (clientUID == null) {
// don't bother - this is not a WS-MC enabled request
return super.processRequest(request);
} else {
// removing replyTo header and faultTo header to prevent addressing server tube from
// treating this request as non-anonymous
request.getMessage().getHeaders().remove(configuration.getAddressingVersion().replyToTag);
request.getMessage().getHeaders().remove(configuration.getAddressingVersion().faultToTag);
}
Packet requestCopy = request.copy(true);
request.addSatellite(new AdditionalResponses());
fiberExecutor.start(request, new AppRequestProcessingCallback(responseStorage, clientUID, configuration), null);
return super.doReturnWith(createEmptyResponse(requestCopy));
} finally {
HaContext.clear();
LOGGER.exiting();
}
}
@Override
public NextAction processResponse(Packet response) {
try {
LOGGER.entering();
// with WS-MC enabled messages, this method gets never invoked
return super.processResponse(response);
} finally {
LOGGER.exiting();
}
}
private NextAction handleMakeConnectionRequest(Packet request, String clientUID) {
try {
LOGGER.entering();
MakeConnectionElement mcElement;
try {
mcElement = request.getMessage().readPayloadAsJAXB(configuration.getRuntimeVersion().getUnmarshaller(configuration.getAddressingVersion()));
} catch (JAXBException ex) {
throw LOGGER.logSevereException(new RxRuntimeException(LocalizationMessages.WSMC_0107_ERROR_UNMARSHALLING_PROTOCOL_MESSAGE(), ex));
}
if (mcElement.getAddress() == null) {
// WS-I RSP v1.0: R2102 If a wsmc:MakeConnection request does not contain a wsmc:Address child element
// (in violation of R2100), the MC-RECEIVER MUST generate a wsmc:MissingSelection fault.
return super.doReturnWith(createSoapFaultResponse(
request,
configuration.getSoapVersion(),
configuration.getAddressingVersion(),
configuration.getRuntimeVersion().protocolVersion.wsmcFaultAction,
configuration.getSoapVersion().faultCodeServer,
configuration.getRuntimeVersion().protocolVersion.missingSelectionFaultCode,
"The MakeConnection element did not contain any selection criteria.",
null));
}
if (!mcElement.getAny().isEmpty()) {
// WS-I RSP v1.0: R2103 If a wsmc:MakeConnection request contains a wsrm:Identifier element
// (in violation of R2101) the MC-RECEIVER MUST generate a wsmc:UnsupportedSelection fault.
List unsupportedSelections = new ArrayList(mcElement.getAny().size());
for (Object element : mcElement.getAny()) {
if (element instanceof Node) {
Node selectionNode = ((Node) element);
unsupportedSelections.add(new SoapFaultDetailEntry(
configuration.getRuntimeVersion().protocolVersion.unsupportedSelectionFaultCode,
new QName(selectionNode.getNamespaceURI(), selectionNode.getLocalName()).toString()));
}
}
return super.doReturnWith(createSoapFaultResponse(
request,
configuration.getSoapVersion(),
configuration.getAddressingVersion(),
configuration.getRuntimeVersion().protocolVersion.wsmcFaultAction,
configuration.getSoapVersion().faultCodeServer,
configuration.getRuntimeVersion().protocolVersion.unsupportedSelectionFaultCode,
"The extension element used in the message selection is not supported by the MakeConnection receiver.",
unsupportedSelections));
}
String selectionUID = configuration.getRuntimeVersion().getClientId(mcElement.getAddress().getValue());
if (selectionUID == null) {
// TODO return a MissingSelection SOAP fault
throw LOGGER.logSevereException(new RxRuntimeException(LocalizationMessages.WSMC_0108_NULL_SELECTION_ADDRESS()));
}
if (clientUID != null && !selectionUID.equals(clientUID)) {
// Fixed WSIT issue #1334:
// This cannot be an excpetion, because according to the WS=MakeConnection specification,
// section 3.2 [ http://docs.oasis-open.org/ws-rx/wsmc/200702/wsmc-1.1-spec-os.html#_Toc162743906 ]:
//
// ...
// Since the message exchange pattern use by MakeConnection is untraditional, the following points
// need to be reiterated for clarification:
// * The MakeConnection message is logically part of a one-way operation; there is no reply message
// to the MakeConnection itself, and any response flowing on the transport back-channel is a pending message.
// * Since there is no reply message to MakeConnection, the WS-Addressing specific rules in
// section 3.4 "Formulating a Reply Message" are not used. Therefore, the value of any wsa:ReplyTo element
// in the MakeConnection message has no effective impact since the WS-Addressing [reply endpoint] property
// that is set by the presence of wsa:ReplyTo is not used.
// ...
//
// Because of the above, we just log a warning message
LOGGER.warning(LocalizationMessages.WSMC_0109_SELECTION_ADDRESS_NOT_MATCHING_WSA_REPLYTO(selectionUID, clientUID));
}
Packet response = null;
final JaxwsMessage pendingMessage = responseStorage.getPendingResponse(selectionUID);
if (pendingMessage != null) {
LOGGER.finer(LocalizationMessages.WSMC_0110_PENDING_MESSAGE_FOUND_FOR_SELECTION_UUID(selectionUID));
if (HighAvailabilityProvider.INSTANCE.isHaEnvironmentConfigured()) {
if (pendingMessage.getPacket() == null) {
// FIXME: loaded from DB without a valid packet - create one
// ...this is a workaround until JAX-WS RI API provides a mechanism how to (de)serialize whole Packet
pendingMessage.setPacket(communicator.createEmptyResponsePacket(request, pendingMessage.getWsaAction()));
}
}
response = pendingMessage.getPacket();
}
if (response == null) {
LOGGER.finer(LocalizationMessages.WSMC_0111_NO_PENDING_MESSAGE_FOUND_FOR_SELECTION_UUID(selectionUID));
response = createEmptyResponse(request);
} else {
Message message = response.getMessage();
if (message != null) {
MessageHeaders headers = message.getHeaders();
headers.add(Headers.create(
configuration.getRuntimeVersion().getJaxbContext(configuration.getAddressingVersion()),
new MessagePendingElement(Boolean.valueOf(selectionUID != null && responseStorage.hasPendingResponse(selectionUID)))));
}
}
return super.doReturnWith(response);
} finally {
LOGGER.exiting();
}
}
@Override
public NextAction processException(Throwable t) {
try {
LOGGER.entering();
return super.processException(t);
} finally {
LOGGER.exiting();
}
}
@Override
public void preDestroy() {
responseStorage.dispose();
super.preDestroy();
}
private String getClientUID(Packet request) {
Header replyToHeader = request.getMessage().getHeaders().get(configuration.getAddressingVersion().replyToTag, false);
if (replyToHeader != null) {
try {
String replyToAddress = replyToHeader.readAsEPR(configuration.getAddressingVersion()).getAddress();
return configuration.getRuntimeVersion().getClientId(replyToAddress);
} catch (XMLStreamException ex) {
throw LOGGER.logSevereException(new RxRuntimeException(LocalizationMessages.WSMC_0103_ERROR_RETRIEVING_WSA_REPLYTO_CONTENT(), ex));
}
}
return null;
}
private boolean isMakeConnectionRequest(final Packet request) {
return configuration.getRuntimeVersion().protocolVersion.wsmcAction.equals(AddressingUtils.getAction(request.getMessage().getHeaders(), configuration.getAddressingVersion(), configuration.getSoapVersion()));
}
private Packet createEmptyResponse(Packet request) {
return request.createServerResponse(null, null, null, "");
}
private Packet createSoapFaultResponse(Packet request, SOAPVersion soapVersion, AddressingVersion av, String action, QName code, QName subcode, String faultReasonText, List detailEntries) {
try {
SOAPFault soapFault = soapVersion.saajSoapFactory.createFault();
// common SOAP1.1 and SOAP1.2 Fault settings
if (faultReasonText != null) {
soapFault.setFaultString(faultReasonText, java.util.Locale.ENGLISH);
}
// SOAP version-specific SOAP Fault settings
switch (soapVersion) {
case SOAP_11:
soapFault.setFaultCode(subcode);
break;
case SOAP_12:
soapFault.setFaultCode(code);
soapFault.appendFaultSubcode(subcode);
if (detailEntries != null && !detailEntries.isEmpty()) {
final Detail detail = soapFault.addDetail();
for (SoapFaultDetailEntry entry : detailEntries) {
detail.addDetailEntry(entry.name).setValue(entry.value);
}
}
break;
default:
throw new RxRuntimeException("Unsupported SOAP version: '" + soapVersion.toString() + "'");
}
Message soapFaultMessage = Messages.create(soapFault);
return request.createServerResponse(soapFaultMessage, av, soapVersion, action);
} catch (SOAPException ex) {
throw new RxRuntimeException("Error creating a SOAP fault", ex);
}
}
}