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.io.File;
020    import java.io.IOException;
021    import java.util.Properties;
022    
023    import javax.jbi.JBIException;
024    import javax.jbi.component.Component;
025    import javax.jbi.component.ComponentLifeCycle;
026    import javax.jbi.component.ServiceUnitManager;
027    import javax.jbi.management.DeploymentException;
028    import javax.jbi.management.LifeCycleMBean;
029    import javax.management.JMException;
030    import javax.management.MBeanAttributeInfo;
031    import javax.management.MBeanOperationInfo;
032    import javax.management.ObjectName;
033    
034    import org.apache.commons.logging.Log;
035    import org.apache.commons.logging.LogFactory;
036    import org.apache.servicemix.jbi.container.ActivationSpec;
037    import org.apache.servicemix.jbi.container.JBIContainer;
038    import org.apache.servicemix.jbi.event.ComponentEvent;
039    import org.apache.servicemix.jbi.event.ComponentListener;
040    import org.apache.servicemix.jbi.management.AttributeInfoHelper;
041    import org.apache.servicemix.jbi.management.BaseLifeCycle;
042    import org.apache.servicemix.jbi.management.ManagementContext;
043    import org.apache.servicemix.jbi.management.OperationInfoHelper;
044    import org.apache.servicemix.jbi.messaging.DeliveryChannelImpl;
045    import org.apache.servicemix.jbi.util.XmlPersistenceSupport;
046    import org.apache.xbean.classloader.DestroyableClassLoader;
047    
048    /**
049     * Defines basic statistics on the Component
050     */
051    public class ComponentMBeanImpl extends BaseLifeCycle implements ComponentMBean {
052        
053        private static final Log LOG = LogFactory.getLog(ComponentMBeanImpl.class);
054        
055        private boolean exchangeThrottling;
056        private long throttlingTimeout = 100;
057        private int throttlingInterval = 1;
058        private Component component;
059        private ComponentLifeCycle lifeCycle;
060        private ServiceUnitManager suManager;
061        private ComponentContextImpl context;
062        private ActivationSpec activationSpec;
063        private ObjectName mBeanName;
064        private JBIContainer container;
065        private ComponentNameSpace componentName;
066        private String description = "POJO Component";
067        private int queueCapacity = 1024;
068        private boolean pojo;
069        private boolean binding;
070        private boolean service;
071        private File stateFile;
072        private String[] sharedLibraries;
073    
074        /**
075         * Construct with it's id and delivery channel Id
076         * 
077         * @param name
078         * @param description
079         * @param component
080         * @param binding
081         * @param service
082         * @param sharedLibraries
083         */
084        public ComponentMBeanImpl(JBIContainer container, 
085                                  ComponentNameSpace name, 
086                                  String description, 
087                                  Component component,
088                                  boolean binding, 
089                                  boolean service,
090                                  String[] sharedLibraries) {
091            this.componentName = name;
092            this.container = container;
093            this.component = component;
094            this.description = description;
095            this.binding = binding;
096            this.service = service;
097            this.sharedLibraries = sharedLibraries;
098        }
099        
100        public void dispose() {
101            ClassLoader cl = component.getClass().getClassLoader();
102            lifeCycle = null;
103            suManager = null;
104            component = null;
105            if (cl instanceof DestroyableClassLoader) {
106                ((DestroyableClassLoader) cl).destroy();
107            }
108            fireEvent(ComponentEvent.COMPONENT_UNINSTALLED);
109        }
110    
111        /**
112         * Register the MBeans for this Component
113         * @param ctx
114         * @return ObjectName
115         * @throws JBIException
116         */
117        public ObjectName registerMBeans(ManagementContext ctx) throws JBIException {
118            try {
119                mBeanName = ctx.createObjectName(this);
120                ctx.registerMBean(mBeanName, this, ComponentMBean.class);
121                return mBeanName;
122            }  catch (Exception e) {
123                String errorStr = "Failed to register MBeans";
124                LOG.error(errorStr, e);
125                throw new JBIException(errorStr, e);
126            }
127        }
128        
129        /**
130         * Unregister Component MBeans
131         * @param ctx
132         * @throws JBIException
133         */
134        public void unregisterMbeans(ManagementContext ctx) throws JBIException {
135            ctx.unregisterMBean(mBeanName);
136        }
137    
138        /**
139         * Set the Context
140         * 
141         * @param ctx
142         */
143        public void setContext(ComponentContextImpl ctx) {
144            this.context = ctx;
145            this.stateFile = ctx.getEnvironment().getStateFile();
146        }
147    
148        /**
149         * Get the JMX ObjectName for any additional MBean for this component. If there is none, return null.
150         * 
151         * @return ObjectName the JMX object name of the additional MBean or null if there is no additional MBean.
152         */
153        public ObjectName getExtensionMBeanName() {
154            if (isInitialized() || isStarted() || isStopped()) {
155                return lifeCycle.getExtensionMBeanName();
156            } else {
157                return null;
158            }
159        }
160        
161        /**
162         * Get the name of the item
163         * @return the name
164         */
165        public String getName() {
166            return componentName.getName();
167        }
168        
169        /**
170         * Get the type of the item
171         * @return the type
172         */
173        public String getType() {
174            return "Component";
175        }
176        
177        public String getSubType() {
178            return "LifeCycle";
179        }
180        
181       /**
182         * Get the Description of the item
183         * @return the description
184         */
185        public String getDescription() {
186            return description;
187        }
188    
189        
190        public void init() throws JBIException {
191            LOG.info("Initializing component: " + getName());
192            if (context != null && component != null) {
193                DeliveryChannelImpl channel = new DeliveryChannelImpl(this);
194                channel.setContext(context);
195                context.setDeliveryChannel(channel);
196                super.init();
197                fireEvent(ComponentEvent.COMPONENT_INITIALIZED);
198                ClassLoader loader = Thread.currentThread().getContextClassLoader();
199                try {
200                    Thread.currentThread().setContextClassLoader(component.getClass().getClassLoader());
201                    getLifeCycle().init(context);
202                } finally {
203                    Thread.currentThread().setContextClassLoader(loader);
204                }
205            }
206        }
207        
208        /**
209         * Start the item.
210         * 
211         * @exception javax.jbi.JBIException if the item fails to start.
212         */
213        public void start() throws javax.jbi.JBIException {
214            LOG.info("Starting component: " + getName());
215            try {
216                doStart();
217                persistRunningState();
218                getContainer().getRegistry().checkPendingAssemblies();
219            } catch (JBIException e) {
220                LOG.error("Could not start component", e);
221                throw e;
222            } catch (RuntimeException e) {
223                LOG.error("Could not start component", e);
224                throw e;
225            } catch (Error e) {
226                LOG.error("Could not start component", e);
227                throw e;
228            }
229        }
230    
231        /**
232         * Stop the item. This suspends current messaging activities.
233         * 
234         * @exception javax.jbi.JBIException if the item fails to stop.
235         */
236        public void stop() throws javax.jbi.JBIException {
237            LOG.info("Stopping component: " + getName());
238            try {
239                doStop();
240                persistRunningState();
241            } catch (JBIException e) {
242                LOG.error("Could not stop component", e);
243                throw e;
244            } catch (RuntimeException e) {
245                LOG.error("Could not start component", e);
246                throw e;
247            } catch (Error e) {
248                LOG.error("Could not start component", e);
249                throw e;
250            }
251        }
252    
253        /**
254         * Shut down the item. The releases resources, preparatory to uninstallation.
255         * 
256         * @exception javax.jbi.JBIException if the item fails to shut down.
257         */
258        public void shutDown() throws javax.jbi.JBIException {
259            LOG.info("Shutting down component: " + getName());
260            try {
261                doShutDown();
262                persistRunningState();
263            } catch (JBIException e) {
264                LOG.error("Could not shutDown component", e);
265                throw e;
266            } catch (RuntimeException e) {
267                LOG.error("Could not start component", e);
268                throw e;
269            } catch (Error e) {
270                LOG.error("Could not start component", e);
271                throw e;
272            }
273        }
274        
275        public void setShutdownStateAfterInstall() {
276            setCurrentState(SHUTDOWN);
277        }
278        
279        /**
280         * Start the item - doesn't persist the state
281         * 
282         * @exception javax.jbi.JBIException if the item fails to start.
283         */
284        public void doStart() throws javax.jbi.JBIException {
285            if (isShutDown()) {
286                // need to re-initialze before starting
287                init();
288            }
289            if (!isStarted()) {
290                ClassLoader loader = Thread.currentThread().getContextClassLoader();
291                try {
292                    Thread.currentThread().setContextClassLoader(component.getClass().getClassLoader());
293                    getLifeCycle().start();
294                } finally {
295                    Thread.currentThread().setContextClassLoader(loader);
296                }
297                super.start();
298                initServiceAssemblies();
299                startServiceAssemblies();
300            }
301            fireEvent(ComponentEvent.COMPONENT_STARTED);
302        }
303    
304        /**
305         * Stop the item - doesn't persist the state
306         * 
307         * @exception javax.jbi.JBIException
308         *                if the item fails to stop.
309         */
310        public void doStop() throws javax.jbi.JBIException {
311            if (isUnknown() || isStarted()) {
312                stopServiceAssemblies();
313                ClassLoader loader = Thread.currentThread().getContextClassLoader();
314                try {
315                    Thread.currentThread().setContextClassLoader(component.getClass().getClassLoader());
316                    getLifeCycle().stop();
317                } finally {
318                    Thread.currentThread().setContextClassLoader(loader);
319                }
320                super.stop();
321            }
322            fireEvent(ComponentEvent.COMPONENT_STOPPED);
323        }
324    
325        /**
326         * Shut down the item - doesn't persist the state
327         * 
328         * @exception javax.jbi.JBIException if the item fails to shut down.
329         */
330        public void doShutDown() throws javax.jbi.JBIException {
331            // Transition from UNKNOWN to SHUTDOWN is done at installation time
332            // In this case or if the component is already shut down, do nothing
333            if (!isUnknown() && !isShutDown()) {
334                doStop();
335                shutDownServiceAssemblies();
336                ClassLoader loader = Thread.currentThread().getContextClassLoader();
337                try {
338                    Thread.currentThread().setContextClassLoader(component.getClass().getClassLoader());
339                    getLifeCycle().shutDown();
340                } finally {
341                    Thread.currentThread().setContextClassLoader(loader);
342                }
343                if (getDeliveryChannel() != null) {
344                    getDeliveryChannel().close();
345                    setDeliveryChannel(null);
346                }
347                lifeCycle = null;
348                suManager = null;
349            }
350            super.shutDown();
351            fireEvent(ComponentEvent.COMPONENT_SHUTDOWN);
352        }
353        
354        
355        /**
356         * Set the initial running state of the Component
357         * @throws JBIException
358         */
359        public void setInitialRunningState() throws JBIException {
360            if (!isPojo()) {
361                String name = getName();
362                String runningState = getRunningStateFromStore();
363                LOG.info("Setting running state for Component: " + name + " to " + runningState);
364                if (runningState != null) {
365                    if (runningState.equals(LifeCycleMBean.STARTED)) {
366                        doStart();
367                    } else if (runningState.equals(LifeCycleMBean.STOPPED)) {
368                        doStart();
369                        doStop();
370                    } else if (runningState.equals(LifeCycleMBean.SHUTDOWN)) {
371                        doShutDown();
372                    }
373                }
374            }
375        }
376        
377        /**
378         * Persist the running state
379         */
380        public void persistRunningState() {
381            if (!isPojo()) {
382                String name = getName();
383                try {
384                    String currentState = getCurrentState();
385                    Properties props = new Properties();
386                    props.setProperty("state", currentState);
387                    XmlPersistenceSupport.write(stateFile, props);
388                } catch (IOException e) {
389                    LOG.error("Failed to write current running state for Component: " + name, e);
390                }
391            }
392        }
393       
394        /**
395         * @return the current running state from disk
396         */
397        public String getRunningStateFromStore() {
398            String result = LifeCycleMBean.UNKNOWN;
399            String name = getName();
400            try {
401                Properties props = (Properties) XmlPersistenceSupport.read(stateFile);
402                result = props.getProperty("state", result);
403            } catch (Exception e) {
404                LOG.error("Failed to read running state for Component: " + name, e);
405            }
406            return result;
407        }
408    
409        /**
410         * @return the capacity of the inbound queue
411         */
412        public int getInboundQueueCapacity() {
413            return queueCapacity;
414        }
415        
416        /**
417         * Set the inbound queue capacity
418         * @param value
419         */
420        public void setInboundQueueCapacity(int value) {
421            if (getDeliveryChannel() != null) {
422                throw new IllegalStateException("The component must be shut down before changing queue capacity");
423            }
424            this.queueCapacity = value;
425        }
426        
427        /**
428         * @return Returns the deliveryChannel.
429         */
430        public DeliveryChannelImpl getDeliveryChannel() {
431            return (DeliveryChannelImpl) context.getDeliveryChannel();
432        }
433    
434        /**
435         * @param deliveryChannel
436         *            The deliveryChannel to set.
437         */
438        public void setDeliveryChannel(DeliveryChannelImpl deliveryChannel) {
439            context.setDeliveryChannel(deliveryChannel);
440        }
441    
442        /**
443         * @return the ActivateionSpec
444         */
445        public ActivationSpec getActivationSpec() {
446            return activationSpec;
447        }
448    
449        /**
450         * @return Returns the pojo.
451         */
452        public boolean isPojo() {
453            return pojo;
454        }
455    
456        /**
457         * Set the ActivationSpec
458         * 
459         * @param activationSpec
460         */
461        public void setActivationSpec(ActivationSpec activationSpec) {
462            this.activationSpec = activationSpec;
463        }
464    
465        /**
466         * Is MessageExchange sender throttling enabled ?
467         * 
468         * @return true if throttling enabled
469         */
470        public boolean isExchangeThrottling() {
471            return exchangeThrottling;
472        }
473    
474        /**
475         * Set message throttling
476         * 
477         * @param value
478         */
479        public void setExchangeThrottling(boolean value) {
480            this.exchangeThrottling = value;
481        }
482    
483        /**
484         * Get the throttling timeout
485         * 
486         * @return throttling tomeout (ms)
487         */
488        public long getThrottlingTimeout() {
489            return throttlingTimeout;
490        }
491    
492        /**
493         * Set the throttling timout
494         * 
495         * @param value (ms)
496         */
497        public void setThrottlingTimeout(long value) {
498            throttlingTimeout = value;
499        }
500    
501        /**
502         * Get the interval for throttling - number of Exchanges set before the throttling timeout is applied
503         * 
504         * @return interval for throttling
505         */
506        public int getThrottlingInterval() {
507            return throttlingInterval;
508        }
509    
510        /**
511         * Set the throttling interval number of Exchanges set before the throttling timeout is applied
512         * 
513         * @param value
514         */
515        public void setThrottlingInterval(int value) {
516            throttlingInterval = value;
517        }
518    
519        /**
520         * Get an array of MBeanAttributeInfo
521         * 
522         * @return array of AttributeInfos
523         * @throws JMException
524         */
525        public MBeanAttributeInfo[] getAttributeInfos() throws JMException {
526            AttributeInfoHelper helper = new AttributeInfoHelper();
527            helper.addAttribute(getObjectToManage(), "componentType", "the type of this component (BC, SE, POJO)");
528            helper.addAttribute(getObjectToManage(), "inboundQueueCapacity", "capacity of the inbound queue");
529            helper.addAttribute(getObjectToManage(), "exchangeThrottling", "apply throttling");
530            helper.addAttribute(getObjectToManage(), "throttlingTimeout", "timeout for throttling");
531            helper.addAttribute(getObjectToManage(), "throttlingInterval", "exchange intervals before throttling");
532            helper.addAttribute(getObjectToManage(), "extensionMBeanName", "extension mbean name");
533            return AttributeInfoHelper.join(super.getAttributeInfos(), helper.getAttributeInfos());
534        }
535    
536        /**
537         * Get an array of MBeanOperationInfo
538         * 
539         * @return array of OperationInfos
540         * @throws JMException
541         */
542        public MBeanOperationInfo[] getOperationInfos() throws JMException {
543            OperationInfoHelper helper = new OperationInfoHelper();
544            return OperationInfoHelper.join(super.getOperationInfos(), helper.getOperationInfos());
545        }
546    
547        public void firePropertyChanged(String name, Object oldValue, Object newValue) {
548            super.firePropertyChanged(name, oldValue, newValue);
549        }
550    
551        protected void initServiceAssemblies() throws DeploymentException {
552        }
553    
554        protected void startServiceAssemblies() throws DeploymentException {
555        }
556    
557        protected void stopServiceAssemblies() throws DeploymentException {
558            Registry registry = getContainer().getRegistry();
559            String[] sas = registry.getDeployedServiceAssembliesForComponent(getName());
560            for (int i = 0; i < sas.length; i++) {
561                ServiceAssemblyLifeCycle sa = registry.getServiceAssembly(sas[i]);
562                if (sa.isStarted()) {
563                    try {
564                        sa.stop(false, false);
565                        registry.addPendingAssembly(sa);
566                    } catch (Exception e) {
567                        LOG.error("Error stopping service assembly " + sas[i]);
568                    }
569                }
570            }
571        }
572    
573        protected void shutDownServiceAssemblies() throws DeploymentException {
574            Registry registry = getContainer().getRegistry();
575            String[] sas = registry.getDeployedServiceAssembliesForComponent(getName());
576            for (int i = 0; i < sas.length; i++) {
577                ServiceAssemblyLifeCycle sa = registry.getServiceAssembly(sas[i]);
578                if (sa.isStopped()) {
579                    try {
580                        sa.shutDown(false);
581                        registry.addPendingAssembly(sa);
582                    } catch (Exception e) {
583                        LOG.error("Error shutting down service assembly " + sas[i]);
584                    }
585                }
586            }
587        }
588        
589        protected void fireEvent(int type) {
590            ComponentEvent event = new ComponentEvent(this, type);
591            ComponentListener[] listeners = (ComponentListener[]) getContainer().getListeners(ComponentListener.class);
592            for (int i = 0; i < listeners.length; i++) {
593                switch (type) {
594                case ComponentEvent.COMPONENT_INITIALIZED:
595                    listeners[i].componentInitialized(event);
596                    break;
597                case ComponentEvent.COMPONENT_STARTED:
598                    listeners[i].componentStarted(event);
599                    break;
600                case ComponentEvent.COMPONENT_STOPPED:
601                    listeners[i].componentStopped(event);
602                    break;
603                case ComponentEvent.COMPONENT_SHUTDOWN:
604                    listeners[i].componentShutDown(event);
605                    break;
606                case ComponentEvent.COMPONENT_UNINSTALLED:
607                    listeners[i].componentUninstalled(event);
608                    break;
609                default:
610                    break;
611                }
612            }
613            
614        }
615    
616        public ComponentLifeCycle getLifeCycle() {
617            if (lifeCycle == null) {
618                lifeCycle = component.getLifeCycle();
619            }
620            return lifeCycle;
621        }
622    
623        public ServiceUnitManager getServiceUnitManager() {
624            if (suManager == null) {
625                suManager = component.getServiceUnitManager();
626            }
627            return suManager;
628        }
629    
630        public JBIContainer getContainer() {
631            return container;
632        }
633    
634        public Component getComponent() {
635            return component;
636        }
637        
638        public ComponentNameSpace getComponentNameSpace() {
639            return componentName;
640        }
641    
642        public ComponentContextImpl getContext() {
643            return context;
644        }
645        public ObjectName getMBeanName() {
646            return mBeanName;
647        }
648        public boolean isBinding() {
649            return binding;
650        }
651        public boolean isService() {
652            return service;
653        }
654    
655        public void setPojo(boolean pojo) {
656            this.pojo = pojo;
657        }
658    
659        public boolean isEngine() {
660            return service;
661        }
662    
663        /**
664         * @return the sharedLibraries
665         */
666        public String[] getSharedLibraries() {
667            return sharedLibraries;
668        }
669    
670        /**
671         * @return the component type
672         */
673        public String getComponentType() {
674            return isBinding() ? "binding-component" : isEngine() ? "service-engine" : "pojo";
675        }
676    
677    }