/*
* 2012-3 Red Hat Inc. and/or its affiliates and other contributors.
*
* 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
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.overlord.rtgov.internal.switchyard.exchange;
import java.util.EventObject;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.namespace.QName;
import org.overlord.rtgov.activity.model.soa.RPCActivityType;
import org.overlord.rtgov.activity.model.soa.RequestReceived;
import org.overlord.rtgov.activity.model.soa.RequestSent;
import org.overlord.rtgov.activity.model.soa.ResponseReceived;
import org.overlord.rtgov.activity.model.soa.ResponseSent;
import org.overlord.rtgov.internal.switchyard.AbstractEventProcessor;
import org.switchyard.Exchange;
import org.switchyard.ExchangePhase;
import org.switchyard.Message;
import org.switchyard.Property;
import org.switchyard.Scope;
import org.switchyard.Service;
import org.switchyard.ServiceReference;
import org.switchyard.config.model.composite.BindingModel;
import org.switchyard.extensions.wsdl.WSDLService;
import org.switchyard.label.BehaviorLabel;
import org.switchyard.metadata.ExchangeContract;
import org.switchyard.metadata.InOnlyService;
import org.switchyard.metadata.InOutService;
import org.switchyard.metadata.Registrant;
import org.switchyard.metadata.ServiceInterface;
import org.switchyard.extensions.java.JavaService;
import org.switchyard.runtime.event.ExchangeCompletionEvent;
import org.switchyard.security.context.SecurityContext;
import org.switchyard.security.context.SecurityContextManager;
import org.switchyard.security.credential.Credential;
/**
* This class provides the abstract Exchange event based implementation of the
* event processor.
*
*/
public abstract class AbstractExchangeEventProcessor extends AbstractEventProcessor {
private static final String GATEWAY_PROPERTY = "gateway";
private static final String UNEXPECTED_FAULT = "ERROR";
private static final String RTGOV_REQUEST_SENT = "rtgov.request.sent";
private static final String RTGOV_REQUEST_RECEIVED = "rtgov.request.received";
private static final Logger LOG=Logger.getLogger(AbstractExchangeEventProcessor.class.getName());
private boolean _completedEvent=false;
/**
* This is the constructor.
*
* @param eventType The event type associated with the processor
* @param completed Whether the event processor represents a completed event
*/
public AbstractExchangeEventProcessor(Class<? extends EventObject> eventType, boolean completed) {
super(eventType);
_completedEvent = completed;
}
/**
* This method obtains the exchange from the supplied event.
*
* @param event The event
* @return The exchange
*/
protected abstract Exchange getExchange(EventObject event);
/**
* {@inheritDoc}
*/
public void handleEvent(EventObject event) {
try {
Exchange exch=getExchange(event);
if (LOG.isLoggable(Level.FINEST)) {
LOG.finest("********* Exchange="+exch);
}
org.switchyard.Message mesg=exch.getMessage();
ExchangePhase phase=exch.getPhase();
if (phase == null) {
LOG.severe("Could not obtain phase from exchange: "+exch);
return;
}
if (mesg == null) {
LOG.severe("Could not obtain message for phase ("+phase+") and exchange: "+exch);
return;
}
org.switchyard.Context context=exch.getContext();
Service provider=exch.getProvider();
ServiceReference consumer=exch.getConsumer();
// TODO: If message is transformed, then should the contentType
// be updated to reflect the transformed type?
String messageId=null;
Property mip=context.getProperty(Exchange.MESSAGE_ID, org.switchyard.Scope.MESSAGE);
if (mip != null) {
messageId = (String)mip.getValue();
}
String contentType=null;
Property ctp=context.getProperty(Exchange.CONTENT_TYPE, org.switchyard.Scope.MESSAGE);
if (ctp != null) {
contentType = ((QName)ctp.getValue()).toString();
// RTGOV-250 - remove java: prefix from Java types, to make the type consistent with
// events reported outside switchyard
if (contentType != null && contentType.startsWith("java:")) {
contentType = contentType.substring(5);
}
}
if (phase == ExchangePhase.IN) {
handleInExchange(exch, provider, consumer, messageId, contentType, mesg);
} else if (phase == ExchangePhase.OUT) {
String relatesTo=null;
Property rtp=context.getProperty(Exchange.RELATES_TO, org.switchyard.Scope.MESSAGE);
if (rtp != null) {
relatesTo = (String)rtp.getValue();
}
handleOutExchange(exch, provider, consumer, messageId, relatesTo, contentType, mesg);
}
} catch (Throwable t) {
LOG.log(Level.SEVERE, java.util.PropertyResourceBundle.getBundle(
"rtgov-switchyard.Messages").getString("RTGOV-SWITCHYARD-1"), t);
}
}
/**
* This method handles the 'in' exchange.
*
* @param exch The exchange
* @param provider The provider
* @param consumer The consumer
* @param messageId The message id
* @param contentType The content type
* @param mesg The message
*/
protected void handleInExchange(Exchange exch,
Service provider, ServiceReference consumer, String messageId,
String contentType, org.switchyard.Message mesg) {
Registrant consumerReg=consumer.getServiceMetadata().getRegistrant();
if (_completedEvent) {
// The return side of a one way exchange, so record a response sent/received
handleOutExchange(exch, provider, consumer, messageId+"onewayreturn", messageId, null, null);
// Nothing to do, as appears to be a one-way exchange
return;
}
Registrant providerReg=(provider == null ? null : provider.getServiceMetadata().getRegistrant());
String intf=getInterface(consumer, provider, consumerReg);
SecurityContextManager scm=new SecurityContextManager(exch.getConsumer().getDomain());
SecurityContext securityContext=scm.getContext(exch);
ExchangeContract contract=exch.getContract();
// Extract service type and operation from the consumer
// (service reference), as provider is not always available
QName serviceType=consumer.getName();
String opName=contract.getConsumerOperation().getName();
if (consumerReg.isBinding()) {
getActivityCollector().startScope();
} else {
// Only record the request being sent, if the
// source is a component, not a binding
RequestSent sent=new RequestSent();
// Only report service type if provider is not a binding
if (providerReg == null
|| !providerReg.isBinding()) {
sent.setServiceType(serviceType.toString());
} else if (providerReg.isBinding()) {
String gatewayName=exch.getContext().<String>getPropertyValue(ExchangeCompletionEvent.GATEWAY_NAME);
java.util.List<BindingModel> bindings=providerReg.<java.util.List<BindingModel>>getConfig();
for (int i=0; gatewayName != null && i < bindings.size(); i++) {
BindingModel bm=bindings.get(i);
if (gatewayName.equals(bm.getName())) {
sent.getProperties().put(GATEWAY_PROPERTY, bm.getType());
}
}
}
sent.setInterface(intf);
sent.setOperation(opName);
sent.setMessageId(messageId);
record(mesg, contentType, sent, securityContext, exch);
if (intf == null) {
// Save activity event in exchange
Property prop=exch.getContext().setProperty(RTGOV_REQUEST_SENT, sent, Scope.EXCHANGE);
prop.addLabels(BehaviorLabel.TRANSIENT.label());
}
}
if (providerReg == null
|| !providerReg.isBinding()) {
RequestReceived recvd=new RequestReceived();
recvd.setServiceType(serviceType.toString());
recvd.setInterface(intf);
recvd.setOperation(opName);
recvd.setMessageId(messageId);
if (consumerReg.isBinding()) {
String gatewayName=exch.getContext().<String>getPropertyValue(ExchangeCompletionEvent.GATEWAY_NAME);
java.util.List<BindingModel> bindings=consumerReg.<java.util.List<BindingModel>>getConfig();
for (int i=0; gatewayName != null && i < bindings.size(); i++) {
BindingModel bm=bindings.get(i);
if (gatewayName.equals(bm.getName())) {
recvd.getProperties().put(GATEWAY_PROPERTY, bm.getType());
}
}
} else {
recvd.setInternal(true);
}
record(mesg, contentType, recvd, securityContext, exch);
// Save activity event in exchange
// RTGOV-262 Need to store this event, event if interface set,
// in case needs to establish relationship from exception response
Property prop=exch.getContext().setProperty(RTGOV_REQUEST_RECEIVED, recvd, Scope.EXCHANGE);
prop.addLabels(BehaviorLabel.TRANSIENT.label());
}
}
/**
* This method handles the 'in' exchange.
*
* @param exch The exchange
* @param provider The provider
* @param consumer The consumer
* @param messageId The message id
* @param relatesTo The relates-to id
* @param contentType The content type
* @param mesg The message
*/
protected void handleOutExchange(Exchange exch,
Service provider, ServiceReference consumer, String messageId, String relatesTo,
String contentType, org.switchyard.Message mesg) {
Registrant consumerReg=consumer.getServiceMetadata().getRegistrant();
Registrant providerReg=(provider == null ? null : provider.getServiceMetadata().getRegistrant());
// Check if interface on associated request needs to be set
String intf=getInterface(consumer, provider, consumerReg);
SecurityContextManager scm=new SecurityContextManager(exch.getConsumer().getDomain());
SecurityContext securityContext=scm.getContext(exch);
ExchangeContract contract=exch.getContract();
// Extract service type and operation from the consumer
// (service reference), as provider is not always available
QName serviceType=consumer.getName();
String opName=contract.getConsumerOperation().getName();
// Attempt to retrieve any stored request activity event
Property rrtw=exch.getContext().getProperty(RTGOV_REQUEST_RECEIVED);
Property rstw=exch.getContext().getProperty(RTGOV_REQUEST_SENT);
RequestReceived rr=(rrtw == null ? null : (RequestReceived)rrtw.getValue());
RequestSent rs=(rstw == null ? null : (RequestSent)rstw.getValue());
if (intf != null) {
if (rr != null) {
rr.setInterface(intf);
}
if (rs != null) {
rs.setInterface(intf);
}
}
// Record the response
if (providerReg == null
|| !providerReg.isBinding()) {
ResponseSent sent=new ResponseSent();
// Only report service type if provider is not a binding
if (providerReg == null
|| !providerReg.isBinding()) {
sent.setServiceType(serviceType.toString());
}
sent.setInterface(intf);
sent.setOperation(opName);
sent.setMessageId(messageId);
if (consumerReg.isBinding()) {
String gatewayName=exch.getContext().<String>getPropertyValue(ExchangeCompletionEvent.GATEWAY_NAME);
java.util.List<BindingModel> bindings=consumerReg.<java.util.List<BindingModel>>getConfig();
for (int i=0; gatewayName != null && i < bindings.size(); i++) {
BindingModel bm=bindings.get(i);
if (gatewayName.equals(bm.getName())) {
sent.getProperties().put(GATEWAY_PROPERTY, bm.getType());
}
}
} else {
sent.setInternal(true);
}
// RTGOV-262 Check if replyTo id not set, due to exception - if so, then
// use request received id if available
if (relatesTo == null && rr != null) {
if (LOG.isLoggable(Level.FINEST)) {
LOG.finest("Exception seems to have occurred, "
+"so establishing relationship to original request: "+rr.getMessageId());
}
relatesTo = rr.getMessageId();
}
sent.setReplyToId(relatesTo);
record(mesg, contentType, sent, securityContext, exch);
}
if (consumerReg.isBinding()) {
getActivityCollector().endScope();
} else {
// Only record the response being received, if the
// target is a component, not a binding
ResponseReceived recvd=new ResponseReceived();
recvd.setServiceType(serviceType.toString());
recvd.setInterface(intf);
recvd.setOperation(opName);
recvd.setMessageId(messageId);
recvd.setReplyToId(relatesTo);
if (providerReg != null && providerReg.isBinding()) {
String gatewayName=exch.getContext().<String>getPropertyValue(ExchangeCompletionEvent.GATEWAY_NAME);
java.util.List<BindingModel> bindings=providerReg.<java.util.List<BindingModel>>getConfig();
for (int i=0; gatewayName != null && i < bindings.size(); i++) {
BindingModel bm=bindings.get(i);
if (gatewayName.equals(bm.getName())) {
recvd.getProperties().put(GATEWAY_PROPERTY, bm.getType());
}
}
} else {
recvd.setInternal(true);
}
record(mesg, contentType, recvd, securityContext, exch);
}
}
/**
* This method extracts the interface from the exchange details.
*
* @param consumer The exchange consumer
* @param provider The exchange provider
* @param consumerReg The consumer registrant
* @return The interface
*/
protected String getInterface(ServiceReference consumer, Service provider, Registrant consumerReg) {
String ret=null;
ServiceInterface intf=null;
if (consumerReg.isBinding()) {
intf = consumer.getInterface();
} else if (provider != null) {
intf = provider.getInterface();
}
if (intf != null) {
if (JavaService.TYPE.equals(intf.getType())) {
ret = ((JavaService)intf).getJavaInterface().getName();
} else if (WSDLService.TYPE.equals(intf.getType())) {
ret = ((WSDLService)intf).getPortType().toString();
} else if (intf instanceof InOnlyService) {
if (((InOnlyService)intf).getOperations().size() > 0) {
ret = ((InOnlyService)intf).getOperations().iterator().next().getInputType().toString();
} else {
LOG.warning("InOnly interface does not have an operation");
ret = "esb";
}
} else if (intf instanceof InOutService) {
if (((InOutService)intf).getOperations().size() > 0) {
ret = ((InOutService)intf).getOperations().iterator().next().getInputType().toString()
+ "->" + ((InOutService)intf).getOperations().iterator().next().getOutputType().toString();
} else {
LOG.warning("InOut interface does not have an operation");
ret = "esb";
}
} else {
LOG.severe("Unknown interface type: "+intf);
}
}
return (ret);
}
/**
* This method records the supplied information as an activity
* event.
*
* @param exchange The exchange
* @param contentType The message content type
* @param at The activity type
* @param sc The optional security context
* @param exch The original exchange event
*/
protected void record(Message msg, String contentType,
RPCActivityType at, SecurityContext sc, Exchange exch) {
if (at != null) {
if (msg != null) {
Object content=msg.getContent();
// Check if content type not set
if (contentType == null) {
// Determine whether checked exception (RTGOV-356)
if (!(content instanceof org.switchyard.HandlerException)) {
contentType = content.getClass().getName();
// By default, set the fault to the exception class name
// This can always be overridden by the information processor
// To make it more readable, if the name ends in Exception, then
// remove it
String faultName=content.getClass().getSimpleName();
if (faultName != null && faultName.endsWith("Exception")) {
faultName = faultName.substring(0, faultName.length()-9);
}
if (LOG.isLoggable(Level.FINEST)) {
LOG.finest("Setting fault for type '"+contentType+"' to: "+faultName);
}
at.setFault(faultName);
} else {
at.setFault(UNEXPECTED_FAULT);
}
}
if (contentType != null) {
at.setContent(getActivityCollector().processInformation(null,
contentType, content, new PropertyAccessor(msg.getContext()), at));
} else if (content != null) {
// Assume this is an exception response
at.setContent(content.toString());
}
}
at.setMessageType(contentType);
// Check if principal has been defined
if (sc != null && sc.getCredentials().size() > 0) {
for (Credential cred : sc.getCredentials()) {
if (cred instanceof org.switchyard.security.credential.NameCredential) {
at.setPrincipal(((org.switchyard.security.credential.NameCredential)cred).getName());
break;
} else if (cred instanceof org.switchyard.security.credential.PrincipalCredential) {
at.setPrincipal(((org.switchyard.security.credential.PrincipalCredential)cred)
.getPrincipal().getName());
break;
}
}
}
recordActivity(exch, at);
}
}
}