/*******************************************************************************
* Copyright (c) 2006-2010 eBay Inc. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*******************************************************************************/
//B''H
package org.ebayopensource.turmeric.runtime.sif.impl.internal.service;
import java.net.URL;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.Executor;
import java.util.concurrent.Future;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.ws.AsyncHandler;
import javax.xml.ws.Response;
import javax.xml.ws.WebServiceException;
import org.ebayopensource.turmeric.runtime.binding.objectnode.ObjectNode;
import org.ebayopensource.turmeric.runtime.binding.utils.CollectionUtils;
import org.ebayopensource.turmeric.runtime.common.binding.DataBindingDesc;
import org.ebayopensource.turmeric.runtime.common.exceptions.ErrorDataFactory;
import org.ebayopensource.turmeric.runtime.common.exceptions.ServiceException;
import org.ebayopensource.turmeric.runtime.common.exceptions.ServiceInvocationException;
import org.ebayopensource.turmeric.runtime.common.impl.attachment.BaseMessageAttachments;
import org.ebayopensource.turmeric.runtime.common.impl.attachment.OutboundMessageAttachments;
import org.ebayopensource.turmeric.runtime.common.impl.internal.pipeline.BaseMessageImpl;
import org.ebayopensource.turmeric.runtime.common.impl.internal.pipeline.InboundMessageImpl;
import org.ebayopensource.turmeric.runtime.common.impl.internal.pipeline.OutboundMessageImpl;
import org.ebayopensource.turmeric.runtime.common.impl.internal.service.ProtocolProcessorDesc;
import org.ebayopensource.turmeric.runtime.common.impl.internal.service.ServiceOperationDescImpl;
import org.ebayopensource.turmeric.runtime.common.impl.internal.utils.IAsyncResponsePoller;
import org.ebayopensource.turmeric.runtime.common.impl.utils.LogManager;
import org.ebayopensource.turmeric.runtime.common.pipeline.Message;
import org.ebayopensource.turmeric.runtime.common.pipeline.Transport;
import org.ebayopensource.turmeric.runtime.common.service.ServiceOperationDesc;
import org.ebayopensource.turmeric.runtime.common.types.ByteBufferWrapper;
import org.ebayopensource.turmeric.runtime.common.types.Cookie;
import org.ebayopensource.turmeric.runtime.common.types.G11nOptions;
import org.ebayopensource.turmeric.runtime.common.types.SOAConstants;
import org.ebayopensource.turmeric.runtime.common.types.SOAHeaders;
import org.ebayopensource.turmeric.runtime.common.types.ServiceAddress;
import org.ebayopensource.turmeric.runtime.errorlibrary.ErrorConstants;
import org.ebayopensource.turmeric.runtime.sif.impl.internal.config.ClientConfigHolder;
import org.ebayopensource.turmeric.runtime.sif.impl.internal.markdown.SOAClientMarkdownStateId;
import org.ebayopensource.turmeric.runtime.sif.impl.internal.markdown.SOAClientMarkdownStateManager;
import org.ebayopensource.turmeric.runtime.sif.impl.internal.pipeline.AsyncResponse;
import org.ebayopensource.turmeric.runtime.sif.impl.internal.pipeline.ClientMessageContextImpl;
import org.ebayopensource.turmeric.runtime.sif.impl.internal.pipeline.ClientMessageProcessor;
import org.ebayopensource.turmeric.runtime.sif.service.RequestContext;
import org.ebayopensource.turmeric.runtime.sif.service.ResponseContext;
import org.ebayopensource.turmeric.runtime.sif.service.ServiceDispatch;
import org.ebayopensource.turmeric.runtime.sif.service.ServiceInvokerOptions;
import com.ebay.kernel.markdown.MarkdownStateSnapshot;
public abstract class BaseServiceDispatchImpl<T> extends ServiceDispatch<T> {
private class LocationPicker {
private List<URL> locations=null;
private int counter=0;
/*public void setLocation(URL location) {
locations = new ArrayList<URL>();
locations.add(location);
}*/
private boolean firstTime = true;
public void setLocations(List<URL> locations) {
this.locations = locations;
}
public List<URL> getLocations() {
return locations;
}
public URL getNextLocation() {
if(locations == null || locations.isEmpty())
return null;
if(locations.size()==1)
return locations.get(0);
if(firstTime){
synchronized(this){
if(firstTime){ //check again. may not be first time now
//randomly get a location
int size = locations.size();
if(size==1)
return locations.get(0);
Random r = new Random();
counter = r.nextInt(size);
firstTime = false;
return locations.get(counter);
}
}
}
counter++;
if(counter>=locations.size())
counter=0;
return locations.get(counter);
}
}
private final LocationPicker locPick = new LocationPicker();
static final int MAX_URL_LENGTH_FOR_REST = 2048;
static final G11nOptions s_emptyG11nOptions = new G11nOptions();
private final ClientMessageProcessor m_messageProcessor;
private URL m_currentServiceLocation;
private final ClientServiceDesc m_serviceDesc;
private final ServiceInvokerOptions m_invokerOptions;
private final String m_serviceVersion;
private final Map<String, String> m_sessionTransportHeaders;
private final Collection<ObjectNode> m_sessionMessageHeaders;
private Map<String, Cookie> m_dispatchCookies;
private RequestContext m_requestContext;
private ResponseContext m_responseContext = null;
private ClientMessageContextImpl m_currentContext = null;
private String m_urlPathInfo;
private G11nOptions m_g11nOptions;
private int m_numTries = 0;
private IAsyncResponsePoller m_servicePoller;
private final Executor m_executor;
BaseServiceDispatchImpl(String opName, List<URL> locations,
ClientServiceDesc serviceDesc, URL wsdlLocation,
ServiceInvokerOptions invokerOptions, String serviceVersion,
Map<String, Cookie> cookies, Map<String, String> transportHeaders,
Collection<ObjectNode> messageHeaders, G11nOptions g11nOptions,
RequestContext requestContext, Executor executor,
IAsyncResponsePoller servicePoller) throws ServiceException {
super(opName);
m_messageProcessor = ClientMessageProcessor.getInstance();
locPick.setLocations(locations);
m_serviceDesc = serviceDesc;
m_invokerOptions = invokerOptions;
m_serviceVersion = serviceVersion;
m_dispatchCookies = cookies;
m_sessionTransportHeaders = transportHeaders;
m_sessionMessageHeaders = messageHeaders;
m_g11nOptions = g11nOptions;
m_requestContext = requestContext;
m_servicePoller = servicePoller;
m_executor = executor;
}
@Override
public Map<String, Object> getRequestContext() {
return getRequestContextInternal(m_requestContext);
}
@Override
public Map<String, Object> getResponseContext() {
return getResponseContextInternal(m_responseContext);
}
public String getUrlPathInfo() {
return m_urlPathInfo;
}
public RequestContext getDispatchRequestContext() {
return m_requestContext;
}
public ResponseContext getDispatchResponseContext() {
if (m_responseContext == null) {
m_responseContext = createResponseContext();
}
return m_responseContext;
}
/**
* Returns the total number of attempted service invocation tries during the
* most recent call to invoke(). The total number of attempted invocations
* is one plus the number of retries attempted.
*
* @return the number of attempted invocations, or zero if invoke() has
* never been called for this Service object.
*/
public int getLastTryCount() {
return m_numTries;
}
private boolean isRetryable(Throwable exception) {
if (getCurrentContext() == null) {
// we could not even create the context, there is no point in
// retries
return false;
}
return m_serviceDesc.getRetryHandler().isRetryable(getCurrentContext(),
exception);
}
private int getApplicationTryCount() {
Integer appLevelNumRetries = m_invokerOptions.getAppLevelNumRetries();
if (appLevelNumRetries == null) {
appLevelNumRetries = m_serviceDesc.getAppLevelNumRetries();
}
if (appLevelNumRetries == null) {
return 1;
}
return appLevelNumRetries.intValue() + 1;
}
protected void invokeWithRetry(String opName, boolean inboundRawMode,
boolean outboundRawMode, Object[] inParams, List<Object> outParams,
ByteBufferWrapper inWrapper, ByteBufferWrapper outWrapper)
throws ServiceInvocationException {
m_numTries = 1;
while (true) {
try {
invokeInternal(opName, inboundRawMode, outboundRawMode,
inParams, outParams, inWrapper, outWrapper);
postInvokeSuccess();
return;
} catch (RuntimeException e) {
handleRetryForRuntimeException(e);
} catch (Error e) {
handleRetryForError(e);
} catch (ServiceInvocationException e) {
handleRetryServiceInvocationException(e);
} catch (ServiceException e) {
handleRetryUnexpectedException(opName, e);
}
}
}
private void handleRetryForRuntimeException(RuntimeException e) {
// Unexpected exception - check if it's on the retry list,
// otherwise re-throw it.
postErrorMarkDown(e);
if (!isRetryable(e) || m_numTries >= getApplicationTryCount()) {
postInvokeError();
throw e;
}
m_numTries++;
}
private void handleRetryForError(Error e) throws Error {
// Unexpected exception - re-throw it. Not subject to retry
// logic.
postInvokeErrorAndMarkDown(e);
ServiceCallHelper.checkMarkdownError(getCurrentContext(), e);
throw e;
}
private void handleRetryServiceInvocationException(
ServiceInvocationException e) throws ServiceInvocationException {
// invokeInternal called checkForErrors, which threw this to
// indicate a system error from either client
// or server side. Check if it's on the retry list, otherwise
// re-throw it.
postErrorMarkDown(e);
if (!isRetryable(e) || m_numTries >= getApplicationTryCount()) {
postInvokeError();
throw e;
}
// TODO: log the decisions being made?
m_numTries++;
}
protected void handleRetryUnexpectedException(String opName, Throwable e)
throws ServiceInvocationException {
// Since ClientMessageProcessor.processMessage() controls most
// exceptions it sees, an exception here would mean we did
// not get as far as that call. Since the last thing done
// before that is creating the message context, we can't
// assume we have even a valid message context, so there's no
// point in trying to accumulate all errors per the checkForErrors()
// call. Most likely there is also no response - responseMsg is null.
// We just pass through retry, and then throw the single
// exception received.
ServiceInvocationException e2 = getServiceInvocationExceptionForServiceException(
opName, e);
handleRetryServiceInvocationException(e2);
}
protected void invokeInternal(String opName, boolean inboundRawMode,
boolean outboundRawMode, Object[] inParams, List<Object> outParams,
ByteBufferWrapper inWrapper, ByteBufferWrapper outWrapper)
throws ServiceException, ServiceInvocationException {
// Pre-ProcessMessage
InboundMessageImpl responseMsg = preProcessMessage(opName,
inboundRawMode, outboundRawMode, inParams, outParams,
inWrapper, outWrapper);
// Message now to be processed by the CMP
getMessageProcessor().processMessage(getCurrentContext(), false);
// Process Response
processResponse(outboundRawMode, outParams, outWrapper, responseMsg);
}
protected void postInvokeError() {
m_requestContext = null;
m_responseContext = null;
//m_urlPathInfo = null;
if (getCurrentContext() != null) {
// transfer headers ...
try {
m_responseContext = createResponseContext(getCurrentContext(),
m_invokerOptions.shouldRecordResponsePayload());
} catch (ServiceException e) {
// should never happen
}
// but no cookies
}
}
void setCookie(Cookie cookie) {
if (m_dispatchCookies == null) {
m_dispatchCookies = new HashMap<String, Cookie>();
}
m_dispatchCookies.put(cookie.getName(), cookie);
}
Map<String, Cookie> getCookiesMap() {
if (m_dispatchCookies == null) {
return Collections.unmodifiableMap(new HashMap<String, Cookie>());
}
return Collections.unmodifiableMap(new HashMap<String, Cookie>(
m_dispatchCookies));
}
static Logger getLogger() {
return LogManager.getInstance(BaseServiceDispatchImpl.class);
}
private InboundMessageImpl preProcessMessage(String opName,
boolean inboundRawMode, boolean outboundRawMode, Object[] inParams,
List<Object> outParams, ByteBufferWrapper inWrapper,
ByteBufferWrapper outWrapper) throws ServiceException {
return preProcessMessage(opName, inboundRawMode, outboundRawMode,
inParams, outParams, inWrapper, outWrapper, false);
}
protected InboundMessageImpl preProcessMessage(String opName,
boolean inboundRawMode, boolean outboundRawMode, Object[] inParams,
List<Object> outParams, ByteBufferWrapper inWrapper,
ByteBufferWrapper outWrapper, boolean useAsync)
throws ServiceException {
// Reset response context
m_responseContext = null;
// Get the ByteBuffer to receive response
ByteBuffer inBuffer = prepareInBufferForRawMode(inboundRawMode,
outboundRawMode, outParams, inWrapper, outWrapper);
// Get the operation description
ServiceOperationDesc operation = getOperationDescription(opName,
inboundRawMode);
// Get transport
Transport transport;
String transportName = m_invokerOptions.getTransportName();
if (transportName != null) {
transportName = transportName.toUpperCase();
transport = getTransport(transportName);
} else {
transportName = m_serviceDesc.getDefTransportName();
transport = m_serviceDesc.getDefTransport();
}
// Check if it is a REST request and get max URL length
boolean isRest = Boolean.TRUE.equals(m_invokerOptions.isREST());
int maxUrlLengthForREST = isRest ? getMaxUrlLengthForRest(MAX_URL_LENGTH_FOR_REST)
: MAX_URL_LENGTH_FOR_REST;
// Get Request Data Binding
DataBindingDesc requestDataBinding = getRequestDataBindingDesc(isRest);
// Get Response Data Binding
DataBindingDesc responseDataBinding = getResponseDataBindingDesc(
requestDataBinding, isRest);
// Get protocol processor
ProtocolProcessorDesc protocolProcessor = getProtocolProcessor(
transport, requestDataBinding, responseDataBinding);
// Get G11n options
G11nOptions configG11nOptions = m_serviceDesc.getG11nOptions() == null ? s_emptyG11nOptions
: m_serviceDesc.getG11nOptions();
G11nOptions g11nOptions = G11nOptions.mergeFallbackOptions(
m_g11nOptions, configG11nOptions);
// Get Transport Headers
Map<String, String> transportHeaders = combineTransportHeaders();
// Get Transport Cookies
Cookie[] cookies = combineCookies();
// Get Message Headers
Collection<ObjectNode> messageHeaders = combineMessageHeaders();
// Get Message for attachments in the request
BaseMessageAttachments outAttachments = !inboundRawMode
&& operation.getRequestType().hasAttachment() ? new OutboundMessageAttachments(
protocolProcessor.getName())
: null;
boolean bufferingMode = false;
if(transportHeaders != null) {
bufferingMode = transportHeaders.containsKey(SOAHeaders.NON_STREAMING_MODE);
}
// Get out bound Message
OutboundMessageImpl requestMsg = new OutboundMessageImpl(true,
transportName, requestDataBinding, g11nOptions,
transportHeaders, cookies, messageHeaders, outAttachments,
operation, isRest, maxUrlLengthForREST, bufferingMode);
// Get in bound Message
InboundMessageImpl responseMsg = new InboundMessageImpl(false,
transportName, responseDataBinding, g11nOptions, null, null,
null, null, operation, bufferingMode);
// Get response Transport Name
String responseTransportName = m_invokerOptions
.getResponseTransportName() == null ? m_serviceDesc
.getResponseTransport() : m_invokerOptions
.getResponseTransportName();
// Get service Version
String serviceVersion = getVersion();
String useCase;
String consumerId = getConsumerId(transportHeaders);
if (consumerId != null && !consumerId.isEmpty()) {
// set the useCase to use consumerId
useCase = SOAConstants.APPNAME_PREFIX + consumerId;
}
else {
useCase = getUseCase(transportHeaders);
}
// Get urlPathInfo
m_urlPathInfo = m_invokerOptions.getUrlPathInfo() == null ? m_serviceDesc
.getUrlPathInfo()
: m_invokerOptions.getUrlPathInfo();
// get host name
m_currentServiceLocation = locPick.getNextLocation();
String hostName = ( m_currentServiceLocation != null ? m_currentServiceLocation.getHost() : null);
ServiceAddress serviceAddress = getServiceAddress(m_currentServiceLocation, m_urlPathInfo, hostName);
URL serviceUrl = serviceAddress.getServiceUrl();
String serviceUrlStr = (serviceUrl != null ? serviceUrl.toString()
: null);
// check for Mark-down
checkMarkdown(opName, serviceAddress);
// Check for recording in response pay load
if (Boolean.TRUE.equals(m_invokerOptions.shouldRecordResponsePayload())) {
responseMsg.recordPayload(Integer.MAX_VALUE);
}
// Client Message context
setCurrentContext(getClientContext(inboundRawMode, outboundRawMode,
inParams, inBuffer, outParams, outWrapper, operation,
transport, protocolProcessor, g11nOptions, requestMsg,
responseMsg, responseTransportName, serviceVersion, useCase,
consumerId, serviceUrlStr, serviceAddress,useAsync));
return responseMsg;
}
private String getConsumerId(Map<String, String> transportHeaders) {
String consumerId = null;
if(transportHeaders != null )
consumerId = transportHeaders.get(SOAHeaders.CONSUMER_ID);
if(consumerId == null || consumerId.isEmpty()) {
consumerId = m_invokerOptions.getConsumerId();
}
if(consumerId == null || consumerId.isEmpty()) {
consumerId = m_serviceDesc.getConsumerId();
}
return consumerId;
}
@SuppressWarnings("deprecation")
private String getUseCase(Map<String, String> transportHeaders) {
String usecase = null;
if(transportHeaders != null )
usecase = transportHeaders.get(SOAHeaders.USECASE_NAME);
if(usecase == null || usecase.isEmpty()) {
usecase = m_invokerOptions.getUseCase();
}
if(usecase == null || usecase.isEmpty()) {
usecase = m_serviceDesc.getUseCase();
}
return usecase;
}
private String getVersion() {
if (m_serviceVersion == null) {
return m_serviceDesc.getServiceVersion();
}
return m_serviceVersion;
}
Map<String, String> getSessionTransportHeaders() {
if (m_sessionTransportHeaders == null) {
return CollectionUtils.EMPTY_STRING_MAP;
}
return Collections.unmodifiableMap(new HashMap<String, String>(
m_sessionTransportHeaders));
}
private Collection<ObjectNode> getSessionMessageHeaders() {
if (m_sessionMessageHeaders == null) {
return Collections
.unmodifiableCollection(new ArrayList<ObjectNode>());
}
return Collections.unmodifiableCollection(new ArrayList<ObjectNode>(
m_sessionMessageHeaders));
}
private void processResponse(boolean outboundRawMode,
List<Object> outParams, ByteBufferWrapper outWrapper,
InboundMessageImpl responseMsg) throws ServiceException,
ServiceInvocationException {
if (outboundRawMode) {
getOutBoundRawData(getCurrentContext().getResponseMessage(),
outWrapper);
if (outWrapper.getByteBuffer() == null
|| outWrapper.getByteBuffer().array() == null
|| outWrapper.getByteBuffer().array().length == 0)
checkForErrors(responseMsg);
} else {
checkForErrors(responseMsg);
getOutParams(getCurrentContext().getResponseMessage(), outParams);
}
}
static public void getOutParams(Message inboundMessage,
List<Object> outParams) throws ServiceException,
ServiceInvocationException {
if (outParams != null) {
int respCparamCount = inboundMessage.getParamCount();
for (int i = 0; i < respCparamCount; i++) {
Object param = inboundMessage.getParam(i);
outParams.add(param);
}
}
}
static public void getOutBoundRawData(Message inboundMessage,
ByteBufferWrapper outWrapper) throws ServiceException {
outWrapper.setByteBuffer(inboundMessage.getByteBuffer());
}
private ClientMessageContextImpl getClientContext(boolean inboundRawMode,
boolean outboundRawMode, Object[] inParams, ByteBuffer inBuffer,
List<Object> outParams, ByteBufferWrapper outWrapper,
ServiceOperationDesc operation, Transport transport,
ProtocolProcessorDesc protocolProcessor, G11nOptions g11nOptions,
OutboundMessageImpl requestMsg, InboundMessageImpl responseMsg,
String responseTransportName, String serviceVersion,
String useCase, String consumerId, String serviceUrl, ServiceAddress serviceAddress, boolean useAsync)
throws ServiceException {
ClientMessageContextImpl ctx = new ClientMessageContextImpl(
m_serviceDesc, operation, protocolProcessor, transport,
requestMsg, responseMsg, serviceAddress, null, serviceVersion,
m_invokerOptions, responseTransportName, useCase, consumerId, g11nOptions
.getCharset(), serviceUrl,useAsync);
ctx.setServicePoller(m_servicePoller);
if (inboundRawMode) {
ctx.setInboundRawMode(true);
requestMsg.setByteBuffer(inBuffer);
} else {
setInParams(inParams, requestMsg);
}
ctx.setOutboundRawMode(outboundRawMode);
ctx.setOutParams(outParams);
ctx.setOutBuffer(outWrapper);
Map<String, Object> requestProperties = null;
if (m_requestContext != null) {
requestProperties = getRequestContextPropertiesInternal(m_requestContext);
for (Map.Entry<String, Object> p : requestProperties.entrySet()) {
ctx.setRequestProperty(p.getKey(), p.getValue());
}
}
return ctx;
}
private ServiceAddress getServiceAddress(URL serviceLocationUrl,
String pathInfo, String hostName) {
ServiceAddress serviceAddress = null;
serviceAddress = new ServiceAddress(hostName, null, serviceLocationUrl,
pathInfo, false);
return serviceAddress;
}
private Transport getTransport(String transportName)
throws ServiceException {
Transport transport;
transport = m_serviceDesc.getTransport(transportName);
if (transport == null) {
throw new ServiceException(ErrorDataFactory.createErrorData(ErrorConstants.SVC_CLIENT_CANNOT_GET_TRANSPORT,
ErrorConstants.ERRORDOMAIN, new Object[] { getAdminName(), transportName }));
}
return transport;
}
private int getMaxUrlLengthForRest(int maxUrlLengthForREST)
throws ServiceException {
Integer maxUrlLengthForRESTInt = m_invokerOptions
.getMaxURLLengthForREST();
if (maxUrlLengthForRESTInt != null) {
maxUrlLengthForREST = maxUrlLengthForRESTInt.intValue();
if (maxUrlLengthForREST <= 0) {
throw new ServiceException(ErrorDataFactory.createErrorData(ErrorConstants.SVC_CLIENT_INVALID_MAX_URL_LENGTH,
ErrorConstants.ERRORDOMAIN, new Object[] { getAdminName(), Integer.valueOf(maxUrlLengthForREST) }));
}
}
return maxUrlLengthForREST;
}
private ServiceOperationDesc getOperationDescription(String opName,
boolean inboundRawMode) throws ServiceException {
ServiceOperationDesc operation;
if (inboundRawMode) {
operation = new ServiceOperationDescImpl(m_serviceDesc
.getServiceId(), opName);
} else {
operation = m_serviceDesc.getOperation(opName);
}
if (operation == null && !inboundRawMode) {
throw new ServiceException(ErrorDataFactory.createErrorData(ErrorConstants.SVC_CLIENT_UNKNOWN_OPERATION,
ErrorConstants.ERRORDOMAIN, new Object[] { opName, getAdminName() }));
}
return operation;
}
private ByteBuffer prepareInBufferForRawMode(boolean inboundRawMode,
boolean outboundRawMode, List<Object> outParams,
ByteBufferWrapper inWrapper, ByteBufferWrapper outWrapper) {
ByteBuffer inBuffer = null;
if (inboundRawMode) {
if (inWrapper == null) {
throw new RuntimeException(
"DII inbound byte buffer wrapper cannot be null");
// TODO:
// create a new ErrorData
}
inBuffer = inWrapper.getByteBuffer();
}
if (outboundRawMode) {
if (outWrapper == null) {
throw new RuntimeException(
"DII outbound byte buffer wrapper cannot be null");
// TODO:
// create a new ErrorData
}
} else {
if (outParams != null) {
outParams.clear();
}
}
return inBuffer;
}
/**
* Compose the transport headers from multiple sources, as an ordered sequence of potential overrides:
* <ol>
* <li>ClientConfig.xml, <transport.../> section</li>
* <li>ClientConfig.xml, <invoker-options><preferred-transport.../></invoker-options> section</li>
* <li>session transport headers</li>
* <li>request transport headers</li>
* </ol>
* @return
*/
private Map<String, String> combineTransportHeaders() {
String transportName = m_invokerOptions.getTransportName();
if (transportName != null) {
transportName = transportName.toUpperCase();
} else {
transportName = m_serviceDesc.getDefTransportName();
}
ClientConfigHolder configHolder = (ClientConfigHolder) m_serviceDesc.getConfig();
Map<String, String> headerOptions = null;
Map<String, Map<String, String>> allHeaderOptions = configHolder.getMessageProcessorConfig().getTransportHeaderOptions();
if (allHeaderOptions != null) {
headerOptions = allHeaderOptions.get(transportName);
}
Map<String, String> result = headerOptions == null
? new HashMap<String, String>()
: new HashMap<String, String>(headerOptions);
Map<String, String> overrideHeaderOptions = configHolder.getTransportOverrideHeaderOptions();
if (overrideHeaderOptions != null) {
result.putAll(overrideHeaderOptions);
}
// now override with session headers
result.putAll(getSessionTransportHeaders());
// final override - the request headers
Map<String, String> reqCtxHeaders =
((m_requestContext == null) ? null
: getRequestContextTransportHeadersInternal(m_requestContext));
result = ServiceCallHelper.combineMaps(result, reqCtxHeaders);
return result;
}
private Collection<ObjectNode> combineMessageHeaders() {
Collection<ObjectNode> reqCtxMessageHeaders = null;
if (m_requestContext != null) {
reqCtxMessageHeaders = getRequestContextMessageHeadersInternal(m_requestContext);
}
Collection<ObjectNode> result = ServiceCallHelper.combineCollections(
getSessionMessageHeaders(), reqCtxMessageHeaders);
if (result == null) {
return null;
}
return result;
}
/**
* Builds custom cookies
*/
private Cookie[] combineCookies() {
Map<String, Cookie> reqCtxCookies = null;
if (m_requestContext != null) {
reqCtxCookies = getRequestContextCookiesInternal(m_requestContext);
}
Map<String, Cookie> cookies = ServiceCallHelper.combineMaps(
getCookiesMap(), reqCtxCookies);
if (cookies == null) {
return null;
}
Cookie[] result = cookies.values().toArray(new Cookie[cookies.size()]);
return result;
}
/**
* Selects protocol processor for this request
*/
private ProtocolProcessorDesc getProtocolProcessor(Transport transport,
DataBindingDesc requestDataBinding,
DataBindingDesc responseDataBinding) throws ServiceException {
String protocolName = m_invokerOptions.getMessageProtocolName();
if (protocolName == null) {
protocolName = m_serviceDesc.getMessageProtocolName();
}
if (protocolName == null) {
return m_serviceDesc.getNullProtocolProcessor();
}
ProtocolProcessorDesc result = m_serviceDesc
.getProtocolProcessor(protocolName);
if (result == null) {
throw new ServiceException(ErrorDataFactory.createErrorData(ErrorConstants.SVC_CLIENT_UNKNOWN_PROTOCOL,
ErrorConstants.ERRORDOMAIN, new Object[] {protocolName, getAdminName() }));
}
String requestPayload = requestDataBinding.getPayloadType();
if (!result.isPayloadSupported(requestPayload)) {
throw new ServiceException(ErrorDataFactory.createErrorData(ErrorConstants.SVC_PROTOCOLPROCESSOR_UNSUPPORTED_REQUEST_FORMAT,
ErrorConstants.ERRORDOMAIN, new Object[] { protocolName, getAdminName(), requestPayload }));
}
String responsePayload = responseDataBinding.getPayloadType();
if (!result.isPayloadSupported(responsePayload)) {
throw new ServiceException(ErrorDataFactory.createErrorData(ErrorConstants.SVC_PROTOCOLPROCESSOR_UNSUPPORTED_RESPONSE_FORMAT,
ErrorConstants.ERRORDOMAIN, new Object[] { protocolName, getAdminName(),responsePayload }));
}
return result;
}
/**
* Finds appropriate DataBindingDesc for message
* serialization/de-serialization
*/
private DataBindingDesc getRequestDataBindingDesc(boolean isRest)
throws ServiceException {
String bindingName = m_invokerOptions.getRequestBinding();
if (bindingName != null) {
DataBindingDesc result = m_serviceDesc
.getDataBindingDesc(bindingName);
if (result == null) {
throw new ServiceException(ErrorDataFactory.createErrorData(ErrorConstants.SVC_CLIENT_UNKNOWN_BINDING,
ErrorConstants.ERRORDOMAIN, new Object[] { bindingName, getAdminName() }));
}
return result;
}
if (isRest) {
return m_serviceDesc.getDefRestRequestDataBinding();
}
DataBindingDesc result = m_serviceDesc.getDefRequestDataBinding();
if (result == null) {
throw new ServiceException(ErrorDataFactory.createErrorData(ErrorConstants.SVC_CLIENT_BINDING_NOT_SPECIFIED,
ErrorConstants.ERRORDOMAIN, new Object[] { getAdminName() }));
}
return result;
}
private String getAdminName() {
return m_serviceDesc.getAdminName();
}
private DataBindingDesc getResponseDataBindingDesc(
DataBindingDesc defBinding, boolean isRest) throws ServiceException {
String bindingName = m_invokerOptions.getResponseBinding();
if (bindingName != null) {
DataBindingDesc result = m_serviceDesc
.getDataBindingDesc(bindingName);
if (result == null) {
throw new ServiceException(ErrorDataFactory.createErrorData(ErrorConstants.SVC_CLIENT_UNKNOWN_BINDING,
ErrorConstants.ERRORDOMAIN, new Object[] { bindingName, getAdminName() }));
}
return result;
}
if (isRest) {
return m_serviceDesc.getDefRestResponseDataBinding();
}
DataBindingDesc result = m_serviceDesc.getDefResponseDataBinding();
if (result != null) {
return result;
}
return defBinding;
}
private void setInParams(Object[] inParams, BaseMessageImpl msg)
throws ServiceException {
int actualParamCount = (inParams != null ? inParams.length : 0);
if (actualParamCount != msg.getParamCount()) {
throw new ServiceException(ErrorDataFactory.createErrorData(ErrorConstants.SVC_CLIENT_INVALID_PARAM_COUNT,
ErrorConstants.ERRORDOMAIN, new Object[] { Integer.toString(actualParamCount),
Integer.toString(msg.getParamCount()) }));
}
if (inParams != null) {
for (int i = 0; i < inParams.length; i++) {
Object value = inParams[i];
msg.setParam(i, value);
}
}
}
private void checkMarkdown(String opName, ServiceAddress serviceAddress)
throws ServiceException {
MarkdownStateSnapshot<SOAClientMarkdownStateId> markdownState = SOAClientMarkdownStateManager
.getInstance().getMarkdownState(m_serviceDesc, opName,
serviceAddress, true);
if (markdownState != null) {
throw new ServiceException(
ErrorDataFactory.createErrorData(ErrorConstants.SVC_RT_SERVICE_MARKDOWN,
ErrorConstants.ERRORDOMAIN, new Object[] {markdownState.getId().getStringId(),
markdownState.getReason() }));
}
}
public static void checkForErrors(BaseMessageImpl responseMsg,
ClientMessageContextImpl context, String adminName)
throws ServiceException, ServiceInvocationException {
List<Throwable> clientErrors = context.getErrorList();
boolean hasClientSystemErrors = (clientErrors != null && !clientErrors.isEmpty());
boolean isAppOnlyException = false;
Object errorResponse = responseMsg.getErrorResponse();
if (errorResponse != null) {
boolean hasServerSystemErrors =
ServiceCallHelper.hasSystemErrorInResponse(context, errorResponse);
if (!hasClientSystemErrors && !hasServerSystemErrors) {
isAppOnlyException = true;
}
// TODO: do we want to extract server errors as well and keep them
// separate in the exception ?
// we have error response and client-or-server errors
throw ServiceCallHelper.createInvocationException(context,
adminName, context.getOperationName(), clientErrors,
errorResponse, isAppOnlyException, hasServerSystemErrors,
context.getRequestGuid());
}
if (hasClientSystemErrors) {
throw ServiceCallHelper.createInvocationException(context,
adminName, context.getOperationName(), clientErrors, null,
false, false, context.getRequestGuid());
}
}
/**
* Check for both system exceptions (client side, or server side contained
* in the error response), and application exceptions in the error response.
*
* Possible outcomes: 1. No errors in our client context, and no error
* response. Returns. 2. No errors in our client context; error response
* contains application error(s) only. Throws app-only
* ServiceInvocationException 3. Client context has errors (which are system
* exceptions by definition), and/or error response contains system errors.
* Collects all system errors together; adds the error response to the list;
* and throws ServiceInvocationException with all these errors.
*/
private void checkForErrors(BaseMessageImpl responseMsg)
throws ServiceException, ServiceInvocationException {
checkForErrors(responseMsg, getCurrentContext(), getAdminName());
}
void postInvokeSuccess() throws ServiceException {
m_requestContext = null;
//m_urlPathInfo = null;
m_responseContext = createResponseContext(getCurrentContext(),
m_invokerOptions.shouldRecordResponsePayload());
if (getCurrentContext() != null) {
// transfer cookies to this session
Message response = getCurrentContext().getResponseMessage();
Cookie[] cookies = response.getCookies();
for (int i = 0; i < cookies.length; i++) {
Cookie cookie = cookies[i];
setCookie(cookie);
}
try {
SOAClientMarkdownStateManager.getInstance().countSuccess(
getCurrentContext());
} catch (Throwable e) {
getLogger().log(
Level.SEVERE,
"Unable to call countSuccess for "
+ m_serviceDesc.getAdminName() + ": "
+ e.toString(), e);
}
}
}
protected void commonHandlingOfError(Throwable exception) {
postInvokeErrorAndMarkDown(exception);
throw new WebServiceException(exception);
}
private void postInvokeErrorAndMarkDown(Throwable exception) {
postInvokeError();
postErrorMarkDown(exception);
}
private void postErrorMarkDown(Throwable e){
// here, we need to be able to create a state if it doeesnt exist, with location specified
// since location can be specified at runtime.
ServiceCallHelper.checkState(getCurrentContext(), m_serviceDesc, m_currentServiceLocation);
ServiceCallHelper.checkMarkdownError(getCurrentContext(), e, m_currentServiceLocation);
}
protected void handlingOfSericeException(String opName, ServiceException e) {
ServiceInvocationException e2 = getServiceInvocationExceptionForServiceException(
opName, e);
commonHandlingOfError(e2);
}
abstract protected InboundMessageImpl preProcessMessageForDispatch(
String opName, T inArg) throws ServiceException;
@Override
protected Response<T> invokeAsync(String opName, T inArg) {
try {
// Do dispatch specific pre processing.
preProcessMessageForDispatch(opName, inArg);
ClientMessageContextImpl ctx = getCurrentContext();
// Message now to be processed by the CMP
getMessageProcessor().processMessage(ctx, true);
if (ctx.getErrorList() != null && !ctx.getErrorList().isEmpty()) {
throw ServiceCallHelper.createInvocationException(ctx, ctx
.getAdminName(), ctx.getOperationName(), ctx
.getErrorList(), null, false, false, ctx
.getRequestGuid());
}
AsyncResponse<T> response = new AsyncResponse<T>(ctx);
getServiePoller().add(ctx.getFutureResponse(), response);
return response;
} catch (RuntimeException e) {
commonHandlingOfError(e);
} catch (Error e) {
commonHandlingOfError(e);
} catch (ServiceInvocationException e) {
commonHandlingOfError(e);
} catch (ServiceException e) {
handlingOfSericeException(opName, e);
}
return null;
}
@Override
protected Future<?> invokeAsync(String opName, T inArg,
AsyncHandler<T> handler) {
try {
// Do dispatch specific pre processing.
preProcessMessageForDispatch(opName, inArg);
// Store the clients handler
ClientMessageContextImpl ctx = getCurrentContext();
ctx.setClientAsyncHandler(handler);
ctx.setExecutor(m_executor);
// Message now to be processed by the CMP
getMessageProcessor().processMessage(ctx, true);
if (ctx.getErrorList() != null && !ctx.getErrorList().isEmpty()) {
throw ServiceCallHelper.createInvocationException(ctx, ctx
.getAdminName(), ctx.getOperationName(), ctx
.getErrorList(), null, false, false, ctx
.getRequestGuid());
}
// Return Response for pull
return getCurrentContext().getFutureResponse();
} catch (RuntimeException e) {
commonHandlingOfError(e);
} catch (Error e) {
commonHandlingOfError(e);
} catch (ServiceInvocationException e) {
commonHandlingOfError(e);
} catch (ServiceException e) {
handlingOfSericeException(opName, e);
}
return null;
}
@Override
protected void invokeOneWay(String opName, T inArg) {
invokeAsync(opName, inArg);
}
private ServiceInvocationException getServiceInvocationExceptionForServiceException(
String opName, Throwable e) {
List<Throwable> clientErrors = new ArrayList<Throwable>(1);
clientErrors.add(e);
ServiceInvocationException e2 = ServiceCallHelper
.createInvocationException(getCurrentContext(), getAdminName(),
opName, clientErrors, null, false, false, null);
return e2;
}
private void setCurrentContext(ClientMessageContextImpl m_currentContext) {
this.m_currentContext = m_currentContext;
}
public ClientMessageContextImpl getCurrentContext() {
return m_currentContext;
}
public ClientMessageProcessor getMessageProcessor() {
return m_messageProcessor;
}
public IAsyncResponsePoller getServiePoller() {
return m_servicePoller;
}
public void setServicePoller(IAsyncResponsePoller servicePoller) {
m_servicePoller = servicePoller;
}
}