001    /**
002     *
003     * Licensed to the Apache Software Foundation (ASF) under one or more
004     * contributor license agreements.  See the NOTICE file distributed with
005     * this work for additional information regarding copyright ownership.
006     * The ASF licenses this file to You under the Apache License, Version 2.0
007     * (the "License"); you may not use this file except in compliance with
008     * the License.  You may obtain a copy of the License at
009     *
010     * http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing, software
013     * distributed under the License is distributed on an "AS IS" BASIS,
014     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015     * See the License for the specific language governing permissions and
016     * limitations under the License.
017     */
018    package org.apache.servicemix.common;
019    
020    import org.apache.commons.logging.Log;
021    import org.apache.commons.logging.LogFactory;
022    import org.apache.servicemix.common.xbean.XBeanServiceUnit;
023    import org.apache.servicemix.common.xbean.BaseXBeanDeployer;
024    import org.w3c.dom.Document;
025    import org.w3c.dom.DocumentFragment;
026    
027    import javax.jbi.component.ComponentContext;
028    import javax.jbi.component.ComponentLifeCycle;
029    import javax.jbi.component.ServiceUnitManager;
030    import javax.jbi.management.DeploymentException;
031    import javax.jbi.messaging.MessageExchange;
032    import javax.jbi.servicedesc.ServiceEndpoint;
033    import javax.xml.namespace.QName;
034    
035    import java.util.Arrays;
036    import java.util.List;
037    import java.util.Iterator;
038    import java.util.Collections;
039    
040    /**
041     * A useful base class for writing new JBI components which includes the {@link ComponentLifeCycle} interface methods so that
042     * you can write a new component in a single class with minimal overloading.
043     *
044     * @version $Revision: 584990 $
045     */
046    public abstract class DefaultComponent extends BaseLifeCycle implements ServiceMixComponent {
047    
048        protected final transient Log logger = LogFactory.getLog(getClass());
049    
050        protected Registry registry;
051        protected BaseServiceUnitManager serviceUnitManager;
052        protected ServiceUnit serviceUnit;
053    
054        public DefaultComponent() {
055            setComponent(this);
056            registry = createRegistry();
057            serviceUnitManager = createServiceUnitManager();
058        }
059    
060        /* (non-Javadoc)
061         * @see javax.jbi.component.Component#getLifeCycle()
062         */
063        public ComponentLifeCycle getLifeCycle() {
064            return this;
065        }
066    
067        /* (non-Javadoc)
068         * @see javax.jbi.component.Component#getServiceUnitManager()
069         */
070        public ServiceUnitManager getServiceUnitManager() {
071            return serviceUnitManager;
072        }
073    
074        /* (non-Javadoc)
075         * @see javax.jbi.component.Component#getServiceDescription(javax.jbi.servicedesc.ServiceEndpoint)
076         */
077        public Document getServiceDescription(ServiceEndpoint endpoint) {
078            if (logger.isDebugEnabled()) {
079                logger.debug("Querying service description for " + endpoint);
080            }
081            String key = EndpointSupport.getKey(endpoint);
082            Endpoint ep = this.registry.getEndpoint(key);
083            if (ep != null) {
084                Document doc = ep.getDescription();
085                if (doc == null) {
086                    if (logger.isDebugEnabled()) {
087                        logger.debug("No description found for " + key);
088                    }
089                }
090                return doc;
091            }
092            else {
093                if (logger.isDebugEnabled()) {
094                    logger.debug("No endpoint found for " + key);
095                }
096                return null;
097            }
098        }
099    
100        /* (non-Javadoc)
101         * @see javax.jbi.component.Component#isExchangeWithConsumerOkay(javax.jbi.servicedesc.ServiceEndpoint, javax.jbi.messaging.MessageExchange)
102         */
103        public boolean isExchangeWithConsumerOkay(ServiceEndpoint endpoint, MessageExchange exchange) {
104            String key = EndpointSupport.getKey(endpoint);
105            Endpoint ep = this.registry.getEndpoint(key);
106            if (ep != null) {
107                if (ep.getRole() != MessageExchange.Role.PROVIDER) {
108                    if (logger.isDebugEnabled()) {
109                        logger.debug("Endpoint " + key + " is a consumer. Refusing exchange with consumer.");
110                    }
111                    return false;
112                }
113                else {
114                    return ep.isExchangeOkay(exchange);
115                }
116            }
117            else {
118                if (logger.isDebugEnabled()) {
119                    logger.debug("No endpoint found for " + key + ". Refusing exchange with consumer.");
120                }
121                return false;
122            }
123        }
124    
125        /* (non-Javadoc)
126         * @see javax.jbi.component.Component#isExchangeWithProviderOkay(javax.jbi.servicedesc.ServiceEndpoint, javax.jbi.messaging.MessageExchange)
127         */
128        public boolean isExchangeWithProviderOkay(ServiceEndpoint endpoint, MessageExchange exchange) {
129            // TODO: check if the selected endpoint is good for us
130            return true;
131        }
132    
133        public QName getEPRServiceName() {
134            return new QName(getEPRUri(), getEPRComponentName());
135        }
136        
137        public QName getEPRElementName() {
138            return new QName(getEPRUri(), "epr");
139        }
140        
141        protected String[] getEPRProtocols() {
142            String protocol = getEPRStrippedComponentName().toLowerCase() + ":";
143            return new String[] { protocol };
144        }
145        
146        private String getEPRComponentName() {
147            String suffix = getClass().getName();
148            suffix = suffix.substring(suffix.lastIndexOf('.') + 1);
149            if (suffix.lastIndexOf('$') > 0) {
150                suffix = suffix.substring(suffix.lastIndexOf('$') + 1);
151            }
152            return suffix;
153        }
154        
155        private String getEPRStrippedComponentName() {
156            String suffix = getEPRComponentName();
157            if (suffix.endsWith("Component")) {
158                suffix = suffix.substring(0, suffix.length() - 9);
159            }
160            return suffix;
161        }
162        
163        private String getEPRUri() {
164            String uri = "urn:servicemix:" + getEPRStrippedComponentName().toLowerCase();
165            return uri;
166        }
167    
168        /* (non-Javadoc)
169         * @see javax.jbi.component.Component#resolveEndpointReference(org.w3c.dom.DocumentFragment)
170         */
171        public ServiceEndpoint resolveEndpointReference(DocumentFragment epr) {
172            String[] protocols = getEPRProtocols();
173            QName elementName = getEPRElementName();
174            QName serviceName = getEPRServiceName();
175            for (int i = 0; i < protocols.length; i++) {
176                ServiceEndpoint ep = ResolvedEndpoint.resolveEndpoint(epr, elementName, serviceName, protocols[i]);
177                if (ep != null) {
178                    return ep;
179                }
180            }
181            return null;
182        }
183    
184    
185        /**
186         * Create the service unit manager.
187         * Derived classes should override this method and return a
188         * BaseServiceUnitManager so that the component is able to
189         * handle service unit deployment.
190         *
191         * The default implementation will create a @{link BaseXBeanDeployer} instance
192         * using the value of @{link #getEndpointClasses()} if that method returns a non-null value
193         * otherwise it returns null.
194         *
195         * @return a newly created service unit manager
196         */
197        protected BaseServiceUnitManager createServiceUnitManager() {
198            Class[] classes = getEndpointClasses();
199            if (classes == null) {
200                return null;
201            }
202            Deployer[] deployers = new Deployer[] { new BaseXBeanDeployer(this, classes) };
203            return new BaseServiceUnitManager(this, deployers);
204        }
205    
206    
207        protected Registry createRegistry() {
208            return new Registry(this);
209        }
210    
211        public ComponentContext getComponentContext() {
212            return getContext();
213        }
214    
215        public String getComponentName() {
216            if (getComponentContext() == null) {
217                return "Component (" + getClass().getName() + ") not yet initialized";
218            }
219            return getComponentContext().getComponentName();
220        }
221    
222        /**
223         * @return Returns the logger.
224         */
225        public Log getLogger() {
226            return logger;
227        }
228    
229        /**
230         * @return Returns the registry.
231         */
232        public Registry getRegistry() {
233            return registry;
234        }
235    
236    
237        /**
238         * Returns the service unit, lazily creating one on demand
239         *
240         * @return the service unit if one is being used.
241         */
242        public ServiceUnit getServiceUnit() {
243            if (serviceUnit == null) {
244                serviceUnit = new XBeanServiceUnit();
245                serviceUnit.setName("#default#");
246                serviceUnit.setComponent(this);
247            }
248            return serviceUnit;
249        }
250    
251        /**
252         * Returns an array of configured endpoints for the component or null if there are no configured endpoints
253         */
254        protected abstract List getConfiguredEndpoints();
255    
256        /**
257         * Returns a list of valid endpoint classes or null if the component does not wish to programmatically
258         * restrict the list of possible endpoint classes
259         *
260         * @return the endpoint classes used to validate configuration or null to disable the validation
261         */
262        protected abstract Class[] getEndpointClasses();
263    
264        /**
265         * A little helper method to turn a possibly null list of endpoints into a list of endpoints
266         */
267        protected static List asList(Object[] endpoints) {
268            if (endpoints == null) {
269                return Collections.EMPTY_LIST;
270            }
271            return Arrays.asList(endpoints);
272        }
273    
274        /* (non-Javadoc)
275        * @see org.servicemix.common.BaseLifeCycle#doInit()
276        */
277        protected void doInit() throws Exception {
278            super.doInit();
279            List endpoints = getConfiguredEndpoints();
280            if (endpoints != null && !endpoints.isEmpty()) {
281                Iterator iter = endpoints.iterator();
282                while (iter.hasNext()) {
283                    Endpoint endpoint = (Endpoint) iter.next();
284                    if (endpoint == null) {
285                        logger.warn("Ignoring null endpoint in list: " + endpoints);
286                        continue;
287                    }
288                    addEndpoint(endpoint);
289                }
290            }
291        }
292    
293        /**
294         * Dynamically adds a new endpoint
295         */
296        public void addEndpoint(Endpoint endpoint) throws Exception {
297            ServiceUnit su = getServiceUnit();
298            endpoint.setServiceUnit(su);
299            validateEndpoint(endpoint);
300            endpoint.validate();
301            su.addEndpoint(endpoint);
302            if (registry.isRegistered(su)) {
303                registry.registerEndpoint(endpoint);
304            } else {
305                registry.registerServiceUnit(su);
306                if (isStarted()) {
307                    su.start();
308                }
309            }
310        }
311    
312        public void removeEndpoint(Endpoint endpoint) throws Exception {
313            ServiceUnit su = endpoint.getServiceUnit();
314            su.removeEndpoint(endpoint);
315        }
316    
317    
318        /**
319         * Provides a hook to validate the statically configured endpoint
320         */
321        protected void validateEndpoint(Endpoint endpoint) throws DeploymentException {
322            Class[] endpointClasses = getEndpointClasses();
323            if (endpointClasses != null) {
324                boolean valid = false;
325                for (int i = 0; i < endpointClasses.length; i++) {
326                    Class endpointClass = endpointClasses[i];
327                    if (endpointClass.isInstance(endpoint)) {
328                        valid = true;
329                    }
330                }
331                if (!valid) {
332                    throw new DeploymentException("The endpoint: " + endpoint
333                            + " is not an instance of any of the allowable types: " + Arrays.asList(endpointClasses));
334                }
335            }
336        }
337    
338    
339        /* (non-Javadoc)
340        * @see org.servicemix.common.BaseLifeCycle#doStart()
341        */
342        protected void doStart() throws Exception {
343            super.doStart();
344            if (serviceUnit != null) {
345                if (!registry.isRegistered(serviceUnit)) {
346                    registry.registerServiceUnit(serviceUnit);
347                }
348                serviceUnit.start();
349            }
350        }
351    
352        /* (non-Javadoc)
353        * @see org.servicemix.common.BaseLifeCycle#doStop()
354        */
355        protected void doStop() throws Exception {
356            if (serviceUnit != null) {
357                serviceUnit.stop();
358            }
359            super.doStop();
360        }
361    
362        /* (non-Javadoc)
363        * @see org.servicemix.common.BaseLifeCycle#doShutDown()
364        */
365        protected void doShutDown() throws Exception {
366            if (serviceUnit != null) {
367                serviceUnit.shutDown();
368            }
369            super.doShutDown();
370        }
371    
372    
373    }