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.container;
018    
019    import java.io.File;
020    import java.util.Calendar;
021    import java.util.Collection;
022    import java.util.EventListener;
023    import java.util.MissingResourceException;
024    import java.util.concurrent.Callable;
025    import java.util.concurrent.FutureTask;
026    import java.util.concurrent.TimeUnit;
027    import java.util.concurrent.atomic.AtomicBoolean;
028    import java.util.logging.Logger;
029    
030    import javax.jbi.JBIException;
031    import javax.jbi.component.Component;
032    import javax.jbi.component.ComponentLifeCycle;
033    import javax.jbi.component.ServiceUnitManager;
034    import javax.jbi.management.DeploymentException;
035    import javax.jbi.management.LifeCycleMBean;
036    import javax.jbi.messaging.ExchangeStatus;
037    import javax.jbi.messaging.MessageExchange;
038    import javax.jbi.messaging.MessagingException;
039    import javax.jbi.servicedesc.ServiceEndpoint;
040    import javax.management.JMException;
041    import javax.management.MBeanServer;
042    import javax.management.ObjectName;
043    import javax.naming.InitialContext;
044    import javax.naming.NamingException;
045    import javax.swing.event.EventListenerList;
046    import javax.transaction.TransactionManager;
047    import javax.xml.namespace.QName;
048    
049    import org.w3c.dom.DocumentFragment;
050    
051    import org.apache.commons.logging.Log;
052    import org.apache.commons.logging.LogFactory;
053    import org.apache.servicemix.JbiConstants;
054    import org.apache.servicemix.components.util.ComponentAdaptor;
055    import org.apache.servicemix.components.util.ComponentAdaptorMEListener;
056    import org.apache.servicemix.components.util.ComponentSupport;
057    import org.apache.servicemix.components.util.PojoLifecycleAdaptor;
058    import org.apache.servicemix.components.util.PojoSupport;
059    import org.apache.servicemix.executors.ExecutorFactory;
060    import org.apache.servicemix.executors.impl.ExecutorFactoryImpl;
061    import org.apache.servicemix.id.IdGenerator;
062    import org.apache.servicemix.jbi.api.Container;
063    import org.apache.servicemix.jbi.event.ComponentListener;
064    import org.apache.servicemix.jbi.event.ContainerAware;
065    import org.apache.servicemix.jbi.event.DeploymentListener;
066    import org.apache.servicemix.jbi.event.EndpointListener;
067    import org.apache.servicemix.jbi.event.ExchangeEvent;
068    import org.apache.servicemix.jbi.event.ExchangeListener;
069    import org.apache.servicemix.jbi.event.ServiceAssemblyListener;
070    import org.apache.servicemix.jbi.event.ServiceUnitListener;
071    import org.apache.servicemix.jbi.framework.AdminCommandsService;
072    import org.apache.servicemix.jbi.framework.AutoDeploymentService;
073    import org.apache.servicemix.jbi.framework.ClientFactory;
074    import org.apache.servicemix.jbi.framework.ComponentContextImpl;
075    import org.apache.servicemix.jbi.framework.ComponentMBeanImpl;
076    import org.apache.servicemix.jbi.framework.ComponentNameSpace;
077    import org.apache.servicemix.jbi.framework.DeploymentService;
078    import org.apache.servicemix.jbi.framework.InstallationService;
079    import org.apache.servicemix.jbi.framework.Registry;
080    import org.apache.servicemix.jbi.listener.MessageExchangeListener;
081    import org.apache.servicemix.jbi.management.BaseLifeCycle;
082    import org.apache.servicemix.jbi.management.BaseSystemService;
083    import org.apache.servicemix.jbi.management.ManagementContext;
084    import org.apache.servicemix.jbi.messaging.MessageExchangeImpl;
085    import org.apache.servicemix.jbi.nmr.Broker;
086    import org.apache.servicemix.jbi.nmr.DefaultBroker;
087    import org.apache.servicemix.jbi.nmr.flow.Flow;
088    
089    /**
090     * The main container
091     *
092     * @version $Revision: 3807 $
093     */
094    public class JBIContainer extends BaseLifeCycle implements Container {
095        /**
096         * Default Container name - must be unique if used in a cluster
097         */
098        public static final String DEFAULT_NAME = "ServiceMix";
099    
100        private static final Log LOG = LogFactory.getLog(JBIContainer.class);
101    
102        protected Broker broker = new DefaultBroker();
103        protected ServiceUnitManager serviceManager;
104        protected ManagementContext managementContext = new ManagementContext();
105        protected EnvironmentContext environmentContext = new EnvironmentContext();
106        protected InstallationService installationService = new InstallationService();
107        protected DeploymentService deploymentService = new DeploymentService();
108        protected AutoDeploymentService autoDeployService = new AutoDeploymentService();
109        protected AdminCommandsService adminCommandsService = new AdminCommandsService();
110        protected BaseSystemService[] services;
111        protected ClientFactory clientFactory = new ClientFactory();
112        protected Registry registry = new Registry();
113        protected boolean autoEnlistInTransaction;
114        protected boolean persistent;
115        protected boolean embedded;
116        protected boolean notifyStatistics;
117        protected EventListenerList listeners = new EventListenerList();
118        protected EventListener[] configuredListeners;
119        protected boolean useShutdownHook = true;
120        protected boolean useNewTransactionModel;
121        protected transient Thread shutdownHook;
122        protected ExecutorFactory executorFactory;
123        private String name = DEFAULT_NAME;
124        private InitialContext namingContext;
125        private MBeanServer mbeanServer;
126        private TransactionManager transactionManager;
127        private String rootDir;
128        private String generatedRootDirPrefix = "target/rootDirs/rootDir";
129        private boolean generateRootDir;
130        private AtomicBoolean started = new AtomicBoolean(false);
131        private AtomicBoolean containerInitialized = new AtomicBoolean(false);
132        private IdGenerator idGenerator = new IdGenerator();
133        private long forceShutdown;
134    
135        /**
136         * Default Constructor
137         */
138        public JBIContainer() {
139        }
140    
141        /**
142         * @return Returns the unique nam for the Container
143         */
144        public String getName() {
145            return name;
146        }
147    
148        /**
149         * @param name The name to set (must be unique within a cluster)
150         */
151        public void setName(String name) {
152            this.name = name;
153        }
154    
155        /**
156         * Get the description
157         *
158         * @return descrption
159         */
160        public String getDescription() {
161            return "ServiceMix JBI Container";
162        }
163    
164        /**
165         * @return Returns the flowName.
166         */
167        public String getFlowName() {
168            String flowNames = getDefaultBroker().getFlowNames();
169            if (flowNames == null) {
170                return null;
171            }
172            String[] flows = flowNames.split(",");
173            if (flows.length > 1) {
174                throw new IllegalStateException("Multiple flows have been defined");
175            }
176            return flows[0];
177        }
178    
179        /**
180         * @param flowName The flow to set.
181         */
182        public void setFlowName(String flowName) {
183            getDefaultBroker().setFlowNames(flowName);
184        }
185    
186        /**
187         * @return Returns the flowNames.
188         */
189        public String getFlowNames() {
190            return getDefaultBroker().getFlowNames();
191        }
192    
193        /**
194         * @param flowNames The flows to set.
195         */
196        public void setFlowNames(String flowNames) {
197            getDefaultBroker().setFlowNames(flowNames);
198        }
199    
200        /**
201         * @return the subscriptionFlowName
202         */
203        public String getSubscriptionFlowName() {
204            return getDefaultBroker().getSubscriptionFlowName();
205        }
206    
207        /**
208         * Set the subscription flow name
209         * @param subscriptionFlowName
210         */
211        public void setSubscriptionFlowName(String subscriptionFlowName) {
212            getDefaultBroker().setSubscriptionFlowName(subscriptionFlowName);
213        }
214    
215        /**
216         * Set the broker message flow
217         *
218         * @param flow
219         */
220        public void setFlow(Flow flow) {
221            getDefaultBroker().setFlows(new Flow[] {flow });
222        }
223    
224        /**
225         * @return the broker message Flow
226         */
227        public Flow getFlow() {
228            Flow[] flows = getDefaultBroker().getFlows();
229            if (flows == null || flows.length == 0) {
230                return null;
231            } else if (flows.length > 1) {
232                throw new IllegalStateException("Multiple flows have been defined");
233            } else {
234                return flows[0];
235            }
236        }
237    
238        /**
239         * Set the broker message flows
240         *
241         * @param flows
242         */
243        public void setFlows(Flow[] flows) {
244            getDefaultBroker().setFlows(flows);
245        }
246    
247        /**
248         * @return the broker message Flows
249         */
250        public Flow[] getFlows() {
251            return getDefaultBroker().getFlows();
252        }
253    
254        public boolean isUseShutdownHook() {
255            return useShutdownHook;
256        }
257    
258        /**
259         * Sets whether or not we should use a shutdown handler to close down the
260         * broker cleanly if the JVM is terminated. It is recommended you leave this
261         * enabled.
262         */
263        public void setUseShutdownHook(boolean useShutdownHook) {
264            this.useShutdownHook = useShutdownHook;
265        }
266    
267        public boolean isUseNewTransactionModel() {
268            return useNewTransactionModel;
269        }
270    
271        /**
272         * Sets whether the new transaction model should be used.  
273         * @param useNewTransactionModel
274         */
275        public void setUseNewTransactionModel(boolean useNewTransactionModel) {
276            this.useNewTransactionModel = useNewTransactionModel;
277        }
278    
279        /**
280         * @return the services
281         */
282        public BaseSystemService[] getServices() {
283            return services;
284        }
285    
286        /**
287         * @param services the services to set
288         */
289        public void setServices(BaseSystemService[] services) {
290            this.services = services;
291        }
292    
293        /**
294         * Get the ManagementContext
295         *
296         * @return the ManagementContext
297         */
298        public ManagementContext getManagementContext() {
299            return managementContext;
300        }
301    
302        /**
303         * @return Return the EnvironmentContext
304         */
305        public EnvironmentContext getEnvironmentContext() {
306            return environmentContext;
307        }
308    
309        /**
310         * @return Return the registry
311         */
312        public Registry getRegistry() {
313            return registry;
314        }
315    
316        /**
317         * Return the DefaultBroker instance
318         */
319        public DefaultBroker getDefaultBroker() {
320            if (!(broker instanceof DefaultBroker)) {
321                throw new IllegalStateException("Broker is not a DefaultBroker");
322            }
323            return (DefaultBroker) broker;
324        }
325    
326        /**
327         * @return Return the NMR broker
328         */
329        public Broker getBroker() {
330            return broker;
331        }
332    
333        /**
334         * Set the Broker to use
335         */
336        public void setBroker(Broker broker) {
337            this.broker = broker;
338        }
339    
340        /**
341         * @return true if creates own MBeanServer if none supplied
342         */
343        public boolean isCreateMBeanServer() {
344            return managementContext.isCreateMBeanServer();
345        }
346    
347        /**
348         * Set the flag to create own MBeanServer if none supplied
349         *
350         * @param enableJMX
351         */
352        public void setCreateMBeanServer(boolean enableJMX) {
353            managementContext.setCreateMBeanServer(enableJMX);
354        }
355    
356        /**
357         * @return Returns the useMBeanServer.
358         */
359        public boolean isUseMBeanServer() {
360            return managementContext.isUseMBeanServer();
361        }
362    
363        /**
364         * @param useMBeanServer The useMBeanServer to set.
365         */
366        public void setUseMBeanServer(boolean useMBeanServer) {
367            managementContext.setUseMBeanServer(useMBeanServer);
368        }
369    
370        /**
371         * @return Returns the useMBeanServer.
372         */
373        public boolean isCreateJmxConnector() {
374            return managementContext.isCreateJmxConnector();
375        }
376    
377        /**
378         * @param createJmxConnector The createJmxConnector to set.
379         */
380        public void setCreateJmxConnector(boolean createJmxConnector) {
381            managementContext.setCreateJmxConnector(createJmxConnector);
382        }
383    
384        /**
385         * @return Returns the monitorInstallationDirectory.
386         */
387        public boolean isMonitorInstallationDirectory() {
388            return autoDeployService.isMonitorInstallationDirectory();
389        }
390    
391        /**
392         * @param monitorInstallationDirectory The monitorInstallationDirectory to set.
393         */
394        public void setMonitorInstallationDirectory(boolean monitorInstallationDirectory) {
395            autoDeployService.setMonitorInstallationDirectory(monitorInstallationDirectory);
396        }
397    
398        /**
399         * @return Returns the monitorDeploymentDirectory.
400         */
401        public boolean isMonitorDeploymentDirectory() {
402            return autoDeployService.isMonitorDeploymentDirectory();
403        }
404    
405        /**
406         * @param monitorDeploymentDirectory The monitorDeploymentDirectory to set.
407         */
408        public void setMonitorDeploymentDirectory(boolean monitorDeploymentDirectory) {
409            autoDeployService.setMonitorDeploymentDirectory(monitorDeploymentDirectory);
410        }
411    
412        /**
413         * @return Returns the installationDir.
414         */
415        public String getInstallationDirPath() {
416            File dir = environmentContext.getInstallationDir();
417            return dir != null ? dir.getAbsolutePath() : "";
418        }
419    
420        /**
421         * Set the installationDir - rge default location is root/<container name>/installation
422         *
423         * @param installationDir
424         */
425        public void setInstallationDirPath(String installationDir) {
426            if (installationDir != null && installationDir.length() > 0) {
427                environmentContext.setInstallationDir(new File(installationDir));
428            }
429        }
430    
431        /**
432         * @return Returns the deploymentDir.
433         */
434        public String getDeploymentDirPath() {
435            File dir = environmentContext.getDeploymentDir();
436            return dir != null ? dir.getAbsolutePath() : "";
437        }
438    
439        /**
440         * @param deploymentDir The deploymentDir to set.
441         */
442        public void setDeploymentDirPath(String deploymentDir) {
443            if (deploymentDir != null && deploymentDir.length() > 0) {
444                environmentContext.setDeploymentDir(new File(deploymentDir));
445            }
446        }
447    
448        /**
449         * @return Returns the monitorInterval (in secs).
450         */
451        public int getMonitorInterval() {
452            return autoDeployService.getMonitorInterval();
453        }
454    
455        /**
456         * @param monitorInterval The monitorInterval to set (in secs).
457         */
458        public void setMonitorInterval(int monitorInterval) {
459            autoDeployService.setMonitorInterval(monitorInterval);
460        }
461    
462        /**
463         * @return the deploymentExtensions
464         */
465        public String getDeploymentExtensions() {
466            return autoDeployService.getExtensions();
467        }
468    
469        /**
470         * @param deploymentExtensions the deploymentExtensions to set
471         */
472        public void setDeploymentExtensions(String deploymentExtensions) {
473            autoDeployService.setExtensions(deploymentExtensions);
474        }
475    
476        /**
477         * Install an component from a url
478         *
479         * @param url
480         * @throws DeploymentException
481         */
482        public void installArchive(String url) throws DeploymentException {
483            installationService.install(url, null, true);
484        }
485    
486        /**
487         * load an archive from an external location. 
488         * The archive can be a Component, Service Assembly or Shared Library.
489         * @param location - can either be a url or filename (if relative - must be relative to the container)
490         * @param autoStart - if true will start the component/service assembly
491         * @throws DeploymentException
492         */
493        public void updateExternalArchive(String location, boolean autoStart) throws DeploymentException {
494            autoDeployService.updateExternalArchive(location, autoStart);
495        }
496    
497        /**
498         * load an archive from an external location and starts it
499         * The archive can be a Component, Service Assembly or Shared Library.
500         * @param location - can either be a url or filename (if relative - must be relative to the container)
501         * @throws DeploymentException
502         */
503        public void updateExternalArchive(String location) throws DeploymentException {
504            updateExternalArchive(location, true);
505        }
506    
507        /**
508         * @return Returns the deploymentService.
509         */
510        public DeploymentService getDeploymentService() {
511            return deploymentService;
512        }
513    
514        /**
515         * @return Returns the installationService.
516         */
517        public InstallationService getInstallationService() {
518            return installationService;
519        }
520    
521        /**
522         * @return the AutomDeploymentService
523         */
524        public AutoDeploymentService getAutoDeploymentService() {
525            return autoDeployService;
526        }
527    
528        /**
529         *
530         * @return the AdminCommandsService
531         */
532        public AdminCommandsService getAdminCommandsService() {
533            return adminCommandsService;
534        }
535    
536        public ClientFactory getClientFactory() {
537            return clientFactory;
538        }
539    
540        public String getGeneratedRootDirPrefix() {
541            return generatedRootDirPrefix;
542        }
543    
544        /**
545         * Sets the prefix used when auto-creating a rootDir property value
546         * which is useful for integration testing inside JUnit test cases
547         * letting each test case create its own empty servicemix install
548         *
549         * @param generatedRootDirPrefix the prefix used to auto-create the
550         * rootDir
551         * @see #setRootDir(String)
552         * @see #setGeneratedRootDirPrefix(String)
553         */
554        public void setGeneratedRootDirPrefix(String generatedRootDirPrefix) {
555            this.generatedRootDirPrefix = generatedRootDirPrefix;
556        }
557    
558        public boolean isGenerateRootDir() {
559            return generateRootDir;
560        }
561        
562        public long getForceShutdown() {
563            return forceShutdown;
564        }
565        
566        /**
567         * Set the timeout (in ms) before a shutdown is forced by cancelling all pending exchanges.
568         * The default value is 0 -- no forced shutdown
569         * 
570         * @param forceShutdown the timeout in ms
571         */
572        public void setForceShutdown(long forceShutdown) {
573            this.forceShutdown = forceShutdown;
574        }
575    
576        /**
577         * Creates an auto-generated rootDir which is useful for integration testing
578         * in JUnit test cases allowing installations of deployments inside an empty
579         * installation of ServiceMix
580         *
581         * @param generateRootDir if true this will enable the auto-generation of the rootDir
582         * if the rootDir property is not configured
583         *
584         * @see #setRootDir(String)
585         * @see #setGeneratedRootDirPrefix(String)
586         */
587        public void setGenerateRootDir(boolean generateRootDir) {
588            this.generateRootDir = generateRootDir;
589        }
590    
591        /**
592         * light weight initialization - default values for mbeanServer, TransactionManager etc are null
593         *
594         * @throws JBIException
595         */
596        public void init() throws JBIException {
597            if (containerInitialized.compareAndSet(false, true)) {
598                LOG.info("ServiceMix " + EnvironmentContext.getVersion() + " JBI Container (" + getName() + ") is starting");
599                LOG.info("For help or more information please see: http://servicemix.apache.org/");
600                addShutdownHook();
601                if (this.executorFactory == null) {
602                    this.executorFactory = createExecutorFactory();
603                }
604                if (this.namingContext == null) {
605                    try {
606                        this.namingContext = new InitialContext();
607                    } catch (NamingException e) {
608                        // Log a warning, with exception only in debug
609                        if (LOG.isDebugEnabled()) {
610                            LOG.warn("Failed to set InitialContext", e);
611                        } else {
612                            LOG.warn("Failed to set InitialContext");
613                        }
614                    }
615                }
616                managementContext.init(this, getMBeanServer());
617                mbeanServer = this.managementContext.getMBeanServer(); // just in case ManagementContext creates it
618                environmentContext.init(this, getRootDir());
619                clientFactory.init(this);
620                if (services != null) {
621                    for (int i = 0; i < services.length; i++) {
622                        services[i].init(this);
623                    }
624                }
625                registry.init(this);
626                broker.init(this);
627                installationService.init(this);
628                deploymentService.init(this);
629                autoDeployService.init(this);
630                adminCommandsService.init(this);
631    
632                // register self with the ManagementContext
633                try {
634                    managementContext.registerMBean(ManagementContext.getContainerObjectName(managementContext.getJmxDomainName(), getName()),
635                                    this, LifeCycleMBean.class);
636                } catch (JMException e) {
637                    throw new JBIException(e);
638                }
639    
640                // Initialize listeners after the whole container has been initialized
641                // so that they can register themselves as JMX mbeans for example
642                if (configuredListeners != null) {
643                    for (int i = 0; i < configuredListeners.length; i++) {
644                        EventListener listener = configuredListeners[i];
645                        addListener(listener);
646                    }
647                }
648            }
649        }
650    
651        /**
652         * start processing
653         *
654         * @throws JBIException
655         */
656        public void start() throws JBIException {
657            checkInitialized();
658            if (started.compareAndSet(false, true)) {
659                managementContext.start();
660                environmentContext.start();
661                clientFactory.start();
662                if (services != null) {
663                    for (int i = 0; i < services.length; i++) {
664                        services[i].start();
665                    }
666                }
667                broker.start();
668                registry.start();
669                installationService.start();
670                deploymentService.start();
671                autoDeployService.start();
672                adminCommandsService.start();
673                super.start();
674                LOG.info("ServiceMix JBI Container (" + getName() + ") started");
675            }
676        }
677    
678        /**
679         * stop the container from processing
680         *
681         * @throws JBIException
682         */
683        public void stop() throws JBIException {
684            checkInitialized();
685            if (started.compareAndSet(true, false)) {
686                LOG.info("ServiceMix JBI Container (" + getName() + ") stopping");
687                adminCommandsService.stop();
688                autoDeployService.stop();
689                deploymentService.stop();
690                installationService.stop();
691                registry.stop();
692                broker.stop();
693                if (services != null) {
694                    for (int i = services.length - 1; i >= 0; i--) {
695                        services[i].stop();
696                    }
697                }
698                clientFactory.stop();
699                environmentContext.stop();
700                managementContext.stop();
701                super.stop();
702            }
703        }
704    
705        /**
706         * After a shutdown the container will require an init before a start ...
707         *
708         * @throws JBIException
709         */
710        public void shutDown() throws JBIException {
711            if (containerInitialized.compareAndSet(true, false)) {
712                LOG.info("Shutting down ServiceMix JBI Container (" + getName() + ") stopped");
713                removeShutdownHook();
714                adminCommandsService.shutDown();
715                autoDeployService.shutDown();
716                deploymentService.shutDown();
717                installationService.shutDown();
718                shutdownRegistry();           
719                broker.shutDown();
720                shutdownServices();
721                clientFactory.shutDown();
722                environmentContext.shutDown();
723                // shutdown the management context last, because it will close the mbean server
724                super.shutDown();
725                managementContext.unregisterMBean(this);
726                managementContext.shutDown();
727                LOG.info("ServiceMix JBI Container (" + getName() + ") stopped");
728            }
729        }
730    
731        private void shutdownServices() throws JBIException {
732            if (services != null) {
733                for (int i = services.length - 1; i >= 0; i--) {
734                    services[i].shutDown();
735                }
736            }
737        }
738    
739        private void shutdownRegistry() throws JBIException {
740            FutureTask<Boolean> shutdown = new FutureTask<Boolean>(new Callable<Boolean>() {
741                public Boolean call() throws Exception {
742                    registry.shutDown();
743                    return true;
744                };
745            });
746    
747            //use daemon thread to run this shutdown task
748            //fix the container hang when shutdown container from the jmx console
749            Thread daemonShutDownThread = new Thread(shutdown);
750            daemonShutDownThread.setDaemon(true);
751            daemonShutDownThread.start();
752            
753            try {
754                if (forceShutdown > 0) {
755                    LOG.info("Waiting another " + forceShutdown + " ms for complete shutdown of the components and service assemblies");
756                    shutdown.get(forceShutdown, TimeUnit.MILLISECONDS);
757                } else {
758                    LOG.info("Waiting for complete shutdown of the components and service assemblies");
759                    shutdown.get();
760                }
761                LOG.info("Components and service assemblies have been shut down");
762            } catch (Exception e) {
763                forceShutdown(e);
764            }
765        }
766    
767        /**
768         * Force a container shutdown by canceling all pending exchanges
769         * 
770         * @param e the exception that caused the forced container shutdown
771         */
772        protected void forceShutdown(Exception e) {
773            LOG.warn("Unable to shutdown components and service assemblies normally: " + e, e);
774            LOG.warn("Forcing shutdown by cancelling all pending exchanges");
775            registry.cancelPendingExchanges();
776        }
777    
778        protected void addShutdownHook() {
779            if (useShutdownHook) {
780                shutdownHook = new Thread("ServiceMix ShutdownHook") {
781                    public void run() {
782                        containerShutdown();
783                    }
784                };
785                Runtime.getRuntime().addShutdownHook(shutdownHook);
786            }
787        }
788    
789        protected void removeShutdownHook() {
790            if (shutdownHook != null) {
791                try {
792                    Runtime.getRuntime().removeShutdownHook(shutdownHook);
793                } catch (Exception e) {
794                    LOG.debug("Caught exception, must be shutting down: " + e);
795                }
796            }
797        }
798    
799        /**
800         * Causes a clean shutdown of the container when the VM is being shut down
801         */
802        protected void containerShutdown() {
803            try {
804                shutDown();
805            } catch (Throwable e) {
806                System.err.println("Failed to shut down: " + e);
807            }
808        }
809    
810        /**
811         * @return theMBean server assocated with the JBI
812         */
813        public synchronized MBeanServer getMBeanServer() {
814            return mbeanServer;
815        }
816    
817        /**
818         * Set the MBeanServer
819         *
820         * @param mbs
821         */
822        public synchronized void setMBeanServer(MBeanServer mbs) {
823            this.mbeanServer = mbs;
824        }
825    
826        /**
827         * @return the naming context
828         */
829        public synchronized InitialContext getNamingContext() {
830            return namingContext;
831        }
832    
833        /**
834         * Set the naming context
835         *
836         * @param ic
837         */
838        public synchronized void setNamingContext(InitialContext ic) {
839            this.namingContext = ic;
840        }
841    
842        /**
843         * @return the TransactionManager for this implementation
844         */
845        public synchronized Object getTransactionManager() {
846            if (transactionManager == null && namingContext != null) {
847                try {
848                    transactionManager = (TransactionManager) namingContext.lookup("java:appserver/TransactionManager");
849                } catch (NamingException e) {
850                    LOG.debug("No transaction manager found from naming context: " + e.getMessage());
851                    try {
852                        transactionManager = (TransactionManager) namingContext.lookup("javax.transaction.TransactionManager");
853                    } catch (NamingException e1) {
854                        LOG.debug("No transaction manager found from naming context: " + e1.getMessage());
855                    }
856                }
857            }
858            return transactionManager;
859        }
860    
861        /**
862         * Set the transaction manager
863         *
864         * @param tm
865         */
866        public synchronized void setTransactionManager(Object tm) {
867            this.transactionManager = (TransactionManager) tm;
868        }
869    
870        /**
871         * @return the root directory path
872         */
873        public synchronized String getRootDir() {
874            if (rootDir == null) {
875                if (isGenerateRootDir()) {
876                    rootDir = createRootDir();
877                } else {
878                    rootDir = "." + File.separator + "rootDir";
879                }
880                LOG.debug("Defaulting to rootDir: " + rootDir);
881            }
882            return this.rootDir;
883        }
884    
885    
886        /**
887         * Set the workspace root
888         *
889         * @param root
890         */
891        public synchronized void setRootDir(String root) {
892            this.rootDir = root;
893        }
894    
895        /**
896         * Route an ExchangePacket to a destination
897         *
898         * @param exchange
899         * @throws MessagingException
900         */
901        public void sendExchange(MessageExchangeImpl exchange) throws MessagingException {
902            try {
903                broker.sendExchangePacket(exchange);
904            } catch (MessagingException e) {
905                throw e;
906            } catch (JBIException e) {
907                throw new MessagingException(e);
908            }
909        }
910    
911        /**
912         * @param context
913         * @param externalEndpoint
914         * @throws JBIException
915         */
916        public void registerExternalEndpoint(ComponentNameSpace cns, ServiceEndpoint externalEndpoint) throws JBIException {
917            registry.registerExternalEndpoint(cns, externalEndpoint);
918        }
919    
920        /**
921         * @param context
922         * @param externalEndpoint
923         * @throws JBIException
924         */
925        public void deregisterExternalEndpoint(ComponentNameSpace cns, ServiceEndpoint externalEndpoint) throws JBIException {
926            registry.deregisterExternalEndpoint(cns, externalEndpoint);
927        }
928    
929        /**
930         * @param context
931         * @param epr
932         * @return matching endpoint or null
933         */
934        public ServiceEndpoint resolveEndpointReference(ComponentContextImpl context, DocumentFragment epr) {
935            return registry.resolveEndpointReference(epr);
936        }
937    
938        /**
939         * @param context
940         * @param service
941         * @param endpointName
942         * @return the matching endpoint
943         */
944        public ServiceEndpoint getEndpoint(ComponentContextImpl context, QName service, String endpointName) {
945            return registry.getEndpoint(service, endpointName);
946        }
947    
948        /**
949         * @param context
950         * @param interfaceName
951         * @return endpoints that match the interface name
952         */
953        public ServiceEndpoint[] getEndpoints(ComponentContextImpl context, QName interfaceName) {
954            return registry.getEndpointsForInterface(interfaceName);
955        }
956    
957        /**
958         * @param context
959         * @param serviceName
960         * @return endpoints for a given service
961         */
962        public ServiceEndpoint[] getEndpointsForService(ComponentContextImpl context, QName serviceName) {
963            return registry.getEndpointsForService(serviceName);
964        }
965    
966        /**
967         * @param context
968         * @param interfaceName
969         * @return endpoints matching the interface name
970         */
971        public ServiceEndpoint[] getExternalEndpoints(ComponentContextImpl context, QName interfaceName) {
972            return registry.getExternalEndpoints(interfaceName);
973        }
974    
975        /**
976         * @param context
977         * @param serviceName
978         * @return external endpoints
979         */
980        public ServiceEndpoint[] getExternalEndpointsForService(ComponentContextImpl context, QName serviceName) {
981            return registry.getExternalEndpointsForService(serviceName);
982        }
983    
984        /**
985         * @param suffix
986         * @param resourceBundleName
987         * @return the Logger
988         * @throws MissingResourceException
989         * @throws JBIException
990         */
991        public Logger getLogger(String suffix, String resourceBundleName) throws MissingResourceException, JBIException {
992            try {
993                return Logger.getLogger(suffix, resourceBundleName);
994            } catch (IllegalArgumentException e) {
995                throw new JBIException("A logger can not be created using resource bundle " + resourceBundleName);
996            }
997        }
998    
999        /**
1000         * Used for Simple POJO's
1001         *
1002         * @param componentName - the unique component ID
1003         * @throws JBIException
1004         */
1005        public void deactivateComponent(String componentName) throws JBIException {
1006            ComponentMBeanImpl component = registry.getComponent(componentName);
1007            if (component != null) {
1008                component.doShutDown();
1009                component.unregisterMbeans(managementContext);
1010                registry.deregisterComponent(component);
1011                environmentContext.unreregister(component);
1012                component.dispose();
1013                LOG.info("Deactivating component " + componentName);
1014            } else {
1015                throw new JBIException("Could not find component " + componentName);
1016            }
1017        }
1018    
1019        /**
1020         * Delete a Component
1021         *
1022         * @param id
1023         * @throws JBIException
1024         */
1025        public void deleteComponent(String id) throws JBIException {
1026            deactivateComponent(id);
1027            environmentContext.removeComponentRootDirectory(id);
1028        }
1029    
1030        /**
1031         * Get the component associated with the given component ID
1032         *
1033         * @param componentName
1034         * @return the component
1035         */
1036        public ComponentMBeanImpl getComponent(String componentName) {
1037            return registry.getComponent(componentName);
1038        }
1039    
1040        /**
1041         * @return all local ComponentConnectors
1042         */
1043        public Collection getLocalComponentConnectors() {
1044            return registry.getComponents();
1045        }
1046    
1047        /**
1048         * Activates a new component
1049         *
1050         * @param activationSpec
1051         * @return Component
1052         * @throws JBIException
1053         */
1054        public Component activateComponent(ActivationSpec activationSpec) throws JBIException {
1055            if (activationSpec.getId() == null) {
1056                if (activationSpec.getComponentName() == null) {
1057                    // lets generate one
1058                    activationSpec.setId(createComponentID());
1059                } else {
1060                    activationSpec.setId(activationSpec.getComponentName());
1061                }
1062            }
1063            String id = activationSpec.getId();
1064            if (id == null) {
1065                throw new IllegalArgumentException("A Registration must have an ID");
1066            }
1067            if (activationSpec.getEndpoint() == null && activationSpec.getService() != null) {
1068                // lets default to the ID
1069                activationSpec.setEndpoint(id);
1070            }
1071            if (activationSpec.getComponentName() == null) {
1072                activationSpec.setComponentName(id);
1073            }
1074            Object bean = activationSpec.getComponent();
1075            if (bean == null) {
1076                throw new IllegalArgumentException("A Registration must have a component associated with it");
1077            }
1078            if (bean instanceof Component) {
1079                Component component = (Component) bean;
1080                if (component instanceof ComponentSupport) {
1081                    defaultComponentServiceAndEndpoint((ComponentSupport) component, activationSpec);
1082                }
1083                activateComponent(component, activationSpec);
1084                return component;
1085            } else if (bean instanceof ComponentLifeCycle) {
1086                // lets support just plain lifecycle pojos
1087                ComponentLifeCycle lifeCycle = (ComponentLifeCycle) bean;
1088                if (bean instanceof PojoSupport) {
1089                    defaultComponentServiceAndEndpoint((PojoSupport) bean, activationSpec);
1090                }
1091                Component adaptor = createComponentAdaptor(lifeCycle, activationSpec);
1092                activateComponent(adaptor, activationSpec);
1093                return adaptor;
1094            } else if (bean instanceof MessageExchangeListener) {
1095                // lets support just plain listener pojos
1096                MessageExchangeListener listener = (MessageExchangeListener) bean;
1097                Component adaptor = createComponentAdaptor(listener, activationSpec);
1098                activateComponent(adaptor, activationSpec);
1099                return adaptor;
1100            } else {
1101                throw new IllegalArgumentException("Component name: " + id
1102                                + " is bound to an object which is not a JBI component, it is of type: " + bean.getClass().getName());
1103            }
1104        }
1105    
1106        /**
1107         * Activate a POJO Component
1108         *
1109         * @param component
1110         * @param componentName
1111         * @return the ObjectName of the MBean for the Component
1112         * @throws JBIException
1113         */
1114        public ObjectName activateComponent(Component component, String componentName) throws JBIException {
1115            ActivationSpec activationSpec = new ActivationSpec();
1116            ComponentNameSpace cns = new ComponentNameSpace(getName(), componentName);
1117            activationSpec.setComponent(component);
1118            activationSpec.setComponentName(cns.getName());
1119            return activateComponent(component, activationSpec);
1120        }
1121    
1122        /**
1123         * Activate A POJO Component
1124         *
1125         * @param component
1126         * @param activationSpec
1127         * @return the ObjectName of the MBean for the Component
1128         * @throws JBIException
1129         */
1130        public ObjectName activateComponent(Component component, ActivationSpec activationSpec) throws JBIException {
1131            return activateComponent(component, "POJO Component", activationSpec, true, false, false, null);
1132        }
1133    
1134        /**
1135         * Called by the Installer MBean
1136         *
1137         * @param installDir
1138         * @param component
1139         * @param description
1140         * @param context
1141         * @param binding
1142         * @param service
1143         * @return the ObjectName of the Component's MBean
1144         * @throws JBIException
1145         */
1146        public ObjectName activateComponent(File installDir, Component component, String description, ComponentContextImpl context,
1147                        boolean binding, boolean service, String[] sharedLibraries) throws JBIException {
1148            ComponentNameSpace cns = context.getComponentNameSpace();
1149            ActivationSpec activationSpec = new ActivationSpec();
1150            activationSpec.setComponent(component);
1151            activationSpec.setComponentName(cns.getName());
1152            return activateComponent(installDir, component, description, context, activationSpec, false, binding, service, sharedLibraries);
1153        }
1154    
1155        /**
1156         * @param component
1157         * @param description
1158         * @param activationSpec
1159         * @param pojo
1160         * @param binding
1161         * @param service
1162         * @return the ObjectName of the Component's MBean
1163         * @throws JBIException
1164         */
1165        public ObjectName activateComponent(Component component, String description, ActivationSpec activationSpec, boolean pojo,
1166                        boolean binding, boolean service, String[] sharedLibraries) throws JBIException {
1167            ComponentNameSpace cns = new ComponentNameSpace(getName(), activationSpec.getComponentName());
1168            if (registry.getComponent(cns) != null) {
1169                throw new JBIException("A component is already registered for " + cns);
1170            }
1171            ComponentContextImpl context = new ComponentContextImpl(this, cns);
1172            return activateComponent(new File("."), component, description, context, activationSpec, pojo, binding, service, sharedLibraries);
1173        }
1174    
1175        /**
1176         * @param installationDir
1177         * @param component
1178         * @param description
1179         * @param context
1180         * @param activationSpec
1181         * @param pojo
1182         * @param binding
1183         * @param service
1184         * @return the ObjectName of the Component's MBean
1185         * @throws JBIException
1186         */
1187        public ObjectName activateComponent(File installationDir, Component component, 
1188                                           String description, ComponentContextImpl context,
1189                                           ActivationSpec activationSpec, boolean pojo, 
1190                                           boolean binding, boolean service, String[] sharedLibraries) throws JBIException {
1191            ObjectName result = null;
1192            ComponentNameSpace cns = new ComponentNameSpace(getName(), activationSpec.getComponentName());
1193            if (LOG.isDebugEnabled()) {
1194                LOG.info("Activating component for: " + cns + " with service: " + activationSpec.getService() + " component: " + component);
1195            }
1196            ComponentMBeanImpl lcc = registry.registerComponent(cns, description, component, binding, service, sharedLibraries);
1197            if (lcc != null) {
1198                lcc.setPojo(pojo);
1199                ComponentEnvironment env = environmentContext.registerComponent(context.getEnvironment(), lcc);
1200                if (env.getInstallRoot() == null) {
1201                    env.setInstallRoot(installationDir);
1202                }
1203                context.activate(component, env, activationSpec);
1204                lcc.setContext(context);
1205                lcc.setActivationSpec(activationSpec);
1206    
1207                if (lcc.isPojo()) {
1208                    //non-pojo's are either started by the auto deployer
1209                    //or manually
1210                    lcc.init();
1211                } else {
1212                    lcc.doShutDown();
1213                }
1214                result = lcc.registerMBeans(managementContext);
1215                // Start the component after mbeans have been registered
1216                // This can be usefull if listeners use them
1217                if (lcc.isPojo() && started.get()) {
1218                    lcc.start();
1219                }
1220            }
1221            return result;
1222        }
1223    
1224        /**
1225         * Allow the service and endpoint name to be configured from the registration, to reduce the amount of XML which is
1226         * required to configure a ServiceMix component
1227         *
1228         * @param component
1229         * @param activationSpec
1230         */
1231        protected void defaultComponentServiceAndEndpoint(PojoSupport component, ActivationSpec activationSpec) {
1232            if (activationSpec.getService() != null) {
1233                component.setService(activationSpec.getService());
1234            }
1235            if (activationSpec.getEndpoint() != null) {
1236                component.setEndpoint(activationSpec.getEndpoint());
1237            }
1238        }
1239    
1240        protected ExecutorFactory createExecutorFactory() throws JBIException {
1241            return new ExecutorFactoryImpl();
1242        }
1243    
1244        /**
1245         * Factory method to create a new component adaptor from the given lifecycle
1246         *
1247         * @param lifeCycle
1248         * @param activationSpec
1249         * @return Component
1250         */
1251        protected Component createComponentAdaptor(ComponentLifeCycle lifeCycle, ActivationSpec activationSpec) {
1252            ComponentAdaptor answer = null;
1253            if (lifeCycle instanceof MessageExchangeListener) {
1254                answer = new ComponentAdaptorMEListener(lifeCycle, activationSpec.getService(), activationSpec.getEndpoint(),
1255                                (MessageExchangeListener) lifeCycle);
1256            } else {
1257                answer = new ComponentAdaptor(lifeCycle, activationSpec.getService(), activationSpec.getEndpoint());
1258            }
1259            answer.setServiceManager(serviceManager);
1260            return answer;
1261        }
1262    
1263        protected Component createComponentAdaptor(MessageExchangeListener listener, ActivationSpec activationSpec) {
1264            ComponentLifeCycle lifecCycle = new PojoLifecycleAdaptor(listener, activationSpec.getService(), activationSpec.getEndpoint());
1265            return new ComponentAdaptorMEListener(lifecCycle, listener);
1266        }
1267    
1268        /**
1269         * Factory method to create a new component ID if none is specified
1270         *
1271         * @return uniqueId
1272         */
1273        protected String createComponentID() {
1274            return idGenerator.generateId();
1275        }
1276    
1277        protected void checkInitialized() throws JBIException {
1278            if (!containerInitialized.get()) {
1279                throw new JBIException("The Container is not initialized - please call init(...)");
1280            }
1281        }
1282    
1283        /**
1284         * Retrieve the value for automatic transaction enlistment.
1285         * @return 
1286         */
1287        public boolean isAutoEnlistInTransaction() {
1288            return autoEnlistInTransaction;
1289        }
1290    
1291        /**
1292         * Set the new value for automatic transaction enlistment.
1293         * When this parameter is set to <code>true</code> and a transaction
1294         * is running when sending / receiving an exchange, this operation will
1295         * automatically be done in the current transaction.
1296         * 
1297         * @param autoEnlistInTransaction
1298         */
1299        public void setAutoEnlistInTransaction(boolean autoEnlistInTransaction) {
1300            this.autoEnlistInTransaction = autoEnlistInTransaction;
1301        }
1302    
1303        public boolean isPersistent() {
1304            return persistent;
1305        }
1306    
1307        /**
1308         * Set the new default value for exchange persistence.
1309         * This value will be the default if none is configured on
1310         * the activation spec of the component or on the message.
1311         * 
1312         * @param persistent
1313         */
1314        public void setPersistent(boolean persistent) {
1315            this.persistent = persistent;
1316        }
1317    
1318        public void addListener(EventListener listener) {
1319            LOG.debug("Adding listener: " + listener.getClass());
1320            if (listener instanceof ContainerAware) {
1321                ContainerAware containerAware = (ContainerAware) listener;
1322                containerAware.setContainer(this);
1323            }
1324            if (listener instanceof ExchangeListener) {
1325                listeners.add(ExchangeListener.class, (ExchangeListener) listener);
1326            }
1327            if (listener instanceof ComponentListener) {
1328                listeners.add(ComponentListener.class, (ComponentListener) listener);
1329            }
1330            if (listener instanceof ServiceAssemblyListener) {
1331                listeners.add(ServiceAssemblyListener.class, (ServiceAssemblyListener) listener);
1332            }
1333            if (listener instanceof ServiceUnitListener) {
1334                listeners.add(ServiceUnitListener.class, (ServiceUnitListener) listener);
1335            }
1336            if (listener instanceof EndpointListener) {
1337                listeners.add(EndpointListener.class, (EndpointListener) listener);
1338            }
1339            if (listener instanceof DeploymentListener) {
1340                listeners.add(DeploymentListener.class, (DeploymentListener) listener);
1341            }
1342        }
1343    
1344        public void removeListener(EventListener listener) {
1345            LOG.debug("Removing listener: " + listener.getClass());
1346            if (listener instanceof ExchangeListener) {
1347                listeners.remove(ExchangeListener.class, (ExchangeListener) listener);
1348            }
1349            if (listener instanceof ComponentListener) {
1350                listeners.remove(ComponentListener.class, (ComponentListener) listener);
1351            }
1352            if (listener instanceof ServiceAssemblyListener) {
1353                listeners.remove(ServiceAssemblyListener.class, (ServiceAssemblyListener) listener);
1354            }
1355            if (listener instanceof ServiceUnitListener) {
1356                listeners.remove(ServiceUnitListener.class, (ServiceUnitListener) listener);
1357            }
1358            if (listener instanceof EndpointListener) {
1359                listeners.remove(EndpointListener.class, (EndpointListener) listener);
1360            }
1361            if (listener instanceof DeploymentListener) {
1362                listeners.remove(DeploymentListener.class, (DeploymentListener) listener);
1363            }
1364        }
1365    
1366        public Object[] getListeners(Class lc) {
1367            return listeners.getListeners(lc);
1368        }
1369    
1370        public void setListeners(EventListener[] listeners) {
1371            configuredListeners = listeners;
1372        }
1373    
1374        public void resendExchange(MessageExchange exchange) throws JBIException {
1375            if (!(exchange instanceof MessageExchangeImpl)) {
1376                throw new IllegalArgumentException("exchange should be a MessageExchangeImpl");
1377            }
1378            MessageExchangeImpl me = (MessageExchangeImpl) exchange;
1379            me.getPacket().setExchangeId(new IdGenerator().generateId());
1380            me.getPacket().setOut(null);
1381            me.getPacket().setFault(null);
1382            me.getPacket().setError(null);
1383            me.getPacket().setStatus(ExchangeStatus.ACTIVE);
1384            me.getPacket().setProperty(JbiConstants.DATESTAMP_PROPERTY_NAME, Calendar.getInstance());
1385            ExchangeListener[] l = (ExchangeListener[]) listeners.getListeners(ExchangeListener.class);
1386            ExchangeEvent event = new ExchangeEvent(me, ExchangeEvent.EXCHANGE_SENT);
1387            for (int i = 0; i < l.length; i++) {
1388                try {
1389                    l[i].exchangeSent(event);
1390                } catch (Exception e) {
1391                    LOG.warn("Error calling listener: " + e.getMessage(), e);
1392                }
1393            }
1394            me.handleSend(false);
1395            sendExchange(me.getMirror());
1396        }
1397    
1398        public boolean isEmbedded() {
1399            return embedded;
1400        }
1401    
1402        public void setEmbedded(boolean embedded) {
1403            this.embedded = embedded;
1404        }
1405    
1406        public void setRmiPort(int portNum) {
1407            getManagementContext().setNamingPort(portNum);
1408        }
1409    
1410        public int getRmiPort() {
1411            return getManagementContext().getNamingPort();
1412        }
1413    
1414        /**
1415         * @return Returns the notifyStatistics.
1416         */
1417        public boolean isNotifyStatistics() {
1418            return notifyStatistics;
1419        }
1420    
1421        /**
1422         * @param notifyStatistics The notifyStatistics to set.
1423         */
1424        public void setNotifyStatistics(boolean notifyStatistics) {
1425            this.notifyStatistics = notifyStatistics;
1426        }
1427    
1428        /**
1429         * @return the executorFactory
1430         */
1431        public ExecutorFactory getExecutorFactory() {
1432            return executorFactory;
1433        }
1434    
1435        /**
1436         * @param executorFactory the executorFactory to set
1437         */
1438        public void setExecutorFactory(ExecutorFactory executorFactory) {
1439            this.executorFactory = executorFactory;
1440        }
1441    
1442    
1443        /**
1444         * Creates a new rootDir
1445         */
1446        protected String createRootDir() {
1447            String prefix = getGeneratedRootDirPrefix();
1448            for (int i = 1; true; i++) {
1449                File file = new File(prefix + i);
1450                if (!file.exists()) {
1451                    file.mkdirs();
1452                    return file.getAbsolutePath();
1453                }
1454            }
1455        }
1456    }