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.Iterator;
022 import java.util.Map;
023 import java.util.Properties;
024 import java.util.concurrent.ConcurrentHashMap;
025
026 import javax.jbi.JBIException;
027 import javax.jbi.management.DeploymentException;
028 import javax.jbi.management.InstallationServiceMBean;
029 import javax.jbi.management.InstallerMBean;
030 import javax.management.Attribute;
031 import javax.management.JMException;
032 import javax.management.MBeanOperationInfo;
033 import javax.management.MBeanServer;
034 import javax.management.ObjectName;
035
036 import org.apache.commons.logging.Log;
037 import org.apache.commons.logging.LogFactory;
038 import org.apache.servicemix.jbi.container.ComponentEnvironment;
039 import org.apache.servicemix.jbi.container.EnvironmentContext;
040 import org.apache.servicemix.jbi.container.JBIContainer;
041 import org.apache.servicemix.jbi.deployment.Component;
042 import org.apache.servicemix.jbi.deployment.Descriptor;
043 import org.apache.servicemix.jbi.deployment.DescriptorFactory;
044 import org.apache.servicemix.jbi.deployment.SharedLibrary;
045 import org.apache.servicemix.jbi.management.BaseSystemService;
046 import org.apache.servicemix.jbi.management.ManagementContext;
047 import org.apache.servicemix.jbi.management.OperationInfoHelper;
048 import org.apache.servicemix.jbi.management.ParameterHelper;
049 import org.apache.servicemix.jbi.util.FileUtil;
050 import org.apache.servicemix.jbi.util.FileVersionUtil;
051
052 /**
053 * Installation Service - installs/uninstalls archives
054 *
055 * @version $Revision: 564900 $
056 */
057 public class InstallationService extends BaseSystemService implements InstallationServiceMBean {
058
059 private static final Log LOG = LogFactory.getLog(InstallationService.class);
060
061 private EnvironmentContext environmentContext;
062
063 private ManagementContext managementContext;
064
065 private Map<String, InstallerMBeanImpl> installers = new ConcurrentHashMap<String, InstallerMBeanImpl>();
066
067 private Map<String, InstallerMBeanImpl> nonLoadedInstallers = new ConcurrentHashMap<String, InstallerMBeanImpl>();
068
069 /**
070 * Get Description
071 *
072 * @return description of this item
073 */
074 public String getDescription() {
075 return "installs/uninstalls Components";
076 }
077
078 /**
079 * Load the installer for a new component from a component installation
080 * package.
081 *
082 * @param installJarURL -
083 * URL locating a jar file containing a JBI Installable
084 * Component.
085 * @return - the JMX ObjectName of the InstallerMBean loaded from
086 * installJarURL.
087 */
088 public synchronized ObjectName loadNewInstaller(String installJarURL) {
089 try {
090 ObjectName result = null;
091 if (LOG.isDebugEnabled()) {
092 LOG.debug("Loading new installer from " + installJarURL);
093 }
094 File tmpDir = AutoDeploymentService.unpackLocation(environmentContext.getTmpDir(), installJarURL);
095 if (tmpDir != null) {
096 Descriptor root = DescriptorFactory.buildDescriptor(tmpDir);
097 if (root != null && root.getComponent() != null) {
098 String componentName = root.getComponent().getIdentification().getName();
099 if (!installers.containsKey(componentName)) {
100 InstallerMBeanImpl installer = doInstallArchive(tmpDir, root);
101 if (installer != null) {
102 result = installer.getObjectName();
103 installers.put(componentName, installer);
104 }
105 } else {
106 throw new RuntimeException("An installer already exists for " + componentName);
107 }
108 } else {
109 throw new RuntimeException("Could not find Component from: " + installJarURL);
110 }
111 } else {
112 throw new RuntimeException("location: " + installJarURL + " isn't valid");
113 }
114 return result;
115 } catch (Throwable t) {
116 LOG.error("Deployment failed", t);
117 if (t instanceof Error) {
118 throw (Error) t;
119 }
120 if (t instanceof RuntimeException) {
121 throw (RuntimeException) t;
122 } else {
123 throw new RuntimeException("Deployment failed: " + t.getMessage());
124 }
125 }
126 }
127
128 /**
129 * Load the InstallerMBean for a previously installed component.
130 *
131 * @param aComponentName -
132 * the component name identifying the installer to load.
133 * @return - the JMX ObjectName of the InstallerMBean loaded from an
134 * existing installation context.
135 */
136 public ObjectName loadInstaller(String aComponentName) {
137 InstallerMBeanImpl installer = installers.get(aComponentName);
138 if (installer == null) {
139 installer = nonLoadedInstallers.get(aComponentName);
140 if (installer != null) {
141 try {
142 // create an MBean for the installer
143 ObjectName objectName = managementContext.createCustomComponentMBeanName("Installer", aComponentName);
144 installer.setObjectName(objectName);
145 managementContext.registerMBean(objectName, installer, InstallerMBean.class,
146 "standard installation controls for a Component");
147 } catch (Exception e) {
148 throw new RuntimeException("Could not load installer", e);
149 }
150 return installer.getObjectName();
151 }
152 }
153 return null;
154 }
155
156 private InstallerMBeanImpl createInstaller(String componentName) throws IOException, DeploymentException {
157 File installationDir = environmentContext.getComponentInstallationDir(componentName);
158 Descriptor root = DescriptorFactory.buildDescriptor(installationDir);
159 Component descriptor = root.getComponent();
160
161 InstallationContextImpl installationContext = new InstallationContextImpl(descriptor);
162 installationContext.setInstall(false);
163 installationContext.setInstallRoot(installationDir);
164 // now build the ComponentContext
165 File componentRoot = environmentContext.getComponentRootDir(componentName);
166 ComponentContextImpl context = buildComponentContext(componentRoot, installationDir, componentName);
167 installationContext.setContext(context);
168 return new InstallerMBeanImpl(container, installationContext);
169 }
170
171 /**
172 * Unload a JBI Installable Component installer.
173 *
174 * @param componentName -
175 * the component name identifying the installer to unload.
176 * @param isToBeDeleted -
177 * true if the component is to be deleted as well.
178 * @return - true if the operation was successful, otherwise false.
179 */
180 public boolean unloadInstaller(String componentName, boolean isToBeDeleted) {
181 boolean result = false;
182 try {
183 container.getBroker().suspend();
184 InstallerMBeanImpl installer = installers.remove(componentName);
185 result = installer != null;
186 if (result) {
187 container.getManagementContext().unregisterMBean(installer);
188 if (isToBeDeleted) {
189 installer.uninstall();
190 } else {
191 nonLoadedInstallers.put(componentName, installer);
192 }
193 }
194 } catch (JBIException e) {
195 String errStr = "Problem shutting down Component: " + componentName;
196 LOG.error(errStr, e);
197 } finally {
198 container.getBroker().resume();
199 }
200 return result;
201 }
202
203 /**
204 * Install a shared library jar.
205 *
206 * @param aSharedLibURI -
207 * URI locating a jar file containing a shared library.
208 * @return - the name of the shared library loaded from aSharedLibURI.
209 */
210 public String installSharedLibrary(String aSharedLibURI) {
211 String result = "";
212 try {
213 File tmpDir = AutoDeploymentService.unpackLocation(environmentContext.getTmpDir(), aSharedLibURI);
214 if (tmpDir != null) {
215 Descriptor root = DescriptorFactory.buildDescriptor(tmpDir);
216 if (root == null) {
217 throw new DeploymentException("Could not find JBI descriptor");
218 }
219 SharedLibrary sl = root.getSharedLibrary();
220 if (sl != null) {
221 result = doInstallSharedLibrary(tmpDir, sl);
222 } else {
223 throw new DeploymentException("JBI descriptor is not a SharedLibrary descriptor");
224 }
225 } else {
226 throw new DeploymentException("Could not find JBI descriptor");
227 }
228 } catch (DeploymentException e) {
229 LOG.error("Deployment failed", e);
230 }
231 return result;
232 }
233
234 /**
235 * Uninstall a shared library.
236 *
237 * @param aSharedLibName -
238 * the name of the shared library to uninstall.
239 * @return - true iff the uninstall was successful.
240 */
241 public boolean uninstallSharedLibrary(String aSharedLibName) {
242 // TODO: should check existence of shared library
243 // and that it is not currently in use
244 container.getRegistry().unregisterSharedLibrary(aSharedLibName);
245 environmentContext.removeSharedLibraryDirectory(aSharedLibName);
246 return true;
247 }
248
249 /**
250 * Initialize the Service
251 *
252 * @param container
253 * @throws JBIException
254 * @throws DeploymentException
255 */
256 public void init(JBIContainer container) throws JBIException {
257 super.init(container);
258 this.environmentContext = container.getEnvironmentContext();
259 this.managementContext = container.getManagementContext();
260 buildState();
261 }
262
263 protected Class getServiceMBean() {
264 return InstallationServiceMBean.class;
265 }
266
267 /**
268 * Install an archive
269 *
270 * @param location
271 * @param props
272 * @param autoStart
273 * @throws DeploymentException
274 */
275 public void install(String location, Properties props, boolean autoStart) throws DeploymentException {
276 File tmpDir = AutoDeploymentService.unpackLocation(environmentContext.getTmpDir(), location);
277 if (tmpDir != null) {
278 Descriptor root = DescriptorFactory.buildDescriptor(tmpDir);
279 if (root != null) {
280 if (root.getComponent() == null) {
281 throw new DeploymentException("JBI descriptor is not a component descriptor");
282 }
283 install(tmpDir, props, root, autoStart);
284 } else {
285 throw new DeploymentException("Could not find JBI descriptor");
286 }
287 } else {
288 throw new DeploymentException("Could not find JBI descriptor");
289 }
290 }
291
292 /**
293 * Install an archive
294 *
295 * @param tmpDir
296 * @param root
297 * @param autoStart
298 * @throws DeploymentException
299 */
300 protected void install(File tmpDir, Properties props, Descriptor root, boolean autoStart) throws DeploymentException {
301 if (root.getComponent() != null) {
302 String componentName = root.getComponent().getIdentification().getName();
303 if (installers.containsKey(componentName)) {
304 throw new DeploymentException("Component " + componentName + " is already installed");
305 }
306 InstallerMBeanImpl installer = doInstallArchive(tmpDir, root);
307 if (installer != null) {
308 try {
309 if (props != null && props.size() > 0) {
310 ObjectName on = installer.getInstallerConfigurationMBean();
311 if (on == null) {
312 LOG.warn("Could not find installation configuration MBean. Installation properties will be ignored.");
313 } else {
314 MBeanServer mbs = managementContext.getMBeanServer();
315 for (Iterator it = props.keySet().iterator(); it.hasNext();) {
316 String key = (String) it.next();
317 String val = props.getProperty(key);
318 try {
319 mbs.setAttribute(on, new Attribute(key, val));
320 } catch (JMException e) {
321 throw new DeploymentException("Could not set installation property: (" + key + " = " + val, e);
322 }
323 }
324 }
325 }
326 installer.install();
327 } catch (JBIException e) {
328 throw new DeploymentException(e);
329 }
330 if (autoStart) {
331 try {
332 ComponentMBeanImpl lcc = container.getComponent(componentName);
333 if (lcc != null) {
334 lcc.start();
335 } else {
336 LOG.warn("No ComponentConnector found for Component " + componentName);
337 }
338 } catch (JBIException e) {
339 String errStr = "Failed to start Component: " + componentName;
340 LOG.error(errStr, e);
341 throw new DeploymentException(e);
342 }
343 }
344 installers.put(componentName, installer);
345 }
346 }
347 }
348
349 /**
350 * Get an array of MBeanOperationInfo
351 *
352 * @return array of OperationInfos
353 * @throws JMException
354 */
355 public MBeanOperationInfo[] getOperationInfos() throws JMException {
356 OperationInfoHelper helper = new OperationInfoHelper();
357 ParameterHelper ph = helper.addOperation(getObjectToManage(), "loadNewInstaller", 1, "load a new Installer ");
358 ph.setDescription(0, "installJarURL", "URL locating the install Jar");
359 ph = helper.addOperation(getObjectToManage(), "loadInstaller", 1, "load installer for a previously installed component");
360 ph.setDescription(0, "componentName", "Name of the Component");
361 ph = helper.addOperation(getObjectToManage(), "unloadInstaller", 2, "unload an installer");
362 ph.setDescription(0, "componentName", "Name of the Component");
363 ph.setDescription(1, "isToBeDeleted", "true if component is to be deleted");
364 ph = helper.addOperation(getObjectToManage(), "installSharedLibrary", 1, "Install a shared library jar");
365 ph.setDescription(0, "sharedLibURI", "URI for the jar to be installed");
366 ph = helper.addOperation(getObjectToManage(), "uninstallSharedLibrary", 1, "Uninstall a shared library jar");
367 ph.setDescription(0, "sharedLibName", "name of the shared library");
368 ph = helper.addOperation(getObjectToManage(), "install", 1, "install and deplot an archive");
369 ph.setDescription(0, "location", "location of archive");
370 ph = helper.addOperation(getObjectToManage(), "install", 2, "install and deplot an archive");
371 ph.setDescription(0, "location", "location of archive");
372 ph.setDescription(1, "autostart", "automatically start the Component");
373 return OperationInfoHelper.join(super.getOperationInfos(), helper.getOperationInfos());
374 }
375
376 protected InstallerMBeanImpl doInstallArchive(File tmpDirectory, Descriptor descriptor) throws DeploymentException {
377 InstallerMBeanImpl installer = null;
378 Component component = descriptor.getComponent();
379 if (component != null) {
380 installer = doInstallComponent(tmpDirectory, component);
381 }
382 return installer;
383 }
384
385 protected String doInstallSharedLibrary(File tmpDirectory, SharedLibrary descriptor) throws DeploymentException {
386 String result = null;
387 if (descriptor != null) {
388 File installationDir = null;
389 try {
390 result = descriptor.getIdentification().getName();
391 File rootDir = environmentContext.createSharedLibraryDirectory(result);
392 installationDir = FileVersionUtil.getNewVersionDirectory(rootDir);
393 if (!tmpDirectory.renameTo(installationDir)) {
394 throw new DeploymentException("Unable to rename " + tmpDirectory + " to " + installationDir);
395 }
396 if (LOG.isDebugEnabled()) {
397 LOG.debug("Moved " + tmpDirectory + " to " + installationDir);
398 }
399 container.getRegistry().registerSharedLibrary(descriptor, installationDir);
400 } catch (Exception e) {
401 LOG.error("Deployment of Shared Library failed", e);
402 // remove any files created for installation
403 FileUtil.deleteFile(installationDir);
404 throw new DeploymentException(e);
405 } finally {
406 FileUtil.deleteFile(tmpDirectory);
407 }
408 }
409 return result;
410 }
411
412 protected InstallerMBeanImpl doInstallComponent(File tmpDirectory, Component descriptor) throws DeploymentException {
413 // move archive to Component directory
414 InstallerMBeanImpl result = null;
415 String name = descriptor.getIdentification().getName();
416 try {
417 File oldInstallationDir = environmentContext.getComponentInstallationDir(name);
418 // try and delete the old version ? - maybe should leave around ??
419 if (!FileUtil.deleteFile(oldInstallationDir)) {
420 LOG.warn("Failed to delete old installation directory: " + oldInstallationDir.getPath());
421 }
422 File componentRoot = environmentContext.createComponentRootDir(name);
423 // this will get the new one
424 File installationDir = environmentContext.getNewComponentInstallationDir(name);
425 tmpDirectory.renameTo(installationDir);
426 if (LOG.isDebugEnabled()) {
427 LOG.debug("Moved " + tmpDirectory + " to " + installationDir);
428 }
429 result = initializeInstaller(installationDir, componentRoot, descriptor);
430 return result;
431 } catch (IOException e) {
432 throw new DeploymentException(e);
433 }
434 }
435
436 private InstallerMBeanImpl initializeInstaller(File installationDir, File componentRoot,
437 Component descriptor) throws DeploymentException {
438 InstallerMBeanImpl result = null;
439 try {
440 String name = descriptor.getIdentification().getName();
441 InstallationContextImpl installationContext = new InstallationContextImpl(descriptor);
442 installationContext.setInstall(true);
443 installationContext.setInstallRoot(installationDir);
444 // now build the ComponentContext
445 ComponentContextImpl context = buildComponentContext(componentRoot, installationDir, name);
446 installationContext.setContext(context);
447 result = new InstallerMBeanImpl(container, installationContext);
448 // create an MBean for the installer
449 ObjectName objectName = managementContext.createCustomComponentMBeanName("Installer", name);
450 result.setObjectName(objectName);
451 managementContext.registerMBean(objectName, result, InstallerMBean.class, "standard installation controls for a Component");
452 } catch (Throwable e) {
453 LOG.error("Deployment of Component failed", e);
454 // remove any files created for installation
455 environmentContext.removeComponentRootDirectory(descriptor.getIdentification().getName());
456 throw new DeploymentException(e);
457 }
458 return result;
459 }
460
461 protected void buildState() {
462 buildSharedLibs();
463 buildComponents();
464 }
465
466 /**
467 * returns true if a shared library is already installed
468 *
469 * @param name
470 * @return true/false
471 */
472 protected boolean containsSharedLibrary(String name) {
473 return container.getRegistry().getSharedLibrary(name) != null;
474 }
475
476 protected void buildSharedLibs() {
477 // walk through shared libaries and add then to the ClassLoaderService
478 File top = environmentContext.getSharedLibDir();
479 if (top != null && top.exists() && top.isDirectory()) {
480 // directory structure is sharedlibraries/<lib name>/version_x/stuff
481 // ...
482 File[] files = top.listFiles();
483 if (files != null) {
484 for (int i = 0; i < files.length; i++) {
485 if (!files[i].isDirectory()) {
486 continue;
487 }
488 File dir = FileVersionUtil.getLatestVersionDirectory(files[i]);
489 if (dir == null) {
490 continue;
491 }
492 Descriptor root = DescriptorFactory.buildDescriptor(dir);
493 if (root == null) {
494 continue;
495 }
496 SharedLibrary sl = root.getSharedLibrary();
497 if (sl == null) {
498 continue;
499 }
500 try {
501 container.getRegistry().registerSharedLibrary(sl, dir);
502 } catch (Exception e) {
503 LOG.error("Failed to initialize sharted library", e);
504 }
505 }
506 }
507 }
508 }
509
510 protected void buildComponents() {
511 // walk through components and add then to the ClassLoaderService
512 File top = environmentContext.getComponentsDir();
513 if (top != null && top.exists() && top.isDirectory()) {
514 // directory structure is components/<component name>/installation
515 // ...
516 File[] files = top.listFiles();
517 if (files != null) {
518 for (int i = 0; i < files.length; i++) {
519 if (!files[i].isDirectory()) {
520 continue;
521 }
522 final File directory = files[i];
523 try {
524 buildComponent(directory);
525 } catch (DeploymentException e) {
526 LOG.error("Could not build Component: " + directory.getName(), e);
527 LOG.warn("Deleting Component directory: " + directory);
528 FileUtil.deleteFile(directory);
529 }
530 }
531 }
532 }
533 }
534
535 protected void buildComponent(File componentDirectory) throws DeploymentException {
536 try {
537 String componentName = componentDirectory.getName();
538 ComponentEnvironment env = container.getEnvironmentContext().getComponentEnvironment(componentName);
539 if (!env.getStateFile().exists()) {
540 // An installer has been created but the component has not been
541 // installed
542 // So remove it
543 FileUtil.deleteFile(componentDirectory);
544 } else {
545 InstallerMBeanImpl installer = createInstaller(componentName);
546 installer.activateComponent();
547 nonLoadedInstallers.put(componentName, installer);
548 }
549 } catch (Throwable e) {
550 LOG.error("Failed to deploy component: " + componentDirectory.getName(), e);
551 throw new DeploymentException(e);
552 }
553 }
554
555 protected ComponentContextImpl buildComponentContext(File componentRoot, File installRoot, String name) throws IOException {
556 ComponentNameSpace cns = new ComponentNameSpace(container.getName(), name);
557 ComponentContextImpl context = new ComponentContextImpl(container, cns);
558 ComponentEnvironment env = new ComponentEnvironment();
559 FileUtil.buildDirectory(componentRoot);
560 File privateWorkspace = environmentContext.createWorkspaceDirectory(name);
561 env.setWorkspaceRoot(privateWorkspace);
562 env.setComponentRoot(componentRoot);
563 env.setInstallRoot(installRoot);
564 context.setEnvironment(env);
565 return context;
566 }
567
568 }