Package com.github.dynamicextensionsalfresco.controlpanel

Source Code of com.github.dynamicextensionsalfresco.controlpanel.BundleHelper

package com.github.dynamicextensionsalfresco.controlpanel;

import aQute.bnd.osgi.Analyzer;
import com.github.dynamicextensionsalfresco.osgi.ManifestUtils;
import com.github.dynamicextensionsalfresco.osgi.RepositoryStoreService;
import com.springsource.util.osgi.manifest.BundleManifest;
import com.springsource.util.osgi.manifest.BundleManifestFactory;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.service.cmr.model.FileFolderService;
import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.namespace.QName;
import org.osgi.framework.*;
import org.osgi.service.packageadmin.PackageAdmin;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.extensions.surf.util.Content;
import org.springframework.extensions.webscripts.servlet.FormData.FormField;
import org.springframework.stereotype.Component;
import org.springframework.util.FileCopyUtils;
import org.springframework.util.StringUtils;

import javax.annotation.PostConstruct;
import java.io.*;
import java.util.*;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.jar.Attributes;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
* Helper for working with {@link Bundle}s.
*
* @author Laurens Fridael
*
*/
@Component
public class BundleHelper {
    private final static Logger logger = LoggerFactory.getLogger(BundleHelper.class);

  private static final String ALFRESCO_DYNAMIC_EXTENSION_HEADER = "Alfresco-Dynamic-Extension";

  /**
   * Tests if the given bundle contains a Dynamic Extension.
   * <p>
   * This implementation looks if the bundle header <code>Alfresco-Dynamic-Extension</code> equals the String "true".
   */
  public static boolean isDynamicExtension(final Bundle bundle) {
    return "true".equals(bundle.getHeaders().get(ALFRESCO_DYNAMIC_EXTENSION_HEADER));
  }

  /* Dependencies */

  @Autowired
  private BundleContext bundleContext;

  @Autowired
  private RepositoryStoreService repositoryStoreService;

  @Autowired
  private FileFolderService fileFolderService;

  @Autowired
  private ContentService contentService;

  @Autowired
  private NodeService nodeService;

  private final Queue<Bundle> bundlesToStart = new ConcurrentLinkedQueue<Bundle>();

  /* Main operations */

    @PostConstruct
    public void registerFrameworkListener() throws Exception {
        bundleContext.addFrameworkListener(new FrameworkListener() {
            @Override
            public void frameworkEvent(FrameworkEvent event) {
                // start any bundles that were recently updated after the PackageAdmin has refreshed (restarted) any dependencies
                Bundle bundle;
                while ((bundle = bundlesToStart.poll()) != null) {
                    try {
                        bundle.start();
                    } catch (BundleException e) {
                        logger.error("Failed to start updated bundle", e);
                    }
                }
            }
        });
    }

    /**
   * Obtains the {@link Bundle}s that comprise the core framework.
   */
  public List<Bundle> getFrameworkBundles() {
    final List<Bundle> bundles = new ArrayList<Bundle>();
    for (final Bundle bundle : bundleContext.getBundles()) {
      if (isDynamicExtension(bundle) == false) {
        bundles.add(bundle);
      }
    }
    return bundles;
  }

  /**
   * Obtains the {@link Bundle}s that comprise the core framework.
   */
  public List<Bundle> getExtensionBundles() {
    final List<Bundle> bundles = new ArrayList<Bundle>();
    for (final Bundle bundle : bundleContext.getBundles()) {
      if (isDynamicExtension(bundle)) {
        bundles.add(bundle);
      }
    }
    return bundles;
  }

  /**
   * Obtains the {@link Bundle} for the given id.
   *
   * @param id BundleId
   * @return The matching {@link Bundle} or null if no match could be found.
   */
  public Bundle getBundle(final long id) {
    return bundleContext.getBundle(id);
  }

  /**
   * Installs an uploaded file as a bundle in the repository.
   * <p>
   * This implementation first saves the upload to a temporary file. It then attempts to install the file as a bundle.
   * If this succeeds, it saves the bundle in the repository.
   *
   * @param file form field
   * @return installed Bundle
   * @throws IOException
   * @throws BundleException
   */
  public Bundle installBundleInRepository(final FormField file) throws IOException, BundleException {
    final File tempFile = saveToTempFile(file.getInputStream());
        return doInstallBundleInRepository(tempFile, file.getFilename());
  }

  /**
   * Installs a bundle using the given {@link Content} and filename.
   *
   * @param content uploaded content
   * @return installted Bundle
   * @throws IOException
   * @throws BundleException
   */
  public Bundle installBundleInRepository(final Content content) throws IOException, BundleException {
    final File tempFile = saveToTempFile(content.getInputStream());
    return doInstallBundleInRepository(tempFile, null);

  }

  public NodeRef uninstallAndDeleteBundle(final Bundle bundle) throws BundleException {
        NodeRef matchingNode = null;
    final Matcher matcher = Pattern.compile("/Company Home(/.+)+/(.+\\.jar)$").matcher(bundle.getLocation());
    if (matcher.matches()) {
      final String filename = matcher.group(2);
      final NodeRef bundleFolder = repositoryStoreService.getBundleFolder(false);
      if (bundleFolder != null) {
        final NodeRef file = fileFolderService.searchSimple(bundleFolder, filename);
        if (file != null) {
          final Map<QName, Serializable> properties = Collections.emptyMap();
          nodeService.addAspect(file, ContentModel.ASPECT_TEMPORARY, properties);
          nodeService.deleteNode(file);
                    matchingNode = file;
                    bundle.uninstall();
        }
      }
    }

        return matchingNode;
  }

  @SuppressWarnings({ "rawtypes", "unchecked" })
  public List<ServiceReference> getAllServices() {
    try {
      return (List) Arrays.asList(bundleContext.getAllServiceReferences(null, null));
    } catch (final InvalidSyntaxException e) {
      throw new RuntimeException(e);
    }
  }

  protected <T> T getService(final Class<T> service) {
    final ServiceReference<T> serviceReference = bundleContext.getServiceReference(service);
    if (serviceReference != null) {
      return bundleContext.getService(serviceReference);
    } else {
      return null;
    }
  }

  /* Utility operations */

  protected Bundle doInstallBundleInRepository(File tempFile, String fileName) throws
        BundleException, IOException {
    try {
      BundleIdentifier identifier = getBundleIdentifier(tempFile);
      if (identifier == null) {
                tempFile = wrapPlainJar(tempFile, fileName);
                if (tempFile != null) {
                    identifier = getBundleIdentifier(tempFile);
                }
                if (identifier == null) {
                    throw new BundleException(
                        "Could not generate Bundle filename. Make sure the content is an OSGi bundle.");
                }
                logger.info("Wrapped plain jar as a OSGi bundle: {}.", identifier.getSymbolicName());
      }
            final String filename = identifier.toJarFilename();
            final String location = generateRepositoryLocation(filename);
      Bundle bundle = bundleContext.getBundle(location);
            boolean classpathBundle = false;
            if (bundle == null) {
                bundle = findBundleBySymbolicName(identifier);
                if (bundle != null) {
                    final NodeRef deletedNode = uninstallAndDeleteBundle(bundle);
                    if (deletedNode != null) {
                        logger.warn(
                            "Deleted existing repository bundle {} with an identical Symbolic name: {}.",
                            deletedNode, identifier.getSymbolicName()
                        );
                        bundle = null;
                    } else {
                        classpathBundle = true;
                    }
                }
            }
      final FileInputStream in = new FileInputStream(tempFile);
      if (bundle != null) {
                // we stop and delay restarting the bundle, as otherwise, the refresh would cause 2 immediate restarts,
                // this in turn causes havoc upon the asynchronous Spring integration startup
                bundle.stop();
        bundle.update(in);

                final PackageAdmin packageAdmin = getPackageAdmin();
                final Bundle[] bundleSet = {bundle};

                // resolve to synchronously assert dependencies are in order
                packageAdmin.resolveBundles(bundleSet);

                if (isFragmentBundle(bundle) == false) {
                    bundlesToStart.offer(bundle);
                    // async operation
                    packageAdmin.refreshPackages(bundleSet);
                }
            } else {
        bundle = bundleContext.installBundle(location, in);
                if (isFragmentBundle(bundle) == false) {
                    bundle.start();
                }
      }
            if (!classpathBundle) {
                final BundleManifest manifest = BundleManifestFactory.createBundleManifest(bundle.getHeaders());
                saveBundleInRepository(tempFile, filename, manifest);
            } else {
                logger.warn("Temporarily updated classpath bundle: {}, update will be reverted after restart.", bundle.getSymbolicName());
            }
            return bundle;
    } finally {
            if (tempFile != null) {
                tempFile.delete();
            }
        }
  }

    @SuppressWarnings("deprecation")
    private PackageAdmin getPackageAdmin() {
        return bundleContext.getService(bundleContext.getServiceReference(PackageAdmin.class));
    }

    private File wrapPlainJar(File tempFile, String fileName) {
        try {
            JarFile jar = new JarFile(tempFile);

            final Analyzer analyzer = new Analyzer();
            final String manifestVersion = ManifestUtils.getImplementationVersion(jar);
            if (manifestVersion != null) {
                analyzer.setBundleVersion(manifestVersion);
            }
            String name = ManifestUtils.getImplementationTitle(jar);
            if (name == null) {
                if (fileName == null) {
                    return null;
                } else {
                    name = fileName.replaceFirst("^(.+)\\.\\w+$", "$1");
                }
            }
            analyzer.setBundleSymbolicName(name);

            analyzer.setJar(tempFile);
            analyzer.setImportPackage("*;resolution:=optional");
            analyzer.setExportPackage("*");
            analyzer.analyze();
            final Manifest manifest = analyzer.calcManifest();
            analyzer.getJar().setManifest(manifest);
            final File wrappedTempFile = File.createTempFile("wrapped", ".jar");
            analyzer.save(wrappedTempFile, true);
            return wrappedTempFile;
        } catch (Exception e) {
            logger.warn(String.format("Failed to wrap plain %s jar using bnd.", tempFile), e);
            return null;
        }
    }


    private Bundle findBundleBySymbolicName(BundleIdentifier identifier) {
        final Bundle[] allBundles = bundleContext.getBundles();
        for (Bundle aBundle : allBundles) {
            if (aBundle.getSymbolicName().equals(identifier.getSymbolicName())) {
                return aBundle;
            }
        }
        return null;
    }

    protected File saveToTempFile(final InputStream data) throws IOException {
    final File tempFile = File.createTempFile("dynamic-extensions-bundle", null);
    tempFile.deleteOnExit();
    FileCopyUtils.copy(data, new FileOutputStream(tempFile));
    return tempFile;
  }

  protected BundleIdentifier getBundleIdentifier(final File tempFile) throws IOException {
    BundleIdentifier identifier = null;
    final JarFile jarFile = new JarFile(tempFile);
    try {
      final Manifest manifest = jarFile.getManifest();
      final Attributes attributes = manifest.getMainAttributes();
      final String symbolicName = attributes.getValue(Constants.BUNDLE_SYMBOLICNAME);
      final String version = attributes.getValue(Constants.BUNDLE_VERSION);
      if (StringUtils.hasText(symbolicName) && StringUtils.hasText(version)) {
        identifier = BundleIdentifier.fromSymbolicNameAndVersion(symbolicName, version);
      }
      return identifier;
    } finally {
      jarFile.close();
    }
  }

  protected void saveBundleInRepository(final File file, final String filename, final BundleManifest manifest)
      throws IOException {
    final NodeRef bundleFolder = repositoryStoreService.getBundleFolder(true);
    NodeRef nodeRef = fileFolderService.searchSimple(bundleFolder, filename);
    if (nodeRef == null) {
      nodeRef = fileFolderService.create(bundleFolder, filename, ContentModel.TYPE_CONTENT).getNodeRef();
    }
    final String title = String.format("%s %s", manifest.getBundleName(), manifest.getBundleVersion());
    nodeService.setProperty(nodeRef, ContentModel.PROP_TITLE, title);
    nodeService.setProperty(nodeRef, ContentModel.PROP_DESCRIPTION, manifest.getBundleDescription());
    final ContentWriter writer = contentService.getWriter(nodeRef, ContentModel.PROP_CONTENT, true);
    writer.setMimetype(MimetypeMap.MIMETYPE_ZIP);
    writer.putContent(new FileInputStream(file));
  }

  protected String generateRepositoryLocation(final String filename) {
    return String.format("%s/%s", getBundleRepositoryLocation(), filename);
  }

  protected boolean isFragmentBundle(final Bundle bundle) {
    return bundle.getHeaders().get(Constants.FRAGMENT_HOST) != null;
  }

  /* Container */

  public String getBundleRepositoryLocation() {
    return repositoryStoreService.getBundleRepositoryLocation();
  }
}
TOP

Related Classes of com.github.dynamicextensionsalfresco.controlpanel.BundleHelper

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.