Package org.springframework.roo.process.manager.internal

Source Code of org.springframework.roo.process.manager.internal.DefaultProcessManager

package org.springframework.roo.process.manager.internal;

import java.util.logging.Level;
import java.util.logging.Logger;

import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.Service;
import org.osgi.framework.FrameworkEvent;
import org.osgi.framework.FrameworkListener;
import org.osgi.service.component.ComponentContext;
import org.osgi.service.startlevel.StartLevel;
import org.springframework.roo.file.monitor.FileMonitorService;
import org.springframework.roo.file.monitor.MonitoringRequest;
import org.springframework.roo.file.monitor.NotifiableFileMonitorService;
import org.springframework.roo.file.undo.UndoManager;
import org.springframework.roo.process.manager.ActiveProcessManager;
import org.springframework.roo.process.manager.CommandCallback;
import org.springframework.roo.process.manager.ProcessManager;
import org.springframework.roo.process.manager.event.AbstractProcessManagerStatusPublisher;
import org.springframework.roo.process.manager.event.ProcessManagerStatus;
import org.springframework.roo.support.logging.HandlerUtils;
import org.springframework.roo.support.osgi.OSGiUtils;

/**
* Default implementation of {@link ProcessManager} interface.
*
* @author Ben Alex
* @since 1.0
*/
@Component(immediate = true)
@Service
public class DefaultProcessManager extends
        AbstractProcessManagerStatusPublisher implements ProcessManager {

    private static final Logger LOGGER = HandlerUtils
            .getLogger(DefaultProcessManager.class);

    private boolean developmentMode = false;
    @Reference private FileMonitorService fileMonitorService;
    private long lastPollDuration = 0;
    private long lastPollTime = 0; // What time the last poll was completed
    private long minimumDelayBetweenPoll = -1; // How many ms must pass at
    @Reference private StartLevel startLevel;
    @Reference private UndoManager undoManager;
    private String workingDir;

    public <T> T execute(final CommandCallback<T> callback) {
        Validate.notNull(callback, "Callback required");
        synchronized (processManagerStatus) {
            // For us to acquire this lock means no other thread has hold of
            // process manager status
            Validate.isTrue(
                    getProcessManagerStatus() == ProcessManagerStatus.AVAILABLE
                            || getProcessManagerStatus() == ProcessManagerStatus.BUSY_EXECUTING,
                    "Unable to execute as another thread has set status to %s",
                    getProcessManagerStatus());
            setProcessManagerStatus(ProcessManagerStatus.BUSY_EXECUTING);
            try {
                return doTransactionally(callback);
            }
            catch (final RuntimeException e) {
                logException(e);
                throw e;
            }
            finally {
                setProcessManagerStatus(ProcessManagerStatus.AVAILABLE);
            }
        }
    }

    /**
     * @return how many milliseconds the last poll execution took to complete (0
     *         = never ran; >0 = last execution time)
     */
    public long getLastPollDuration() {
        return lastPollDuration;
    }

    /**
     * @return how many milliseconds must pass between each poll (0 = manual
     *         only; <0 = auto-scaled; >0 = interval)
     */
    public long getMinimumDelayBetweenPoll() {
        return minimumDelayBetweenPoll;
    }

    public boolean isDevelopmentMode() {
        return developmentMode;
    }

    public void setDevelopmentMode(final boolean developmentMode) {
        this.developmentMode = developmentMode;

        // To assist with debugging, development mode does not undertake undo
        // operations
        undoManager.setUndoEnabled(!developmentMode);
    }

    /**
     * @param minimumDelayBetweenPoll how many milliseconds must pass between
     *            each poll
     */
    public void setMinimumDelayBetweenPoll(final long minimumDelayBetweenPoll) {
        this.minimumDelayBetweenPoll = minimumDelayBetweenPoll;
    }

    public void terminate() {
        synchronized (processManagerStatus) {
            // To get this far this thread has a lock on process manager status,
            // so we control process manager and can terminate its background
            // timer thread
            if (getProcessManagerStatus() != ProcessManagerStatus.TERMINATED) {
                // The thread started above will terminate of its own accord,
                // given we are shutting down
                setProcessManagerStatus(ProcessManagerStatus.TERMINATED);
            }
        }
    }

    public void timerBasedPoll() {
        try {
            if (minimumDelayBetweenPoll == 0) {
                // Manual polling only, we never allow the timer to kick of a
                // poll
                return;
            }

            long effectiveMinimumDelayBetweenPoll = minimumDelayBetweenPoll;
            if (effectiveMinimumDelayBetweenPoll < 0) {
                // A negative minimum delay between poll means auto-scaling is
                // used
                if (lastPollDuration < 500) {
                    // We've never done a poll, or they are very fast
                    effectiveMinimumDelayBetweenPoll = 0;
                }
                else {
                    // Use the last duration (we might make this sliding scale
                    // in the future)
                    effectiveMinimumDelayBetweenPoll = lastPollDuration;
                }
            }
            final long started = System.currentTimeMillis();
            if (started < lastPollTime + effectiveMinimumDelayBetweenPoll) {
                // Too soon to re-poll
                return;
            }
            backgroundPoll();
            // Record the completion time so we can ensure we don't re-poll too
            // soon
            lastPollTime = System.currentTimeMillis();

            // Compute how many milliseconds it took to run
            lastPollDuration = lastPollTime - started;
            if (lastPollDuration == 0) {
                // Ensure it correctly reflects that it has ever run
                lastPollDuration = 1;
            }
        }
        catch (final Throwable t) {
            LOGGER.log(Level.SEVERE, t.getMessage(), t);
        }
    }

    protected void activate(final ComponentContext context) {
        workingDir = OSGiUtils.getRooWorkingDirectory(context);
        context.getBundleContext().addFrameworkListener(
                new FrameworkListener() {
                    public void frameworkEvent(final FrameworkEvent event) {
                        if (startLevel.getStartLevel() >= 99) {
                            // We check we haven't already started, as this
                            // event listener will be called several times at SL
                            // >= 99
                            if (getProcessManagerStatus() == ProcessManagerStatus.STARTING) {
                                // A proper synchronized process manager status
                                // check will take place in the
                                // completeStartup() method
                                completeStartup();
                            }
                        }
                    }
                });

        // Now start a thread that will undertake a background poll every second
        final Thread t = new Thread(new Runnable() {
            public void run() {
                // Unsynchronized lookup of terminated status to avoid anything
                // blocking the termination of the thread
                while (getProcessManagerStatus() != ProcessManagerStatus.TERMINATED) {
                    // We only bother doing a poll if we seem to be available (a
                    // proper synchronized check happens later)
                    if (getProcessManagerStatus() == ProcessManagerStatus.AVAILABLE) {
                        timerBasedPoll();
                    }
                    try {
                        Thread.sleep(1000);
                    }
                    catch (final InterruptedException ignoreAndContinue) {
                    }
                }
            }
        }, "Spring Roo Process Manager Background Polling Thread");
        t.start();
    }

    protected void deactivate(final ComponentContext context) {
        // We have lost a required component (eg UndoManager; ROO-1037)
        terminate(); // Safe to call even if we'd terminated earlier
    }

    private boolean backgroundPoll() {
        // Quickly determine if another thread is running; we don't need to sit
        // around and wait (we'll get called again in a few hundred milliseconds
        // anyway)
        if (getProcessManagerStatus() != ProcessManagerStatus.AVAILABLE) {
            return false;
        }
        synchronized (processManagerStatus) {
            // Do the check again, now this thread has a lock on
            // processManagerStatus
            if (getProcessManagerStatus() != ProcessManagerStatus.AVAILABLE) {
                throw new IllegalStateException(
                        "Process manager status "
                                + getProcessManagerStatus()
                                + " but background thread acquired synchronization lock");
            }

            setProcessManagerStatus(ProcessManagerStatus.BUSY_POLLING);

            try {
                doTransactionally(null);
            }
            catch (final Throwable t) {
                // We don't want a poll failure to cause the background polling
                // thread to die
                logException(t);
            }
            finally {
                setProcessManagerStatus(ProcessManagerStatus.AVAILABLE);
            }
        }
        return true;
    }

    private void completeStartup() {
        synchronized (processManagerStatus) {
            if (getProcessManagerStatus() != ProcessManagerStatus.STARTING) {
                throw new IllegalStateException("Process manager status "
                        + getProcessManagerStatus() + " but should be STARTING");
            }
            setProcessManagerStatus(ProcessManagerStatus.COMPLETING_STARTUP);
            try {
                // Register the initial monitoring request
                doTransactionally(new MonitoringRequestCommand(
                        fileMonitorService,
                        MonitoringRequest
                                .getInitialSubTreeMonitoringRequest(workingDir),
                        true));
            }
            catch (final Throwable t) {
                logException(t);
            }
            finally {
                setProcessManagerStatus(ProcessManagerStatus.AVAILABLE);
            }
        }
    }

    private <T> T doTransactionally(final CommandCallback<T> callback) {
        T result = null;
        try {
            ActiveProcessManager.setActiveProcessManager(this);

            // Run the requested operation
            if (callback == null) {
                fileMonitorService.scanAll();
            }
            else {
                result = callback.callback();
            }

            // Flush the undo manager so that any changes it has been holding
            // are written to disk and the file monitor service
            undoManager.flush();

            // Guarantee scans repeat until there are no more changes detected
            while (fileMonitorService.isDirty()) {
                if (fileMonitorService instanceof NotifiableFileMonitorService) {
                    ((NotifiableFileMonitorService) fileMonitorService)
                            .scanNotified();
                }
                else {
                    fileMonitorService.scanAll();
                }
                // In case something else happened as a result of event
                // notifications above
                undoManager.flush();
            }

            // It all seems to have worked, so clear the undo history
            setProcessManagerStatus(ProcessManagerStatus.RESETTING_UNDOS);

            undoManager.reset();

        }
        catch (final RuntimeException e) {
            // Something went wrong, so attempt to undo
            try {
                setProcessManagerStatus(ProcessManagerStatus.UNDOING);
                throw e;
            }
            finally {
                undoManager.undo();
            }
        }
        finally {
            // TODO: Review in consultation with Christian as STS is clearing
            // active process manager itself
            // ActiveProcessManager.clearActiveProcessManager();
        }

        return result;
    }

    private void logException(final Throwable t) {
        final Throwable root = ObjectUtils.defaultIfNull(
                ExceptionUtils.getRootCause(t), t);
        if (developmentMode) {
            LOGGER.log(Level.FINE, root.getMessage(), root);
        }
        else {
            String message = root.getMessage();
            if (StringUtils.isBlank(message)) {
                final StackTraceElement[] trace = root.getStackTrace();
                if (trace != null && trace.length > 0) {
                    message = root.getClass().getSimpleName() + " at "
                            + trace[0].toString();
                }
                else {
                    message = root.getClass().getSimpleName();
                }
            }
            LOGGER.log(Level.FINE, message);
        }
    }
}
TOP

Related Classes of org.springframework.roo.process.manager.internal.DefaultProcessManager

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.