Package com.intellij.execution.process

Source Code of com.intellij.execution.process.ProcessHandler

/*
* Copyright 2000-2007 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.execution.process;

import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.UserDataHolderBase;
import com.intellij.util.concurrency.Semaphore;

import java.io.OutputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

public abstract class ProcessHandler extends UserDataHolderBase {
  private static final Logger LOG = Logger.getInstance("#com.intellij.execution.process.ProcessHandler");
  private final List<ProcessListener> myListeners = new CopyOnWriteArrayList<ProcessListener>();

  private static final int STATE_INITIAL     = 0;
  private static final int STATE_STARTING    = 1;
  private static final int STATE_STARTED     = 2;
  private static final int STATE_TERMINATING = 3;
  private static final int STATE_TERMINATED  = 4;

  private int myState = STATE_INITIAL;

  private final Semaphore    myWaitSemaphore;
  private ProcessListener myEventMulticaster;

  protected ProcessHandler() {
    myEventMulticaster = createEventMulticaster();
    myWaitSemaphore = new Semaphore();
    myWaitSemaphore.down();
  }

  public void startNotify() {
    if(isStartNotified()) {
      LOG.assertTrue(false, "startNotify called already");
      return;
    }
    myState = STATE_STARTING;
    try {
      myEventMulticaster.startNotified(new ProcessEvent(this));
    }
    finally {
      myState = STATE_STARTED;
    }
  }

  protected abstract void destroyProcessImpl();

  protected abstract void detachProcessImpl();

  public abstract boolean detachIsDefault();

  public void waitFor() {
    myWaitSemaphore.waitFor();
  }

  public void destroyProcess() {
    afterStartNotified(new Runnable() {
      public void run() {
        if (isProcessTerminated() || isProcessTerminating()) return;
        myState = STATE_TERMINATING;
        fireProcessWillTerminate(true);
        destroyProcessImpl();
      }
    });
  }

  public void detachProcess() {
    afterStartNotified(new Runnable() {
      public void run() {
        if (isProcessTerminated() || isProcessTerminating()) return;
        myState = STATE_TERMINATING;
        fireProcessWillTerminate(false);
        detachProcessImpl();
      }
    });
  }

  public boolean isProcessTerminated() {
    return myState == STATE_TERMINATED;
  }

  public boolean isProcessTerminating() {
    return myState == STATE_TERMINATING;
  }

  public void addProcessListener(final ProcessListener listener) {
    synchronized(myListeners) {
      myListeners.add(listener);
    }
  }

  public void removeProcessListener(final ProcessListener listener) {
    synchronized(myListeners) {
      myListeners.remove(listener);
    }
  }

  protected void notifyProcessDetached() {
    notifyTerminated(0, false);
  }

  protected void notifyProcessTerminated(final int exitCode) {
    notifyTerminated(exitCode, true);
  }

  private void notifyTerminated(final int exitCode, final boolean willBeDestroyed) {
    afterStartNotified(new Runnable() {
      public void run() {
        LOG.assertTrue(isStartNotified(), "Start notify is not called");

        if (!isProcessTerminating() && !isProcessTerminated()) {
          myState = STATE_TERMINATING;
          try {
            fireProcessWillTerminate(willBeDestroyed);
          }
          catch (Throwable e) {
            LOG.error(e);
          }
        }

        LOG.assertTrue(isStartNotified(), "All events should be fired after startNotify is called");
        try {
          myEventMulticaster.processTerminated(new ProcessEvent(ProcessHandler.this, exitCode));
        }
        catch (Throwable e) {
          LOG.error(e);
        }
        finally {
          myState = STATE_TERMINATED;
          myWaitSemaphore.up();
        }
      }
    });
  }

  public void notifyTextAvailable(final String text, final Key outputType) {
    final ProcessEvent event = new ProcessEvent(this, text);
    myEventMulticaster.onTextAvailable(event, outputType);
  }

  public abstract OutputStream getProcessInput();

  private void fireProcessWillTerminate(final boolean willBeDestroyed) {
    LOG.assertTrue(isStartNotified(), "All events should be fired after startNotify is called");
    myEventMulticaster.processWillTerminate(new ProcessEvent(this), willBeDestroyed);
  }

  private void afterStartNotified(final Runnable runnable) {
    if (isStartNotified()) {
      runnable.run();
    }
    else {
      addProcessListener(new ProcessAdapter() {
        public void startNotified(ProcessEvent event) {
          removeProcessListener(this);
          runnable.run();
        }
      });
    }
  }

  public boolean isStartNotified() {
    return myState > STATE_INITIAL;
  }

  private ProcessListener createEventMulticaster() {
    final Class<ProcessListener> listenerClass = ProcessListener.class;
    return (ProcessListener)Proxy.newProxyInstance(listenerClass.getClassLoader(), new Class[] {listenerClass}, new InvocationHandler() {
      public Object invoke(Object object, Method method, Object[] params) throws Throwable {
        final Iterator<ProcessListener> iterator;
        synchronized (myListeners) {
          iterator = myListeners.iterator();
        }
        while (iterator.hasNext()) {
          final ProcessListener processListener = iterator.next();
          try {
            method.invoke(processListener, params);
          }
          catch (Throwable e) {
            LOG.error(e);
          }
        }
        return null;
      }
    });
  }
}
TOP

Related Classes of com.intellij.execution.process.ProcessHandler

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.