001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    package org.apache.servicemix.jbi.nmr;
018    
019    import java.util.ArrayList;
020    import java.util.List;
021    
022    import javax.jbi.JBIException;
023    import javax.jbi.component.Component;
024    import javax.jbi.messaging.MessageExchange;
025    import javax.jbi.messaging.MessageExchange.Role;
026    import javax.jbi.messaging.MessagingException;
027    import javax.jbi.servicedesc.ServiceEndpoint;
028    import javax.management.JMException;
029    import javax.management.MBeanOperationInfo;
030    import javax.xml.namespace.QName;
031    
032    import org.apache.commons.logging.Log;
033    import org.apache.commons.logging.LogFactory;
034    import org.apache.servicemix.jbi.api.EndpointResolver;
035    import org.apache.servicemix.jbi.container.ActivationSpec;
036    import org.apache.servicemix.jbi.container.JBIContainer;
037    import org.apache.servicemix.jbi.framework.ComponentContextImpl;
038    import org.apache.servicemix.jbi.framework.ComponentMBeanImpl;
039    import org.apache.servicemix.jbi.framework.ComponentNameSpace;
040    import org.apache.servicemix.jbi.framework.Registry;
041    import org.apache.servicemix.jbi.management.BaseSystemService;
042    import org.apache.servicemix.jbi.management.ManagementContext;
043    import org.apache.servicemix.jbi.management.OperationInfoHelper;
044    import org.apache.servicemix.jbi.messaging.MessageExchangeImpl;
045    import org.apache.servicemix.jbi.nmr.flow.DefaultFlowChooser;
046    import org.apache.servicemix.jbi.nmr.flow.Flow;
047    import org.apache.servicemix.jbi.nmr.flow.FlowChooser;
048    import org.apache.servicemix.jbi.nmr.flow.FlowProvider;
049    import org.apache.servicemix.jbi.resolver.ConsumerComponentEndpointFilter;
050    import org.apache.servicemix.jbi.resolver.EndpointChooser;
051    import org.apache.servicemix.jbi.resolver.EndpointFilter;
052    import org.apache.servicemix.jbi.resolver.FirstChoicePolicy;
053    import org.apache.servicemix.jbi.resolver.ProducerComponentEndpointFilter;
054    import org.apache.servicemix.jbi.servicedesc.AbstractServiceEndpoint;
055    import org.apache.servicemix.jbi.servicedesc.ExternalEndpoint;
056    import org.apache.servicemix.jbi.servicedesc.InternalEndpoint;
057    import org.apache.servicemix.jbi.servicedesc.LinkedEndpoint;
058    
059    /**
060     * The Broker handles Nomalised Message Routing within ServiceMix
061     * 
062     * @version $Revision: 384328 $
063     */
064    public class DefaultBroker extends BaseSystemService implements Broker {
065    
066        private static final Log LOG = LogFactory.getLog(DefaultBroker.class);
067    
068        private Registry registry;
069        private String flowNames = "seda";
070        private String subscriptionFlowName;
071        private Flow[] flows;
072        private EndpointChooser defaultServiceChooser = new FirstChoicePolicy();
073        private EndpointChooser defaultInterfaceChooser = new FirstChoicePolicy();
074        private SubscriptionManager subscriptionManager = new SubscriptionManager();
075        private FlowChooser defaultFlowChooser = new DefaultFlowChooser();
076    
077        /**
078         * Constructor
079         */
080        public DefaultBroker() {
081        }
082    
083        /**
084         * Get the description
085         * 
086         * @return description
087         */
088        public String getDescription() {
089            return "Normalized Message Router";
090        }
091    
092        public SubscriptionManager getSubscriptionManager() {
093            return subscriptionManager;
094        }
095    
096        /**
097         * Sets the subscription manager
098         */
099        public void setSubscriptionManager(SubscriptionManager subscriptionManager) {
100            this.subscriptionManager = subscriptionManager;
101        }
102    
103        /**
104         * initialize the broker
105         * 
106         * @param container
107         * @throws JBIException
108         */
109        public void init(JBIContainer container) throws JBIException {
110            super.init(container);
111            this.registry = container.getRegistry();
112            // Create and initialize flows
113            if (this.flows == null) {
114                String[] names = flowNames.split(",");
115                flows = new Flow[names.length];
116                for (int i = 0; i < names.length; i++) {
117                    flows[i] = FlowProvider.getFlow(names[i]);
118                    flows[i].init(this);
119                }
120            } else {
121                for (int i = 0; i < flows.length; i++) {
122                    flows[i].init(this);
123                }
124            }
125            subscriptionManager.init(this, registry);
126        }
127    
128        protected Class<BrokerMBean> getServiceMBean() {
129            return BrokerMBean.class;
130        }
131    
132        /**
133         * Get the name of the Container
134         * 
135         * @return containerName
136         */
137        public String getContainerName() {
138            return container.getName();
139        }
140    
141        /**
142         * Get the ManagementContext
143         * 
144         * @return the managementContext
145         */
146        public ManagementContext getManagementContext() {
147            return container.getManagementContext();
148        }
149    
150        /**
151         * Get the Registry
152         * 
153         * @return the registry
154         */
155        public Registry getRegistry() {
156            return registry;
157        }
158    
159        /**
160         * start brokering
161         * 
162         * @throws JBIException
163         */
164        public void start() throws JBIException {
165            for (int i = 0; i < flows.length; i++) {
166                flows[i].start();
167            }
168            super.start();
169        }
170    
171        /**
172         * stop brokering
173         * 
174         * @throws JBIException
175         */
176        public void stop() throws JBIException {
177            for (int i = 0; i < flows.length; i++) {
178                flows[i].stop();
179            }
180            super.stop();
181        }
182    
183        /**
184         * shutdown all Components
185         * 
186         * @throws JBIException
187         */
188        public void shutDown() throws JBIException {
189            stop();
190            for (int i = 0; i < flows.length; i++) {
191                flows[i].shutDown();
192            }
193            container.deactivateComponent(SubscriptionManager.COMPONENT_NAME);
194            super.shutDown();
195            container.getManagementContext().unregisterMBean(this);
196        }
197    
198        /**
199         * @return Returns the flow.
200         */
201        public String getFlowNames() {
202            return flowNames;
203        }
204    
205        /**
206         * @param flowName
207         *            The flow to set.
208         */
209        public void setFlowNames(String flowNames) {
210            this.flowNames = flowNames;
211        }
212    
213        /**
214         * @return the subscriptionFlowName
215         */
216        public String getSubscriptionFlowName() {
217            return subscriptionFlowName;
218        }
219    
220        /**
221         * Set the subscription flow name
222         * 
223         * @param subscriptionFlowName
224         */
225        public void setSubscriptionFlowName(String subscriptionFlowName) {
226            this.subscriptionFlowName = subscriptionFlowName;
227        }
228    
229        /**
230         * Set the flow
231         * 
232         * @param flow
233         */
234        public void setFlows(Flow[] flows) {
235            this.flows = flows;
236        }
237    
238        /**
239         * @return the Flow
240         */
241        public Flow[] getFlows() {
242            return this.flows;
243        }
244    
245        /**
246         * suspend the flow to prevent any message exchanges
247         */
248        public void suspend() {
249            for (int i = 0; i < flows.length; i++) {
250                flows[i].suspend();
251            }
252        }
253    
254        /**
255         * resume message exchange processing
256         */
257        public void resume() {
258            for (int i = 0; i < flows.length; i++) {
259                flows[i].resume();
260            }
261        }
262    
263        /**
264         * Route an ExchangePacket to a destination
265         * 
266         * @param exchange
267         * @throws JBIException
268         */
269        public void sendExchangePacket(MessageExchange me) throws JBIException {
270            MessageExchangeImpl exchange = (MessageExchangeImpl) me;
271            if (exchange.getRole() == Role.PROVIDER && exchange.getDestinationId() == null) {
272                resolveAddress(exchange);
273            }
274    
275            boolean foundRoute = false;
276            // If we found a destination, or this is a reply
277            if (exchange.getEndpoint() != null || exchange.getRole() == Role.CONSUMER) {
278                foundRoute = true;
279                Flow flow = defaultFlowChooser.chooseFlow(flows, exchange);
280                if (flow == null) {
281                    throw new MessagingException("Unable to choose a flow for exchange: " + exchange);
282                }
283                flow.send(exchange);
284            }
285    
286            if (exchange.getRole() == Role.PROVIDER) {
287                getSubscriptionManager().dispatchToSubscribers(exchange);
288            }
289    
290            if (!foundRoute) {
291                boolean throwException = true;
292                ActivationSpec activationSpec = exchange.getActivationSpec();
293                if (activationSpec != null) {
294                    throwException = activationSpec.isFailIfNoDestinationEndpoint();
295                }
296                if (throwException) {
297                    throw new MessagingException("Could not find route for exchange: " + exchange + " for service: " + exchange.getService()
298                                    + " and interface: " + exchange.getInterfaceName());
299                } else if (exchange.getMirror().getSyncState() == MessageExchangeImpl.SYNC_STATE_SYNC_SENT) {
300                    exchange.handleAccept();
301                    ComponentContextImpl ctx = (ComponentContextImpl) getSubscriptionManager().getContext();
302                    exchange.setDestinationId(ctx.getComponentNameSpace());
303                    // TODO: this will fail if exchange is InOut
304                    getSubscriptionManager().done(exchange);
305                }
306            }
307        }
308    
309        protected void resolveAddress(MessageExchangeImpl exchange) throws JBIException {
310            ServiceEndpoint theEndpoint = exchange.getEndpoint();
311            if (theEndpoint != null) {
312                if (theEndpoint instanceof ExternalEndpoint) {
313                    throw new JBIException("External endpoints can not be used for routing: should be an internal or dynamic endpoint.");
314                }
315                if (!(theEndpoint instanceof AbstractServiceEndpoint)) {
316                    throw new JBIException(
317                                    "Component-specific endpoints can not be used for routing: should be an internal or dynamic endpoint.");
318                }
319            }
320            // Resolve linked endpoints
321            if (theEndpoint instanceof LinkedEndpoint) {
322                QName svcName = ((LinkedEndpoint) theEndpoint).getToService();
323                String epName = ((LinkedEndpoint) theEndpoint).getToEndpoint();
324                ServiceEndpoint ep = registry.getInternalEndpoint(svcName, epName);
325                if (ep == null) {
326                    throw new JBIException("Could not resolve linked endpoint: " + theEndpoint);
327                }
328                theEndpoint = ep;
329            }
330    
331            // get the context which created the exchange
332            ComponentContextImpl context = exchange.getSourceContext();
333            if (theEndpoint == null) {
334                QName serviceName = exchange.getService();
335                QName interfaceName = exchange.getInterfaceName();
336    
337                // check in order, ServiceName then InterfaceName
338                // check to see if there is a match on the serviceName
339                if (serviceName != null) {
340                    ServiceEndpoint[] endpoints = registry.getEndpointsForService(serviceName);
341                    endpoints = getMatchingEndpoints(endpoints, exchange);
342                    theEndpoint = getServiceChooser(exchange).chooseEndpoint(endpoints, context, exchange);
343                    if (theEndpoint == null) {
344                        LOG.warn("ServiceName (" + serviceName + ") specified for routing, but can't find it registered");
345                    }
346                }
347                if (theEndpoint == null && interfaceName != null) {
348                    ServiceEndpoint[] endpoints = registry.getEndpointsForInterface(interfaceName);
349                    endpoints = getMatchingEndpoints(endpoints, exchange);
350                    theEndpoint = (InternalEndpoint) getInterfaceChooser(exchange).chooseEndpoint(endpoints, context, exchange);
351                    if (theEndpoint == null) {
352                        LOG.warn("InterfaceName (" + interfaceName + ") specified for routing, but can't find any matching components");
353                    }
354                }
355                if (theEndpoint == null) {
356                    // lets use the resolver on the activation spec if
357                    // applicable
358                    ActivationSpec activationSpec = exchange.getActivationSpec();
359                    if (activationSpec != null) {
360                        EndpointResolver destinationResolver = activationSpec.getDestinationResolver();
361                        if (destinationResolver != null) {
362                            try {
363                                EndpointFilter filter = createEndpointFilter(context, exchange);
364                                theEndpoint = (InternalEndpoint) destinationResolver.resolveEndpoint(context, exchange, filter);
365                            } catch (JBIException e) {
366                                throw new MessagingException("Failed to resolve endpoint: " + e, e);
367                            }
368                        }
369                    }
370                }
371            }
372            if (theEndpoint != null) {
373                exchange.setEndpoint(theEndpoint);
374            }
375            if (LOG.isTraceEnabled()) {
376                LOG.trace("Routing exchange " + exchange + " to: " + theEndpoint);
377            }
378        }
379    
380        /**
381         * Filter the given endpoints by asking to the provider and consumer if they
382         * are both ok to process the exchange.
383         * 
384         * @param endpoints
385         *            an array of internal endpoints to check
386         * @param exchange
387         *            the exchange that will be serviced
388         * @return an array of endpoints on which both consumer and provider agrees
389         */
390        protected ServiceEndpoint[] getMatchingEndpoints(ServiceEndpoint[] endpoints, MessageExchangeImpl exchange) {
391            List<ServiceEndpoint> filtered = new ArrayList<ServiceEndpoint>();
392            ComponentMBeanImpl consumer = getRegistry().getComponent(exchange.getSourceId());
393    
394            for (int i = 0; i < endpoints.length; i++) {
395                ComponentNameSpace id = ((InternalEndpoint) endpoints[i]).getComponentNameSpace();
396                if (id != null) {
397                    ComponentMBeanImpl provider = getRegistry().getComponent(id);
398                    if (provider != null
399                            && (!consumer.getComponent().isExchangeWithProviderOkay(endpoints[i], exchange)
400                                    || !provider.getComponent().isExchangeWithConsumerOkay(endpoints[i], exchange))) {
401                        continue;
402                    }
403                }
404                filtered.add(endpoints[i]);
405            }
406            return filtered.toArray(new ServiceEndpoint[filtered.size()]);
407        }
408    
409        /**
410         * @return the default EndpointChooser
411         */
412        public EndpointChooser getDefaultInterfaceChooser() {
413            return defaultInterfaceChooser;
414        }
415    
416        /**
417         * Set the default EndpointChooser
418         * 
419         * @param defaultInterfaceChooser
420         */
421        public void setDefaultInterfaceChooser(EndpointChooser defaultInterfaceChooser) {
422            this.defaultInterfaceChooser = defaultInterfaceChooser;
423        }
424    
425        /**
426         * @return the default EndpointChooser
427         */
428        public EndpointChooser getDefaultServiceChooser() {
429            return defaultServiceChooser;
430        }
431    
432        /**
433         * Set default EndpointChooser
434         * 
435         * @param defaultServiceChooser
436         */
437        public void setDefaultServiceChooser(EndpointChooser defaultServiceChooser) {
438            this.defaultServiceChooser = defaultServiceChooser;
439        }
440    
441        /**
442         * @return the defaultFlowChooser
443         */
444        public FlowChooser getDefaultFlowChooser() {
445            return defaultFlowChooser;
446        }
447    
448        /**
449         * @param defaultFlowChooser
450         *            the defaultFlowChooser to set
451         */
452        public void setDefaultFlowChooser(FlowChooser defaultFlowChooser) {
453            this.defaultFlowChooser = defaultFlowChooser;
454        }
455    
456        /**
457         * Returns the endpoint chooser for endpoints found by service which will
458         * use the chooser on the exchange's activation spec if available otherwise
459         * will use the default
460         * 
461         * @param exchange
462         * @return the EndpointChooser
463         */
464        protected EndpointChooser getServiceChooser(MessageExchangeImpl exchange) {
465            EndpointChooser chooser = null;
466            ActivationSpec activationSpec = exchange.getActivationSpec();
467            if (activationSpec != null) {
468                chooser = activationSpec.getServiceChooser();
469            }
470            if (chooser == null) {
471                chooser = defaultServiceChooser;
472            }
473            return chooser;
474        }
475    
476        /**
477         * Returns the endpoint chooser for endpoints found by service which will
478         * use the chooser on the exchange's activation spec if available otherwise
479         * will use the default
480         * 
481         * @param exchange
482         * @return the EndpointChooser
483         */
484        protected EndpointChooser getInterfaceChooser(MessageExchangeImpl exchange) {
485            EndpointChooser chooser = null;
486            ActivationSpec activationSpec = exchange.getActivationSpec();
487            if (activationSpec != null) {
488                chooser = activationSpec.getInterfaceChooser();
489            }
490            if (chooser == null) {
491                chooser = defaultInterfaceChooser;
492            }
493            return chooser;
494        }
495    
496        /**
497         * Factory method to create an endpoint filter for the given component
498         * context and message exchange
499         * 
500         * @param context
501         * @param exchange
502         * @return the EndpointFilter
503         */
504        protected EndpointFilter createEndpointFilter(ComponentContextImpl context, MessageExchangeImpl exchange) {
505            Component component = context.getComponent();
506            if (exchange.getRole() == Role.PROVIDER) {
507                return new ConsumerComponentEndpointFilter(component);
508            } else {
509                return new ProducerComponentEndpointFilter(component);
510            }
511        }
512    
513        /**
514         * Get an array of MBeanOperationInfo
515         * 
516         * @return array of OperationInfos
517         * @throws JMException
518         */
519        public MBeanOperationInfo[] getOperationInfos() throws JMException {
520            OperationInfoHelper helper = new OperationInfoHelper();
521            helper.addOperation(getObjectToManage(), "suspend", "suspend the NMR processing");
522            helper.addOperation(getObjectToManage(), "resume", "resume the NMR processing");
523    
524            return OperationInfoHelper.join(super.getOperationInfos(), helper.getOperationInfos());
525        }
526    
527        public JBIContainer getContainer() {
528            return container;
529        }
530    
531    }