/* * ============================================================================ * GNU Lesser General Public License * ============================================================================ * * Beanlet - JSE Application Container. * Copyright (C) 2006 Leon van Zantvoort * * This library 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 library 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 should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. * * Leon van Zantvoort * 243 Acalanes Drive #11 * Sunnyvale, CA 94086 * USA * * zantvoort@users.sourceforge.net * http://beanlet.org */ package org.beanlet.impl; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.FutureTask; import java.util.concurrent.Executor; import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import org.beanlet.common.event.ScheduleEventImpl; import org.jargo.ComponentEventException; import org.jargo.ComponentFactory; import org.jargo.ComponentReference; import org.jargo.ComponentReferenceLifecycle; import org.jargo.ComponentLifecycle; import org.jargo.Event; public final class ScheduleBeanletLifecycleImpl<T> implements ComponentLifecycle<T>, ComponentReferenceLifecycle<T> { private static final Event event = new ScheduleEventImpl(); private final Executor executor; private final int threads; private final boolean once; private final long initialDelay; private final long delay; private final long rate; private final CronExpression cron; private final boolean fireAll; private final boolean interrupt; private final boolean join; private final List<AtomicReference<Future>> futures; private final ThreadLocal<Future> threadLocal; private final CountDownLatch latch; private final Lock lock; private final Condition condition; public ScheduleBeanletLifecycleImpl(Executor executor, boolean once, long initialDelay, long delay, long rate, CronExpression cron, boolean fireAll, boolean interrupt, boolean join) { this.executor = executor; this.threads = 1; this.once = once; this.initialDelay = initialDelay; this.delay = delay; this.rate = rate; this.cron = cron; this.fireAll = fireAll; this.interrupt = interrupt; this.join = join; this.futures = new ArrayList<AtomicReference<Future>>(); this.threadLocal = new ThreadLocal<Future>(); this.latch = new CountDownLatch(threads); for (int i = 0; i < threads; i++) { futures.add(i, new AtomicReference<Future>()); } this.lock = new ReentrantLock(); this.condition = lock.newCondition(); } public void onCreate(ComponentFactory<T> factory) { if (factory.getComponentMetaData().isStatic()) { doCreate(factory.create()); } } public void onDestroy(ComponentFactory<T> factory) { if (factory.getComponentMetaData().isStatic()) { doDestroy(factory.create()); } } public void onCreate(ComponentReference<T> reference) { if (!reference.getComponentMetaData().isStatic()) { doCreate(reference); } } public void onDestroy(ComponentReference<T> reference) { if (!reference.getComponentMetaData().isStatic()) { doDestroy(reference); } } private void doCreate(ComponentReference reference) { final ComponentReference ref; if (!reference.getComponentMetaData().isStatic()) { // Maintain no strong reference to non static beanlet references. ref = reference.weakReference(); } else { // Always maintain a strong reference to static beanlet references. ref = reference; } final ClassLoader loader = AccessController.doPrivileged( new PrivilegedAction<ClassLoader>() { public ClassLoader run() { // PERMISSION: java.lang.RuntimePermission getClassLoader return Thread.currentThread().getContextClassLoader(); } }); for (int i = 0; i < threads; i++) { final AtomicReference<Future> future = futures.get(i); final AtomicReference<Throwable> throwable = new AtomicReference<Throwable>(); final Runnable runnable = new Runnable() { public void run() { try { ref.execute(event); } catch (ComponentEventException e) { throwable.set(e); throw e; } catch (RuntimeException e) { throwable.set(e); throw e; } catch (Error e) { throwable.set(e); throw e; } } }; final long start = System.currentTimeMillis(); class Task extends FutureTask<Object> { private Date deadline; Task(Runnable runnable) { this(runnable, null); } Task(Runnable runnable, Date deadline) { super(runnable, null); this.deadline = deadline; } public void run() { final ClassLoader org = AccessController.doPrivileged( new PrivilegedAction<ClassLoader>() { public ClassLoader run() { Thread thread = Thread.currentThread(); // PERMISSION: java.lang.RuntimePermission getClassLoader ClassLoader tmp = thread.getContextClassLoader(); // PERMISSION: java.lang.RuntimePermission getClassLoader thread.setContextClassLoader(loader); return tmp; } }); try { try { threadLocal.set(this); do { if (deadline == null) { // This is only executed once. if (initialDelay > 0 || cron != null) { deadline = new Date(start + initialDelay); if (cron != null) { deadline = cron.nextFireTime(deadline); } while (!isDone() && ref.isValid()) { lock.lock(); try { if (!condition.awaitUntil(deadline)) { break; } } catch (InterruptedException e) { // Ignore. } finally { lock.unlock(); } } if (isDone() || !ref.isValid()) { // Prevent first execution if // component is destroyed during // initial delay. break; } } else { deadline = new Date(start); } } else { while (!once && !isDone() && ref.isValid() && deadline != null) { lock.lock(); try { if (!condition.awaitUntil(deadline)) { break; } } catch (InterruptedException e) { // Ignore. } finally { lock.unlock(); } } } runAndReset(); if (delay > 0) { deadline = new Date( System.currentTimeMillis() + delay); } else if (cron != null || rate > 0) { if (fireAll) { if (cron != null) { deadline = cron.nextFireTime(deadline); } else if (rate > 0) { deadline = new Date(deadline.getTime() + rate); } else { assert false; } } else { Date now = new Date(); do { if (cron != null) { deadline = cron.nextFireTime(now); } else if (rate > 0) { deadline = new Date(deadline.getTime() + rate); } else { assert false; } } while (deadline != null && deadline.before(now)); } } } while (!once && !isDone() && ref.isValid() && deadline != null); } finally { if (!once && ref.isValid() && deadline != null) { Task task = new Task(runnable, deadline); future.set(task); if (!isCancelled()) { executor.execute(task); } else { future.set(null); if (threadLocal.get() != null) { latch.countDown(); threadLocal.remove(); } } } else { if (threadLocal.get() != null) { latch.countDown(); threadLocal.remove(); } } try { Throwable t = throwable.get(); if (t != null) { throw t; } } catch (RuntimeException e) { throw e; } catch (Error e) { throw e; } catch (Throwable t) { assert false : t; } } } finally { // Cleary possible interrupted state. Thread.interrupted(); AccessController.doPrivileged( new PrivilegedAction<Object>() { public Object run() { // PERMISSION: java.lang.RuntimePermission setContextClassLoader Thread.currentThread().setContextClassLoader(org); return null; } }); } } }; Task task = new Task(runnable); future.set(task); executor.execute(task); } } private void doDestroy(ComponentReference reference) { Future localFuture = threadLocal.get(); for (AtomicReference<Future> future : futures) { Future f = future.getAndSet(null); if (f != localFuture) { if (f != null) { f.cancel(interrupt); } } } lock.lock(); try { condition.signalAll(); } finally { lock.unlock(); } if (join) { if (localFuture != null) { // Prevent a deadlock. latch.countDown(); threadLocal.remove(); } try { latch.await(); } catch (InterruptedException e) { throw new RuntimeException(e); } } if (localFuture != null) { localFuture.cancel(interrupt); } } }