edu.umn.msi.tropix.common.concurrent.impl.RepeaterSupplierImpl.java Source code

Java tutorial

Introduction

Here is the source code for edu.umn.msi.tropix.common.concurrent.impl.RepeaterSupplierImpl.java

Source

/*******************************************************************************
 * Copyright 2009 Regents of the University of Minnesota. All rights
 * reserved.
 * Copyright 2009 Mayo Foundation for Medical Education and Research.
 * All rights reserved.
 *
 * This program is 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
 *
 * 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 INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS
 * OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A
 * PARTICULAR PURPOSE.  See the License for the specific language
 * governing permissions and limitations under the License.
 *
 * Contributors:
 * Minnesota Supercomputing Institute - initial API and implementation
 ******************************************************************************/

package edu.umn.msi.tropix.common.concurrent.impl;

import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

import javax.annotation.PreDestroy;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.jmx.export.annotation.ManagedAttribute;
import org.springframework.jmx.export.annotation.ManagedOperation;
import org.springframework.jmx.export.annotation.ManagedResource;

import com.google.common.base.Supplier;

import edu.umn.msi.tropix.common.concurrent.Repeater;
import edu.umn.msi.tropix.common.logging.ExceptionUtils;
import edu.umn.msi.tropix.common.shutdown.ShutdownAware;
import edu.umn.msi.tropix.common.shutdown.ShutdownException;

/**
 * This class supplies instances of the {@link Repeater} interface. Many different
 * options for how to configuration repitition can be configured via the setters in 
 * this class. Additionally, when instatitated as a Spring bean these options may be
 * altered at Runtime via JMX.
 * 
 * @author John Chilton
 *
 * @param <T> Type parameters of Repeaters to create.
 */
@ManagedResource
public class RepeaterSupplierImpl<T extends Runnable> implements Supplier<Repeater<T>>, ShutdownAware {
    private static final Log LOG = LogFactory.getLog(RepeaterSupplierImpl.class);
    private long maxAttempts = -1;
    private long maxSleepDuration = Long.MAX_VALUE;
    private long sleepDurationDelta = 1;
    private long initialSleepDuration = 1;
    private TimeUnit sleepTimeUnit = TimeUnit.SECONDS;
    private final ConcurrentHashMap<Long, RepeaterImpl> repeaterMap = new ConcurrentHashMap<Long, RepeaterImpl>();
    private boolean shutdown = false;

    @ManagedOperation
    public void killRepeater(final long threadId) {
        final RepeaterImpl repeater = this.repeaterMap.get(threadId);
        if (repeater != null) {
            repeater.kill();
        }
    }

    @ManagedOperation
    public String activeRepeaters() {
        final StringBuilder activeRepeaters = new StringBuilder();
        for (final Entry<Long, RepeaterImpl> entry : this.repeaterMap.entrySet()) {
            activeRepeaters.append(entry.getKey() + "[" + entry.getValue() + "]");
        }
        return activeRepeaters.toString();
    }

    public Repeater<T> get() {
        final RepeaterImpl repeaterImpl = new RepeaterImpl();
        return repeaterImpl;
    }

    class RepeaterImpl implements Repeater<T> {
        private T baseRunnable;
        private boolean succeeded = false;
        private long sleepDuration = RepeaterSupplierImpl.this.initialSleepDuration;
        private boolean killed;

        public void kill() {
            this.killed = true;
        }

        public void setBaseRunnable(final T baseRunnable) {
            this.baseRunnable = baseRunnable;
        }

        private void doSleep() {
            try {
                RepeaterSupplierImpl.this.sleepTimeUnit.sleep(this.sleepDuration);
            } catch (final InterruptedException e) {
                ExceptionUtils.logQuietly(LOG, e);
            }
            this.sleepDuration += RepeaterSupplierImpl.this.sleepDurationDelta;
            this.sleepDuration = (this.sleepDuration > RepeaterSupplierImpl.this.maxSleepDuration)
                    ? RepeaterSupplierImpl.this.maxSleepDuration
                    : this.sleepDuration;
        }

        public boolean done(final long attempts) {
            return RepeaterSupplierImpl.this.shutdown || this.succeeded || this.killed
                    || (RepeaterSupplierImpl.this.maxAttempts != -1
                            && attempts >= RepeaterSupplierImpl.this.maxAttempts);
        }

        public void run() {
            long attempts = 0;
            RuntimeException lastException = null;
            RepeaterSupplierImpl.this.repeaterMap.put(Thread.currentThread().getId(), this);
            try {
                boolean firstAttempt = true;
                while (!this.done(attempts)) {
                    if (firstAttempt) {
                        firstAttempt = false;
                    } else {
                        this.doSleep();
                    }
                    try {
                        this.baseRunnable.run();
                        this.succeeded = true;
                    } catch (final Throwable t) {
                        lastException = ExceptionUtils.convertException(t);
                    }
                    attempts++;
                }
            } finally {
                RepeaterSupplierImpl.this.repeaterMap.remove(Thread.currentThread().getId());
            }
            if (!this.succeeded) {
                if (RepeaterSupplierImpl.this.shutdown) {
                    throw new ShutdownException();
                } else {
                    throw lastException;
                }
            }
        }

        public String toString() {
            return this.baseRunnable.toString();
        }

    }

    @PreDestroy
    public void destroy() {
        this.shutdown = true;
    }

    public void setSleepTimeUnit(final TimeUnit sleepTimeUnit) {
        this.sleepTimeUnit = sleepTimeUnit;
    }

    @ManagedAttribute
    public void setSleepTimeUnit(final String sleepTimeUnitString) {
        final TimeUnit sleepTimeUnit = Enum.valueOf(TimeUnit.class, sleepTimeUnitString);
        if (sleepTimeUnit != null) {
            this.setSleepTimeUnit(sleepTimeUnit);
        }
    }

    @ManagedAttribute
    public void setMaxAttempts(final long maxAttempts) {
        this.maxAttempts = maxAttempts;
    }

    @ManagedAttribute
    public void setMaxSleepDuration(final long maxSleepDuration) {
        this.maxSleepDuration = maxSleepDuration;
    }

    @ManagedAttribute
    public void setSleepDurationDelta(final long sleepDurationDelta) {
        this.sleepDurationDelta = sleepDurationDelta;
    }

    @ManagedAttribute
    public void setInitialSleepDuration(final long initialSleepDuration) {
        this.initialSleepDuration = initialSleepDuration;
    }

    @ManagedAttribute
    public long getMaxAttempts() {
        return this.maxAttempts;
    }

    @ManagedAttribute
    public long getMaxSleepDuration() {
        return this.maxSleepDuration;
    }

    @ManagedAttribute
    public long getSleepDurationDelta() {
        return this.sleepDurationDelta;
    }

    @ManagedAttribute
    public long getInitialSleepDuration() {
        return this.initialSleepDuration;
    }

    @ManagedAttribute
    public String getSleepTimeUnit() {
        return this.sleepTimeUnit.name();
    }

}