/* * ProActive Parallel Suite(TM): * The Open Source library for parallel and distributed * Workflows & Scheduling, Orchestration, Cloud Automation * and Big Data Analysis on Enterprise Grids & Clouds. * * Copyright (c) 2007 - 2017 ActiveEon * Contact: contact@activeeon.com * * This library 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: version 3 of * the License. * * This program 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 this program. If not, see <http://www.gnu.org/licenses/>. * * If needed, contact us to obtain a release under GPL Version 2 or 3 * or a different license than the AGPL. */ package org.ow2.proactive.scheduler.core; import java.lang.reflect.Method; import java.util.LinkedList; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import org.apache.log4j.Logger; import org.objectweb.proactive.core.UniqueID; import org.objectweb.proactive.utils.NamedThreadFactory; import org.ow2.proactive.scheduler.common.SchedulerEventListener; import org.ow2.proactive.scheduler.core.properties.PASchedulerProperties; import org.ow2.proactive.threading.ReifiedMethodCall; /** * ClientRequestHandler is used to delegate event call to client. * This class should be used with the ThreadPoolController which handles threads. * * @author The ProActive Team * @since ProActive Scheduling 2.0 */ public class ClientRequestHandler { public static final Logger logger = Logger.getLogger(ClientRequestHandler.class); /** Number of threads used by the thread pool for clients events sending */ private static final int THREAD_NUMBER = PASchedulerProperties.SCHEDULER_LISTENERS_THREADNUMBER.getValueAsInt(); /** thread pool */ private static final ExecutorService threadPoolForNetworkCalls = Executors.newFixedThreadPool(THREAD_NUMBER, new NamedThreadFactory("ClientEventHandlerPool")); private static final AtomicInteger requestLeft = new AtomicInteger(); public static void terminate() { // Precondition: no new event is emitted try { synchronized (requestLeft) { //obviously wait for events to be sent only if their are events if (requestLeft.get() > 0) { requestLeft.wait(); } } threadPoolForNetworkCalls.shutdown(); threadPoolForNetworkCalls.awaitTermination(13, TimeUnit.SECONDS); } catch (Exception e) { } } /** Busy state of this client request queue */ private final AtomicBoolean busy = new AtomicBoolean(false); /** Client id on which to send the request */ private final UniqueID clientId; /** Client (listener) on which to send the request */ private final SchedulerEventListener client; /** Events queue to be stored */ private final LinkedList<ReifiedMethodCall> eventCallsToStore; /** Cross reference to the front-end : used to mark client as dirty */ private final SchedulerFrontendState frontend; /** * Create a new instance of ClientRequestHandler * * @param frontend a link to the front-end * @param clientId the Id of the client on which to talk to. * @param client the reference on the client itself. */ public ClientRequestHandler(SchedulerFrontendState frontend, UniqueID clientId, SchedulerEventListener client) { this.client = client; this.frontend = frontend; this.clientId = clientId; this.eventCallsToStore = new LinkedList<>(); } /** * Add an event to the request queue of this client * * @param method the method to be called (must be a method implemented by the client) * @param args the argument to be passed to the method */ public void addEvent(Method method, Object... args) { synchronized (eventCallsToStore) { eventCallsToStore.add(new ReifiedMethodCall(method, args)); requestLeft.incrementAndGet(); } tryStartTask(); } /** * Try to create a task with new events to send, and start it in the thread pool. * Can do nothing if some previous events are currently being sent. * * Can be called from two different thread, even if it is private! */ @SuppressWarnings("unchecked") private void tryStartTask() { synchronized (eventCallsToStore) { if (eventCallsToStore.size() > 0 && !busy.get()) { LinkedList<ReifiedMethodCall> tasks = (LinkedList<ReifiedMethodCall>) eventCallsToStore.clone(); eventCallsToStore.clear(); busy.set(true); threadPoolForNetworkCalls.execute(new TaskRunnable(tasks)); } } } /** * TaskRunnable is the task in charge to send the events in its list. * * @author The ProActive Team * @since ProActive Scheduling 2.0 */ class TaskRunnable implements Runnable { /** Events queue to be sent */ private final LinkedList<ReifiedMethodCall> eventCallsToSend; /** * Create a new instance of Task * * @param eventCallsToSend */ public TaskRunnable(LinkedList<ReifiedMethodCall> eventCalls) { if (eventCalls == null || eventCalls.size() == 0) { throw new IllegalArgumentException("List argument must not be null nor empty !"); } this.eventCallsToSend = eventCalls; } /** * {@inheritDoc} */ public void run() { try { //unlock shutdown request if needed if (requestLeft.addAndGet(-eventCallsToSend.size()) == 0) { synchronized (requestLeft) { //inner synchronized OK since only used during termination (= no new event) requestLeft.notify(); } } //loop on the list and send events while (!eventCallsToSend.isEmpty()) { ReifiedMethodCall methodCall = eventCallsToSend.removeFirst(); methodCall.getMethod().invoke(client, methodCall.getArguments()); } busy.set(false); //try to empty the events list if no event comes from the core tryStartTask(); } catch (Throwable t) { if (logger.isDebugEnabled()) { logger.debug("Error during sending event to the cleint " + clientId, t); } //remove this client from Frontend (client dead or timed out) frontend.markAsDirty(clientId); //do not set busy here, we don't want to wait N times for the network timeout //so when client is dead keep busy to avoid re-execution of this run method by another thread busy.set(false); } } } }