| /* |
| * Copyright (c) 2020 Kentyou. |
| * All rights reserved. This program and the accompanying materials |
| * are 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 |
| * |
| * Contributors: |
| * Kentyou - initial API and implementation |
| */ |
| package org.eclipse.sensinact.gateway.util.stack; |
| |
| import java.util.Deque; |
| import java.util.LinkedList; |
| import java.util.Timer; |
| import java.util.TimerTask; |
| import java.util.concurrent.atomic.AtomicBoolean; |
| |
| /** |
| * Stack of <code><E></code> elements connected to an |
| * handler to which elements are one by one transmitted. |
| * The purpose of this class is asynchronous treatment |
| * |
| * @author <a href="mailto:christophe.munilla@cea.fr">Christophe Munilla</a> |
| */ |
| public class StackEngine<E, H extends StackEngineHandler<E>> implements Runnable { |
| private final int UNLIMITED_SIZE = -1; |
| private final Object lock = new Object(); |
| private int maxStackSize; |
| |
| private final Deque<E> elements; |
| private final H handler; |
| private final AtomicBoolean running; |
| private final AtomicBoolean closing; |
| private final AtomicBoolean locked; |
| private TimerTask timer; |
| |
| |
| /** |
| * Constructor |
| */ |
| public StackEngine(H handler) { |
| this.handler = handler; |
| this.elements = new LinkedList<E>(); |
| this.running = new AtomicBoolean(false); |
| this.closing = new AtomicBoolean(false); |
| this.locked = new AtomicBoolean(false); |
| this.maxStackSize = UNLIMITED_SIZE; |
| } |
| |
| /** |
| * @inheritDoc |
| * @see java.lang.Runnable#run() |
| */ |
| @Override |
| public void run() { |
| synchronized (lock) { |
| this.running.set(true); |
| } |
| while (running()) { |
| E element = pop(); |
| try { |
| if (element != null) { |
| handler.doHandle(element); |
| |
| } else { |
| if (closing.get()) { |
| stop(); |
| break; |
| } |
| try { |
| Thread.sleep(150); |
| |
| } catch (InterruptedException e) { |
| Thread.interrupted(); |
| stop(); |
| break; |
| } |
| } |
| } catch (Exception e) { |
| e.printStackTrace(); |
| stop(); |
| break; |
| } |
| } |
| synchronized (lock) { |
| this.elements.clear(); |
| } |
| } |
| |
| /** |
| * Puts the element passed as |
| * parameter to the tail of the events list |
| * |
| * @param event the element to store |
| */ |
| public void push(E element) { |
| if (this.closing.get()) { |
| return; |
| } |
| if (element == null) { |
| return; |
| } |
| int maxSize = UNLIMITED_SIZE; |
| |
| if ((maxSize = this.getMaxStackSize()) != UNLIMITED_SIZE && this.length() == maxSize) { |
| return; |
| } |
| synchronized (lock) { |
| this.elements.addLast(element); |
| } |
| } |
| |
| /** |
| * Puts the element passed as |
| * parameter to the tail of the events list |
| * |
| * @param event the element to store |
| */ |
| public void pushFirst(E element) { |
| if (this.closing.get()) { |
| return; |
| } |
| if (element == null) { |
| return; |
| } |
| int maxSize = UNLIMITED_SIZE; |
| |
| if ((maxSize = this.getMaxStackSize()) != UNLIMITED_SIZE && this.length() == maxSize) { |
| return; |
| } |
| synchronized (lock) { |
| this.elements.addFirst(element); |
| } |
| } |
| |
| /** |
| * Defines the maximum size of the Stack of <code><E></code> |
| * typed elements of this StackEngine. If a new <code><E></code> |
| * typed element is received while the stack has reached |
| * its maximum size the oldest one is removed to allow the |
| * last received to be put over the stack |
| * |
| * @param stackSize the maximum size of the Stack of |
| * <code><E></code> typed elements |
| * of this StackEngine |
| */ |
| public void setMaxStackSize(int stackSize) { |
| if (stackSize <= 0) { |
| this.maxStackSize = UNLIMITED_SIZE; |
| return; |
| } |
| this.maxStackSize = stackSize; |
| } |
| |
| /** |
| * Returns the maximum size of the Stack of <code><E></code> |
| * typed elements of this StackEngine. |
| * |
| * @return the maximum size of the Stack of |
| * <code><E></code> typed elements |
| * of this StackEngine |
| */ |
| public int getMaxStackSize() { |
| return this.maxStackSize; |
| } |
| |
| /** |
| * Returns the number of <code><E></code> elements |
| * stored in the stack |
| * |
| * @return the length of the stack |
| */ |
| public int length() { |
| int length = 0; |
| synchronized (lock) { |
| length = this.elements.size(); |
| } |
| return length; |
| } |
| |
| /** |
| * Removes and returns the element |
| * from the head of the events list |
| * |
| * @return the first of stored elements |
| */ |
| E pop() { |
| E element = null; |
| if (locked()) { |
| return element; |
| } |
| synchronized (lock) { |
| if (!this.elements.isEmpty()) { |
| element = this.elements.removeFirst(); |
| } |
| } |
| return element; |
| } |
| |
| /** |
| * Stops this StackEngine |
| */ |
| public void stop() { |
| synchronized (lock) { |
| this.running.set(false); |
| } |
| } |
| |
| /** |
| * Returns this StackEngine's running |
| * state |
| * |
| * @return the running state of this |
| * StackEngine |
| */ |
| public boolean running() { |
| boolean running = false; |
| synchronized (lock) { |
| running = this.running.get(); |
| } |
| return running; |
| } |
| |
| /** |
| * Returns this StackEngine's lock |
| * state |
| * |
| * @return the lock state of this |
| * StackEngine |
| */ |
| public boolean locked() { |
| boolean locked = false; |
| synchronized (lock) { |
| locked = this.locked.get(); |
| } |
| return locked; |
| } |
| |
| /** |
| * Sets this StackEngine's lock |
| * state |
| * |
| * @param locked the lock state of this |
| * StackEngine |
| */ |
| protected void locked(boolean locked) { |
| synchronized (lock) { |
| if (timer != null) { |
| timer.cancel(); |
| timer = null; |
| } |
| this.locked.set(locked); |
| } |
| } |
| |
| /** |
| * Defines this StackEngine's lock state as |
| * true for the delay specified as parameter |
| * |
| * @param delay |
| * the lock delay of this StackEngine |
| */ |
| /** |
| * @param delay |
| */ |
| public void locked(long delay) { |
| synchronized (lock) { |
| if (timer != null) { |
| timer.cancel(); |
| timer = null; |
| } |
| timer = new TimerTask() { |
| @Override |
| public void run() { |
| StackEngine.this.locked(false); |
| } |
| }; |
| new Timer().schedule(timer, delay); |
| this.locked.set(true); |
| } |
| } |
| |
| /** |
| * Waits until the stack is empty for closing |
| * it |
| */ |
| public void closeWhenEmpty() { |
| this.closing.set(true); |
| } |
| } |