/******************************************************************************* * 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.common.impl.internal.pipeline; import java.util.List; import org.ebayopensource.turmeric.runtime.common.exceptions.ErrorDataFactory; import org.ebayopensource.turmeric.runtime.common.exceptions.ServiceException; import org.ebayopensource.turmeric.runtime.common.impl.internal.g11n.GlobalRegistryConfigManager; import org.ebayopensource.turmeric.runtime.common.impl.internal.monitoring.SystemMetricDefs; import org.ebayopensource.turmeric.runtime.common.impl.internal.service.ServiceDesc; import org.ebayopensource.turmeric.runtime.common.impl.protocolprocessor.soap.BaseSOAPProtocolProcessor; import org.ebayopensource.turmeric.runtime.common.pipeline.Dispatcher; import org.ebayopensource.turmeric.runtime.common.pipeline.LoggingHandlerStage; import org.ebayopensource.turmeric.runtime.common.pipeline.MessageProcessingStage; import org.ebayopensource.turmeric.runtime.common.pipeline.Pipeline; import org.ebayopensource.turmeric.runtime.common.pipeline.ProtocolProcessor; import org.ebayopensource.turmeric.runtime.errorlibrary.ErrorConstants; import org.ebayopensource.turmeric.common.v1.types.ErrorData; import org.ebayopensource.turmeric.common.v1.types.ErrorSeverity; /** * Defines base class for both Client and Server Message Processors (CMP and * SMP) * * This class is responsible for invoking protocol processors, handlers and * request/response dispatchers. CMP and SMP are expected to modify the behavior * as necessary to prepare MessageContext or useAsync flag. * * On the receiving side, Servlet will construct MessageContext from its * HttpservletRequest, and then pass MessageContext to SMP. In the case of * client, Service will take arguments from the user Proxy construct * MessageContext and then call CMP. * * A message processor primarily has 4 stages of processing: -- running the * message through an input pipeline -- giving it to a request dispatcher -- * running the message through an output pipeline -- Giving the response to a * Response dispatcher * * A message processor also invokes protocol processor to perform protocol * specific tasks. For example, Axis2 will be plugged in as SOAP message * processor to process soap requests. * * @author ichernyshev, smalladi */ public abstract class BaseMessageProcessorImpl { /** * Invoke handlers, protocol processor and dispatchers for the given context * NOTE: async processing logic has been removed for SOA 2.0. Async * processing logic will be reintroduced post SOA 2.0. */ @SuppressWarnings("unchecked") public final void processMessageInternal(BaseMessageContextImpl ctx, boolean useAsync) { MessageContextAccessorImpl.blockPreviousContext(); try { long processingStartTime = System.nanoTime(); // Pass the start time into ctx for later reference recordMessageProcessingStartTime(ctx, processingStartTime); // run request pipeline runRequestSequence(ctx); // give it to request dispatcher if (!ctx.hasErrors() && !ctx.hasResponseResidentErrors()) { try { dispatchInternal(ctx, useAsync); } catch (Throwable e) { handleRequestDispatchException(ctx, e); handleAbortedRequestDispatch(ctx); } } else { handleAbortedRequestDispatch(ctx); } if (!useAsync) { // run response pipeline in case of sync executions or pipeline // errors runResponseSequence(ctx); updateMonitoringAfterProcessing(ctx, processingStartTime); } } finally { MessageContextAccessorImpl.resetContext(); } } public abstract void dispatchInternal(BaseMessageContextImpl ctx, boolean useAsync) throws Throwable; public final void processResponseInternal(BaseMessageContextImpl ctx) { MessageContextAccessorImpl.blockPreviousContext(); try { ServiceDesc serviceDesc = ctx.getServiceDesc(); try { Dispatcher requestDispatcher = serviceDesc .getRequestDispatcher(); requestDispatcher.retrieve(ctx, ctx.getFutureResponse()); } catch (Throwable e) { handleRequestDispatchException(ctx, e); handleAbortedRequestDispatch(ctx); } runResponseSequence(ctx); Long processingStartTime = (Long) ctx .getProperty(SystemMetricDefs.CTX_KEY_MSG_PROCESSING_STARTED); updateMonitoringAfterProcessing(ctx, processingStartTime .longValue()); } finally { MessageContextAccessorImpl.resetContext(); } } private void recordMessageProcessingStartTime(BaseMessageContextImpl ctx, long processingStartTime) { try { ctx.setProperty(SystemMetricDefs.CTX_KEY_MSG_PROCESSING_STARTED, new Long(processingStartTime)); } catch (ServiceException e) { handleRequestProcessingException(ctx, e); } } void updateMonitoringAfterProcessing(BaseMessageContextImpl ctx, long processingStartTime) { long duration = System.nanoTime() - processingStartTime; ctx.updateSvcAndOpMetric(SystemMetricDefs.OP_TIME_TOTAL, duration); if (shouldCountAsFailedRequest(ctx)) { ctx.incrementSvcAndOpMetric(SystemMetricDefs.OP_ERR_FAILED_CALLS); } } protected boolean shouldCountAsFailedRequest(BaseMessageContextImpl ctx) { if ( ctx.hasErrors() ) return true; if ( !ctx.hasResponseResidentErrors() ) return false; /** * RRE's only count as failed requests if the severity was severe enough */ List<ErrorData> errorDataList = ctx.getResponseResidentErrorList(); for ( ErrorData errorData : errorDataList ) if ( isError( errorData ) ) return true; return false; } private boolean isError( ErrorData errorData ) { return errorData.getSeverity() == ErrorSeverity.ERROR; } private void runRequestSequence(BaseMessageContextImpl ctx) { ServiceDesc serviceDesc = ctx.getServiceDesc(); // flip the processing stage ctx.changeProcessingStage(MessageProcessingStage.REQUEST_PIPELINE); // first set up logging handler ctx.runLoggingHandlerStage(LoggingHandlerStage.REQUEST_STARTED); long startTime = System.nanoTime(); // run the protocol processor try { ProtocolProcessor protocol = ctx.getProtocolProcessor(); protocol.beforeRequestPipeline(ctx); } catch (Throwable e) { handleRequestProtocolProcessingException(ctx, e); } // run any logic that has to run before pipeline (e.g. version checks) try { beforeRequestPipeline(ctx); } catch (Throwable e) { handleRequestProcessingException(ctx, e); } // run the pipeline try { Pipeline requestPipeline = serviceDesc.getRequestPipeline(); requestPipeline.invoke(ctx); } catch (Throwable e) { handleRequestProcessingException(ctx, e); } // run the protocol processor try { ProtocolProcessor protocol = ctx.getProtocolProcessor(); protocol.beforeRequestDispatch(ctx); } catch (Throwable e) { handleRequestProtocolProcessingException(ctx, e); } // flip the processing stage ctx.changeProcessingStage(MessageProcessingStage.REQUEST_DISPATCH); ctx.runLoggingHandlerStage(LoggingHandlerStage.BEFORE_REQUEST_DISPATCH); long duration = System.nanoTime() - startTime; ctx.updateSvcAndOpMetric(SystemMetricDefs.OP_TIME_PIPELINE_REQUEST, startTime, duration); } void runResponseSequence(BaseMessageContextImpl ctx) { ServiceDesc serviceDesc = ctx.getServiceDesc(); long startTime = System.nanoTime(); // flip the processing stage ctx.changeProcessingStage(MessageProcessingStage.RESPONSE_PIPELINE); ctx.runLoggingHandlerStage(LoggingHandlerStage.RESPONSE_STARTED); // run the protocol processor try { ProtocolProcessor protocol = ctx.getProtocolProcessor(); protocol.beforeResponsePipeline(ctx); } catch (Throwable e) { handleResponseProtocolProcessingException(ctx, e); } // run the pipeline try { Pipeline responsePipeline = serviceDesc.getResponsePipeline(); responsePipeline.invoke(ctx); } catch (Throwable e) { handleResponseProcessingException(ctx, e); } // run the protocol processor try { ProtocolProcessor protocol = ctx.getProtocolProcessor(); protocol.beforeResponseDispatch(ctx); } catch (Throwable e) { handleResponseProtocolProcessingException(ctx, e); } // flip the processing stage ctx.changeProcessingStage(MessageProcessingStage.RESPONSE_DISPATCH); boolean canProcessResponse = true; try { processPreResponseDispatchErrors(ctx); } catch (Throwable e) { // cannot do anything if errors are not processed canProcessResponse = false; handlePostResponseDispatchException(ctx, e); } long duration = System.nanoTime() - startTime; ctx.updateSvcAndOpMetric(SystemMetricDefs.OP_TIME_PIPELINE_RESPONSE, startTime, duration); if (canProcessResponse) { /* * Returning response is a seperate step. It is also abstracted out * through a dispatcher. In the case of a servlet, the * responseDispatcher is simply writing the data to the * HTTPResponse's outputstream. In the case of a Loaclbinding and * client cases, the response dispatcher simply returns and does * nothing. In the case of an asynchronous model, the response * dispatcher may use a connection and send the response back. */ // TODO: add timing for response dispatcher on server (both sync and // async cases) // TO DO: fix async case - we should use callback and uplink here as // we cannot call logger yet try { // run through the response dispatcher. Dispatcher responseDispatcher = serviceDesc .getResponseDispatcher(); responseDispatcher.dispatchSynchronously(ctx); } catch (Throwable e) { handlePostResponseDispatchException(ctx, e); } } ctx.changeProcessingStage(MessageProcessingStage.RESPONSE_COMPLETE); ctx.runLoggingHandlerStage(LoggingHandlerStage.RESPONSE_COMPLETE); } protected void beforeRequestPipeline(BaseMessageContextImpl ctx) throws ServiceException { // let subclasses override } protected void handleAbortedRequestDispatch(BaseMessageContextImpl ctx) { // let subclasses override } protected void processPreResponseDispatchErrors(BaseMessageContextImpl ctx) throws ServiceException { // let subclasses override } protected void handleRequestProcessingException(BaseMessageContextImpl ctx, Throwable e) { ctx.addError(e); } protected void handleRequestDispatchException(BaseMessageContextImpl ctx, Throwable e) { Object soapFault = ctx.getProperty(BaseSOAPProtocolProcessor.SOAP_FAULT_OBJECT); if(soapFault != null){ ctx.addError(new Exception("Received Soap Fault: " + soapFault)); } ctx.addError(e); } protected void handleRequestProtocolProcessingException( BaseMessageContextImpl ctx, Throwable e) { ctx.addError(e); } protected void handleResponseProcessingException( BaseMessageContextImpl ctx, Throwable e) { ctx.addError(e); } protected void handleResponseProtocolProcessingException( BaseMessageContextImpl ctx, Throwable e) { Object soapFault = ctx.getProperty(BaseSOAPProtocolProcessor.SOAP_FAULT_OBJECT); if(soapFault != null){ ctx.addError(new Exception("Received Soap Fault: " + soapFault)); } ctx.addError(e); } protected void handlePostResponseDispatchException( BaseMessageContextImpl ctx, Throwable e) { ctx.addError(e); } protected static void initializeCommonSubsystems() throws ServiceException { ErrorDataFactory.initialize(ErrorConstants.ERRORDOMAIN); ErrorDataFactory.initialize(org.ebayopensource.turmeric.security.errorlibrary.ErrorConstants.ERRORDOMAIN); GlobalRegistryConfigManager.getInstance(); } }