/* * Copyright 2012 The Solmix Project * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software 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 * Lesser General Public License for more details. * * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.gnu.org/licenses/ * or see the FSF site: http://www.fsf.org. */ package org.solmix.eventservice.deliver; import java.util.Iterator; import java.util.List; import java.util.concurrent.TimeoutException; import org.solmix.eventservice.EventDeliver; import org.solmix.eventservice.EventTask; import org.solmix.eventservice.util.EventThreadPool; import org.solmix.eventservice.util.SyncEventThread; /** * * @author solmix.f@gmail.com * @version 110035 2011-10-1 */ public class SyncDeliver implements EventDeliver { private final EventThreadPool threadPool; private int timeout; /** The matchers for ignore timeout handling. */ private Matcher[] ignoreTimeoutMatcher; public SyncDeliver(final EventThreadPool threadPool, int timeout, String[] ignoreTimeout) { this.threadPool = threadPool; update(timeout, ignoreTimeout); } public void update(final int timeout, final String[] ignoreTimeout) { this.timeout = timeout; if (ignoreTimeout == null || ignoreTimeout.length == 0) { ignoreTimeoutMatcher = null; } else { Matcher[] ignoreTimeoutMatcher = new Matcher[ignoreTimeout.length]; for (int i = 0; i < ignoreTimeout.length; i++) { String value = ignoreTimeout[i]; if (value != null) { value = value.trim(); } if (value != null && value.length() > 0) { if (value.endsWith(".")) { ignoreTimeoutMatcher[i] = new PackageMatcher(value.substring(0, value.length() - 1)); } else if (value.endsWith("*")) { ignoreTimeoutMatcher[i] = new SubPackageMatcher(value.substring(0, value.length() - 1)); } else { ignoreTimeoutMatcher[i] = new ClassMatcher(value); } } } this.ignoreTimeoutMatcher = ignoreTimeoutMatcher; } } /** * {@inheritDoc} * * @see org.solmix.eventservice.EventDeliver#execute(java.util.List) */ @Override public void execute(List<EventTask> tasks) { final Thread sleepingThread = Thread.currentThread(); final SyncEventThread syncThread = sleepingThread instanceof SyncEventThread ? (SyncEventThread) sleepingThread : null; final Iterator<EventTask> i = tasks.iterator(); while (i.hasNext()) { final EventTask task = i.next(); if (!useTimeout(task)) { // no timeout, we can directly execute task.execute(); } else if (syncThread != null) { // if this is a cascaded event, we directly use this thread // otherwise we could end up in a starvation final long startTime = System.currentTimeMillis(); task.execute(); if (System.currentTimeMillis() - startTime > timeout) { task.blackListHandler(); } } else { final Rendezvous startBarrier = new Rendezvous(); final Rendezvous timerBarrier = new Rendezvous(); threadPool.executeTask(new Runnable() { @Override public void run() { try { // notify the outer thread to start the timer startBarrier.waitForRendezvous(); // execute the task task.execute(); // stop the timer timerBarrier.waitForRendezvous(); } catch (final IllegalStateException ise) { // this can happen on shutdown, so we ignore it } } }); // we wait for the inner thread to start startBarrier.waitForRendezvous(); // timeout handling // we sleep for the sleep time // if someone wakes us up it's the finished inner task try { timerBarrier.waitAttemptForRendezvous(timeout); } catch (final TimeoutException ie) { // if we timed out, we have to blacklist the handler task.blackListHandler(); } } } } private boolean useTimeout(EventTask task) { // we only check the classname if a timeout is configured if (timeout > 0) { final Matcher[] ignoreTimeoutMatcher = this.ignoreTimeoutMatcher; if (ignoreTimeoutMatcher != null) { final String className = task.getHandlerClassName(); for (int i = 0; i < ignoreTimeoutMatcher.length; i++) { if (ignoreTimeoutMatcher[i] != null) { if (ignoreTimeoutMatcher[i].match(className)) { return false; } } } } return true; } return false; } /** * The matcher interface for checking if timeout handling is disabled for the handler. Matching is based on the * class name of the event handler. */ private static interface Matcher { boolean match(String className); } /** Match a package. */ private static final class PackageMatcher implements Matcher { private final String packageName; public PackageMatcher(final String name) { packageName = name; } @Override public boolean match(String className) { final int pos = className.lastIndexOf('.'); return pos > -1 && className.substring(0, pos).equals(packageName); } } /** Match a package or sub package. */ private static final class SubPackageMatcher implements Matcher { private final String packageName; public SubPackageMatcher(final String name) { packageName = name + '.'; } @Override public boolean match(String className) { final int pos = className.lastIndexOf('.'); return pos > -1 && className.substring(0, pos + 1).startsWith(packageName); } } /** Match a class name. */ private static final class ClassMatcher implements Matcher { private final String className; public ClassMatcher(final String name) { className = name; } @Override public boolean match(String className) { return className.equals(this.className); } } }