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

Source Code of org.springframework.roo.addon.roobot.client.AddOnRooBotOperationsImpl

package org.springframework.roo.addon.roobot.client;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.net.URL;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.logging.Logger;
import java.util.zip.ZipInputStream;

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

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
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.classpath.preferences.Preferences;
import org.springframework.roo.classpath.preferences.PreferencesService;
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.logging.HandlerUtils;
import org.springframework.roo.support.util.XmlUtils;
import org.springframework.roo.uaa.UaaRegistrationService;
import org.springframework.roo.url.stream.UrlInputStreamService;
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(immediate = true)
@Service
public class AddOnRooBotOperationsImpl implements AddOnRooBotOperations {

    public static final String ADDON_UPGRADE_STABILITY_LEVEL = "ADDON_UPGRADE_STABILITY_LEVEL";
    private static final Logger LOGGER = HandlerUtils
            .getLogger(AddOnRooBotOperationsImpl.class);
    private static final List<String> NO_UPGRADE_BSN_LIST = Arrays.asList(
            "org.springframework.uaa.client",
            "org.springframework.roo.url.stream.jdk",
            "org.springframework.roo.url.stream",
            "org.springframework.roo.file.monitor",
            "org.springframework.roo.file.monitor.polling",
            "org.springframework.roo.file.monitor.polling.roo",
            "org.springframework.roo.bootstrap",
            "org.springframework.roo.classpath",
            "org.springframework.roo.classpath.javaparser",
            "org.springframework.roo.deployment.support",
            "org.springframework.roo.felix",
            "org.springframework.roo.file.undo",
            "org.springframework.roo.metadata",
            "org.springframework.roo.model",
            "org.springframework.roo.osgi.bundle",
            "org.springframework.roo.osgi.roo.bundle",
            "org.springframework.roo.process.manager",
            "org.springframework.roo.project", "org.springframework.roo.root",
            "org.springframework.roo.shell",
            "org.springframework.roo.shell.jline",
            "org.springframework.roo.shell.jline.osgi",
            "org.springframework.roo.shell.osgi",
            "org.springframework.roo.startlevel",
            "org.springframework.roo.support",
            "org.springframework.roo.support.osgi",
            "org.springframework.roo.uaa");

    @Reference private PgpService pgpService;
    @Reference private PreferencesService preferencesService;
    @Reference private Shell shell;
    @Reference private UrlInputStreamService urlInputStreamService;

    private Map<String, Bundle> bundleCache;
    private ComponentContext context;
    private final DateFormat dateFormat = new SimpleDateFormat(
            "yyyy-MM-dd hh:mm:ss");
    private final Object mutex = new Object();
    private Preferences prefs;
    private volatile Thread rooBotEagerDownload;
    private boolean rooBotIndexDownload = true;
    private String rooBotXmlUrl = "http://spring-roo-repository.springsource.org/roobot/roobot.xml.zip";
    private Map<String, Bundle> searchResultCache;

    protected void activate(final ComponentContext context) {
        this.context = context;
        prefs = preferencesService
                .getPreferencesFor(AddOnRooBotOperationsImpl.class);
        bundleCache = new HashMap<String, Bundle>();
        searchResultCache = new HashMap<String, Bundle>();
        final BundleContext bundleContext = context.getBundleContext();
        if (bundleContext != null) {
            final String roobot = bundleContext.getProperty("roobot.url");
            if (roobot != null && roobot.length() > 0) {
                rooBotXmlUrl = roobot;
            }
            rooBotIndexDownload = Boolean.valueOf(bundleContext
                    .getProperty("roobot.index.dowload"));
        }
        if (rooBotIndexDownload) {
            rooBotEagerDownload = new Thread(new Runnable() {
                public void run() {
                    synchronized (mutex) {
                        populateBundleCache(true);
                    }
                }
            }, "Spring Roo RooBot Add-In Index Eager Download");
            rooBotEagerDownload.start();
        }
    }

    public void addOnInfo(final AddOnBundleSymbolicName bsn) {
        Validate.notNull(bsn, "A valid add-on bundle symbolic name is required");
        synchronized (mutex) {
            String bsnString = bsn.getKey();
            if (bsnString.contains(";")) {
                bsnString = bsnString.split(";")[0];
            }
            final Bundle bundle = bundleCache.get(bsnString);
            if (bundle == null) {
                LOGGER.warning("Unable to find specified bundle with symbolic name: "
                        + bsn.getKey());
                return;
            }
            addOnInfo(bundle, bundle.getBundleVersion(bsn.getKey()));
        }
    }

    private void addOnInfo(final Bundle bundle,
            final BundleVersion bundleVersion) {
        final StringBuilder sb = new StringBuilder(bundleVersion.getVersion());
        if (bundle.getVersions().size() > 1) {
            sb.append(" [available versions: ");
            for (final BundleVersion version : BundleVersion
                    .orderByVersion(new ArrayList<BundleVersion>(bundle
                            .getVersions()))) {
                sb.append(version.getVersion()).append(", ");
            }
            sb.delete(sb.length() - 2, sb.length()).append("]");
        }
        logInfo("Name", bundleVersion.getPresentationName());
        logInfo("BSN", bundle.getSymbolicName());
        logInfo("Version", sb.toString());
        logInfo("Roo Version", bundleVersion.getRooVersion());
        logInfo("Ranking", Float.toString(bundle.getRanking()));
        logInfo("JAR Size", bundleVersion.getSize() + " bytes");
        logInfo("PGP Signature", bundleVersion.getPgpKey() + " signed by "
                + bundleVersion.getPgpDescriptions());
        logInfo("OBR URL", bundleVersion.getObrUrl());
        logInfo("JAR URL", bundleVersion.getUri());
        for (final Entry<String, String> entry : bundleVersion.getCommands()
                .entrySet()) {
            logInfo("Commands", "'" + entry.getKey() + "' [" + entry.getValue()
                    + "]");
        }
        logInfo("Description", bundleVersion.getDescription());
        int cc = 0;
        for (final Comment comment : bundle.getComments()) {
            logInfo("Comment " + ++cc,
                    "Rating ["
                            + comment.getRating().name()
                            + "], grade ["
                            + DateFormat.getDateInstance(DateFormat.SHORT)
                                    .format(comment.getDate()) + "], Comment ["
                            + comment.getComment() + "]");
        }
    }

    public void addOnInfo(final String bundleKey) {
        Validate.notBlank(bundleKey, "A valid bundle ID is required");
        synchronized (mutex) {
            Bundle bundle = null;
            if (searchResultCache != null) {
                bundle = searchResultCache.get(String.format("%02d",
                        Integer.parseInt(bundleKey)));
            }
            if (bundle == null) {
                LOGGER.warning("A valid bundle ID is required");
                return;
            }
            addOnInfo(bundle, bundle.getBundleVersion(bundleKey));
        }
    }

    private AddOnStabilityLevel checkAddOnStabilityLevel(
            AddOnStabilityLevel addOnStabilityLevel) {
        if (addOnStabilityLevel == null) {
            addOnStabilityLevel = AddOnStabilityLevel.fromLevel(prefs.getInt(
                    ADDON_UPGRADE_STABILITY_LEVEL, /* default */
                    AddOnStabilityLevel.RELEASE.getLevel()));
        }
        return addOnStabilityLevel;
    }

    private int countBundles() {
        final BundleContext bc = context.getBundleContext();
        if (bc != null) {
            final org.osgi.framework.Bundle[] bundles = bc.getBundles();
            if (bundles != null) {
                return bundles.length;
            }
        }
        return 0;
    }

    protected void deactivate(final ComponentContext context) {
        if (rooBotEagerDownload != null && rooBotEagerDownload.isAlive()) {
            rooBotEagerDownload = null;
        }
    }

    private List<Bundle> filterList(final List<Bundle> bundles,
            final boolean trustedOnly, final boolean compatibleOnly,
            final boolean communityOnly, final String requiresCommand,
            final boolean onlyRelevantBundles) {
        final List<Bundle> filteredList = new ArrayList<Bundle>();
        List<PGPPublicKeyRing> keys = null;
        if (trustedOnly) {
            keys = pgpService.getTrustedKeys();
        }
        bundle_loop: for (final Bundle bundle : bundles) {
            final BundleVersion latest = bundle.getLatestVersion();
            if (onlyRelevantBundles && !(bundle.getSearchRelevance() > 0)) {
                continue bundle_loop;
            }
            if (trustedOnly && !isTrustedKey(keys, latest.getPgpKey())) {
                continue bundle_loop;
            }
            if (communityOnly
                    && latest
                            .getObrUrl()
                            .equals("http://spring-roo-repository.springsource.org/repository.xml")) {
                continue bundle_loop;
            }
            if (compatibleOnly && !isCompatible(latest.getRooVersion())) {
                continue bundle_loop;
            }
            if (isBundleInstalled(bundle)) {
                continue bundle_loop;
            }
            if (requiresCommand != null && requiresCommand.length() > 0) {
                boolean matchingCommand = false;
                for (final 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;
    }

    public List<Bundle> findAddons(final boolean showFeedback,
            final String searchTerms, boolean refresh,
            final int linesPerResult, int maxResults,
            final boolean trustedOnly, final boolean compatibleOnly,
            final boolean communityOnly, final String requiresCommand) {
        synchronized (mutex) {
            if (maxResults > 99) {
                maxResults = 99;
            }
            if (maxResults < 1) {
                maxResults = 10;
            }
            if (bundleCache.isEmpty()) {
                // We should refresh regardless in this case
                refresh = true;
            }
            if (refresh && populateBundleCache(false)) {
                if (showFeedback) {
                    LOGGER.info("Successfully downloaded Roo add-on Data");
                }
            }
            if (bundleCache.size() != 0) {
                boolean onlyRelevantBundles = false;
                if (searchTerms != null && !"".equals(searchTerms)) {
                    onlyRelevantBundles = true;
                    final String[] terms = searchTerms.split(",");
                    for (final Bundle bundle : bundleCache.values()) {
                        // First set relevance of all bundles to zero
                        bundle.setSearchRelevance(0f);
                        int hits = 0;
                        final BundleVersion latest = bundle.getLatestVersion();
                        for (final String term : terms) {
                            if ((bundle.getSymbolicName() + ";" + latest
                                    .getSummary()).toLowerCase().contains(
                                    term.trim().toLowerCase())
                                    || term.equals("*")) {
                                hits++;
                            }
                        }
                        bundle.setSearchRelevance(hits / terms.length);
                    }
                }
                final List<Bundle> bundles = Bundle
                        .orderBySearchRelevance(new ArrayList<Bundle>(
                                bundleCache.values()));
                final List<Bundle> filteredSearchResults = filterList(bundles,
                        trustedOnly, compatibleOnly, communityOnly,
                        requiresCommand, onlyRelevantBundles);
                if (showFeedback) {
                    printResultList(filteredSearchResults, maxResults,
                            linesPerResult);
                }
                return filteredSearchResults;
            }

            // There is a problem with the add-on index
            if (showFeedback) {
                LOGGER.info("No add-ons known. Are you online? Try the 'download status' command");
            }

            return null;
        }
    }

    public Map<String, Bundle> getAddOnCache(final boolean refresh) {
        synchronized (mutex) {
            if (refresh) {
                populateBundleCache(false);
            }
            return Collections.unmodifiableMap(bundleCache);
        }
    }

    private Map<String, Bundle> getUpgradableBundles(
            final AddOnStabilityLevel asl) {
        final Map<String, Bundle> bundles = new HashMap<String, Bundle>();
        if (context == null) {
            return bundles;
        }
        final BundleContext bundleContext = context.getBundleContext();
        for (final org.osgi.framework.Bundle bundle : bundleContext
                .getBundles()) {
            final Bundle b = bundleCache.get(bundle.getSymbolicName());
            if (b == null) {
                continue;
            }
            final BundleVersion bundleVersion = b.getLatestVersion();
            final String rooBotBundleVersion = bundleVersion.getVersion();
            final Object ebv = bundle.getHeaders().get("Bundle-Version");
            if (ebv == null) {
                continue;
            }
            final String exisingBundleVersion = ebv.toString().trim();
            if (isCompatible(b.getLatestVersion().getRooVersion())
                    && rooBotBundleVersion
                            .compareToIgnoreCase(exisingBundleVersion) > 0
                    && asl.getLevel() > AddOnStabilityLevel
                            .getAddOnStabilityLevel(exisingBundleVersion)) {

                bundles.put(b.getSymbolicName() + ";" + exisingBundleVersion, b);
            }
        }
        return bundles;
    }

    private String getVersionForCompatibility() {
        return UaaRegistrationService.SPRING_ROO.getMajorVersion() + "."
                + UaaRegistrationService.SPRING_ROO.getMinorVersion();
    }

    private InstallOrUpgradeStatus installAddon(
            final BundleVersion bundleVersion, final String bsn) {
        final InstallOrUpgradeStatus status = installOrUpgradeAddOn(
                bundleVersion, bsn, true);
        switch (status) {
        case SUCCESS:
            LOGGER.info("Successfully installed add-on: "
                    + bundleVersion.getPresentationName() + " [version: "
                    + bundleVersion.getVersion() + "]");
            LOGGER.warning("[Hint] Please consider rating this add-on with the following command:");
            LOGGER.warning("[Hint] addon feedback bundle --bundleSymbolicName "
                    + bsn.substring(
                            0,
                            bsn.indexOf(";") != -1 ? bsn.indexOf(";") : bsn
                                    .length())
                    + " --rating ... --comment \"...\"");
            break;
        case SHELL_RESTART_NEEDED:
            LOGGER.warning("You have upgraded a Roo core addon. To complete this installation please restart the Roo shell.");
            break;
        case PGP_VERIFICATION_NEEDED:
            LOGGER.warning("PGP verification of the bundle required");
            break;
        default:
            LOGGER.warning("Unable to install add-on: "
                    + bundleVersion.getPresentationName() + " [version: "
                    + bundleVersion.getVersion() + "]");
            break;
        }

        return status;
    }

    public InstallOrUpgradeStatus installAddOn(final AddOnBundleSymbolicName bsn) {
        synchronized (mutex) {
            Validate.notNull(bsn,
                    "A valid add-on bundle symbolic name is required");
            String bsnString = bsn.getKey();
            if (bsnString.contains(";")) {
                bsnString = bsnString.split(";")[0];
            }
            final Bundle bundle = bundleCache.get(bsnString);
            if (bundle == null) {
                LOGGER.warning("Could not find specified bundle with symbolic name: "
                        + bsn.getKey());
                return InstallOrUpgradeStatus.FAILED;
            }
            return installAddon(bundle.getBundleVersion(bsn.getKey()),
                    bsn.getKey());
        }
    }

    public InstallOrUpgradeStatus installAddOn(final String bundleKey) {
        synchronized (mutex) {
            Validate.notBlank(bundleKey, "A valid bundle ID is required");
            Bundle bundle = null;
            if (searchResultCache != null) {
                bundle = searchResultCache.get(String.format("%02d",
                        Integer.parseInt(bundleKey)));
            }
            if (bundle == null) {
                LOGGER.warning("To install an addon a valid bundle ID is required");
                return InstallOrUpgradeStatus.FAILED;
            }
            return installAddon(bundle.getBundleVersion(bundleKey),
                    bundle.getSymbolicName());
        }
    }

    private InstallOrUpgradeStatus installOrUpgradeAddOn(
            final BundleVersion bundleVersion, final String bsn,
            final boolean install) {
        if (!verifyRepository(bundleVersion.getObrUrl())) {
            return InstallOrUpgradeStatus.INVALID_OBR_URL;
        }

        final int count = countBundles();
        final boolean requiresWrappedCoreDependency = bundleVersion
                .getDescription().contains("#wrappedCoreDependency");

        boolean success = !(requiresWrappedCoreDependency && !shell
                .executeCommand("osgi obr url add --url http://spring-roo-repository.springsource.org/repository.xml"));
        success &= shell.executeCommand("osgi obr url add --url "
                + bundleVersion.getObrUrl());
        success &= shell.executeCommand("osgi obr start --bundleSymbolicName "
                + bsn);
        success &= shell.executeCommand("osgi obr url remove --url "
                + bundleVersion.getObrUrl());
        success &= !(requiresWrappedCoreDependency && !shell
                .executeCommand("osgi obr url remove --url http://spring-roo-repository.springsource.org/repository.xml"));

        if (install && count == countBundles()) {
            // Most likely PgP verification required before the bundle can be
            // installed, no log needed
            return InstallOrUpgradeStatus.PGP_VERIFICATION_NEEDED;
        }
        return success ? InstallOrUpgradeStatus.SUCCESS
                : InstallOrUpgradeStatus.FAILED;
    }

    private boolean isBundleInstalled(final Bundle search) {
        final BundleContext bundleContext = context.getBundleContext();
        for (final org.osgi.framework.Bundle bundle : bundleContext
                .getBundles()) {
            final String bsn = (String) bundle.getHeaders().get(
                    "Bundle-SymbolicName");
            if (StringUtils.isNotBlank(bsn)
                    && bsn.equals(search.getSymbolicName())) {
                return true;
            }
        }
        return false;
    }

    private boolean isCompatible(final String version) {
        return version.equals(getVersionForCompatibility());
    }

    @SuppressWarnings("unchecked")
    private boolean isTrustedKey(final List<PGPPublicKeyRing> keys,
            final String keyId) {
        for (final PGPPublicKeyRing keyRing : keys) {
            final Iterator<PGPPublicKey> it = keyRing.getPublicKeys();
            while (it.hasNext()) {
                final PGPPublicKey pgpKey = it.next();
                if (new PgpKeyId(pgpKey).equals(new PgpKeyId(keyId))) {
                    return true;
                }
            }
        }
        return false;
    }

    public void listAddOns(boolean refresh, final int linesPerResult,
            final int maxResults, final boolean trustedOnly,
            final boolean compatibleOnly, final boolean communityOnly,
            final String requiresCommand) {
        synchronized (mutex) {
            if (bundleCache.isEmpty()) {
                // We should refresh regardless in this case
                refresh = true;
            }
            if (refresh && populateBundleCache(false)) {
                LOGGER.info("Successfully downloaded Roo add-on Data");
            }
            if (bundleCache.size() != 0) {
                final List<Bundle> bundles = Bundle
                        .orderByRanking(new ArrayList<Bundle>(bundleCache
                                .values()));
                final List<Bundle> filteredList = filterList(bundles,
                        trustedOnly, compatibleOnly, communityOnly,
                        requiresCommand, false);
                printResultList(filteredList, maxResults, linesPerResult);
            }
            else {
                LOGGER.info("No add-ons known. Are you online? Try the 'download status' command");
            }
        }
    }

    private void logInfo(final String label, String content) {
        final StringBuilder sb = new StringBuilder();
        sb.append(label);
        for (int i = 0; i < 13 - label.length(); i++) {
            sb.append(".");
        }
        sb.append(": ");
        if (content.length() < 65) {
            sb.append(content);
            LOGGER.info(sb.toString());
        }
        else {
            final List<String> split = new ArrayList<String>(
                    Arrays.asList(content.split("\\s")));
            if (split.size() == 1) {
                while (content.length() > 65) {
                    sb.append(content.substring(0, 65));
                    content = content.substring(65);
                    LOGGER.info(sb.toString());
                    sb.setLength(0);
                    sb.append("               ");
                }
                if (content.length() > 0) {
                    LOGGER.info(sb.append(content).toString());
                }
            }
            else {
                while (split.size() > 0) {
                    while (!split.isEmpty()
                            && split.get(0).length() + sb.length() < 79) {
                        sb.append(split.get(0)).append(" ");
                        split.remove(0);
                    }
                    LOGGER.info(sb.toString().substring(0,
                            sb.toString().length() - 1));
                    sb.setLength(0);
                    sb.append("               ");
                }
            }
        }
    }

    private boolean populateBundleCache(final boolean startupTime) {
        boolean success = false;
        InputStream is = null;
        ByteArrayInputStream bais = null;
        ByteArrayOutputStream baos = null;
        try {
            final DocumentBuilderFactory dbf = DocumentBuilderFactory
                    .newInstance();
            final DocumentBuilder db = dbf.newDocumentBuilder();
            if (rooBotXmlUrl.startsWith("http://")) {
                // Handle it as HTTP
                final URL httpUrl = new URL(rooBotXmlUrl);
                final 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)
                        LOGGER.warning(failureMessage);
                    }
                    return false;
                }
                // It appears we can acquire the URL, so let's do it
                is = urlInputStreamService.openConnection(httpUrl);
            }
            else {
                // Fall back to normal protocol handler (likely in local
                // development testing etc)
                is = new URL(rooBotXmlUrl).openStream();
            }
            if (is == null) {
                LOGGER.warning("Could not connect to Roo Addon bundle repository index");
                return false;
            }

            final ZipInputStream zip = new ZipInputStream(is);
            zip.getNextEntry();

            baos = new ByteArrayOutputStream();
            IOUtils.copy(zip, baos);

            bais = new ByteArrayInputStream(baos.toByteArray());
            final Document roobotXml = db.parse(bais);
            if (roobotXml != null) {
                populateBundleCache(roobotXml);
                success = true;
            }
            zip.close();
        }
        catch (final Throwable ignored) {
        }
        finally {
            IOUtils.closeQuietly(is);
            IOUtils.closeQuietly(baos);
            IOUtils.closeQuietly(bais);
        }
        if (success && startupTime) {
            printAddonStats();
        }
        return success;
    }

    private void populateBundleCache(final Document roobotXml)
            throws ParseException {
        bundleCache.clear();
        for (final Element bundleElement : XmlUtils.findElements(
                "/roobot/bundles/bundle", roobotXml.getDocumentElement())) {
            final String bsn = bundleElement.getAttribute("bsn");
            if (NO_UPGRADE_BSN_LIST.contains(bsn)) {
                // List only add-ons which are not core (see ROO-2190)
                continue;
            }
            final List<Comment> comments = new ArrayList<Comment>();
            for (final 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"))));
            }
            final Bundle bundle = new Bundle(bundleElement.getAttribute("bsn"),
                    new Float(bundleElement.getAttribute("uaa-ranking")),
                    comments);
            for (final Element versionElement : XmlUtils.findElements(
                    "versions/version", bundleElement)) {
                if (bsn != null && bsn.length() > 0 && versionElement != null) {
                    String signedBy = "";
                    final String pgpKey = versionElement
                            .getAttribute("pgp-key-id");
                    if (pgpKey != null && pgpKey.length() > 0) {
                        final Element pgpSigned = XmlUtils.findFirstElement(
                                "/roobot/pgp-keys/pgp-key[@id='" + pgpKey
                                        + "']/pgp-key-description",
                                roobotXml.getDocumentElement());
                        if (pgpSigned != null) {
                            signedBy = pgpSigned.getAttribute("text");
                        }
                    }

                    final Map<String, String> commands = new HashMap<String, String>();
                    for (final Element shell : XmlUtils.findElements(
                            "shell-commands/shell-command", versionElement)) {
                        commands.put(shell.getAttribute("command"),
                                shell.getAttribute("help"));
                    }

                    final StringBuilder versionBuilder = new StringBuilder();
                    versionBuilder.append(versionElement.getAttribute("major"))
                            .append(".")
                            .append(versionElement.getAttribute("minor"));
                    final String versionMicro = versionElement
                            .getAttribute("micro");
                    if (versionMicro != null && versionMicro.length() > 0) {
                        versionBuilder.append(".").append(versionMicro);
                    }
                    final 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 {
                        final String[] split = rooVersion.split("\\.");
                        if (split.length > 2) {
                            // Only interested in major.minor
                            rooVersion = split[0] + "." + split[1];
                        }
                    }
                    final 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);
            }
        }
    }

    private void printAddonStats() {
        String msg = null;
        final AddOnStabilityLevel currentLevel = AddOnStabilityLevel
                .fromLevel(prefs.getInt(ADDON_UPGRADE_STABILITY_LEVEL,
                        AddOnStabilityLevel.RELEASE.getLevel()));
        final Map<String, Bundle> currentLevelBundles = getUpgradableBundles(currentLevel);
        if (currentLevelBundles.size() > 0) {
            msg = currentLevelBundles.size() + " upgrade"
                    + (currentLevelBundles.size() > 1 ? "s" : "")
                    + " available";
        }
        final Map<String, Bundle> anyLevelBundles = getUpgradableBundles(AddOnStabilityLevel.ANY);
        if (anyLevelBundles.size() != 0) {
            if (msg == null) {
                msg = "0 upgrades available";
            }
            final int plusSize = anyLevelBundles.size()
                    - currentLevelBundles.size();
            msg += " (plus " + plusSize + " upgrade"
                    + (plusSize > 1 ? "s" : "")
                    + " not visible due to your version stability setting of "
                    + currentLevel.name() + ")";
        }
        if (msg != null) {
            Thread.currentThread().setName(""); // Prevent thread name from
                                                // being presented in Roo shell
            LOGGER.info(msg);
        }
    }

    private void printResultList(final Collection<Bundle> bundles,
            int maxResults, final int linesPerResult) {
        int bundleId = 1;
        searchResultCache.clear();
        final StringBuilder sb = new StringBuilder();
        final List<PGPPublicKeyRing> keys = pgpService.getTrustedKeys();
        LOGGER.info(bundles.size()
                + " found, sorted by rank; T = trusted developer; R = Roo "
                + getVersionForCompatibility() + " compatible");
        LOGGER.warning("ID T R DESCRIPTION -------------------------------------------------------------");
        for (final Bundle bundle : bundles) {
            if (maxResults-- == 0) {
                break;
            }
            final BundleVersion latest = bundle.getLatestVersion();
            final String bundleKey = String.format("%02d", bundleId++);
            searchResultCache.put(bundleKey, bundle);
            sb.append(bundleKey);
            sb.append(isTrustedKey(keys, latest.getPgpKey()) ? " Y " : " - ");
            sb.append(isCompatible(latest.getRooVersion()) ? "Y " : "- ");
            sb.append(latest.getVersion());
            sb.append(" ");
            final List<String> split = new ArrayList<String>(
                    Arrays.asList(latest.getDescription().split("\\s")));
            int lpr = linesPerResult;
            while (split.size() > 0 && --lpr >= 0) {
                while (!split.isEmpty()
                        && split.get(0).length() + sb.length() < (lpr == 0 ? 77
                                : 80)) {
                    sb.append(split.get(0)).append(" ");
                    split.remove(0);
                }
                String line = sb.toString().substring(0,
                        sb.toString().length() - 1);
                if (lpr == 0 && split.size() > 0) {
                    line += "...";
                }
                LOGGER.info(line);
                sb.setLength(0);
                sb.append("       ");
            }
            if (sb.toString().trim().length() > 0) {
                LOGGER.info(sb.toString());
            }
            sb.setLength(0);
        }
        printSeparator();
        LOGGER.info("[HINT] use 'addon info id --searchResultId ..' to see details about a search result");
        LOGGER.info("[HINT] use 'addon install id --searchResultId ..' to install a specific search result, or");
        LOGGER.info("[HINT] use 'addon install bundle --bundleSymbolicName TAB' to install a specific add-on version");
    }

    private void printSeparator() {
        LOGGER.warning("--------------------------------------------------------------------------------");
    }

    public InstallOrUpgradeStatus removeAddOn(final BundleSymbolicName bsn) {
        synchronized (mutex) {
            Validate.notNull(bsn, "Bundle symbolic name required");
            boolean success = false;
            final int count = countBundles();
            success = shell
                    .executeCommand("osgi uninstall --bundleSymbolicName "
                            + bsn.getKey());
            InstallOrUpgradeStatus status;
            if (count == countBundles() || !success) {
                LOGGER.warning("Unable to remove add-on: " + bsn.getKey());
                status = InstallOrUpgradeStatus.FAILED;
            }
            else {
                LOGGER.info("Successfully removed add-on: " + bsn.getKey());
                status = InstallOrUpgradeStatus.SUCCESS;
            }
            return status;
        }
    }

    public Integer searchAddOns(final boolean showFeedback,
            final String searchTerms, final boolean refresh,
            final int linesPerResult, final int maxResults,
            final boolean trustedOnly, final boolean compatibleOnly,
            final boolean communityOnly, final String requiresCommand) {
        final List<Bundle> result = findAddons(showFeedback, searchTerms,
                refresh, linesPerResult, maxResults, trustedOnly,
                compatibleOnly, communityOnly, requiresCommand);
        return result != null ? result.size() : null;
    }

    public InstallOrUpgradeStatus upgradeAddOn(final AddOnBundleSymbolicName bsn) {
        synchronized (mutex) {
            Validate.notNull(bsn,
                    "A valid add-on bundle symbolic name is required");
            String bsnString = bsn.getKey();
            if (bsnString.contains(";")) {
                bsnString = bsnString.split(";")[0];
            }
            final Bundle bundle = bundleCache.get(bsnString);
            if (bundle == null) {
                LOGGER.warning("Could not find specified bundle with symbolic name: "
                        + bsn.getKey());
                return InstallOrUpgradeStatus.FAILED;
            }
            final BundleVersion bundleVersion = bundle.getBundleVersion(bsn
                    .getKey());
            final InstallOrUpgradeStatus status = installOrUpgradeAddOn(
                    bundleVersion, bsn.getKey(), false);
            if (status.equals(InstallOrUpgradeStatus.SUCCESS)) {
                LOGGER.info("Successfully upgraded: "
                        + bundle.getSymbolicName() + " [version: "
                        + bundleVersion.getVersion() + "]");
                LOGGER.warning("Please restart the Roo shell to complete the upgrade");
            }
            else if (status.equals(InstallOrUpgradeStatus.FAILED)) {
                LOGGER.warning("Unable to upgrade: " + bundle.getSymbolicName()
                        + " [version: " + bundleVersion.getVersion() + "]");
            }
            return status;
        }
    }

    public InstallOrUpgradeStatus upgradeAddOn(final String bundleId) {
        synchronized (mutex) {
            Validate.notBlank(bundleId, "A valid bundle ID is required");
            Bundle bundle = null;
            if (searchResultCache != null) {
                bundle = searchResultCache.get(String.format("%02d",
                        Integer.parseInt(bundleId)));
            }
            if (bundle == null) {
                LOGGER.warning("A valid bundle ID is required");
                return InstallOrUpgradeStatus.FAILED;
            }
            final BundleVersion bundleVersion = bundle
                    .getBundleVersion(bundleId);
            final InstallOrUpgradeStatus status = installOrUpgradeAddOn(
                    bundleVersion, bundle.getSymbolicName(), false);
            if (status.equals(InstallOrUpgradeStatus.SUCCESS)) {
                LOGGER.info("Successfully upgraded: "
                        + bundle.getSymbolicName() + " [version: "
                        + bundleVersion.getVersion() + "]");
                LOGGER.warning("Please restart the Roo shell to complete the upgrade");
            }
            else if (status.equals(InstallOrUpgradeStatus.FAILED)) {
                LOGGER.warning("Unable to upgrade: " + bundle.getSymbolicName()
                        + " [version: " + bundleVersion.getVersion() + "]");
            }
            return status;
        }
    }

    public void upgradeAddOns() {
        synchronized (mutex) {
            final AddOnStabilityLevel addonStabilityLevel = checkAddOnStabilityLevel(null);
            final Map<String, Bundle> bundles = getUpgradableBundles(addonStabilityLevel);
            boolean upgraded = false;
            for (final Bundle bundle : bundles.values()) {
                final BundleVersion bundleVersion = bundle.getLatestVersion();
                final InstallOrUpgradeStatus status = installOrUpgradeAddOn(
                        bundleVersion, bundle.getSymbolicName(), false);
                if (status.equals(InstallOrUpgradeStatus.SUCCESS)) {
                    LOGGER.info("Successfully upgraded: "
                            + bundle.getSymbolicName() + " [version: "
                            + bundleVersion.getVersion() + "]");
                    upgraded = true;
                }
                else if (status.equals(InstallOrUpgradeStatus.FAILED)) {
                    LOGGER.warning("Unable to upgrade: "
                            + bundle.getSymbolicName() + " [version: "
                            + bundleVersion.getVersion() + "]");
                }
            }
            if (upgraded) {
                LOGGER.warning("Please restart the Roo shell to complete the upgrade");
            }
            else {
                LOGGER.info("No add-ons / components are available for upgrade for level: "
                        + addonStabilityLevel.name());
            }
        }
    }

    public void upgradesAvailable(AddOnStabilityLevel addonStabilityLevel) {
        synchronized (mutex) {
            addonStabilityLevel = checkAddOnStabilityLevel(addonStabilityLevel);
            final Map<String, Bundle> bundles = getUpgradableBundles(addonStabilityLevel);
            if (bundles.isEmpty()) {
                LOGGER.info("No add-ons / components are available for upgrade for level: "
                        + addonStabilityLevel.name());
            }
            else {
                LOGGER.info("The following add-ons / components are available for upgrade for level: "
                        + addonStabilityLevel.name());
                printSeparator();
                for (final Entry<String, Bundle> entry : bundles.entrySet()) {
                    final BundleVersion latest = entry.getValue()
                            .getLatestVersion();
                    if (latest != null) {
                        LOGGER.info("[level: "
                                + AddOnStabilityLevel.fromLevel(
                                        AddOnStabilityLevel
                                                .getAddOnStabilityLevel(latest
                                                        .getVersion())).name()
                                + "] " + entry.getKey() + " > "
                                + latest.getVersion());
                    }
                }
                printSeparator();
            }
        }
    }

    public void upgradeSettings(AddOnStabilityLevel addOnStabilityLevel) {
        if (addOnStabilityLevel == null) {
            addOnStabilityLevel = checkAddOnStabilityLevel(null);
            LOGGER.info("Current Add-on Stability Level: "
                    + addOnStabilityLevel.name());
        }
        else {
            boolean success = true;
            prefs.putInt(ADDON_UPGRADE_STABILITY_LEVEL,
                    addOnStabilityLevel.getLevel());
            try {
                prefs.flush();
            }
            catch (final IllegalStateException ignore) {
                success = false;
            }
            if (success) {
                LOGGER.info("Add-on Stability Level: "
                        + addOnStabilityLevel.name() + " stored");
            }
            else {
                LOGGER.warning("Unable to store add-on stability level at this time");
            }
        }
    }

    private boolean verifyRepository(final String repoUrl) {
        final DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        Document doc = null;
        try {
            URL obrUrl = null;
            obrUrl = new URL(repoUrl);
            final DocumentBuilder db = dbf.newDocumentBuilder();
            if (obrUrl.toExternalForm().endsWith(".zip")) {
                ByteArrayInputStream bais = null;
                ByteArrayOutputStream baos = null;
                ZipInputStream zip = null;
                try {
                    zip = new ZipInputStream(obrUrl.openStream());
                    zip.getNextEntry();

                    baos = new ByteArrayOutputStream();
                    final 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);
                        }
                    }
                    bais = new ByteArrayInputStream(baos.toByteArray());
                    doc = db.parse(bais);
                }
                finally {
                    IOUtils.closeQuietly(zip);
                    IOUtils.closeQuietly(bais);
                    IOUtils.closeQuietly(baos);
                }
            }
            else {
                doc = db.parse(obrUrl.openStream());
            }
            Validate.notNull(doc,
                    "RooBot was unable to parse the repository document of this add-on");
            for (final Element resource : XmlUtils.findElements("resource",
                    doc.getDocumentElement())) {
                if (resource.hasAttribute("uri")) {
                    if (!resource.getAttribute("uri").startsWith("httppgp")) {
                        LOGGER.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 (final Exception e) {
            throw new IllegalStateException(
                    "RooBot was unable to parse the repository document of this add-on",
                    e);
        }
        return true;
    }
}
TOP

Related Classes of org.springframework.roo.addon.roobot.client.AddOnRooBotOperationsImpl

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.