/** * This file is part of SecureNIO. Copyright (C) 2014 K. Dermitzakis * <dermitza@gmail.com> * * SecureNIO is free software: you can redistribute it and/or modify it under * the terms of the GNU Affero General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * SecureNIO is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Affero General Public License for more * details. * * You should have received a copy of the GNU Affero General Public License * along with SecureNIO. If not, see <http://www.gnu.org/licenses/>. */ package ch.dermitza.securenio.socket.secure; import ch.dermitza.securenio.util.logging.LoggerHandler; import java.util.ArrayDeque; import java.util.logging.Logger; /** * A threaded implementation of a worker processing tasks required by an * {@link javax.net.ssl.SSLEngine} that is associated with a * {@link SecureSocket}. <p> The worker sequentially processes tasks that need * to be completed, in a FIFO fashion, notifying the associated * {@link TaskListener} once they have been completed. The {@link TaskWorker} is * otherwise waiting for incoming tasks via the {@link #addSocket(SecureSocket)} * method. * * @author K. Dermitzakis * @version 0.19 * @since 0.18 */ public class TaskWorker implements Runnable { private static final Logger logger = LoggerHandler.getLogger(TaskWorker.class.getName()); private final ArrayDeque<SecureSocket> queue = new ArrayDeque<>(); //private final ArrayList<TaskListener> listeners = new ArrayList<>(); private boolean running = false; private final TaskListener listener; /** * Create a {@link TaskWorker} instance with a single {@link TaskListener} * reference. The {@link TaskListener} is notified whenever any task has * finished being processed by the TaskWorker. * * @param listener The {@link TaskListener} to be notified of completed * tasks */ public TaskWorker(TaskListener listener) { this.listener = listener; } /** * Add a {@link SecureSocket} with an underlying * {@link javax.net.ssl.SSLEngine} that requires a task to be run. Tasks are * run in a FIFO queue according to order of socket insertion. * * @param socket The SecureSocket that requires a task to be run */ public void addSocket(SecureSocket socket) { synchronized (queue) { queue.add(socket); //fireListenersNeeded(task.getSocket()); queue.notify(); } } /** * The run() method of the {@link TaskWorker}. Here, sequential processing * of {@link javax.net.ssl.SSLEngine} tasks that need to be completed is * done in a FIFO fashion, notifying the associated {@link TaskListener} * once they have been completed. The {@link TaskWorker} is otherwise * waiting for incoming tasks via the {@link #addSocket(SecureSocket)} * method. */ @Override public void run() { logger.config("Initializing..."); running = true; SecureSocket socket; runLoop: while (running) { // Wait for data to become available synchronized (queue) { while (queue.isEmpty()) { // Check whether someone asked us to shutdown // If its the case, and as the queue is empty // we are free to break from the main loop and // call shutdown(); if (!running) { break runLoop; } try { queue.wait(); } catch (InterruptedException e) { } } // Queue has some data here // get the first instance socket = queue.remove(); } // Run the runnable in this thread Runnable r; while ((r = socket.getEngine().getDelegatedTask()) != null) { r.run(); } // Runnable finished running here, signal the listener listener.taskComplete(socket); } shutdown(); } /** * Check whether the {@link TaskWorker} is running. * * @return true if it is running, false otherwise */ public boolean isRunning() { return this.running; } /** * Set the running status of the {@link TaskWorker}. If the running status * of the worker is set to false, the TaskWorker is interrupted (if waiting * for a task) in order to cleanly shutdown. * * @param running Whether the TaskWorker should run or not */ public void setRunning(boolean running) { this.running = running; // If the worker is already blocked in queue.wait() // and someone asked us to shutdown, // we should interrupt it so that it shuts down // after processing all possible pending requests if (!running) { synchronized (queue) { queue.notify(); } } } /** * Shutdown procedure. This method is called if the {@link TaskWorker} was * asked to shutdown; it cleanly process the shutdown procedure. */ private void shutdown() { logger.config("Shutting down..."); // Clear the queue queue.clear(); // Remove all listener references //listeners.clear(); } }