co.cask.cdap.common.service.RetryOnStartFailureService.java Source code

Java tutorial

Introduction

Here is the source code for co.cask.cdap.common.service.RetryOnStartFailureService.java

Source

/*
 * Copyright  2015 Cask Data, 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 co.cask.cdap.common.service;

import com.google.common.base.Supplier;
import com.google.common.util.concurrent.AbstractService;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.Service;
import com.google.common.util.concurrent.Uninterruptibles;
import org.apache.twill.common.Threads;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.TimeUnit;

/**
 * A Guava {@link Service} that wrap around another {@link Service} such that, if the wrapped service failed
 * to start, it will get restarted based on the {@link RetryStrategy}.
 */
public class RetryOnStartFailureService extends AbstractService {

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

    private final Thread startupThread;
    private final String delegateServiceName;
    private volatile Service currentDelegate;

    /**
     * Creates a new instance.
     *
     * @param delegate a {@link Supplier} that gives new instance of the delegating Service.
     * @param retryStrategy strategy to use for retrying
     */
    public RetryOnStartFailureService(final Supplier<Service> delegate, final RetryStrategy retryStrategy) {
        final Service service = delegate.get();
        this.delegateServiceName = service.getClass().getSimpleName();
        this.startupThread = new Thread("Endure-Service-" + delegateServiceName) {
            @Override
            public void run() {
                int failures = 0;
                long startTime = System.currentTimeMillis();
                long delay = 0L;
                Service delegateService = service;

                while (delay >= 0 && !isInterrupted()) {
                    try {
                        delegateService.startAndWait();
                        currentDelegate = delegateService;
                        break;
                    } catch (Throwable t) {
                        LOG.debug("Exception raised when starting service {}", delegateServiceName, t);

                        delay = retryStrategy.nextRetry(++failures, startTime);
                        if (delay < 0) {
                            LOG.error("Failed to start service {} after {} retries in {}ms", delegateServiceName,
                                    failures, System.currentTimeMillis() - startTime);
                            notifyFailed(t);
                            break;
                        }

                        try {
                            TimeUnit.MILLISECONDS.sleep(delay);
                            LOG.debug("Retry to start service {}", delegateServiceName);
                            delegateService = delegate.get();
                        } catch (InterruptedException e) {
                            interrupt();
                        }
                    }
                }

                if (isInterrupted()) {
                    LOG.warn("Stop requested for service {} during start failure retry.", delegateServiceName);
                }
            }
        };
    }

    @Override
    protected void doStart() {
        startupThread.start();
        notifyStarted();
    }

    @Override
    protected void doStop() {
        startupThread.interrupt();
        Uninterruptibles.joinUninterruptibly(startupThread);

        Service service = currentDelegate;
        if (service != null) {
            Futures.addCallback(service.stop(), new FutureCallback<State>() {
                @Override
                public void onSuccess(State result) {
                    notifyStopped();
                }

                @Override
                public void onFailure(Throwable t) {
                    notifyFailed(t);
                }
            }, Threads.SAME_THREAD_EXECUTOR);
        } else {
            notifyStopped();
        }
    }

    @Override
    public String toString() {
        return "EndureService{" + delegateServiceName + "}";
    }
}