/* * Copyright 2007-2010 Sun Microsystems, Inc. * * This file is part of Project Darkstar Server. * * Project Darkstar Server is free software: you can redistribute it * and/or modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation and * distributed hereunder to you. * * Project Darkstar Server 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * * -- */ package com.sun.sgs.impl.util; import java.util.concurrent.Callable; import com.sun.sgs.auth.Identity; import com.sun.sgs.kernel.TransactionScheduler; /** * An abstract utility class to run a transactional task that returns a * value. A subclass of this class must implement the abstract {@code * call} method which is invoked in a transaction.<p> * * Here's an example of running a {@code KernelCallable} that returns a * boolean value:<p> * * <pre> * boolean result = KernelCallable.call( * new KernelCallable<Boolean>("MyKernelCallable") { * public Boolean call() { * return ...; * } * }, * txnScheduler, taskOwner); * </pre> * @param <R> the type of the result (the return value of the {@code call} * method) */ public abstract class KernelCallable<R> extends AbstractKernelRunnable implements Callable<R> { /** The result of invoking the {@code call} method. */ private R result; /** The flag to indicate whether the {@code call} method is complete. */ private boolean done; /** * Constructs an instance with the specified {@code name}. * * @param name a descriptive name (or {@code null}) for use in the * {@code toString} method */ public KernelCallable(String name) { super(name); } /** * {@inheritDoc} <p> * * This implementation invokes the {@code call} method of this * instance and sets the result. * * @throws IllegalStateException if this task has already been successfully * run via an invocation of the {@link * #call(com.sun.sgs.impl.util.KernelCallable, * com.sun.sgs.kernel.TransactionScheduler, * com.sun.sgs.auth.Identity) call} method */ @Override public synchronized void run() throws Exception { if (done) { throw new IllegalStateException("already completed"); } result = call(); } /** * This method should be called to indicate that the {@code call} method * has been invoked for this {@code KernelCallable} and the internal result * is available for retrieval. After this method has been invoked, further * attempts to execute this task (either via the {@link * #call(com.sun.sgs.impl.util.KernelCallable, * com.sun.sgs.kernel.TransactionScheduler, com.sun.sgs.auth.Identity) call} * method or through the {@code TransactionScheduler}) will throw * {@code IllegalStateException}. */ private synchronized void setDone() { done = true; } /** * Returns the result, previously set by invoking the {@link #run} * method. * * @return the result * @throws IllegalStateException if the {@link #run} method has not * completed */ private synchronized R getResult() { if (!done) { throw new IllegalStateException("not done"); } return result; } /** * Runs the specified {@code callable} (by invoking its {@code call} * method) in a transaction using the specified {@code txnScheduler} * and {@code taskOwner} and returns the result. The specified * {@code callable} can only be used once. * * @param <R> the return type of the {@code KernelCallable} * @param callable a callable to invoke * @param txnScheduler a transaction scheduler * @param taskOwner an identity for the task's owner * @return the result of executing the {@code callable} * @throws Exception if running the specified {@code callable} throws * an {@code Exception} */ public static <R> R call(KernelCallable<R> callable, TransactionScheduler txnScheduler, Identity taskOwner) throws Exception { txnScheduler.runTask(callable, taskOwner); callable.setDone(); return callable.getResult(); } }