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: 695836 $
045     */
046    public class DefaultComponent extends AsyncBaseLifeCycle 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        protected ComponentLifeCycle lifeCycle;
054    
055        public DefaultComponent() {
056            setComponent(this);
057            registry = createRegistry();
058            serviceUnitManager = createServiceUnitManager();
059            XBeanServiceUnit su = new XBeanServiceUnit();
060            su.setName("#default#");
061            su.setComponent(this);
062            serviceUnit = su;
063            registry.registerServiceUnit(serviceUnit);
064        }
065    
066        /* (non-Javadoc)
067         * @see javax.jbi.component.Component#getLifeCycle()
068         */
069        public ComponentLifeCycle getLifeCycle() {
070            if (lifeCycle == null) {
071                try {
072                    // This should fail if not inside smx3
073                    lifeCycle = new SyncLifeCycleWrapper(this);
074                } catch (Throwable t) {
075                    // In such a case, just not wrap the lifecycle
076                    lifeCycle = this;
077                }
078            }
079            return lifeCycle;
080        }
081    
082        /* (non-Javadoc)
083         * @see javax.jbi.component.Component#getServiceUnitManager()
084         */
085        public ServiceUnitManager getServiceUnitManager() {
086            return serviceUnitManager;
087        }
088    
089        /* (non-Javadoc)
090         * @see javax.jbi.component.Component#getServiceDescription(javax.jbi.servicedesc.ServiceEndpoint)
091         */
092        public Document getServiceDescription(ServiceEndpoint endpoint) {
093            if (logger.isDebugEnabled()) {
094                logger.debug("Querying service description for " + endpoint);
095            }
096            String key = EndpointSupport.getKey(endpoint);
097            Endpoint ep = this.registry.getEndpoint(key);
098            if (ep != null) {
099                Document doc = ep.getDescription();
100                if (doc == null) {
101                    if (logger.isDebugEnabled()) {
102                        logger.debug("No description found for " + key);
103                    }
104                }
105                return doc;
106            }
107            else {
108                if (logger.isDebugEnabled()) {
109                    logger.debug("No endpoint found for " + key);
110                }
111                return null;
112            }
113        }
114    
115        /* (non-Javadoc)
116         * @see javax.jbi.component.Component#isExchangeWithConsumerOkay(javax.jbi.servicedesc.ServiceEndpoint, javax.jbi.messaging.MessageExchange)
117         */
118        public boolean isExchangeWithConsumerOkay(ServiceEndpoint endpoint, MessageExchange exchange) {
119            String key = EndpointSupport.getKey(endpoint);
120            Endpoint ep = this.registry.getEndpoint(key);
121            if (ep != null) {
122                if (ep.getRole() != MessageExchange.Role.PROVIDER) {
123                    if (logger.isDebugEnabled()) {
124                        logger.debug("Endpoint " + key + " is a consumer. Refusing exchange with consumer.");
125                    }
126                    return false;
127                }
128                else {
129                    return ep.isExchangeOkay(exchange);
130                }
131            }
132            else {
133                if (logger.isDebugEnabled()) {
134                    logger.debug("No endpoint found for " + key + ". Refusing exchange with consumer.");
135                }
136                return false;
137            }
138        }
139    
140        /* (non-Javadoc)
141         * @see javax.jbi.component.Component#isExchangeWithProviderOkay(javax.jbi.servicedesc.ServiceEndpoint, javax.jbi.messaging.MessageExchange)
142         */
143        public boolean isExchangeWithProviderOkay(ServiceEndpoint endpoint, MessageExchange exchange) {
144            // TODO: check if the selected endpoint is good for us
145            return true;
146        }
147    
148        public QName getEPRServiceName() {
149            return new QName(getEPRUri(), getEPRComponentName());
150        }
151        
152        public QName getEPRElementName() {
153            return new QName(getEPRUri(), "epr");
154        }
155        
156        protected String[] getEPRProtocols() {
157            String protocol = getEPRStrippedComponentName().toLowerCase() + ":";
158            return new String[] { protocol };
159        }
160        
161        private String getEPRComponentName() {
162            String suffix = getClass().getName();
163            suffix = suffix.substring(suffix.lastIndexOf('.') + 1);
164            if (suffix.lastIndexOf('$') > 0) {
165                suffix = suffix.substring(suffix.lastIndexOf('$') + 1);
166            }
167            return suffix;
168        }
169        
170        private String getEPRStrippedComponentName() {
171            String suffix = getEPRComponentName();
172            if (suffix.endsWith("Component")) {
173                suffix = suffix.substring(0, suffix.length() - 9);
174            }
175            return suffix;
176        }
177        
178        private String getEPRUri() {
179            String uri = "urn:servicemix:" + getEPRStrippedComponentName().toLowerCase();
180            return uri;
181        }
182    
183        /* (non-Javadoc)
184         * @see javax.jbi.component.Component#resolveEndpointReference(org.w3c.dom.DocumentFragment)
185         */
186        public ServiceEndpoint resolveEndpointReference(DocumentFragment epr) {
187            String[] protocols = getEPRProtocols();
188            QName elementName = getEPRElementName();
189            QName serviceName = getEPRServiceName();
190            for (int i = 0; i < protocols.length; i++) {
191                ServiceEndpoint ep = ResolvedEndpoint.resolveEndpoint(epr, elementName, serviceName, protocols[i]);
192                if (ep != null) {
193                    return ep;
194                }
195            }
196            return null;
197        }
198    
199    
200        /**
201         * Create the service unit manager.
202         * Derived classes should override this method and return a
203         * BaseServiceUnitManager so that the component is able to
204         * handle service unit deployment.
205         *
206         * The default implementation will create a @{link BaseXBeanDeployer} instance
207         * using the value of @{link #getEndpointClasses()} if that method returns a non-null value
208         * otherwise it returns null.
209         *
210         * @return a newly created service unit manager
211         */
212        protected BaseServiceUnitManager createServiceUnitManager() {
213            Class[] classes = getEndpointClasses();
214            if (classes == null) {
215                return null;
216            }
217            Deployer[] deployers = new Deployer[] { new BaseXBeanDeployer(this, classes) };
218            return new BaseServiceUnitManager(this, deployers);
219        }
220    
221    
222        protected Registry createRegistry() {
223            return new Registry(this);
224        }
225    
226        public ComponentContext getComponentContext() {
227            return getContext();
228        }
229    
230        public String getComponentName() {
231            if (getComponentContext() == null) {
232                return "Component (" + getClass().getName() + ") not yet initialized";
233            }
234            return getComponentContext().getComponentName();
235        }
236    
237        /**
238         * @return Returns the logger.
239         */
240        public Log getLogger() {
241            return logger;
242        }
243    
244        /**
245         * @return Returns the registry.
246         */
247        public Registry getRegistry() {
248            return registry;
249        }
250    
251    
252        /**
253         * Returns the service unit, lazily creating one on demand
254         *
255         * @return the service unit if one is being used.
256         */
257        public ServiceUnit getServiceUnit() {
258            return serviceUnit;
259        }
260    
261        /**
262         * Returns an array of configured endpoints for the component or null if there are no configured endpoints
263         */
264        protected List getConfiguredEndpoints() {
265            return null;
266        }
267    
268        /**
269         * Returns a list of valid endpoint classes or null if the component does not wish to programmatically
270         * restrict the list of possible endpoint classes
271         *
272         * @return the endpoint classes used to validate configuration or null to disable the validation
273         */
274        protected Class[] getEndpointClasses() {
275            return null;
276        }
277    
278        /**
279         * A little helper method to turn a possibly null list of endpoints into a list of endpoints
280         */
281        protected static List asList(Object[] endpoints) {
282            if (endpoints == null) {
283                return Collections.EMPTY_LIST;
284            }
285            return Arrays.asList(endpoints);
286        }
287    
288        /**
289         * Dynamically adds a new endpoint
290         */
291        public synchronized void addEndpoint(Endpoint endpoint) throws Exception {
292            endpoint.setServiceUnit(serviceUnit);
293            validateEndpoint(endpoint);
294            endpoint.validate();
295            registry.registerEndpoint(endpoint);
296            serviceUnit.addEndpoint(endpoint);
297        }
298    
299        public synchronized void removeEndpoint(Endpoint endpoint) throws Exception {
300            registry.unregisterEndpoint(endpoint);
301            endpoint.getServiceUnit().removeEndpoint(endpoint);
302        }
303    
304    
305        public boolean isKnownEndpoint(Endpoint endpoint) {
306            Class[] endpointClasses = getEndpointClasses();
307            if (endpointClasses != null) {
308                for (int i = 0; i < endpointClasses.length; i++) {
309                    Class endpointClass = endpointClasses[i];
310                    if (endpointClass.isInstance(endpoint)) {
311                        return true;
312                    }
313                }
314                return false;
315            }
316            return true;
317        }
318    
319        /**
320         * Provides a hook to validate the statically configured endpoint
321         */
322        protected void validateEndpoint(Endpoint endpoint) throws DeploymentException {
323            if (!isKnownEndpoint(endpoint)) {
324                throw new DeploymentException("The endpoint: " + endpoint
325                        + " is not an instance of any of the allowable types: " + Arrays.asList(getEndpointClasses()));
326            }
327        }
328    
329    
330        /* (non-Javadoc)
331         * @see org.apache.servicemix.common.AsyncBaseLifeCycle#doInit()
332         */
333        @Override
334        protected void doInit() throws Exception {
335            super.doInit();
336            List endpoints = getConfiguredEndpoints();
337            if (endpoints != null && !endpoints.isEmpty()) {
338                Iterator iter = endpoints.iterator();
339                while (iter.hasNext()) {
340                    Endpoint endpoint = (Endpoint) iter.next();
341                    if (endpoint == null) {
342                        logger.warn("Ignoring null endpoint in list: " + endpoints);
343                        continue;
344                    }
345                    addEndpoint(endpoint);
346                }
347            }
348            serviceUnit.init();
349        }
350    
351        /* (non-Javadoc)
352         * @see org.apache.servicemix.common.AsyncBaseLifeCycle#doStart()
353         */
354        @Override
355        protected void doStart() throws Exception {
356            super.doStart();
357            serviceUnit.start();
358        }
359    
360        /* (non-Javadoc)
361         * @see org.apache.servicemix.common.AsyncBaseLifeCycle#doStop()
362         */
363        @Override
364        protected void doStop() throws Exception {
365            serviceUnit.stop();
366            super.doStop();
367        }
368    
369        /* (non-Javadoc)
370        * @see org.apache.servicemix.common.AsyncBaseLifeCycle#doShutDown()
371        */
372        @Override
373        protected void doShutDown() throws Exception {
374            serviceUnit.shutDown();
375            super.doShutDown();
376        }
377    
378    
379    }