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 }