gt:             List<Object> rv = new ArrayList<Object>();
context: /*
   *   Copyright 2011 Glencoe Software, Inc. All rights reserved.
   *   Use is subject to license terms supplied in LICENSE.txt
   */
  
  package omero.cmd;
  
  import java.util.ArrayList;
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  import java.util.concurrent.atomic.AtomicInteger;
  import java.util.concurrent.atomic.AtomicReference;
  
  import ome.conditions.InternalException;
  import ome.services.util.Executor;
  import ome.services.util.ReadOnlyStatus;
  import ome.system.Principal;
  import ome.system.ServiceFactory;
  import ome.util.SqlAction;
  import omero.LockTimeout;
  import omero.ServerError;
  
  import org.hibernate.Session;
  import org.perf4j.StopWatch;
  import org.perf4j.slf4j.Slf4JStopWatch;
  import org.slf4j.LoggerFactory;
  import org.springframework.transaction.annotation.Transactional;
  
  import com.google.common.collect.MapMaker;
  
  import Ice.Current;
  import Ice.Identity;
  
  /**
   * Servant for the handle proxy from the Command API. This is also a
   * {@link Runnable} and is passed to a ThreadPool instance
   *
   * <pre>
   * Transitions:
   *
   *      +------------------o [FINISHED]
   *      |                        o
   *      |                        |
   * (CREATED) ---o READY o===o RUNNING o===o CANCELLING ---o [CANCELLED]
   *      |           |                           o                o
   *      |           |---------------------------|                |
   *      +--------------------------------------------------------+
   *
   * </pre>
   *
   * @author Josh Moore, josh at glencoesoftware.com
   * @since 4.3.2
   */
  public class HandleI implements _HandleOperations, IHandle,
         SessionAware {
  
      private static enum State {
          CREATED, READY, RUNNING, CANCELLING, CANCELLED, FINISHED;
      }
  
      private static final long serialVersionUID = 15920349984928755L;
  
      private static final MapMaker mapMaker = new MapMaker();
  
      private final ReadOnlyStatus readOnly;
  
      /**
       * Timeout in seconds that cancellation should wait.
       */
      private final int cancelTimeoutMs;
  
      /**
       * Callbacks that have been added by clients.
       */
      private final Map<String, CmdCallbackPrx> callbacks = mapMaker.makeMap();
  
      /**
       * State-diagram location. This instance is also used as a lock around
       * certain critical sections.
       */
      private final AtomicReference<State> state = new AtomicReference<State>();
  
      /**
       * Final response. If this value is non-null, then clients will assume that
       * processing is finished.
       */
      private final AtomicReference<Response> rsp = new AtomicReference<Response>();
  
      /**
       * Current step. should only be incremented during {@link #steps(SqlAction, Session, ServiceFactory)}.
       */
      private final AtomicInteger currentStep = new AtomicInteger();
  
      /**
       * Status which will be returned by {@link #getStatus()} and as a part of
       * the {@link Response} value.
       */
      private final Status status = new Status();
  
      /**
       * Context to be passed to
       * {@link Executor#execute(Map, Principal, ome.services.util.Executor.Work)}
       * for properly setting the call context.
       */
      private/* final */Map<String, String> callContext;
  
      /**
       * The principal, i.e. the session information, about the current users
       * logins. This will be passed to the {@link #executor} instance for logging
       * in.
       */
      private/* final */Principal principal;
  
      /**
       * Executor which will be used to provide access to the Hibernate
       * {@link Session} in a background thread.
       */
      private/* final */Executor executor;
  
      /**
       * The identity of this servant, used during logging and similar operations.
       */
      private/* final */Ice.Identity id;
  
      private/* final */SessionI sess;
  
      private/* final */IRequest req;
  
      private/* final */Helper helper;
  
      //
      // INTIALIZATION
      //
  
      @Deprecated
      public HandleI(int cancelTimeoutMs) {
          this(new ReadOnlyStatus(false, false), cancelTimeoutMs);
          LoggerFactory.getLogger(getClass()).info("assuming read-write repository");
      }
  
      /**
       * Create a {@link HandleI} (at {@code CREATED} in the state diagram)
       * with the given cancel timeout in milliseconds.
       * @param readOnly the read-only status
       * @param cancelTimeoutMs the cancel timeout (in milliseconds)
       */
      public HandleI(ReadOnlyStatus readOnly, int cancelTimeoutMs) {
          this.readOnly = readOnly;
          this.cancelTimeoutMs = cancelTimeoutMs;
          this.state.set(State.CREATED);
      }
  
      public void setSession(SessionI session) throws ServerError {
          this.sess = session;
          this.principal = sess.getPrincipal();
          this.executor = sess.getExecutor();
      }
  
      public void initialize(Identity id, IRequest req, Map<String, String> ctx) {
          this.id = id;
          this.req = req;
          this.callContext = ctx;
          this.helper = new Helper((Request)req, status, null, null, null);
      }
  
      //
      // CALLBACKS
      //
  
      public void addCallback(CmdCallbackPrx cb, Current __current) {
          Ice.Identity id = cb.ice_getIdentity();
          String key = Ice.Util.identityToString(id);
          helper.info("Add callback: %s", key);
          cb = CmdCallbackPrxHelper.checkedCast(cb.ice_oneway());
          callbacks.put(key, cb);
      }
  
      public void removeCallback(CmdCallbackPrx cb, Current __current) {
          Ice.Identity id = cb.ice_getIdentity();
          String key = Ice.Util.identityToString(id);
          helper.info("Remove callback: %s", key);
          cb = CmdCallbackPrxHelper.checkedCast(cb.ice_oneway());
          callbacks.remove(key);
      }
  
      /**
       * Calls the proper notification on all callbacks based on the current
       * position in the state diagram. If that is anything other than
       * {@code CANCELLED} or {@code FINISHED} then
       * {@link CmdCallbackPrx#step(int, int)} is called.
       */
      public void notifyCallbacks() {
          final State state = this.state.get();
          final boolean finished = state.equals(State.FINISHED);
          final boolean cancelled = state.equals(State.CANCELLED);
          for (final CmdCallbackPrx prx : callbacks.values()) {
              try {
                  Response rsp = this.rsp.get();
                  if (finished || cancelled) {
                      if (cancelled) {
                          helper.info("notify cancelled: %s/%s", rsp, status);
                      } else {
                          helper.info("notify finished: %s/%s", rsp, status);
                      }
                      prx.finished(rsp, status);
                  } else {
                      int step = currentStep.get();
                      helper.info("notify step %s of %s", step, status.steps);
                      prx.step(step, status.steps);
                  }
              } catch (Exception e) {
                  sess.handleCallbackException(e);
              }
          }
      }
  
      //
      // GETTERS
      //
  
      public Request getRequest(Current __current) {
          helper.info("getRequest: %s",  req);
          return (Request) req;
      }
  
      public Response getResponse(Current __current) {
          Response rsp = this.rsp.get();
          helper.info("getResponse: %s", rsp);
          return rsp;
      }
  
      public Status getStatus(Current __current) {
          helper.info("getStatus: %s", status);
          return status;
      }
  
      //
      // STATE MGMT
      //
  
      public boolean cancel(Current __current) throws LockTimeout {
          try {
              boolean cancelled = cancelWithoutNotification();
              if (cancelled) {
                  try {
                      helper.cancel(new ERR(), null, "cancel-called");
                  } catch (Cancel c) {
                      // Duh. we're cancel'ing. This is expected.
                  }
              }
              return cancelled;
          } finally {
              notifyCallbacks();
          }
      }
  
      private boolean cancelWithoutNotification() throws LockTimeout {
  
          helper.info("Cancelling...");
  
          // If we can successfully catch the CREATED or READY state,
          // then there's no reason to wait for anything else.
          if (state.compareAndSet(State.CREATED, State.CANCELLED)
                  || state.compareAndSet(State.READY, State.CANCELLED)) {
              return true;
          }
  
          long start = System.currentTimeMillis();
          while (cancelTimeoutMs >= (System.currentTimeMillis() - start)) {
  
              // This is the most important case. If things are running, then
              // we want to set "CANCELLING" as quickly as possible.
              if (state.compareAndSet(State.RUNNING, State.CANCELLING)
                      || state.compareAndSet(State.READY, State.CANCELLING)
                      || state.compareAndSet(State.CANCELLING, State.CANCELLING)) {
  
                  try {
                      Thread.sleep(cancelTimeoutMs / 10);
                  } catch (InterruptedException e) {
                      // Igoring the interruption since the while block
                      // will properly handle another iteration.
                  }
  
              }
  
              // These are end states, so we'll just return.
              // See the transition states in the class javadoc
              if (state.compareAndSet(State.CANCELLED, State.CANCELLED)) {
                  return true;
  
              } else if (state.compareAndSet(State.FINISHED, State.FINISHED)) {
                  return false;
              }
  
          }
  
          // The only time that state gets set to CANCELLING is in the try
          // block above. If we've exited the while without switching to CANCELLED
          // then we've failed, and the value should be rolled back.
          //
          // If #run() noticed this before hand, then it would already have
          // moved from RUNNING to CANCELLED, and so the following statement would
          // return false, in which case we print out a warning so that in a later
          // version we can be more careful about retrying.
          if (!state.compareAndSet(State.CANCELLING, State.RUNNING)) {
                  helper.warn("Can't reset to RUNNING. State already changed.\n"
                      + "This could be caused either by another thread having\n"
                      + "already set the state back to RUNNING, or by the state\n"
                      + "having changed to CANCELLED. In either case, it is safe\n"
                      + "to throw the exception, and have the user recall cancel.");
          }
  
          LockTimeout lt = new LockTimeout();
          lt.backOff = 5000;
          lt.message = "timed out while waiting on CANCELLED state";
          lt.seconds = cancelTimeoutMs / 1000;
          throw lt;
  
      }
  
      //
      // CloseableServant. See documentation in interface.
      //
  
      public void close(Ice.Current current) {
          sess.unregisterServant(id); // No exception
          try {
              closeWithoutNotification(current);
          } finally {
              notifyCallbacks();
          }
      }
  
      private void closeWithoutNotification(Ice.Current current) {
          helper.info("Closing...");
          final State s = state.get();
          if (!State.FINISHED.equals(s) && !State.CANCELLED.equals(s)) {
              helper.info("Handle closed before finished! State=" + state.get());
              try {
                  cancel(current);
              }
              catch (LockTimeout e) {
                  helper.warn("Cancel failed");
              }
          }
      }
  
      //
      // Runnable
      //
  
      /**
       *
       * NB: Executes only if at {@code CREATED} in the state diagram.
       */
      @SuppressWarnings("unchecked")
      public void run() {
  
          // If we're not in the created state, then do nothing
          // since something has gone wrong.
          if (!state.compareAndSet(State.CREATED, State.READY)) {
              return; // EARLY EXIT!
          }
  
          StopWatch sw = new Slf4JStopWatch();
          try {
              Map<String, String> merged = mergeContexts();
              final List<Object> rv;
              if (req instanceof ReadOnlyStatus.IsAware && ((ReadOnlyStatus.IsAware) req).isReadOnly(readOnly)) {
                  rv = (List<Object>) executor.execute(merged, principal,
                          new RunSteps(this, "run (ro)", Ice.Util.identityToString(id), req) {
                      @Transactional(readOnly = true)
                      public List<Object> doWork(Session session, ServiceFactory sf) {
                          return innerWork(session, sf);
                      }
                  });
              } else {
                  rv = (List<Object>) executor.execute(merged, principal,
                          new RunSteps(this, "run (rw)", Ice.Util.identityToString(id), req) {
                      @Transactional(readOnly = false)
                      public List<Object> doWork(Session session, ServiceFactory sf) {
                          return innerWork(session, sf);
                      }
                  });
              }
  
              // Post-process
              for (int step = 0; step < status.steps; step++) {
                  Object obj = rv.get(step);
                  req.buildResponse(step, obj);
              }
  
          } catch (Cancel cancel) {
              helper.debug("Request cancelled by %s", cancel.getCause());
              // If this is a cancel, then fail or similar has already
              // been called and the response will be properly set.
          } catch (Throwable t) {
              helper.warn("Request rolled back by %s", t.getCause());
              helper.fail(new ERR(), t, "run-fail");
          } finally {
              // getResponse will be called regardless of return/exception state
              // and therefore any cleanup can happen there as soon as the response
              // is non-null.
              rsp.set(req.getResponse());
              sw.stop("omero.request.tx");
              notifyCallbacks();
          }
      }
  
      private Map<String, String> mergeContexts() {
  
          final Map<String, String> merged = new HashMap<String, String>();
          final Map<String, String> reqCctx = req.getCallContext();
  
          if (callContext != null) {
              helper.debug("User callContext: %s", callContext);
              merged.putAll(callContext);
          }
  
          if (reqCctx != null) {
              helper.debug("Request callContext: %s", reqCctx);
              merged.putAll(reqCctx);
          }
  
          return merged;
      }
  
      public List<Object> steps(SqlAction sql, Session session, ServiceFactory sf) throws Cancel {
          StopWatch swWhole = new Slf4JStopWatch();
          try {
  
              // Initialize. Any exceptions should cancel the process
  