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