Java tutorial
/** * Copyright [2012] [Datasalt Systems S.L.] * * 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 com.datasalt.pangool.solr; import java.util.concurrent.TimeUnit; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.hadoop.mapreduce.TaskInputOutputContext; import org.apache.hadoop.util.Progressable; /** * This class runs a background thread that once ever 5 seconds checks to see if a progress report is needed. If a * report is needed it is issued. * * A simple counter {@link #threadsNeedingHeartBeat} handles the number of threads requesting a heart beat. * * The expected usage pattern is * * <pre> * try { * heartBeater.needHeartBeat(); * do something that may take a while * } finally { * heartBeater.cancelHeartBeat(); * } * </pre> * <p> * <b>This class has been copied from SOLR-1301 patch although it might be slightly different from it.</b> * <p> */ public class HeartBeater extends Thread { public static Log LOG = LogFactory.getLog(HeartBeater.class); /** * count of threads asking for heart beat, at 0 no heart beat done. This could be an atomic long but then missmatches * in need/cancel could result in negative counts. */ volatile int threadsNeedingHeartBeat = 0; Progressable progress; /** * The amount of time to wait between checks for the need to issue a heart beat. In milliseconds. */ long waitTimeMs = TimeUnit.MILLISECONDS.convert(5, TimeUnit.SECONDS); public Progressable getProgress() { return progress; } /** * Create the heart beat object thread set it to daemon priority and start the thread. When the count in * {@link #threadsNeedingHeartBeat} is positive, the heart beat will be issued on the progress object ever 5 seconds. */ public HeartBeater(Progressable progress) { setDaemon(true); this.progress = progress; LOG.info("Heart beat reporting class is " + progress.getClass().getName()); start(); } public void setProgress(Progressable progress) { this.progress = progress; } @Override public void run() { LOG.info("HeartBeat thread running"); while (true) { try { synchronized (this) { if (threadsNeedingHeartBeat > 0) { progress.progress(); if (LOG.isInfoEnabled()) { LOG.info(String.format("Issuing heart beat for %d threads", threadsNeedingHeartBeat)); } } else { if (LOG.isInfoEnabled()) { LOG.info(String.format("heartbeat skipped count %d", threadsNeedingHeartBeat)); } } this.wait(waitTimeMs); } } catch (Throwable e) { LOG.error("HeartBeat throwable", e); } } } /** * inform the background thread that heartbeats are to be issued. Issue a heart beat also */ public synchronized void needHeartBeat() { threadsNeedingHeartBeat++; // Issue a progress report right away, // just in case the the cancel comes before the background thread issues a // report. // If enough cases like this happen the 600 second timeout can occur progress.progress(); if (threadsNeedingHeartBeat == 1) { // this.notify(); // wake up the heartbeater } } /** * inform the background thread that this heartbeat request is not needed. This must be called at some point after * each {@link #needHeartBeat()} request. */ public synchronized void cancelHeartBeat() { if (threadsNeedingHeartBeat > 0) { threadsNeedingHeartBeat--; } else { Exception e = new Exception("Dummy"); e.fillInStackTrace(); LOG.warn("extra call to cancelHeartBeat", e); } } @SuppressWarnings("rawtypes") public void setStatus(String status) { if (progress instanceof TaskInputOutputContext) { ((TaskInputOutputContext) progress).setStatus(status); } } }