Package org.commoncrawl.rpc

Source Code of org.commoncrawl.rpc.InProcessActor$IncomingMessage

package org.commoncrawl.rpc;

import java.io.IOException;
import java.util.concurrent.ThreadPoolExecutor;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.commoncrawl.async.EventLoop;
import org.commoncrawl.rpc.MessageData.Status;
import org.commoncrawl.util.shared.CCStringUtils;

/**
* Actor - Actors are concurrent tasks that respond to messages via a strictly
* defined interface. An Actor can process messages in it's own EventLoop (thread)
* or it can utilize a passed in ThreadPool object to execute each message in the
* context of a worker thread.
*
* Actor is an abstract class and inorder to create an Actor instance you must
* overload the Actor's dispatch method which is called whenever an Actor
* receives a message.
*
* To communicate with an Actor, you open a Channel to it (via the createChannel
* API). You can specify a source Actor, a source EventLoop or a source ThreadPool
* when opening a Channel to an Actor. The Actor will then send reply messages
* to the Channel owner in the context of the sender's EventLoop thread or ThreadPool.
*
* The CommonCrawl RPC Compiler allows you to specify a well defined Message
* Interface via the Service keyword. Whenever the compiler processes a Service
* definition, it automatically creates the appropriate stubs necessary to create
* an Actor from the Service Interface specification.
*
* For example, if you defined a data structures and a serivce using the CommonCrawl
* RPC IDL:
*
* class KeyValueTuple {
*  ustring key = 1;
*  buffer  value = 2;
* }
*
* class KeyValueIOResult {
*  enum Status {
*     SUCCESS = 0;
*     FAILURE = 1;
*  }
*  ustring key = 1;
*  int     result = 2;
*  ustring optionalErrorDesc = 3;
* }   
*
* Service KeyValueWriterService {
*   method writeKeyValuePair(in KeyValueTuple,out KeyValueIOResult);
* }
*
* The RPC compiler would create for you the following code:
*
* A Service interface definition as such:
*
  public interface KeyValueWriterService extends Service {
 
     void writeKeyValuePair(IncomingMessage<KeyValueTuple,KeyValueIOResult> message) throws RPCException;
  }

* A typed stub, that in combination with a channel, you can use to make
* asynchronous calls to the Service:
*
* public static class AsyncStub extends Service.AsyncStub {
   
    // constructor
    public AsyncStub(Channel channel) {
      super(channel);
    }
   
    public OutgoingMessage<KeyValueTuple,KeyValueIOResult> writeKeyValuePair(KeyValueTuple input,OutgoingMessage.Callback<KeyValueTuple,KeyValueIOResult> callback) throws RPCException ...    
  }
 

  * And a Factory to construct an Actor that adheres to an responds to the
  * Service specification:
  public static class ActorFactory {
    public static Actor createActorForService(final KeyValueWriterService instance,ThreadPoolExecutor executor,Actor.Events optionalListener) throws IOException  ....
  }
 
  ** Using this generated code, you would construct an Actor as such:
 
  Actor keyValueWriter = KeyValueWriterService.ActorFactory.createActorForService(
    new KeyValueWriterService() {
     
      void writeKeyValuePair(IncomingMessage<KeyValueTuple,KeyValueIOResult> message) throws RPCException {
       
        // THIS CODE RUNS IN THE CONTEXT OF THE ACTOR's EVENTLOOP THREAD
        // OR IN A THREAD-POOL THREAD, DEPENDING ON HOW YOU CREATED THE ACTOR
        KeyValueTuple tuple = message.getInput();
       
        ... do some work here...
       
        message.getOutput().setKey(message.getInput().getKey());
        message.getOutput().setStatus(KeyValueIOResult.Status.SUCCESS);
       
        // WHEN YOU CALL COMPLETE REQUEST,
        // THE MESSAGE WILL BE QUEUED UP AND EXECUTED IN THE CONTEXT OF
        // THE CALLING THREAD'S (Channel Owners') CONTEXT.
       
        message.completeRequest();
      }
    },
    //no thread pool .. use event loop thread
    null,
    // event callback
    new Actor.Events() { ... }
   );
  
   ....
   // SOME OTHER THREAD CONTEXT ...
   // CREATE A CHANNEL TO THE ABOVE ACTOR (RESPONSES PROCESSED IN THREAD POOL)
   Channel channel = keyValueWriter.createChannel(threadPool);
   // CREATE A STUB TO TALK TO SEND MESSAGES TO THE ACTOR
   KeyValueWriterService.AsyncStub stub = new KeyValueWriterService.AsyncStub(channel);
   // create and populate a tuple ...
   KeyValueTuple tuple = new KeyValueTuple();
   tuple.setKey ....
   // send a message to the actor via the stub 
   stub.writeKeyValuePair(tuple,new new OutgoingMessage.Callback() {
     requestComplete(OutgoingMessage message) {
       // this code will run in the context of the sender's thread (thread pool)
       // in this case ...
       if (message.getStatus() == Status.Success) {
         // do something ...
       }
     }
   });
*
*
*   
* @author rana
*
*/
public abstract class InProcessActor {
 
  public static final Log LOG = LogFactory.getLog(InProcessActor.class); /** interface specification **/

  // the event loop in which this actor resides 
  EventLoop _eventLoop;
  // owns event loop
  boolean   _ownsEventLoop;
  // optional thread pool executor
  ThreadPoolExecutor _executor;
 
  /**
   *  Actors broadcast startup and shutdown events
   *  If you want to do some setup work within the context of
   *  the Actor thread (EventLoop thread), you should do so in the
   *  actor's onStartup event callback.
   *
   */
  public static interface Events {
    /**
     * called from within the context of the actor thread
     * during startup
     */
    public void onStartup(InProcessActor actor);
   
    /**
     * called from within the context of the actor thread
     * during shutdown
     */
    public void onShutdown(InProcessActor actor);   
  }
 
  // optional event listener
  Events _eventListener;
 
  /**
   * constructor ... Actors are constructed via typed factory methods
   *
   * @param optionalExecutor
   * @throws IOException
   */
  public InProcessActor(ThreadPoolExecutor optionalExecutor,Events optionalEventListener) throws IOException {
    if (optionalExecutor == null) {
      _eventLoop = new EventLoop();
      _ownsEventLoop = true;
    }
    else {
      _executor = optionalExecutor;
    }
    _eventListener = optionalEventListener;
  }
 
  public void start() {
    if (_ownsEventLoop) {
      _eventLoop.start();
    }
    if (_eventListener != null) {
      Runnable callback = new Runnable() {
 
        @Override
        public void run() {
          _eventListener.onStartup(InProcessActor.this);
        }
       
      };
      if (_eventLoop != null && _eventLoop.getEventThread() == Thread.currentThread()) {
        callback.run();
      }
      else {
        if (_eventLoop != null)
          _eventLoop.queueAsyncRunnable(callback);
        else
          _executor.submit(callback);
      }
    }
  }
 
  public void stop() {
   
    Runnable callback = new Runnable() {

      @Override
      public void run() {
        if (_eventListener != null) {
          _eventListener.onShutdown(InProcessActor.this);
        }
       
        if (_ownsEventLoop) {
          _eventLoop.stop();
        }
      }
    };
    if (_eventLoop != null && _eventLoop.getEventThread() == Thread.currentThread()) {
      callback.run();
    }
    else {
      if (_eventLoop != null)
        _eventLoop.queueAsyncRunnable(callback);
      else
        _executor.submit(callback);
    }
  }
 
  @SuppressWarnings("unchecked")
  public static class IncomingMessage extends IncomingMessageContext {
    OutgoingMessageContext _source;
    public IncomingMessage(OutgoingMessageContext source,Channel channel,int requestId, RPCStruct input, RPCStruct output) {
      super(channel, requestId, input, output);
      _source = source;
    }
   
    public String getServiceName() {
      return _source._serviceName;
    }
   
    public String getMethodName() {
      return _source._methodName;
    }
  }
 
  /**
   * create a channel to This actor and send responses to the
   * specified source Actor
   *
   * @param sourceActor
   * @return a Channel object
   */
  public final Channel createChannel(final InProcessActor sourceActor) {
    return createChannel(sourceActor,null,null);
  }
 
  /**
   * create a channel to This actor and run the response callback 
   * in the specified EventLoop
   * @param sourceEventLoop
   * @return a Channel object
   */
  public final Channel createChannel(final EventLoop sourceEventLoop) {
    return createChannel(null,sourceEventLoop,null);
  }

  /**
   * create a channel to This actor and run the response callback
   * in the specified ThreadPool
   *
   * @param sourceExecutor
   * @return a Channel object
   */
  public final Channel createChannel(ThreadPoolExecutor sourceExecutor) {
    return createChannel(null,null,sourceExecutor);
  }

  /**
   * create a channel to This actor and run the response callback
   * in target actor's EventLoop or ThreadPool
   *
   * @return a Channel object
   */
 
  public final Channel createChannel() {
    return createChannel(null,null,null);
  }

  /**
   * An extension of Channel that adds awareness of the attached InProcessActor
   * @author rana
   *
   */
  public static interface InProcessChannel extends Channel {
    public InProcessActor getActor();
  }
 
 
  private final Channel createChannel(final InProcessActor optionalSourceActor,final EventLoop optionalSourceEventLoop,final ThreadPoolExecutor optionalSourceExecutor) {
   
    return new InProcessChannel() {

      InProcessActor     _optionalSourceActor = optionalSourceActor;
      EventLoop _optionalSourceEventLoop = optionalSourceEventLoop;
      ThreadPoolExecutor _optionalSourceExecutor = optionalSourceExecutor;

      @Override
      public void sendRequest(final OutgoingMessageContext<? extends RPCStruct, ? extends RPCStruct> originalMessage)throws RPCException {
        final IncomingMessage incomingMessage
          = new IncomingMessage(originalMessage,
            this,
            originalMessage.getRequestId(),
            (RPCStruct)originalMessage.getInput(),(RPCStruct)originalMessage.getOutput());

        final Channel channel = this;
       
        Runnable callback = new Runnable() {

          @SuppressWarnings("unchecked")
          @Override
          public void run() {
            try {
              dispatch(channel, incomingMessage);
            }
            catch (RPCException e) {
              LOG.error(CCStringUtils.stringifyException(e));
              incomingMessage.setStatus(Status.Error_RPCFailed);
              incomingMessage.setErrorDesc(CCStringUtils.stringifyException(e));
              try {
                sendResponse(incomingMessage);
              } catch (RPCException e1) {
                LOG.error(CCStringUtils.stringifyException(e1));
              }
            }
          }
         
        };
       
        if (_executor != null) {
         _executor.submit(callback);
        }
        else {
          _eventLoop.queueAsyncRunnable(callback);
        }
      }

      @SuppressWarnings("unchecked")
      @Override
      public void sendResponse(final IncomingMessageContext<? extends RPCStruct, ? extends RPCStruct> message)
          throws RPCException {
       
        Runnable closure = new Runnable() {

          @Override
          public void run() {
              final IncomingMessage localMessage = (IncomingMessage)message;
             
              if (localMessage._source.getCallback() != null) {
                localMessage._source.setOutput(message.getOutput());
                localMessage._source.setStatus(message.getStatus());
                localMessage._source.setErrorDesc(message.getErrorDesc());
               
                localMessage._source.getCallback().requestComplete(localMessage._source);
              }
            }
        };
        
        if (_optionalSourceActor != null) {
          if (_optionalSourceActor._executor != null) {
            _optionalSourceActor._executor.submit(closure);
          }
          else  {
            _optionalSourceActor._eventLoop.queueAsyncRunnable(closure);
          }
        }
        else if (_optionalSourceEventLoop != null){
          _optionalSourceEventLoop.queueAsyncRunnable(closure);
        }
        else if (_optionalSourceExecutor != null) {
          _optionalSourceExecutor.submit(closure);
        }
        else {
          closure.run();
        }
      }
     
      @Override
      public InProcessActor getActor() {
        return InProcessActor.this;
      }
    };
  }
 

 
 
  /**
   * Overload this method to handle message dispatches ... 
   *
   * @param channel
   * @param message
   * @throws RPCException
   */
  abstract public void dispatch(final Channel channel, final IncomingMessage message) throws RPCException;
 
}
TOP

Related Classes of org.commoncrawl.rpc.InProcessActor$IncomingMessage

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.