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.framework;
018    
019    import java.util.ArrayList;
020    import java.util.Collection;
021    import java.util.HashSet;
022    import java.util.Iterator;
023    import java.util.List;
024    import java.util.Map;
025    import java.util.Set;
026    import java.util.concurrent.ConcurrentHashMap;
027    
028    import javax.jbi.JBIException;
029    import javax.jbi.component.ComponentContext;
030    import javax.jbi.servicedesc.ServiceEndpoint;
031    import javax.management.JMException;
032    import javax.management.ObjectName;
033    import javax.xml.namespace.QName;
034    
035    import org.apache.commons.logging.Log;
036    import org.apache.commons.logging.LogFactory;
037    import org.apache.servicemix.jbi.event.EndpointEvent;
038    import org.apache.servicemix.jbi.event.EndpointListener;
039    import org.apache.servicemix.jbi.framework.support.EndpointProcessor;
040    import org.apache.servicemix.jbi.servicedesc.AbstractServiceEndpoint;
041    import org.apache.servicemix.jbi.servicedesc.ExternalEndpoint;
042    import org.apache.servicemix.jbi.servicedesc.InternalEndpoint;
043    import org.apache.servicemix.jbi.servicedesc.LinkedEndpoint;
044    
045    /**
046     * Registry for Components
047     * 
048     * @version $Revision: 564900 $
049     */
050    public class EndpointRegistry {
051        
052        private static final Log LOG = LogFactory.getLog(EndpointRegistry.class);
053        
054        private Registry registry;
055        
056        private Map<AbstractServiceEndpoint, Endpoint> endpointMBeans;
057        
058        private Map<String, ServiceEndpoint> internalEndpoints;
059        
060        private Map<String, ServiceEndpoint> externalEndpoints;
061        
062        private Map<String, ServiceEndpoint> linkedEndpoints;
063        
064        private Map<QName, InterfaceConnection> interfaceConnections;
065        
066        private List<EndpointProcessor> endpointProcessors;
067        
068        /**
069         * Constructor
070         * 
071         * @param cr
072         */
073        public EndpointRegistry(Registry registry) {
074            this.registry = registry;
075            this.endpointMBeans = new ConcurrentHashMap<AbstractServiceEndpoint, Endpoint>();
076            this.internalEndpoints = new ConcurrentHashMap<String, ServiceEndpoint>();
077            this.externalEndpoints = new ConcurrentHashMap<String, ServiceEndpoint>();
078            this.linkedEndpoints = new ConcurrentHashMap<String, ServiceEndpoint>();
079            this.interfaceConnections = new ConcurrentHashMap<QName, InterfaceConnection>();
080            this.endpointProcessors = getEndpointProcessors();
081        }
082        
083        private List<EndpointProcessor> getEndpointProcessors() {
084            List<EndpointProcessor> l = new ArrayList<EndpointProcessor>();
085            String[] classes = {"org.apache.servicemix.jbi.framework.support.SUDescriptorProcessor",
086                                "org.apache.servicemix.jbi.framework.support.WSDL1Processor",
087                                "org.apache.servicemix.jbi.framework.support.WSDL2Processor" };
088            for (int i = 0; i < classes.length; i++) {
089                try {
090                    EndpointProcessor p = (EndpointProcessor) Class.forName(classes[i]).newInstance();
091                    p.init(registry);
092                    l.add(p);
093                } catch (Throwable e) {
094                    LOG.warn("Disabled endpoint processor '" + classes[i] + "': " + e);
095                }
096            }
097            return l;
098        }
099        
100        public ServiceEndpoint[] getEndpointsForComponent(ComponentNameSpace cns) {
101            Collection<ServiceEndpoint> endpoints = new ArrayList<ServiceEndpoint>();
102            for (Iterator<ServiceEndpoint> iter = getInternalEndpoints().iterator(); iter.hasNext();) {
103                InternalEndpoint endpoint = (InternalEndpoint) iter.next();
104                if (cns.equals(endpoint.getComponentNameSpace())) {
105                    endpoints.add(endpoint);
106                }
107            }
108            return asEndpointArray(endpoints);
109        }
110        
111        public ServiceEndpoint[] getAllEndpointsForComponent(ComponentNameSpace cns) {
112            Collection<ServiceEndpoint> endpoints = new ArrayList<ServiceEndpoint>();
113            for (Iterator<ServiceEndpoint> iter = getInternalEndpoints().iterator(); iter.hasNext();) {
114                InternalEndpoint endpoint = (InternalEndpoint) iter.next();
115                if (cns.equals(endpoint.getComponentNameSpace())) {
116                    endpoints.add(endpoint);
117                }
118            }
119            for (Iterator<ServiceEndpoint> iter = getExternalEndpoints().iterator(); iter.hasNext();) {
120                ExternalEndpoint endpoint = (ExternalEndpoint) iter.next();
121                if (cns.equals(endpoint.getComponentNameSpace())) {
122                    endpoints.add(endpoint);
123                }
124            }
125            return asEndpointArray(endpoints);
126        }
127        
128        /**
129         * Returns a collection of Endpoint objects
130         */
131        public Collection<Endpoint> getEndpointMBeans() {
132            return endpointMBeans.values();
133        }
134    
135        /**
136         * Get all endpoints for a given service
137         * 
138         * @param serviceName
139         * @return array of endpoints
140         */
141        public ServiceEndpoint[] getEndpointsForService(QName serviceName) {
142            Collection<ServiceEndpoint> collection = getEndpointsByService(serviceName, getInternalEndpoints());
143            return asEndpointArray(collection);
144        }
145    
146        /**
147         * This will return the endpoints for all services and endpoints that implement the named interface (portType in
148         * WSDL 1.1). This method does NOT include external endpoints.
149         * 
150         * @param interfaceName qualified name of interface/portType that is implemented by the endpoint; if
151         * <code>null</code> then all activated endpoints in the JBI environment must be returned.
152         * @return an array of available endpoints for the specified interface name; must be non-null; may be empty.
153         */
154        public ServiceEndpoint[] getEndpointsForInterface(QName interfaceName) {
155            if (interfaceName == null) {
156                return asEndpointArray(internalEndpoints.values());
157            }
158            InterfaceConnection conn = interfaceConnections.get(interfaceName);
159            if (conn != null) {
160                String key = getKey(conn.service, conn.endpoint);
161                ServiceEndpoint ep = internalEndpoints.get(key);
162                if (ep == null) {
163                    LOG.warn("Connection for interface " + interfaceName + " could not find target for service "
164                                    + conn.service + " and endpoint " + conn.endpoint);
165                    return new ServiceEndpoint[0];
166                } else {
167                    return new ServiceEndpoint[] {ep };
168                }
169            }
170            Collection<ServiceEndpoint> result = getEndpointsByInterface(interfaceName, getInternalEndpoints());
171            return asEndpointArray(result);
172        }
173    
174        /**
175         * Activate an endpoint
176         * 
177         * @param provider
178         * @param serviceName
179         * @param endpointName
180         * @return the endpoint
181         * @throws JBIException 
182         */
183        public InternalEndpoint registerInternalEndpoint(ComponentContextImpl provider, 
184                                                         QName serviceName, 
185                                                         String endpointName) throws JBIException {
186            // Create endpoint
187            String key = getKey(serviceName, endpointName);
188            InternalEndpoint registered = (InternalEndpoint) internalEndpoints.get(key);
189            // Check if the endpoint has already been activated by another component
190            if (registered != null && registered.isLocal()) {
191                throw new JBIException("An internal endpoint for service " + serviceName
192                                            + " and endpoint " + endpointName + " is already registered");
193            }        
194            // Create a new endpoint
195            InternalEndpoint serviceEndpoint = new InternalEndpoint(provider.getComponentNameSpace(), endpointName, serviceName);
196            // Get interface from activationSpec
197            if (provider.getActivationSpec().getInterfaceName() != null) {
198                serviceEndpoint.addInterface(provider.getActivationSpec().getInterfaceName());
199            }
200            // Get interfaces
201            for (Iterator<EndpointProcessor> it = endpointProcessors.iterator(); it.hasNext();) {
202                EndpointProcessor p = it.next();
203                p.process(serviceEndpoint);
204            }
205            // Set remote namespaces
206            if (registered != null) {
207                InternalEndpoint[] remote = registered.getRemoteEndpoints();
208                for (int i = 0; i < remote.length; i++) {
209                    serviceEndpoint.addRemoteEndpoint(remote[i]);
210                }
211            }
212            // Register endpoint
213            internalEndpoints.put(key, serviceEndpoint);
214            registerEndpoint(serviceEndpoint);
215            fireEvent(serviceEndpoint, EndpointEvent.INTERNAL_ENDPOINT_REGISTERED);
216            return serviceEndpoint;
217        }
218    
219        /**
220         * Called by component context when endpoints are being deactivated.
221         * 
222         * @param provider
223         * @param serviceEndpoint
224         */
225        public void unregisterInternalEndpoint(ComponentContext provider, InternalEndpoint serviceEndpoint) {
226            if (serviceEndpoint.isClustered()) {
227                fireEvent(serviceEndpoint, EndpointEvent.INTERNAL_ENDPOINT_UNREGISTERED);
228                // set endpoint to be no more local
229                serviceEndpoint.setComponentName(null);
230            } else {
231                String key = getKey(serviceEndpoint);
232                internalEndpoints.remove(key);
233                unregisterEndpoint(serviceEndpoint);
234                fireEvent(serviceEndpoint, EndpointEvent.INTERNAL_ENDPOINT_UNREGISTERED);
235            }
236        }
237        
238        /**
239         * Registers a remote endpoint
240         * 
241         * @param remote
242         */
243        public void registerRemoteEndpoint(InternalEndpoint remote) {
244            InternalEndpoint endpoint = (InternalEndpoint) internalEndpoints.get(getKey(remote));
245            // Create endpoint if not already existing
246            if (endpoint == null) {
247                endpoint = new InternalEndpoint(null, remote.getEndpointName(), remote.getServiceName());
248                internalEndpoints.put(getKey(endpoint), endpoint);
249            }
250            // Add remote endpoint
251            endpoint.addRemoteEndpoint(remote);
252            fireEvent(remote, EndpointEvent.REMOTE_ENDPOINT_REGISTERED);
253        }
254        
255        /**
256         * Unregisters a remote endpoint
257         * 
258         * @param remote
259         */
260        public void unregisterRemoteEndpoint(InternalEndpoint remote) {
261            String key = getKey(remote);
262            InternalEndpoint endpoint = (InternalEndpoint) internalEndpoints.get(key);
263            if (endpoint != null) {
264                endpoint.removeRemoteEndpoint(remote);
265                if (!endpoint.isClustered() && !endpoint.isLocal()) {
266                    internalEndpoints.remove(key);
267                    unregisterEndpoint(endpoint);
268                }
269                fireEvent(remote, EndpointEvent.REMOTE_ENDPOINT_UNREGISTERED);
270            }
271        }
272    
273        /**
274         * Get the named ServiceEndpoint, if activated
275         * 
276         * @param service
277         * @param name
278         * @return the activated ServiceEndpoint or null
279         */
280        public ServiceEndpoint getEndpoint(QName service, String name) {
281            String key = getKey(service, name);
282            ServiceEndpoint ep = linkedEndpoints.get(key);
283            if (ep == null) {
284                ep = internalEndpoints.get(key);
285            }
286            return ep;
287        }
288        
289        public ServiceEndpoint getInternalEndpoint(QName service, String name) {
290            return internalEndpoints.get(getKey(service, name));
291        }
292    
293        /**
294         * Registers the given external endpoint with the NMR. This indicates to the NMR that the given endpoint is used as
295         * a proxy for external service consumers to access an internal service of the same service name (but a different
296         * endpoint name).
297         * 
298         * @param provider
299         * @param externalEndpoint the external endpoint to be registered, must be non-null.
300         * @throws JBIException 
301         */
302        public void registerExternalEndpoint(ComponentNameSpace cns, ServiceEndpoint externalEndpoint) throws JBIException {
303            ExternalEndpoint serviceEndpoint = new ExternalEndpoint(cns, externalEndpoint); 
304            if (externalEndpoints.get(getKey(serviceEndpoint)) != null) {
305                throw new JBIException("An external endpoint for service " + externalEndpoint.getServiceName()
306                                        + " and endpoint " + externalEndpoint.getEndpointName() + " is already registered");
307            }
308            registerEndpoint(serviceEndpoint);
309            externalEndpoints.put(getKey(serviceEndpoint), serviceEndpoint);
310            fireEvent(serviceEndpoint, EndpointEvent.EXTERNAL_ENDPOINT_REGISTERED);
311        }
312    
313        /**
314         * Deregisters the given external endpoint with the NMR. This indicates to the NMR that the given external endpoint
315         * can no longer be used as a proxy for external service consumers to access an internal service of the same service
316         * name.
317         * 
318         * @param provider
319         * @param externalEndpoint the external endpoint to be deregistered; must be non-null.
320         */
321        public void unregisterExternalEndpoint(ComponentNameSpace cns, ServiceEndpoint externalEndpoint) {
322            ExternalEndpoint ep = (ExternalEndpoint) externalEndpoints.remove(getKey(externalEndpoint));
323            unregisterEndpoint(ep);
324            fireEvent(ep, EndpointEvent.EXTERNAL_ENDPOINT_UNREGISTERED);
325        }
326    
327        /**
328         * This methods returns only registered external endpoints
329         * 
330         * @param interfaceName qualified name of interface implemented by the endpoints; must be non-null.
331         * @return an array of available external endpoints for the specified interface name; must be non-null; may be
332         * empty.
333         */
334        public ServiceEndpoint[] getExternalEndpointsForInterface(QName interfaceName) {
335            Collection<ServiceEndpoint> endpoints = getEndpointsByInterface(interfaceName, getExternalEndpoints());
336            return asEndpointArray(endpoints);
337        }
338    
339        /**
340         * Get external endpoints for the service
341         * 
342         * @param serviceName qualified name of service that contains the endpoints; must be non-null.
343         * @return an array of available external endpoints for the specified service name; must be non-null; may be empty.
344         */
345        public ServiceEndpoint[] getExternalEndpointsForService(QName serviceName) {
346            Collection<ServiceEndpoint> endpoints = getEndpointsByService(serviceName, getExternalEndpoints());
347            return asEndpointArray(endpoints);
348        }
349    
350        /**
351         * Helper method to convert the given collection into an array of endpoints
352         * 
353         * @param collection
354         * @return array of endpoints
355         */
356        protected ServiceEndpoint[] asEndpointArray(Collection<ServiceEndpoint> collection) {
357            if (collection == null) {
358                return new ServiceEndpoint[0];
359            }
360            ServiceEndpoint[] answer = new ServiceEndpoint[collection.size()];
361            answer = collection.toArray(answer);
362            return answer;
363        }
364    
365        /**
366         * return a collection of endpoints
367         * 
368         * @param serviceName
369         * @param endpoints
370         * @return collection of endpoints
371         */
372        protected Collection<ServiceEndpoint> getEndpointsByService(QName serviceName, Collection<ServiceEndpoint> endpoints) {
373            Collection<ServiceEndpoint> answer = new ArrayList<ServiceEndpoint>();
374            for (Iterator<ServiceEndpoint> i = endpoints.iterator(); i.hasNext();) {
375                ServiceEndpoint endpoint = i.next();
376                if (endpoint.getServiceName().equals(serviceName)) {
377                    answer.add(endpoint);
378                }
379            }
380            return answer;
381        }
382        
383        /**
384         * Filters the given endpoints and returns those implementing the
385         * given interface name.  If interfaceName is null, then no filter
386         * is applied.
387         * 
388         */
389        protected Collection<ServiceEndpoint> getEndpointsByInterface(QName interfaceName, Collection<ServiceEndpoint> endpoints) {
390            if (interfaceName == null) {
391                return endpoints;
392            }
393            Set<ServiceEndpoint> answer = new HashSet<ServiceEndpoint>();
394            for (Iterator<ServiceEndpoint> i = endpoints.iterator(); i.hasNext();) {
395                ServiceEndpoint endpoint = i.next();
396                QName[] interfaces = endpoint.getInterfaces();
397                if (interfaces != null) {
398                    for (int k = 0; k < interfaces.length; k++) {
399                        QName qn = interfaces[k];
400                        if (qn != null && qn.equals(interfaceName)) {
401                            answer.add(endpoint);
402                            break;
403                        }
404                    }
405                }
406            }
407            return answer;
408        }
409    
410        /**
411         * @return all default endpoints
412         */
413        protected Collection<ServiceEndpoint> getInternalEndpoints() {
414            return internalEndpoints.values();
415        }
416    
417        /**
418         * @return all external endpoints
419         */
420        protected Collection<ServiceEndpoint> getExternalEndpoints() {
421            return externalEndpoints.values();
422        }
423    
424        /**
425         * Registers an endpoint connection.
426         * 
427         * @param fromSvc
428         * @param fromEp
429         * @param toSvc
430         * @param toEp
431         * @param link
432         * @throws JBIException
433         */
434        public void registerEndpointConnection(QName fromSvc, String fromEp, 
435                                               QName toSvc, String toEp, String link) throws JBIException {
436            LinkedEndpoint ep = new LinkedEndpoint(fromSvc, fromEp, toSvc, toEp, link);
437            if (linkedEndpoints.get(getKey(ep)) != null) {
438                throw new JBIException("An endpoint connection for service " + ep.getServiceName() + " and name "
439                                            + ep.getEndpointName() + " is already registered");
440            }
441            linkedEndpoints.put(getKey(ep), ep);
442            registerEndpoint(ep);
443            fireEvent(ep, EndpointEvent.LINKED_ENDPOINT_REGISTERED);
444        }
445    
446        /**
447         * Unregister an endpoint connection.
448         * 
449         * @param fromSvc
450         * @param fromEp
451         */
452        public void unregisterEndpointConnection(QName fromSvc, String fromEp) {
453            LinkedEndpoint ep = (LinkedEndpoint) linkedEndpoints.remove(getKey(fromSvc, fromEp));
454            unregisterEndpoint(ep);
455            fireEvent(ep, EndpointEvent.LINKED_ENDPOINT_UNREGISTERED);
456        }
457        
458        /**
459         * Registers an interface connection.
460         * 
461         * @param fromItf
462         * @param toSvc
463         * @param toEp
464         * @throws JBIException
465         */
466        public void registerInterfaceConnection(QName fromItf, QName toSvc, String toEp) throws JBIException {
467            if (interfaceConnections.get(fromItf) != null) {
468                throw new JBIException("An interface connection for " + fromItf + " is already registered");
469            }
470            interfaceConnections.put(fromItf, new InterfaceConnection(toSvc, toEp));
471        }
472    
473        /**
474         * Unregisters an interface connection.
475         * 
476         * @param fromItf
477         */
478        public void unregisterInterfaceConnection(QName fromItf) {
479            interfaceConnections.remove(fromItf);
480            
481        }
482        
483        private void registerEndpoint(AbstractServiceEndpoint serviceEndpoint) {
484            try {
485                Endpoint endpoint = new Endpoint(serviceEndpoint, registry);
486                ObjectName objectName = registry.getContainer().getManagementContext().createObjectName(endpoint);
487                registry.getContainer().getManagementContext().registerMBean(objectName, endpoint, EndpointMBean.class);
488                endpointMBeans.put(serviceEndpoint, endpoint);
489            } catch (JMException e) {
490                LOG.error("Could not register MBean for endpoint", e);
491            }
492        }
493        
494        private void unregisterEndpoint(AbstractServiceEndpoint se) {
495            Endpoint ep = endpointMBeans.remove(se);
496            try {
497                registry.getContainer().getManagementContext().unregisterMBean(ep);
498            } catch (JBIException e) {
499                LOG.error("Could not unregister MBean for endpoint", e);
500            }
501        }
502    
503        private String getKey(ServiceEndpoint ep) {
504            return getKey(ep.getServiceName(), ep.getEndpointName());
505        }
506        
507        private String getKey(QName svcName, String epName) {
508            return svcName + epName;
509        }
510    
511        private static class InterfaceConnection {
512            QName service;
513            String endpoint;
514            InterfaceConnection(QName service, String endpoint) {
515                this.service = service;
516                this.endpoint = endpoint;
517            }
518        }
519    
520        protected void fireEvent(ServiceEndpoint ep, int type) {
521            EndpointEvent event = new EndpointEvent(ep, type);
522            EndpointListener[] listeners = (EndpointListener[]) registry.getContainer().getListeners(EndpointListener.class);
523            for (int i = 0; i < listeners.length; i++) {
524                switch (type) {
525                case EndpointEvent.INTERNAL_ENDPOINT_REGISTERED:
526                    listeners[i].internalEndpointRegistered(event);
527                    break;
528                case EndpointEvent.INTERNAL_ENDPOINT_UNREGISTERED:
529                    listeners[i].internalEndpointUnregistered(event);
530                    break;
531                case EndpointEvent.EXTERNAL_ENDPOINT_REGISTERED:
532                    listeners[i].externalEndpointRegistered(event);
533                    break;
534                case EndpointEvent.EXTERNAL_ENDPOINT_UNREGISTERED:
535                    listeners[i].externalEndpointUnregistered(event);
536                    break;
537                case EndpointEvent.LINKED_ENDPOINT_REGISTERED:
538                    listeners[i].linkedEndpointRegistered(event);
539                    break;
540                case EndpointEvent.LINKED_ENDPOINT_UNREGISTERED:
541                    listeners[i].linkedEndpointUnregistered(event);
542                    break;
543                case EndpointEvent.REMOTE_ENDPOINT_REGISTERED:
544                    listeners[i].remoteEndpointRegistered(event);
545                    break;
546                case EndpointEvent.REMOTE_ENDPOINT_UNREGISTERED:
547                    listeners[i].remoteEndpointUnregistered(event);
548                    break;
549                default:
550                    break;
551                }
552            }
553            
554        }
555    
556    }