/**
* <a href="http://servicemix.org">ServiceMix: The open source ESB</a>
*
* Copyright 2005 RAJD Consultancy Ltd
*
* 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.servicemix.jbi.nmr;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.servicemix.jbi.container.ActivationSpec;
import org.servicemix.jbi.container.JBIContainer;
import org.servicemix.jbi.framework.ComponentContextImpl;
import org.servicemix.jbi.framework.ComponentNameSpace;
import org.servicemix.jbi.framework.LocalComponentConnector;
import org.servicemix.jbi.framework.Registry;
import org.servicemix.jbi.management.BaseLifeCycle;
import org.servicemix.jbi.management.ManagementContext;
import org.servicemix.jbi.management.OperationInfoHelper;
import org.servicemix.jbi.messaging.MessageExchangeImpl;
import org.servicemix.jbi.nmr.flow.Flow;
import org.servicemix.jbi.nmr.flow.FlowProvider;
import org.servicemix.jbi.resolver.ConsumerComponentEndpointFilter;
import org.servicemix.jbi.resolver.EndpointChooser;
import org.servicemix.jbi.resolver.EndpointFilter;
import org.servicemix.jbi.resolver.EndpointResolver;
import org.servicemix.jbi.resolver.FirstChoicePolicy;
import org.servicemix.jbi.resolver.ProducerComponentEndpointFilter;
import org.servicemix.jbi.servicedesc.AbstractServiceEndpoint;
import org.servicemix.jbi.servicedesc.ExternalEndpoint;
import org.servicemix.jbi.servicedesc.InternalEndpoint;
import javax.jbi.JBIException;
import javax.jbi.component.Component;
import javax.jbi.management.LifeCycleMBean;
import javax.jbi.messaging.MessagingException;
import javax.jbi.messaging.MessageExchange.Role;
import javax.jbi.servicedesc.ServiceEndpoint;
import javax.management.JMException;
import javax.management.MBeanOperationInfo;
import javax.resource.spi.work.WorkManager;
import javax.xml.namespace.QName;
import java.util.ArrayList;
import java.util.List;
/**
* The Broker handles Nomalised Message Routing within ServiceMix
*
* @version $Revision: 738 $
*/
public class Broker extends BaseLifeCycle {
private JBIContainer container;
private Registry registry;
private String flowName = "seda";
private String subscriptionFlowName = null;
private WorkManager workManager;
private Flow flow;
private final static Log log = LogFactory.getLog(Broker.class);
private EndpointChooser defaultServiceChooser = new FirstChoicePolicy();
private EndpointChooser defaultInterfaceChooser = new FirstChoicePolicy();
private SubscriptionManager subscriptionManager = new SubscriptionManager();
/**
* Constructor
*/
public Broker() {
}
/**
* Get the description
*
* @return description
*/
public String getDescription() {
return "Normalized Message Router";
}
/**
* @return Returns the workManager.
*/
public WorkManager getWorkManager() {
return workManager;
}
/**
* @param workManager
* The workManager to set.
*/
public void setWorkManager(WorkManager workManager) {
this.workManager = workManager;
}
public SubscriptionManager getSubscriptionManager() {
return subscriptionManager;
}
/**
* Sets the subscription manager
*/
public void setSubscriptionManager(SubscriptionManager subscriptionManager) {
this.subscriptionManager = subscriptionManager;
}
/**
* initialize the broker
*
* @param container
* @throws JBIException
*/
public void init(JBIContainer container) throws JBIException{
this.container = container;
this.workManager = container.getWorkManager();
this.registry = container.getRegistry();
if(this.flow == null){
this.flow = FlowProvider.getFlow(flowName);
}
this.flow.init(this, null);
if (subscriptionManager.getFlow() == null && subscriptionManager.getFlowName() == null) {
if (subscriptionFlowName == null || subscriptionFlowName.equals(flowName)){
subscriptionManager.setFlow(flow);
} else {
subscriptionManager.setFlowName(subscriptionFlowName);
}
}
subscriptionManager.init(this, registry);
if (flow != subscriptionManager.getFlow()) {
subscriptionManager.getFlow().init(this, "subscription");
}
container.getManagementContext().registerSystemService(this, LifeCycleMBean.class);
}
/**
* Get the name of the Container
*
* @return containerName
*/
public String getContainerName() {
return container.getName();
}
/**
* Get the ManagementContext
*
* @return the managementContext
*/
public ManagementContext getManagementContext() {
return container.getManagementContext();
}
/**
* Get the Registry
*
* @return the registry
*/
public Registry getRegistry() {
return registry;
}
/**
* start brokering
*
* @throws JBIException
*/
public void start() throws JBIException {
flow.start();
if (subscriptionManager.getFlow() != flow && subscriptionManager.getFlow() != null) {
subscriptionManager.getFlow().start();
}
super.start();
}
/**
* stop brokering
*
* @throws JBIException
*/
public void stop() throws JBIException {
flow.stop();
if (subscriptionManager.getFlow() != flow && subscriptionManager.getFlow() != null) {
subscriptionManager.getFlow().stop();
}
super.stop();
}
/**
* shutdown all Components
*
* @throws JBIException
*/
public void shutDown() throws JBIException {
stop();
flow.shutDown();
if (subscriptionManager.getFlow() != flow && subscriptionManager.getFlow() != null) {
subscriptionManager.getFlow().shutDown();
}
super.shutDown();
container.getManagementContext().unregisterMBean(this);
}
/**
* @return Returns the flow.
*/
public String getFlowName() {
return flowName;
}
/**
* @param flowName
* The flow to set.
*/
public void setFlowName(String flowName) {
this.flowName = flowName;
}
/**
* @return the subscriptionFlowName
*/
public String getSubscriptionFlowName() {
return subscriptionFlowName;
}
/**
* Set the subscription flow name
* @param subscriptionFlowName
*/
public void setSubscriptionFlowName(String subscriptionFlowName) {
this.subscriptionFlowName = subscriptionFlowName;
}
/**
* Set the flow
*
* @param flow
*/
public void setFlow(Flow flow) {
this.flow = flow;
}
/**
* @return the Flow
*/
public Flow getFlow() {
return this.flow;
}
/**
* suspend the flow to prevent any message exchanges
*/
public void suspend() {
flow.suspend();
}
/**
* resume message exchange processing
*/
public void resume() {
flow.resume();
}
/**
* Route an ExchangePacket to a destination
*
* @param exchange
* @throws JBIException
*/
public void sendExchangePacket(MessageExchangeImpl exchange) throws JBIException {
if (exchange.getRole() == Role.PROVIDER && exchange.getDestinationId() == null) {
resolveAddress(exchange);
}
boolean foundRoute = false;
// If we found a destination, or this is a reply
if (exchange.getEndpoint() != null || exchange.getRole() == Role.CONSUMER) {
foundRoute = true;
flow.send(exchange);
}
if (exchange.getRole() == Role.PROVIDER) {
getSubscriptionManager().dispatchToSubscribers(exchange);
}
if (!foundRoute) {
boolean throwException = true;
ActivationSpec activationSpec = exchange.getActivationSpec();
if (activationSpec != null) {
throwException = activationSpec.isFailIfNoDestinationEndpoint();
}
if (throwException) {
throw new MessagingException("Could not find route for exchange: " + exchange + " for service: " + exchange.getService() + " and interface: "
+ exchange.getInterfaceName());
} else if (exchange.getMirror().getSyncState() == MessageExchangeImpl.SYNC_STATE_SYNC_SENT) {
exchange.handleAccept();
ComponentContextImpl ctx = (ComponentContextImpl) getSubscriptionManager().getContext();
exchange.setDestinationId(ctx.getComponentNameSpace());
// TODO: this will fail if exchange is InOut
getSubscriptionManager().done(exchange);
}
}
}
protected void resolveAddress(MessageExchangeImpl exchange) throws JBIException {
ServiceEndpoint theEndpoint = exchange.getEndpoint();
if (theEndpoint != null) {
if (theEndpoint instanceof ExternalEndpoint) {
throw new JBIException("External endpoints can not be used for routing: should be an internal or dynamic endpoint.");
}
if (theEndpoint instanceof AbstractServiceEndpoint == false) {
throw new JBIException("Component-specific endpoints can not be used for routing: should be an internal or dynamic endpoint.");
}
}
// get the context which created the exchange
ComponentContextImpl context = exchange.getSourceContext();
if (theEndpoint == null) {
QName serviceName = exchange.getService();
QName interfaceName = exchange.getInterfaceName();
// check in order, ServiceName then InterfaceName
// check to see if there is a match on the serviceName
if (serviceName != null) {
ServiceEndpoint[] endpoints = registry.getEndpointsForService(serviceName);
endpoints = getMatchingEndpoints(endpoints, exchange);
theEndpoint = getServiceChooser(exchange).chooseEndpoint(endpoints, context, exchange);
if (theEndpoint == null) {
log.warn("ServiceName (" + serviceName + ") specified for routing, but can't find it registered");
}
}
if (theEndpoint == null && interfaceName != null) {
ServiceEndpoint[] endpoints = registry.getEndpoints(interfaceName);
endpoints = getMatchingEndpoints(endpoints, exchange);
theEndpoint = (InternalEndpoint) getInterfaceChooser(exchange).chooseEndpoint(endpoints, context, exchange);
if (theEndpoint == null) {
log.warn("InterfaceName (" + interfaceName + ") specified for routing, but can't find any matching components");
}
}
if (theEndpoint == null) {
// lets use the resolver on the activation spec if
// applicable
ActivationSpec activationSpec = exchange.getActivationSpec();
if (activationSpec != null) {
EndpointResolver destinationResolver = activationSpec.getDestinationResolver();
if (destinationResolver != null) {
try {
EndpointFilter filter = createEndpointFilter(context, exchange);
theEndpoint = (InternalEndpoint) destinationResolver.resolveEndpoint(context, exchange, filter);
}
catch (JBIException e) {
throw new MessagingException("Failed to resolve endpoint: " + e, e);
}
}
}
}
}
if (theEndpoint != null) {
exchange.setEndpoint(theEndpoint);
exchange.setDestinationId(((AbstractServiceEndpoint) theEndpoint).getComponentNameSpace());
}
if (log.isTraceEnabled()) {
log.trace("Routing exchange " + exchange + " to: " + theEndpoint);
}
}
/**
* Filter the given endpoints by asking to the provider and consumer
* if they are both ok to process the exchange.
*
* @param endpoints an array of internal endpoints to check
* @param exchange the exchange that will be serviced
* @return an array of endpoints on which both consumer and provider agrees
*/
protected ServiceEndpoint[] getMatchingEndpoints(ServiceEndpoint[] endpoints, MessageExchangeImpl exchange) {
List filtered = new ArrayList();
LocalComponentConnector consumer = getRegistry().getLocalComponentConnector(exchange.getSourceId());
for (int i = 0; i < endpoints.length; i++) {
ComponentNameSpace id = ((AbstractServiceEndpoint) endpoints[i]).getComponentNameSpace();
LocalComponentConnector provider = getRegistry().getLocalComponentConnector(id);
if (provider != null) {
if (consumer.getComponent().isExchangeWithProviderOkay(endpoints[i], exchange) &&
provider.getComponent().isExchangeWithConsumerOkay(endpoints[i], exchange)) {
filtered.add(endpoints[i]);
}
} else {
filtered.add(endpoints[i]);
}
}
return (ServiceEndpoint[]) filtered.toArray(new ServiceEndpoint[filtered.size()]);
}
/**
* @return the default EndpointChooser
*/
public EndpointChooser getDefaultInterfaceChooser() {
return defaultInterfaceChooser;
}
/**
* Set the default EndpointChooser
*
* @param defaultInterfaceChooser
*/
public void setDefaultInterfaceChooser(EndpointChooser defaultInterfaceChooser) {
this.defaultInterfaceChooser = defaultInterfaceChooser;
}
/**
* @return the default EndpointChooser
*/
public EndpointChooser getDefaultServiceChooser() {
return defaultServiceChooser;
}
/**
* Set default EndpointChooser
*
* @param defaultServiceChooser
*/
public void setDefaultServiceChooser(EndpointChooser defaultServiceChooser) {
this.defaultServiceChooser = defaultServiceChooser;
}
/**
* Returns the endpoint chooser for endpoints found by service which will
* use the chooser on the exchange's activation spec if available otherwise
* will use the default
*
* @param exchange
* @return the EndpointChooser
*/
protected EndpointChooser getServiceChooser(MessageExchangeImpl exchange) {
EndpointChooser chooser = null;
ActivationSpec activationSpec = exchange.getActivationSpec();
if (activationSpec != null) {
chooser = activationSpec.getServiceChooser();
}
if (chooser == null) {
chooser = defaultServiceChooser;
}
return chooser;
}
/**
* Returns the endpoint chooser for endpoints found by service which will
* use the chooser on the exchange's activation spec if available otherwise
* will use the default
*
* @param exchange
* @return the EndpointChooser
*/
protected EndpointChooser getInterfaceChooser(MessageExchangeImpl exchange) {
EndpointChooser chooser = null;
ActivationSpec activationSpec = exchange.getActivationSpec();
if (activationSpec != null) {
chooser = activationSpec.getInterfaceChooser();
}
if (chooser == null) {
chooser = defaultInterfaceChooser;
}
return chooser;
}
/**
* Factory method to create an endpoint filter for the given component
* context and message exchange
*
* @param context
* @param exchange
* @return the EndpointFilter
*/
protected EndpointFilter createEndpointFilter(ComponentContextImpl context, MessageExchangeImpl exchange) {
Component component = context.getComponent();
if (exchange.getRole() == Role.PROVIDER) {
return new ConsumerComponentEndpointFilter(component);
}
else {
return new ProducerComponentEndpointFilter(component);
}
}
/**
* Get an array of MBeanOperationInfo
*
* @return array of OperationInfos
* @throws JMException
*/
public MBeanOperationInfo[] getOperationInfos() throws JMException {
OperationInfoHelper helper = new OperationInfoHelper();
helper.addOperation(getObjectToManage(), "suspend", "suspend the NMR processing");
helper.addOperation(getObjectToManage(), "resume", "resume the NMR processing");
return OperationInfoHelper.join(super.getOperationInfos(), helper.getOperationInfos());
}
public JBIContainer getContainer() {
return container;
}
}