org.eclipse.gyrex.p2.internal.installer.PackageScanner.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.gyrex.p2.internal.installer.PackageScanner.java

Source

/*******************************************************************************
 * Copyright (c) 2011, 2012 AGETO Service GmbH and others.
 * 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:
 *     Gunnar Wagenknecht - initial API and implementation
 *******************************************************************************/
package org.eclipse.gyrex.p2.internal.installer;

import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;

import org.eclipse.equinox.p2.core.IProvisioningAgent;
import org.eclipse.equinox.p2.core.IProvisioningAgentProvider;

import org.eclipse.gyrex.cloud.environment.INodeEnvironment;
import org.eclipse.gyrex.p2.internal.P2Activator;
import org.eclipse.gyrex.p2.internal.P2Debug;
import org.eclipse.gyrex.p2.internal.packages.IPackageManager;
import org.eclipse.gyrex.p2.internal.packages.PackageDefinition;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.ISchedulingRule;
import org.eclipse.core.runtime.jobs.Job;

import org.osgi.framework.InvalidSyntaxException;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * The package scanner scans for new or revoked packages regularly. If new (or
 * revoked) packages were found a job will be scheduled which performs the
 * modifications.
 */
public class PackageScanner extends Job {

    static class MutexRule implements ISchedulingRule {

        private final Object object;

        public MutexRule(final Object object) {
            this.object = object;
        }

        public boolean contains(final ISchedulingRule rule) {
            return rule == this;
        }

        public boolean isConflicting(final ISchedulingRule rule) {
            if (rule instanceof MutexRule) {
                return object.equals(((MutexRule) rule).object);
            }
            return false;
        }
    }

    private static final Logger LOG = LoggerFactory.getLogger(PackageScanner.class);

    private static final long DEFAULT_INITIAL_SLEEP_TIME = TimeUnit.MINUTES.toMillis(5);
    private static final long DEFAULT_MAX_SLEEP_TIME = TimeUnit.HOURS.toMillis(2);
    private static final AtomicReference<PackageScanner> instanceRef = new AtomicReference<PackageScanner>();

    /**
     * Returns the singleton package scanner instance.
     * 
     * @return the singleton package scanner instance
     */
    public static PackageScanner getInstance() {
        final PackageScanner scanner = instanceRef.get();
        if (null != scanner) {
            return scanner;
        }
        instanceRef.compareAndSet(null, new PackageScanner());
        return instanceRef.get();
    }

    private final long initialSleepTime;
    private final long maxSleepTime;
    private long sleepTime = DEFAULT_INITIAL_SLEEP_TIME;

    /**
     * Creates a new instance.
     */
    PackageScanner() {
        super("Software Package Scanner");
        initialSleepTime = Math.max(
                Long.getLong("gyrex.p2.packageScanner.initialSleepTime", DEFAULT_INITIAL_SLEEP_TIME),
                TimeUnit.SECONDS.toMillis(15));
        maxSleepTime = Math.min(Long.getLong("gyrex.p2.packageScanner.maxSleepTime", DEFAULT_MAX_SLEEP_TIME),
                TimeUnit.HOURS.toMillis(48));
        setSystem(true);
        setPriority(LONG);
        setRule(new MutexRule(PackageScanner.class));
    }

    private IStatus doRun(final IProgressMonitor monitor) {
        IProvisioningAgent agent = null;
        try {
            final INodeEnvironment nodeEnvironment = P2Activator.getInstance().getService(INodeEnvironment.class);

            // get agent
            agent = P2Activator.getInstance().getService(IProvisioningAgentProvider.class).createAgent(null);
            if (agent == null) {
                throw new IllegalStateException(
                        "The current system has not been provisioned using p2. Unable to acquire provisioning agent.");
            }

            // collect packages that should be rolled out
            final Set<PackageDefinition> packagesToInstall = new HashSet<PackageDefinition>();
            final Set<PackageDefinition> packagesToRemove = new HashSet<PackageDefinition>();

            final IPackageManager packageManager = P2Activator.getInstance().getPackageManager();
            final Collection<PackageDefinition> packages = packageManager.getPackages();
            for (final PackageDefinition packageDefinition : packages) {
                // check filter
                if (StringUtils.isNotBlank(packageDefinition.getNodeFilter())) {
                    try {
                        if (!nodeEnvironment.matches(packageDefinition.getNodeFilter())) {
                            if (P2Debug.nodeInstallation) {
                                LOG.debug("Ignoring package {}. Not applicable to current node.",
                                        packageDefinition.getId());
                            }
                            continue;
                        }
                    } catch (final InvalidSyntaxException e) {
                        if (P2Debug.nodeInstallation) {
                            LOG.debug("Ignoring package {}. Error in node filter syntax: {}",
                                    packageDefinition.getId(), e.getMessage());
                        }
                        continue;
                    }
                }

                // check if package has been rolled out on local node
                final boolean installed = PackageInstallState.isInstalled(agent, packageDefinition);

                // add as roll-out or removal
                if (packageManager.isMarkedForInstall(packageDefinition)) {
                    if (!installed) {
                        if (P2Debug.nodeInstallation) {
                            LOG.debug("Found new package to install: {}", packageDefinition.getId());
                        }
                        packagesToInstall.add(packageDefinition);
                    } else {
                        if (P2Debug.nodeInstallation) {
                            LOG.debug("Package {} already installed. Will be ignored.", packageDefinition.getId());
                        }
                    }
                } else if (packageManager.isMarkedForUninstall(packageDefinition)) {
                    if (installed) {
                        if (P2Debug.nodeInstallation) {
                            LOG.debug("Found package that should be removed: {}", packageDefinition.getId());
                        }
                        packagesToRemove.add(packageDefinition);
                    } else {
                        if (P2Debug.nodeInstallation) {
                            LOG.debug("Package {} not installed. Will be ignored.", packageDefinition.getId());
                        }
                    }
                } else {
                    if (P2Debug.nodeInstallation) {
                        LOG.debug("Package {} neither marked for installation nor for removal. Will be ignored.",
                                packageDefinition.getId());
                    }
                }
            }

            // check if there is something to install
            // also check if there is an active install session
            // in such a case we force the installer job to run in order
            // to catch up or recover
            if (packagesToInstall.isEmpty() && packagesToRemove.isEmpty()
                    && (null == PackageInstallState.getActiveInstallSessionId())) {
                if (P2Debug.nodeInstallation) {
                    LOG.debug("Nothing to install or remove.");
                }
                return Status.OK_STATUS;
            }

            // schedule installer job
            LOG.info(
                    "Pending software package modifications found. Scheduling software installation for local node.");
            final PackageInstallerJob packageInstallerJob = new PackageInstallerJob(packagesToInstall,
                    packagesToRemove);
            packageInstallerJob.schedule(500l);

            // done
            return Status.OK_STATUS;
        } catch (final IllegalStateException e) {
            LOG.warn("Unable to for new software packages. System does not seem to be ready. {}",
                    ExceptionUtils.getRootCauseMessage(e));
            return Status.CANCEL_STATUS;
        } catch (final Exception e) {
            LOG.error("Error while checking for new or revoked software packages. {}",
                    ExceptionUtils.getRootCauseMessage(e), e);
            return Status.CANCEL_STATUS;
        } finally {
            // close agent
            if (null != agent) {
                agent.stop();
            }
        }
    }

    @Override
    protected IStatus run(final IProgressMonitor monitor) {
        try {
            final IStatus status = doRun(monitor);
            if (!status.isOK()) {
                // implement a back-off sleeping time
                sleepTime = Math.min(sleepTime * 2, maxSleepTime);
            } else {
                // reset sleep time
                sleepTime = initialSleepTime;
            }
            return status;
        } finally {
            // reschedule
            if (P2Debug.nodeInstallation) {
                LOG.debug("Rescheduling installer to run again in {} minutes",
                        TimeUnit.MILLISECONDS.toMinutes(sleepTime));
            }
            schedule(sleepTime);
        }
    }

}