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.net.MalformedURLException;
021    import java.net.URL;
022    import java.util.ArrayList;
023    import java.util.List;
024    
025    import javax.jbi.JBIException;
026    import javax.jbi.component.Bootstrap;
027    import javax.jbi.component.Component;
028    import javax.jbi.management.DeploymentException;
029    import javax.jbi.management.InstallerMBean;
030    import javax.management.InstanceNotFoundException;
031    import javax.management.MBeanRegistrationException;
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.JBIContainer;
037    import org.apache.xbean.classloader.DestroyableClassLoader;
038    import org.apache.xbean.classloader.JarFileClassLoader;
039    
040    /**
041     * InstallerMBean defines standard installation and uninstallation controls for Binding Components and Service Engines.
042     * Binding Components and Service Engines.
043     * 
044     * @version $Revision: 564900 $
045     */
046    public class InstallerMBeanImpl implements InstallerMBean {
047        
048        private static final Log LOG = LogFactory.getLog(InstallerMBeanImpl.class);
049        
050        private InstallationContextImpl context;
051        private JBIContainer container;
052        private ObjectName objectName;
053        private ObjectName extensionMBeanName;
054        private Bootstrap bootstrap;
055        private boolean initialized;
056    
057        /**
058         * Constructor for the InstallerMBean
059         * 
060         * @param container
061         * @param ic
062         * @throws DeploymentException
063         */
064        public InstallerMBeanImpl(JBIContainer container, 
065                                  InstallationContextImpl ic) throws DeploymentException {
066            this.container = container;
067            this.context = ic;
068            bootstrap = createBootstrap();
069            initBootstrap();
070        }
071        
072        private void initBootstrap() throws DeploymentException {
073            try {
074                if (!initialized) {
075                    // Unregister a previously registered extension mbean,
076                    // in case the bootstrap has not done it
077                    try {
078                        if (extensionMBeanName != null
079                                && container.getMBeanServer() != null
080                                && container.getMBeanServer().isRegistered(extensionMBeanName)) {
081                            container.getMBeanServer().unregisterMBean(extensionMBeanName);
082                        }
083                    } catch (InstanceNotFoundException e) {
084                        // ignore
085                    } catch (MBeanRegistrationException e) {
086                        // ignore
087                    }
088                    // Init bootstrap
089                    bootstrap.init(this.context);
090                    extensionMBeanName = bootstrap.getExtensionMBeanName();
091                    initialized = true;
092                }
093            } catch (JBIException e) {
094                LOG.error("Could not initialize bootstrap", e);
095                throw new DeploymentException(e);
096            } 
097        }
098        
099        protected void cleanUpBootstrap() throws DeploymentException {
100            try {
101                bootstrap.cleanUp();
102            } catch (JBIException e) {
103                LOG.error("Could not initialize bootstrap", e);
104                throw new DeploymentException(e);
105            } finally {
106                initialized = false;
107            }
108        }
109        
110        private Bootstrap createBootstrap() throws DeploymentException {
111            ClassLoader oldCl = Thread.currentThread().getContextClassLoader();
112            org.apache.servicemix.jbi.deployment.Component descriptor = context.getDescriptor();
113            try {
114                ClassLoader cl = buildClassLoader(
115                                        context.getInstallRootAsDir(),
116                                        descriptor.getBootstrapClassPath().getPathElements(),
117                                        descriptor.isBootstrapClassLoaderDelegationParentFirst(),
118                                        null);
119                Thread.currentThread().setContextClassLoader(cl);
120                Class bootstrapClass = cl.loadClass(descriptor.getBootstrapClassName());
121                return (Bootstrap) bootstrapClass.newInstance();
122            } catch (MalformedURLException e) {
123                LOG.error("Could not create class loader", e);
124                throw new DeploymentException(e);
125            } catch (ClassNotFoundException e) {
126                LOG.error("Class not found: " + descriptor.getBootstrapClassName(), e);
127                throw new DeploymentException(e);
128            } catch (InstantiationException e) {
129                LOG.error("Could not instantiate : " + descriptor.getBootstrapClassName(), e);
130                throw new DeploymentException(e);
131            } catch (IllegalAccessException e) {
132                LOG.error("Illegal access on: " + descriptor.getBootstrapClassName(), e);
133                throw new DeploymentException(e);
134            } finally {
135                Thread.currentThread().setContextClassLoader(oldCl);
136            }
137        }
138    
139        /**
140         * Get the installation root directory path for this BC or SE.
141         * 
142         * @return the full installation path of this component.
143         */
144        public String getInstallRoot() {
145            return context.getInstallRoot();
146        }
147    
148        /**
149         * Install a BC or SE.
150         * 
151         * @return JMX ObjectName representing the ComponentLifeCycle for the installed component, or null if the
152         * installation did not complete.
153         * @throws javax.jbi.JBIException if the installation fails.
154         */
155        public ObjectName install() throws JBIException {
156            if (isInstalled()) {
157                throw new DeploymentException("Component is already installed");
158            }
159            initBootstrap();
160            bootstrap.onInstall();
161            // TODO: the bootstrap may change the class path for the component,
162            // so we need to persist it somehow
163            ObjectName result = null;
164            try {
165                result = activateComponent();
166                ComponentMBeanImpl lcc = container.getComponent(context.getComponentName());
167                lcc.persistRunningState();
168                context.setInstall(false);
169            } finally {
170                cleanUpBootstrap();
171            }
172            return result;
173        }
174        
175        public ObjectName activateComponent() throws JBIException {
176            ObjectName result = null;
177            ClassLoader oldCl = Thread.currentThread().getContextClassLoader();
178            org.apache.servicemix.jbi.deployment.Component descriptor = context.getDescriptor();
179            try {
180                ClassLoader cl = buildClassLoader(
181                                        context.getInstallRootAsDir(),
182                                        (String[]) context.getClassPathElements().toArray(new String[0]),
183                                        descriptor.isComponentClassLoaderDelegationParentFirst(),
184                                        context.getSharedLibraries());
185                Thread.currentThread().setContextClassLoader(cl);
186                Class componentClass = cl.loadClass(descriptor.getComponentClassName());
187                Component component = (Component) componentClass.newInstance();
188                result = container.activateComponent(
189                                        context.getInstallRootAsDir(), 
190                                        component, 
191                                        context.getComponentDescription(),
192                                        (ComponentContextImpl) context.getContext(), 
193                                        context.isBinding(), 
194                                        context.isEngine(), 
195                                        context.getSharedLibraries());
196            } catch (MalformedURLException e) {
197                LOG.error("Could not create class loader", e);
198                throw new DeploymentException(e);
199            } catch (NoClassDefFoundError e) {
200                LOG.error("Class not found: " + descriptor.getBootstrapClassName(), e);
201                throw new DeploymentException(e);
202            } catch (ClassNotFoundException e) {
203                LOG.error("Class not found: " + descriptor.getBootstrapClassName(), e);
204                throw new DeploymentException(e);
205            } catch (InstantiationException e) {
206                LOG.error("Could not instantiate : " + descriptor.getBootstrapClassName(), e);
207                throw new DeploymentException(e);
208            } catch (IllegalAccessException e) {
209                LOG.error("Illegal access on: " + descriptor.getBootstrapClassName(), e);
210                throw new DeploymentException(e);
211            } catch (JBIException e) {
212                LOG.error("Could not initialize : " + descriptor.getBootstrapClassName(), e);
213                throw new DeploymentException(e);
214            }  finally {
215                Thread.currentThread().setContextClassLoader(oldCl);
216            }
217            return result;
218        }
219    
220        /**
221         * Determine whether or not the component is installed.
222         * 
223         * @return true if this component is currently installed, false if not.
224         */
225        public boolean isInstalled() {
226            return !context.isInstall();
227        }
228    
229        /**
230         * Uninstall a BC or SE. This completely removes the component from the JBI system.
231         * 
232         * @throws javax.jbi.JBIException if the uninstallation fails.
233         */
234        public void uninstall() throws javax.jbi.JBIException {
235            // TODO: check component status
236            // the component must not be started and not have any SUs deployed
237            if (!isInstalled()) {
238                throw new DeploymentException("Component is not installed");
239            }
240            String componentName = context.getComponentName();
241            try {
242                container.deactivateComponent(componentName);
243                bootstrap.onUninstall();
244                context.setInstall(true);
245            } finally {
246                cleanUpBootstrap();
247                
248                // If it was found by a destroyable classloader destroy it
249                // XXX Should we be holding the classloader as a member as always destroying it?
250                if (bootstrap.getClass().getClassLoader() instanceof DestroyableClassLoader) {
251                    ((DestroyableClassLoader) bootstrap.getClass().getClassLoader()).destroy();
252                }
253                System.gc();
254                container.getEnvironmentContext().removeComponentRootDirectory(componentName);
255            }
256        }
257    
258        /**
259         * Get the installer configuration MBean name for this component.
260         * 
261         * @return the MBean object name of the Installer Configuration MBean.
262         * @throws javax.jbi.JBIException if the component is not in the LOADED state or any error occurs during processing.
263         */
264        public ObjectName getInstallerConfigurationMBean() throws javax.jbi.JBIException {
265            return extensionMBeanName;
266        }
267        /**
268         * @return Returns the objectName.
269         */
270        public ObjectName getObjectName() {
271            return objectName;
272        }
273        /**
274         * @param objectName The objectName to set.
275         */
276        public void setObjectName(ObjectName objectName) {
277            this.objectName = objectName;
278        }
279    
280        /**
281         * Buld a Custom ClassLoader
282         * 
283         * @param dir
284         * @param classPathNames
285         * @param parentFirst
286         * @param list
287         * @return ClassLoader
288         * @throws MalformedURLException
289         * @throws MalformedURLException
290         * @throws DeploymentException
291         */
292        private ClassLoader buildClassLoader(
293                        File dir,
294                        String[] classPathNames, 
295                        boolean parentFirst,
296                        String[] list) throws MalformedURLException, DeploymentException {
297            
298            // Make the current ClassLoader the parent
299            ClassLoader[] parents;
300            
301            // Create a new parent if there are some shared libraries
302            if (list != null && list.length > 0) {
303                parents = new ClassLoader[list.length];
304                for (int i = 0; i < parents.length; i++) {
305                    org.apache.servicemix.jbi.framework.SharedLibrary sl = 
306                        container.getRegistry().getSharedLibrary(list[i]);
307                    if (sl == null) {
308                        throw new DeploymentException("Shared library " + list[i] + " is not installed");
309                    }
310                    parents[i] = sl.getClassLoader();
311                }
312            } else {
313                parents = new ClassLoader[] {getClass().getClassLoader() };
314            }
315            
316            List urls = new ArrayList();
317            for (int i = 0; i < classPathNames.length; i++) {
318                File file = new File(dir, classPathNames[i]);
319                if (!file.exists()) {
320                    LOG.warn("Unable to add File " + file
321                            + " to class path as it doesn't exist: "
322                            + file.getAbsolutePath());
323                }
324                urls.add(file.toURL());
325            }
326    
327            ClassLoader cl = new JarFileClassLoader(
328                            "Component ClassLoader",
329                            (URL[]) urls.toArray(new URL[urls.size()]),
330                            parents, 
331                            !parentFirst,
332                            new String[0],
333                            new String[] {"java.", "javax." }); 
334            if (LOG.isDebugEnabled()) {
335                LOG.debug("Component class loader: " + cl);
336            }
337            return cl;
338        }
339    
340    }