/* Copyright (c) 2012 LinkedIn Corp. 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.linkedin.d2.discovery.event; import static com.linkedin.d2.discovery.util.LogUtil.debug; import static com.linkedin.d2.discovery.util.LogUtil.error; import static com.linkedin.d2.discovery.util.LogUtil.info; import static com.linkedin.d2.discovery.util.LogUtil.warn; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class PropertyEventThread extends Thread { private static final Logger _log = LoggerFactory.getLogger(PropertyEventThread.class); private BlockingQueue<PropertyEvent> _messages; public PropertyEventThread(String name) { this(name, Integer.MAX_VALUE); } public PropertyEventThread(String name, int size) { this(name, size, false); } public PropertyEventThread(String name, int size, boolean start) { _messages = new LinkedBlockingQueue<PropertyEvent>(size); setDaemon(true); setName("PropertyEventThread-" + getId() + "-" + name); if (start) { start(); } } public int getRemainingCapacity() { return _messages.remainingCapacity(); } public int getQueuedMessageCount() { return _messages.size(); } @Override public void start() { info(_log, "starting thread: ", getName()); super.start(); } public boolean send(PropertyEvent message) { if (message == null) { warn(_log, "send called with null message"); } else if (!isAlive()) { error(_log, "sending message while thread ", getName(), " is down: ", message); } else { debug(_log, getName(), " got message: ", message); return _messages.add(message); } int remainingCapacity = getRemainingCapacity(); if (remainingCapacity < 1000) { warn(_log, "remaining capacity for thread ", getName(), " is dangerously low: ", remainingCapacity); } return false; } @Override public void run() { boolean shutdown = false; while (!shutdown || _messages.size() > 0) { try { PropertyEvent message = _messages.poll(1, TimeUnit.SECONDS); if (message != null) { message.run(); } else { debug(_log, "received null message in thread: ", getName()); } } catch (InterruptedException e) { shutdown = true; info(_log, "thread ", getName(), " got interrupt, so shutting down"); } catch (Exception e) { _log.error("Unhandled exception in event thread", e); } } info(_log, "thread ", getName(), " finished run() and is now dying"); } // interfaces public static abstract class PropertyEvent implements Runnable { private String _description; public PropertyEvent(String description) { _description = description; } @Override public String toString() { return "PropertyEvent [_description=" + _description + "]"; } @Override final public void run() { try { innerRun(); } catch (RuntimeException e) { _log.error("Received exception from: " + _description, e); } } abstract public void innerRun(); } public static interface PropertyEventShutdownCallback { void done(); } }