Package org.springframework.roo.addon.roobot.eclipse.client

Source Code of org.springframework.roo.addon.roobot.eclipse.client.AddOnRooBotEclipseOperationsImpl

/*******************************************************************************
*  Copyright (c) 2012 VMware, Inc.
*  All rights reserved. This program and the accompanying materials
*  are made available under the terms of the Eclipse Public License v1.0
*  which accompanies this distribution, and is available at
*  http://www.eclipse.org/legal/epl-v10.html
*
*  Contributors:
*      VMware, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.roo.addon.roobot.eclipse.client;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.logging.Logger;
import java.util.zip.ZipInputStream;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.apache.commons.lang3.Validate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.Service;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPPublicKeyRing;
import org.osgi.framework.BundleContext;
import org.osgi.service.component.ComponentContext;
import org.springframework.roo.addon.roobot.client.model.Bundle;
import org.springframework.roo.addon.roobot.client.model.BundleVersion;
import org.springframework.roo.addon.roobot.client.model.Comment;
import org.springframework.roo.addon.roobot.client.model.Rating;
import org.springframework.roo.felix.BundleSymbolicName;
import org.springframework.roo.felix.pgp.PgpKeyId;
import org.springframework.roo.felix.pgp.PgpService;
import org.springframework.roo.shell.Shell;
import org.springframework.roo.support.util.FileUtils;
import org.springframework.roo.support.util.XmlUtils;
import org.springframework.roo.uaa.UaaRegistrationService;
import org.springframework.roo.url.stream.UrlInputStreamService;
import org.springsource.ide.eclipse.commons.frameworks.core.internal.plugins.Plugin;
import org.springsource.ide.eclipse.commons.frameworks.core.internal.plugins.PluginService.InstallOrUpgradeStatus;
import org.springsource.ide.eclipse.commons.frameworks.core.internal.plugins.PluginVersion;
import org.w3c.dom.Document;
import org.w3c.dom.Element;


/**
* Implementation of commands that are available via the Roo shell.
*
* @author Stefan Schmidt
* @author Ben Alex
* @since 1.1
*/
@Component
@Service
public class AddOnRooBotEclipseOperationsImpl implements AddOnRooBotEclipseOperations {

  private Map<String, Bundle> bundleCache;
  @Reference private Shell shell;
  @Reference private PgpService pgpService;
  @Reference private UrlInputStreamService urlInputStreamService;
  private static final Logger log = Logger.getLogger(AddOnRooBotEclipseOperationsImpl.class.getName());
  private Properties props;
  private ComponentContext context;
  private static String ROOBOT_XML_URL = "http://spring-roo-repository.springsource.org/roobot/roobot.xml.zip";
  private DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
  private final Class<AddOnRooBotEclipseOperationsImpl> mutex = AddOnRooBotEclipseOperationsImpl.class;
//  private Preferences prefs;
 
  public static final String ADDON_UPGRADE_STABILITY_LEVEL = "ADDON_UPGRADE_STABILITY_LEVEL";
 
  protected void activate(ComponentContext context) {
    this.context = context;
    //prefs = Preferences.userNodeForPackage(AddOnRooBotEclipseOperationsImpl.class);
    bundleCache = new HashMap<String, Bundle>();
    Thread t = new Thread(new Runnable() {
      public void run() {
        synchronized (mutex) {
          populateBundleCache(true);
        }
      }
    }, "Spring Roo RooBot Add-In Index Eager Download");
    t.start();
    props = new Properties();
    try {
      props.load(FileUtils.getInputStream(getClass(), "manager.properties"));
    } catch (IOException e) {
      e.printStackTrace();
    }
  }

  public boolean trust(PluginVersion pluginVersion) {
    BundleVersion bundleVersion = ((RooAddOnVersion)pluginVersion).getBundleVersion();
    pgpService.trust(new PgpKeyId(bundleVersion.getPgpKey()));
    return true;
  }
 
  public InstallOrUpgradeStatus installOrUpgradeAddOn(PluginVersion pluginVersion, boolean install) {
    synchronized (mutex) {
    BundleVersion bundleVersion = ((RooAddOnVersion)pluginVersion).getBundleVersion();
    Bundle bundle = ((RooAddOnVersion)pluginVersion).getBundle();
    if (!verifyRepository(bundleVersion.getObrUrl())) {
      return InstallOrUpgradeStatus.INVALID_REPOSITORY_URL;
    }
    boolean success = true
    int count = countBundles();
    boolean requiresWrappedCoreDep = bundleVersion.getDescription().contains("#wrappedCoreDependency");
    if (requiresWrappedCoreDep && !shell.executeCommand("osgi obr url add --url http://spring-roo-repository.springsource.org/repository.xml")) {
      success = false;
    }
    if (!shell.executeCommand("osgi obr url add --url " + bundleVersion.getObrUrl())) {
      success = false;
    }
    if (!shell.executeCommand("osgi obr start --bundleSymbolicName " + bundle.getSymbolicName())) {
      success = false;
    }
    if (!shell.executeCommand("osgi obr url remove --url " + bundleVersion.getObrUrl())) {
      success = false;
    }
    if (requiresWrappedCoreDep && !shell.executeCommand("osgi obr url remove --url http://spring-roo-repository.springsource.org/repository.xml")) {
      success = false;
    }
    if (install && count == countBundles()) {
      return InstallOrUpgradeStatus.VERIFICATION_NEEDED; // most likely PgP verification required before the bundle can be installed, no log needed
    }

    if (success) {
      return InstallOrUpgradeStatus.SUCCESS;
    } else {
      return InstallOrUpgradeStatus.FAILED;
    }
    }
  }

  public InstallOrUpgradeStatus removeAddOn(PluginVersion pluginVersion) {
    Bundle bundle = ((RooAddOnVersion)pluginVersion).getBundle();
    BundleSymbolicName bsn = new BundleSymbolicName(bundle.getSymbolicName());
    synchronized (mutex) {
      Validate.notNull(bsn, "Bundle symbolic name required");
      boolean success = false;
      int count = countBundles();
      success = shell.executeCommand("osgi uninstall --bundleSymbolicName " + bsn.getKey());
      if (count == countBundles() || !success) {
        return InstallOrUpgradeStatus.FAILED;
      } else {
        return InstallOrUpgradeStatus.SUCCESS;
      }
    }
  }
 
  public List<Plugin> searchAddOns(String searchTerms, boolean refresh, boolean trustedOnly, boolean compatibleOnly, String requiresCommand) {
    synchronized (mutex) {
      if (bundleCache.size() == 0) {
        // We should refresh regardless in this case
        refresh = true;
      }
      if (refresh && populateBundleCache(false)) {
      }
      if (bundleCache.size() != 0) {
        boolean onlyRelevantBundles = false;
        if (searchTerms != null && !"".equals(searchTerms)) {
          onlyRelevantBundles = true;
          String [] terms = searchTerms.split(",");
          for (Bundle bundle: bundleCache.values()) {
            //first set relevance of all bundles to zero
            bundle.setSearchRelevance(0f);
            int hits = 0;
            BundleVersion latest = bundle.getLatestVersion();
            for (String term: terms) {
              if ((bundle.getSymbolicName() + ";" + latest.getSummary()).toLowerCase().contains(term.trim().toLowerCase()) || term.equals("*")) {
                hits++;
              }
            }
            bundle.setSearchRelevance(hits / terms.length);
          }
        }
        List<Bundle> bundles = Bundle.orderBySearchRelevance(new ArrayList<Bundle>(bundleCache.values()));
        LinkedList<Bundle> filteredSearchResults = filterList(bundles, trustedOnly, compatibleOnly, requiresCommand, onlyRelevantBundles);
        return convertToAddOns(filteredSearchResults);
      }
      return null;
    }
  }

  private List<Plugin> convertToAddOns(
      LinkedList<Bundle> filteredSearchResults) {
    BundleContext bc = context.getBundleContext();
    org.osgi.framework.Bundle[] bundles = bc.getBundles();
    Map<String, org.osgi.framework.Bundle> installedBundleBySymbolicName = new HashMap<String, org.osgi.framework.Bundle>();
    for (org.osgi.framework.Bundle bundle : bundles) {
      installedBundleBySymbolicName.put(bundle.getSymbolicName(), bundle);
    }
   
    ArrayList<Plugin> result = new ArrayList<Plugin>();
    for (Bundle bundle : filteredSearchResults) {
      org.osgi.framework.Bundle installedBundle = installedBundleBySymbolicName.get(bundle.getSymbolicName());
     
      // create add-on for each bundle
      Plugin plugin = new Plugin(bundle.getSymbolicName());
     
      // add all available versions
      List<BundleVersion> versions = BundleVersion.orderByVersion(bundle.getVersions());
      for (BundleVersion version : versions) {
        RooAddOnVersion addOnVersion = new RooAddOnVersion(bundle,
            version);
        addOnVersion.setTitle(version.getPresentationName());
        addOnVersion.setVersion(version.getVersion());
        addOnVersion.setDescription(version.getDescription());
        addOnVersion.setRuntimeVersion(version.getRooVersion());
        // name needs to match between bundle and version
        addOnVersion.setName(plugin.getName());
       
        if (installedBundle != null && installedBundle.getVersion().toString().equals(version.getVersion())) {
          addOnVersion.setInstalled(true);
        }
       
        plugin.addVersion(addOnVersion);
        plugin.setLatestReleasedVersion(addOnVersion);
      }
     
      result.add(plugin);
    }
    return result;
  }

//  public void upgradeSettings(AddOnStabilityLevel addOnStabilityLevel) {
//    if (addOnStabilityLevel == null) {
//      addOnStabilityLevel = checkAddOnStabilityLevel(addOnStabilityLevel);
//      log.info("Current Add-on Stability Level: " + addOnStabilityLevel.name());
//    } else {
//      boolean success = true;
//      prefs.putInt(ADDON_UPGRADE_STABILITY_LEVEL, addOnStabilityLevel.getLevel());
//      try {
//        prefs.flush();
//      } catch (BackingStoreException ignore) {
//        success = false;
//      }
//      if (success) {
//        log.info("Add-on Stability Level: " + addOnStabilityLevel.name() + " stored");
//      } else {
//        log.warning("Unable to store add-on stability level at this time");
//      }
//    }
//  }
   
  public Map<String, Bundle> getAddOnCache(boolean refresh) {
    synchronized (mutex) {
      if (refresh) {
        populateBundleCache(false);
      }
      return Collections.unmodifiableMap(bundleCache);
    }
  }
 
  private LinkedList<Bundle> filterList(List<Bundle> bundles, boolean trustedOnly, boolean compatibleOnly, String requiresCommand, boolean onlyRelevantBundles) {
    LinkedList<Bundle> filteredList = new LinkedList<Bundle>();
    List<PGPPublicKeyRing> keys = null;
    if (trustedOnly) {
      keys = pgpService.getTrustedKeys();
    }
    bundle_loop: for (Bundle bundle: bundles) {
      BundleVersion latest = bundle.getLatestVersion();
      if (onlyRelevantBundles && !(bundle.getSearchRelevance() > 0)) {
        continue bundle_loop;
      }
      if (trustedOnly && !isTrustedKey(keys, latest.getPgpKey())) {
        continue bundle_loop;
      }
      if (compatibleOnly && !isCompatible(latest.getRooVersion())) {
        continue bundle_loop;
      }
      if (requiresCommand != null && requiresCommand.length() > 0) {
        boolean matchingCommand = false;
        for (String cmd : latest.getCommands().keySet()) {
          if (cmd.startsWith(requiresCommand) || requiresCommand.startsWith(cmd)) {
            matchingCommand = true;
            break;
          }
        }
        if (!matchingCommand) {
          continue bundle_loop;
        }
      }
      filteredList.add(bundle);
    }
    return filteredList;
  }
 
  @SuppressWarnings("unchecked")
  private boolean isTrustedKey(List<PGPPublicKeyRing> keys, String keyId) {
    for (PGPPublicKeyRing keyRing: keys) {
      Iterator<PGPPublicKey> it = keyRing.getPublicKeys();
      while (it.hasNext()) {
        PGPPublicKey pgpKey = (PGPPublicKey) it.next();
        if (new PgpKeyId(pgpKey).equals(new PgpKeyId(keyId))) {
          return true;
        }
      }
    }
    return false;
  }

  private boolean populateBundleCache(boolean startupTime) {
    boolean success = false;
    InputStream is = null;
    try {
      DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
      DocumentBuilder db = dbf.newDocumentBuilder();
      String url = props.getProperty("roobot.url", ROOBOT_XML_URL);
      if (url == null) {
        log.warning("Bundle properties could not be loaded");
        return false;
      }
      if (url.startsWith("http://")) {
        // Handle it as HTTP
        URL httpUrl = new URL(url);
        String failureMessage = urlInputStreamService.getUrlCannotBeOpenedMessage(httpUrl);
        if (failureMessage != null) {
          if (!startupTime) {
            // This wasn't just an eager startup time attempt, so let's display the error reason
            // (for startup time, we just fail quietly)
            log.warning(failureMessage);
          }
          return false;
        }
        // It appears we can acquire the URL, so let's do it
        is = urlInputStreamService.openConnection(httpUrl);
      } else {
        // Fallback to normal protocol handler (likely in local development testing etc
        is = new URL(url).openStream();
      }
      if (is == null) {
        log.warning("Could not connect to Roo Addon bundle repository index");
        return false;
      }
     
      ZipInputStream zip = new ZipInputStream(is);
      zip.getNextEntry();
     
      ByteArrayOutputStream baos = new ByteArrayOutputStream();
      byte[] buffer = new byte[8192];
      int length = -1;
      while (zip.available() > 0) {
        length = zip.read(buffer, 0, 8192);
        if (length > 0) {
          baos.write(buffer, 0, length);
        }
      }
     
      ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
      Document roobotXml = db.parse(bais);
     
      if (roobotXml != null) {
        bundleCache.clear();
        for (Element bundleElement : XmlUtils.findElements("/roobot/bundles/bundle", roobotXml.getDocumentElement())) {
          String bsn = bundleElement.getAttribute("bsn");
          List<Comment> comments = new LinkedList<Comment>();
          for (Element commentElement: XmlUtils.findElements("comments/comment", bundleElement)) {
            comments.add(new Comment(Rating.fromInt(new Integer(commentElement.getAttribute("rating"))), commentElement.getAttribute("comment"), dateFormat.parse(commentElement.getAttribute("date"))));
          }
          Bundle bundle = new Bundle(bundleElement.getAttribute("bsn"), new Float(bundleElement.getAttribute("uaa-ranking")).floatValue(), comments);
           
          for (Element versionElement: XmlUtils.findElements("versions/version", bundleElement)) {
            if (bsn != null && bsn.length() > 0 && versionElement != null) {
              String signedBy = "";
              String pgpKey = versionElement.getAttribute("pgp-key-id");
              if (pgpKey != null && pgpKey.length() > 0) {
                Element pgpSigned = XmlUtils.findFirstElement("/roobot/pgp-keys/pgp-key[@id='" + pgpKey + "']/pgp-key-description", roobotXml.getDocumentElement());
                if (pgpSigned != null) {
                  signedBy = pgpSigned.getAttribute("text");
                }
              }
             
              Map<String, String> commands = new HashMap<String, String>();
              for (Element shell : XmlUtils.findElements("shell-commands/shell-command", versionElement)) {
                commands.put(shell.getAttribute("command"), shell.getAttribute("help"));
              }
             
              StringBuilder versionBuilder = new StringBuilder();
              versionBuilder.append(versionElement.getAttribute("major")).append(".").append(versionElement.getAttribute("minor"));
              String versionMicro = versionElement.getAttribute("micro");
              if (versionMicro != null && versionMicro.length() > 0) {
                versionBuilder.append(".").append(versionMicro);
              }
              String versionQualifier = versionElement.getAttribute("qualifier");
              if (versionQualifier != null && versionQualifier.length() > 0) {
                versionBuilder.append(".").append(versionQualifier);
              }
             
              String rooVersion = versionElement.getAttribute("roo-version");
              if (rooVersion.equals("*") || rooVersion.length() == 0) {
                rooVersion = getVersionForCompatibility();
              } else {
                String[] split = rooVersion.split("\\.");
                if (split.length > 2) {
                  //only interested in major.minor
                  rooVersion = split[0] + "." + split[1];
                }
              }
             
              BundleVersion version = new BundleVersion(versionElement.getAttribute("url"), versionElement.getAttribute("obr-url"), versionBuilder.toString(), versionElement.getAttribute("name"), new Long(versionElement.getAttribute("size")).longValue(), versionElement.getAttribute("description"), pgpKey, signedBy, rooVersion, commands);
              // For security reasons we ONLY accept httppgp:// add-on versions
              if (!version.getUri().startsWith("httppgp://")) {
                continue;
              }
              bundle.addVersion(version);
            }
            bundleCache.put(bsn, bundle);
          }
        }
        success = true;
      }
      zip.close();
      baos.close();
      bais.close();
    } catch (Throwable ignore) {
    } finally {
      try {
        if (is != null) {
          is.close();
        }
      } catch (IOException ignored) {
      }
    }
    if (success && startupTime) {
      //printAddonStats();
    }
    return success;
  }


  private int countBundles() {
    BundleContext bc = context.getBundleContext();
    if (bc != null) {
      org.osgi.framework.Bundle[] bundles = bc.getBundles();
      if (bundles != null) {
        return bundles.length;
      }
    }
    return 0;
  }
 
  private boolean verifyRepository(String repoUrl) {
    DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
    Document doc = null;
    try {
      URL obrUrl = null;
      obrUrl = new URL(repoUrl);
      DocumentBuilder db = dbf.newDocumentBuilder();
      if (obrUrl.toExternalForm().endsWith(".zip")) {
        ZipInputStream zip = new ZipInputStream(obrUrl.openStream());
        zip.getNextEntry();
       
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        byte[] buffer = new byte[8192];
        int length = -1;
        while (zip.available() > 0) {
          length = zip.read(buffer, 0, 8192);
          if (length > 0) {
            baos.write(buffer, 0, length);
          }
        }
        ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
        doc = db.parse(bais);
      } else {
        doc = db.parse(obrUrl.openStream());
      }
      Validate.notNull(doc, "RooBot was unable to parse the repository document of this add-on");
      for (Element resource: XmlUtils.findElements("resource", doc.getDocumentElement())) {
        if (resource.hasAttribute("uri")) {
          if (!resource.getAttribute("uri").startsWith("httppgp")) {
            log.warning("Sorry, the resource " + resource.getAttribute("uri") + " does not follow HTTPPGP conventions mangraded by Spring Roo so the OBR file at " + repoUrl + " is unacceptable at this time");
            return false;
          }
        }
      }
      doc = null;
    } catch (Exception e) {
      throw new IllegalStateException("RooBot was unable to parse the repository document of this add-on", e);
    }
    return true;
  }
 
//  private AddOnStabilityLevel checkAddOnStabilityLevel(AddOnStabilityLevel addOnStabilityLevel) {
//    if (addOnStabilityLevel == null) {
//      addOnStabilityLevel = AddOnStabilityLevel.fromLevel(prefs.getInt(ADDON_UPGRADE_STABILITY_LEVEL, /* default */ AddOnStabilityLevel.RELEASE.getLevel()));
//    }
//    return addOnStabilityLevel;
//  }

  private boolean isCompatible(String version) {
    return version.equals(getVersionForCompatibility());
  }
 
  private String getVersionForCompatibility() {
    return UaaRegistrationService.SPRING_ROO.getMajorVersion() + "." + UaaRegistrationService.SPRING_ROO.getMinorVersion();
  }
 
}
TOP

Related Classes of org.springframework.roo.addon.roobot.eclipse.client.AddOnRooBotEclipseOperationsImpl

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.