blob: c676ffb35e1e2d3f63f9ac1447ccf2ee5f46d17d [file] [log] [blame]
/*
* 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>&lt;E&gt;</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>&lt;E&gt;</code>
* typed elements of this StackEngine. If a new <code>&lt;E&gt;</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>&lt;E&gt;</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>&lt;E&gt;</code>
* typed elements of this StackEngine.
*
* @return the maximum size of the Stack of
* <code>&lt;E&gt;</code> typed elements
* of this StackEngine
*/
public int getMaxStackSize() {
return this.maxStackSize;
}
/**
* Returns the number of <code>&lt;E&gt;</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);
}
}