io.smartspaces.util.process.StandardNativeApplicationRunnerCollection.java Source code

Java tutorial

Introduction

Here is the source code for io.smartspaces.util.process.StandardNativeApplicationRunnerCollection.java

Source

/*
 * Copyright (C) 2016 Keith M. Hughes
 * Copyright (C) 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

package io.smartspaces.util.process;

import io.smartspaces.SimpleSmartSpacesException;
import io.smartspaces.system.SmartSpacesEnvironment;
import io.smartspaces.util.process.NativeApplicationRunner.NativeApplicationRunnerState;

import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

import org.apache.commons.logging.Log;

import com.google.common.collect.Lists;

/**
 * A collection of {@link NativeApplicationRunner} instances.
 *
 * <p>
 * This collection ensures the runners are properly sampled and shutdown
 * properly, do not attempt to handle them yourself.
 *
 * @author Keith M. Hughes
 */
public class StandardNativeApplicationRunnerCollection implements NativeApplicationRunnerCollection {

    /**
     * The count for the countdown latch for
     * {@link #runNativeApplicationRunner(NativeApplicationDescription, long)}.
     */
    public static final int BLOCKING_RUN_COUNTDOWN_COUNT = 1;

    /**
     * The default for how often to sample the runners to see if they are still
     * running, in milliseconds.
     */
    public static final long SAMPLING_PERIOD_DEFAULT = 500;

    /**
     * How often to sample the runners to see if they are still running, in
     * milliseconds.
     */
    private long samplingPeriod = SAMPLING_PERIOD_DEFAULT;

    /**
     * The parser for native applications.
     */
    private NativeApplicationRunnerParser runnerParser = new StandardNativeApplicationRunnerParser();

    /**
     * The space environment to use.
     */
    private final SmartSpacesEnvironment spaceEnvironment;

    /**
     * The logger for issues.
     */
    private final Log log;

    /**
     * All runners currently running.
     */
    private final List<NativeApplicationRunner> runners = Lists.newCopyOnWriteArrayList();

    /**
     * The factory for new runners.
     */
    private final NativeApplicationRunnerFactory runnerFactory;

    /**
     * The future used to control the sampling thread.
     */
    private Future<?> samplingFuture;

    /**
     * Construct a new collection.
     *
     * @param spaceEnvironment
     *          the space environment for the collection to use
     * @param log
     *          the logger
     */
    public StandardNativeApplicationRunnerCollection(SmartSpacesEnvironment spaceEnvironment, Log log) {
        this.spaceEnvironment = spaceEnvironment;
        this.log = log;

        runnerFactory = new SimpleNativeApplicationRunnerFactory(runnerParser, spaceEnvironment);
    }

    @Override
    public synchronized void startup() {
        if (samplingFuture == null) {
            for (NativeApplicationRunner runner : runners) {
                runner.startup();
            }
            samplingFuture = spaceEnvironment.getExecutorService().scheduleAtFixedRate(new Runnable() {
                @Override
                public void run() {
                    sampleRunners();
                }
            }, samplingPeriod, samplingPeriod, TimeUnit.MILLISECONDS);
        }
    }

    @Override
    public synchronized void shutdown() {
        if (samplingFuture != null) {
            samplingFuture.cancel(true);
            samplingFuture = null;
        }

        for (NativeApplicationRunner runner : runners) {
            runner.shutdown();
        }
    }

    @Override
    public NativeApplicationRunner addNativeApplicationRunner(NativeApplicationDescription description) {
        NativeApplicationRunner runner = newNativeApplicationRunner();
        runner.configure(description);

        addNativeApplicationRunner(runner);

        return runner;
    }

    @Override
    public synchronized void addNativeApplicationRunner(NativeApplicationRunner runner) {
        runners.add(runner);

        if (samplingFuture != null) {
            runner.startup();
        }
    }

    @Override
    public NativeApplicationRunner newNativeApplicationRunner() {
        return runnerFactory.newPlatformNativeApplicationRunner(log);
    }

    @Override
    public NativeApplicationRunnerState runNativeApplicationRunner(NativeApplicationDescription description,
            long waitTime) {
        NativeApplicationRunner runner = newNativeApplicationRunner();
        runner.configure(description);

        final CountDownLatch runnerComplete = new CountDownLatch(BLOCKING_RUN_COUNTDOWN_COUNT);
        runner.addNativeApplicationRunnerListener(new BaseNativeApplicationRunnerListener() {

            @Override
            public void onNativeApplicationRunnerStartupFailed(NativeApplicationRunner runner) {
                runnerComplete.countDown();
            }

            @Override
            public void onNativeApplicationRunnerShutdown(NativeApplicationRunner runner) {
                runnerComplete.countDown();
            }
        });

        addNativeApplicationRunner(runner);

        try {
            if (!runnerComplete.await(waitTime, TimeUnit.MILLISECONDS)) {
                SimpleSmartSpacesException.throwFormattedException("The command %s did not complete in %d msec",
                        description, waitTime);
            }
        } catch (SimpleSmartSpacesException e) {
            throw e;
        } catch (InterruptedException e) {
            SimpleSmartSpacesException.throwFormattedException("The command %s wait was interrupted", description);
        }

        return runner.getState();
    }

    /**
     * Sample all runners and see if they are still running.
     */
    private void sampleRunners() {
        // No need to synchronize as the collection is properly threadsafe.
        for (NativeApplicationRunner runner : runners) {
            if (!runner.isRunning()) {
                // This works because of the copy on write
                runners.remove(runner);
            }
        }
    }
}