Package com.ericsson.otp.stdlib

Source Code of com.ericsson.otp.stdlib.OtpGenServer

package com.ericsson.otp.stdlib;

import java.util.logging.Logger;

import com.ericsson.otp.erlang.OtpErlangAtom;
import com.ericsson.otp.erlang.OtpErlangException;
import com.ericsson.otp.erlang.OtpErlangExit;
import com.ericsson.otp.erlang.OtpErlangList;
import com.ericsson.otp.erlang.OtpErlangObject;
import com.ericsson.otp.erlang.OtpErlangPid;
import com.ericsson.otp.erlang.OtpErlangRef;
import com.ericsson.otp.erlang.OtpErlangString;
import com.ericsson.otp.erlang.OtpErlangTuple;
import com.ericsson.otp.erlang.OtpMbox;
import com.ericsson.otp.erlang.OtpNode;

/**
* An abstract class for implementing a gen_server-like process. It should work
* as OTP gen_server. The methods you would usually implement on the callback
* module are abstract here so you have to implement it when inheriting from
* this class. Then, if you get hold of this process PID or registered name from
* a linked Erlang node, you can use gen_server functions (e.g.
* gen_server:call/2) with it.
*
* @author Fernando Benavides <elbrujohalcon@inaka.net>
*/
public abstract class OtpGenServer extends OtpSysProcess {
  private static final Logger  jlog      = Logger.getLogger(OtpGenServer.class
                            .getName());
  private static final long  DEFAULT_TIMEOUT  = 5000;
  /**
   * Use it as an equivalent to Erlang's infinity atom
   */
  public static final long  INFINITY    = 0;

  /**
   * Equivalent to
   * {@link OtpGenServer#call(OtpNode, String, String, OtpErlangObject, long)}
   * with timeout = 5000
   *
   * @param host
   *            This node
   * @param server
   *            gen_server PID
   * @param call
   *            message to send
   * @return the response from the server or null if timeout was met
   * @throws OtpErlangException
   *             If the response from the server is invalid
   */
  public static OtpErlangObject call(OtpNode host, OtpErlangPid server,
      OtpErlangObject call) throws OtpErlangException {
    return call(host, server, call, DEFAULT_TIMEOUT);
  }

  /**
   * Makes a synchronous call to the gen_server server by sending a request
   * and waiting until a reply arrives or a timeout occurs. The gen_server
   * will call Module:handle_call/3 to handle the request.
   *
   * @param host
   *            This node
   * @param server
   *            gen_server PID
   * @param call
   *            message to send
   * @param timeout
   *            Timeout is a long which specifies how many milliseconds to
   *            wait for a reply, or 0 to wait indefinitely. If no reply is
   *            received within the specified time, the function call returns
   *            null.
   * @return the response from the server or null if timeout was met
   * @throws OtpErlangException
   *             If the response from the server is invalid
   */
  public static OtpErlangObject call(OtpNode host, OtpErlangPid server,
      OtpErlangObject call, long timeout) throws OtpErlangException {
    OtpMbox caller = host.createMbox();
    OtpErlangTuple from = new OtpErlangTuple(new OtpErlangObject[] {
        caller.self(), host.createRef() });
    OtpErlangObject msg = new OtpErlangTuple(new OtpErlangObject[] {
        new OtpErlangAtom("$gen_call"), from, call });
    caller.send(server, msg);
    OtpErlangObject res = timeout == 0 ? caller.receive() : caller
        .receive(timeout);
    if (res == null) {
      return null;
    } else if (res instanceof OtpErlangTuple
        && ((OtpErlangTuple) res).arity() == 2) {
      return ((OtpErlangTuple) res).elementAt(1);
    } else {
      throw new OtpErlangException("Invalid Response: " + res);
    }
  }

  /**
   * Equivalent to
   * {@link OtpGenServer#call(OtpNode, String, String, OtpErlangObject, long)}
   * with timeout = 5000
   *
   * @param host
   *            This node
   * @param server
   *            gen_server registered name
   * @param node
   *            node where the gen_server lives
   * @param call
   *            message to send
   * @return the response from the server or null if timeout was met
   * @throws OtpErlangException
   *             If the response from the server is invalid
   */
  public static OtpErlangObject call(OtpNode host, String server,
      String node, OtpErlangObject call) throws OtpErlangException {
    return call(host, server, node, call, DEFAULT_TIMEOUT);
  }

  /**
   * Makes a synchronous call to the gen_server server by sending a request
   * and waiting until a reply arrives or a timeout occurs. The gen_server
   * will call Module:handle_call/3 to handle the request.
   *
   * @param host
   *            This node
   * @param server
   *            gen_server registered name
   * @param node
   *            node where the gen_server lives
   * @param call
   *            message to send
   * @param timeout
   *            Timeout is an long which specifies how many milliseconds to
   *            wait for a reply, or 0 to wait indefinitely. If no reply is
   *            received within the specified time, the function call returns
   *            null.
   * @return the response from the server or null if timeout was met
   * @throws OtpErlangException
   *             If the response from the server is invalid
   */
  public static OtpErlangObject call(OtpNode host, String server,
      String node, OtpErlangObject call, long timeout)
      throws OtpErlangException {
    OtpMbox caller = host.createMbox();
    OtpErlangTuple from = new OtpErlangTuple(new OtpErlangObject[] {
        caller.self(), host.createRef() });
    OtpErlangObject msg = new OtpErlangTuple(new OtpErlangObject[] {
        new OtpErlangAtom("$gen_call"), from, call });
    caller.send(server, node, msg);
    OtpErlangObject res = timeout == 0 ? caller.receive() : caller
        .receive(timeout);
    if (res == null) {
      return null;
    } else if (res instanceof OtpErlangTuple
        && ((OtpErlangTuple) res).arity() == 2) {
      return ((OtpErlangTuple) res).elementAt(1);
    } else {
      throw new OtpErlangException("Invalid Response: " + res);
    }
  }

  /**
   * Sends an asynchronous request to the a gen_server and returns
   * immediately, ignoring if the destination node or gen_server does not
   * exist. The gen_server will call Module:handle_cast/2 to handle the
   * request.
   *
   * @param host
   *            This node
   * @param server
   *            gen_server PID
   * @param cast
   *            message to send
   */
  public static void cast(OtpNode host, OtpErlangPid server,
      OtpErlangObject cast) {
    OtpMbox caller = host.createMbox();
    OtpErlangObject msg = new OtpErlangTuple(new OtpErlangObject[] {
        new OtpErlangAtom("$gen_cast"), cast });
    caller.send(server, msg);
  }

  /**
   * Sends an asynchronous request to the a gen_server and returns
   * immediately, ignoring if the destination node or gen_server does not
   * exist. The gen_server will call Module:handle_cast/2 to handle the
   * request.
   *
   * @param host
   *            This node
   * @param server
   *            gen_server name
   * @param node
   *            node where the gen_server lives
   * @param cast
   *            message to send
   */
  public static void cast(OtpNode host, String server, String node,
      OtpErlangObject cast) {
    OtpMbox caller = host.createMbox();
    OtpErlangObject msg = new OtpErlangTuple(new OtpErlangObject[] {
        new OtpErlangAtom("$gen_cast"), cast });
    caller.send(server, node, msg);
  }

  /**
   * Returns a reply to the calling process
   *
   * @param host
   *            This Node
   * @param from
   *            The caller
   * @param reply
   *            The response to be sent
   */
  public static void reply(OtpNode host, OtpErlangTuple from,
      OtpErlangObject reply) {
    OtpErlangPid to = (OtpErlangPid) from.elementAt(0);
    OtpErlangRef tag = (OtpErlangRef) from.elementAt(1);
    OtpErlangTuple tuple = new OtpErlangTuple((new OtpErlangObject[] { tag,
        reply }));
    host.createMbox().send(to, tuple);
  }

  /**********************************************************************************************/

  protected OtpGenServer(OtpNode host) {
    super(host);
  }

  protected OtpGenServer(OtpNode host, String name) {
    super(host, name);
  }

  @Override
  protected void loop() {
    boolean running = true;
    try {
      while (running) {
        try {
          OtpErlangObject o = this.getMbox().receive();
          jlog.finer("Received Request: " + o);
          running = decodeMsg(o);
        } catch (OtpErlangExit oee) {
          OtpErlangObject reason = oee.reason();
          jlog.warning("Linked process exited. Reason: " + reason);
          try {
            handleExit(oee);
          } catch (OtpStopException ose) {
            running = false;
          }
        }
      }
    } catch (OtpErlangException oee) {
      terminate(oee);
    }
    terminate(null);
  };

  private boolean decodeMsg(OtpErlangObject message)
      throws OtpErlangException {
    try {
      if (message instanceof OtpErlangTuple) {
        OtpErlangTuple msg = (OtpErlangTuple) message;
        OtpErlangAtom kind = (OtpErlangAtom) msg.elementAt(0);
        if (kind.atomValue().equals("system") && msg.arity() == 3) {
          OtpErlangTuple from = (OtpErlangTuple) msg.elementAt(1);
          OtpErlangObject req = msg.elementAt(2);
          handleSystemMessage(req, from);
        } else if (kind.atomValue().equals("$gen_call")
            && ((OtpErlangTuple) message).arity() == 3) {
          OtpErlangTuple from = (OtpErlangTuple) msg.elementAt(1);
          OtpErlangObject cmd = msg.elementAt(2);
          try {
            OtpErlangObject reply = handleCall(cmd, from);
            reply(from, reply);
          } catch (OtpStopReplyException ose) {
            reply(from, ose.getResponse());
            return false;
          }
        } else if (kind.atomValue().equals("$gen_cast")
            && ((OtpErlangTuple) message).arity() == 2) {
          OtpErlangObject cmd = msg.elementAt(1);
          handleCast(cmd);
        } else {
          handleInfo(message);
        }
      } else {
        handleInfo(message);
      }
    } catch (OtpContinueException ose) {
    } catch (OtpStopException ose) {
      return false;
    }
    return true;
  }

  @Override
  protected final OtpErlangList getMiscStatus() {
    OtpErlangTuple header = new OtpErlangTuple(new OtpErlangObject[] {
        new OtpErlangAtom("header"),
        new OtpErlangString("Status for generic server "
            + this.getMbox().getName()) });
    OtpErlangTuple data = new OtpErlangTuple(
        new OtpErlangObject[] {
            new OtpErlangAtom("data"),
            new OtpErlangList(
                new OtpErlangObject[] { getSpecificStatus() }) });
    OtpErlangList miscStatus = new OtpErlangList(new OtpErlangObject[] {
        header, data });
    return miscStatus;
  }

  /**
   * Override to add specific information to get_status calls
   *
   * @return a list of specific information to be included in the process
   *         status description
   */
  protected OtpErlangList getSpecificStatus() {
    return new OtpErlangList(new OtpErlangTuple(new OtpErlangObject[] {
        new OtpErlangString("State"),
        new OtpErlangString(this.toString()) }));
  }

  /**********************************************************************************************/

  protected abstract OtpErlangObject handleCall(OtpErlangObject cmd,
      OtpErlangTuple from) throws OtpStopException, OtpContinueException,
      OtpErlangException;

  protected abstract void handleCast(OtpErlangObject cmd)
      throws OtpStopException, OtpErlangException;

  protected abstract void handleInfo(OtpErlangObject cmd)
      throws OtpStopException;

  protected abstract void terminate(OtpErlangException oee);
}
TOP

Related Classes of com.ericsson.otp.stdlib.OtpGenServer

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.