/FileExtraction/Java_unseen/dlitz_resin/modules/resin/src/com/caucho/jca/ra/WorkManagerImpl.java
/*
 * Copyright (c) 1998-2011 Caucho Technology -- all rights reserved
 *
 * This file is part of Resin(R) Open Source
 *
 * Each copy or derived work must preserve the copyright notice and this
 * notice unmodified.
 *
 * Resin Open Source is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * Resin Open Source 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, or any warranty
 * of NON-INFRINGEMENT.  See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Resin Open Source; if not, write to the
 *   Free SoftwareFoundation, Inc.
 *   59 Temple Place, Suite 330
 *   Boston, MA 02111-1307  USA
 *
 * @author Scott Ferguson
 */

package com.caucho.jca.ra;

import com.caucho.env.thread.ThreadPool;
import com.caucho.inject.Module;
import com.caucho.util.Alarm;
import com.caucho.util.L10N;

import javax.resource.spi.work.ExecutionContext;
import javax.resource.spi.work.Work;
import javax.resource.spi.work.WorkEvent;
import javax.resource.spi.work.WorkException;
import javax.resource.spi.work.WorkListener;
import javax.resource.spi.work.WorkManager;
import java.util.ArrayList;

/**
 * Implementation of the work manager.
 */
@Module
public class WorkManagerImpl implements WorkManager {
  private static final L10N L = new L10N(WorkManagerImpl.class);
  private ArrayList<Work> _activeTasks = new ArrayList<Work>();

  private volatile boolean _isClosed;

  /**
   * Constructor.
   */
  WorkManagerImpl()
  {
  }

  /**
   * Accepts a work instance for processing.  The call blocks until
   * the work instance completes.
   */
  public void doWork(Work work)
    throws WorkException
  {
    doWork(work, INDEFINITE, null, null);
  }

  /**
   * Accepts a work instance for processing.  The call blocks until
   * the work instance completes.
   */
  public void doWork(Work work,
                     long startTimeout,
                     ExecutionContext context,
                     WorkListener listener)
    throws WorkException
  {
    boolean isStart = false;
    
    try {
      WorkException exn = null;
      
      synchronized (this) {
        if (_isClosed)
          exn = new WorkException(L.l("Work task can't be started from closed context."));
        else if (_activeTasks.contains(work))
          exn = new WorkException(L.l("Reentrant Work tasks are not allowed."));
        else {
          isStart = true;

          _activeTasks.add(work);
        }
      }

      if (listener == null) {
      }
      else if (isStart)
        listener.workAccepted(new WorkEvent(this, WorkEvent.WORK_ACCEPTED,
                                            work, null, 0));
      else {
        listener.workRejected(new WorkEvent(this, WorkEvent.WORK_REJECTED,
                                            work, exn, 0));
      }
      
      if (exn != null)
        throw exn;

      if (listener != null)
        listener.workStarted(new WorkEvent(this, WorkEvent.WORK_STARTED,
                                           work, null, 0));
      
      work.run();

      if (listener != null)
        listener.workCompleted(new WorkEvent(this, WorkEvent.WORK_COMPLETED,
                                             work, null, 0));
    } finally {
      synchronized (this) {
        _activeTasks.remove(work);
      }
    }
  }

  /**
   * Accepts a work instance for processing.  The call blocks until
   * the work instance starts, but does not wait not until the completion.
   */
  public long startWork(Work work)
    throws WorkException
  {
    return startWork(work, INDEFINITE, null, null);
  }

  /**
   * Accepts a work instance for processing.  The call blocks until
   * the work instance starts, but does not wait not until the completion.
   */
  public long startWork(Work work,
                        long startTimeout,
                        ExecutionContext context,
                        WorkListener listener)
    throws WorkException
  {
    long start = Alarm.getCurrentTime();
    
    startWork(work, startTimeout, context, listener, true);

    return Alarm.getCurrentTime() - start;
  }

  /**
   * Schedules a work instance.
   */
  public void scheduleWork(Work work)
    throws WorkException
  {
    // XXX: since there's no delay in start work, currently, 
    scheduleWork(work, INDEFINITE, null, null);
  }

  /**
   * Schedules a work instance.
   */
  public void scheduleWork(Work work,
                           long startTimeout,
                           ExecutionContext context,
                           WorkListener listener)
    throws WorkException
  {
    startWork(work, startTimeout, context, listener, false);
  }

  /**
   * Accepts a work instance for processing.  The call blocks until
   * the work instance starts, but does not wait not until the completion.
   */
  private long startWork(Work work,
                         long startTimeout,
                         ExecutionContext context,
                         WorkListener listener,
                         boolean waitForStart)
    throws WorkException
  {
    boolean isStart = false;
    
    WorkException exn = null;

    try {
      synchronized (this) {
        if (_isClosed)
          exn = new WorkException(L.l("Work task can't be started from closed context."));
        else if (_activeTasks.contains(work))
          exn = new WorkException(L.l("Reentrant Work tasks are not allowed."));
        else
          _activeTasks.add(work);
      }

      if (exn != null) {
        if (listener != null)
          listener.workRejected(new WorkEvent(this, WorkEvent.WORK_REJECTED,
                                              work, exn, 0));
        throw exn;
      }
      else if (listener != null)
        listener.workAccepted(new WorkEvent(this, WorkEvent.WORK_ACCEPTED,
                                            work, null, 0));

      ClassLoader loader = Thread.currentThread().getContextClassLoader();
      WorkThread workThread = new WorkThread(this, work, loader, listener);

      if (listener != null)
        listener.workStarted(new WorkEvent(this, WorkEvent.WORK_STARTED,
                                           work, null, 0));

      if (waitForStart)
        isStart = ThreadPool.getThreadPool().start(workThread, startTimeout);
      else
        isStart = ThreadPool.getThreadPool().schedule(workThread, startTimeout);
    } finally {
      synchronized (this) {
        if (! isStart)
          _activeTasks.remove(work);
      }
    }

    return 0;
  }

  void completeWork(Work work)
  {
    synchronized (this) {
      _activeTasks.remove(work);
    }
  }

  /**
   * Closes the work manager.
   */
  public void destroy()
  {
    synchronized (this) {
      if (_isClosed)
        return;

      _isClosed = true;
    }

    ArrayList<Work> activeTasks = new ArrayList<Work>();

    synchronized (this) {
      activeTasks.addAll(_activeTasks);
    }

    for (int i = 0; i < activeTasks.size(); i++)
      activeTasks.get(i).release();
  }
}
