예제 #1
0
 @Override
 public OtpErlangObject call(
     final int timeout,
     final OtpErlangObject gleader,
     final String module,
     final String fun,
     final String signature,
     final Object... args0)
     throws RpcException {
   if (stopped) {
     return null;
   }
   tryConnect();
   OtpErlangObject result;
   try {
     result =
         rpcHelper.rpcCall(
             localNode,
             data.getNodeName(),
             false,
             gleader,
             module,
             fun,
             timeout,
             signature,
             args0);
   } catch (final SignatureException e) {
     throw new RpcException(e);
   }
   return result;
 }
예제 #2
0
 @Override
 public void send(final String name, final Object msg) {
   try {
     tryConnect();
     rpcHelper.send(localNode, data.getNodeName(), name, msg);
   } catch (final SignatureException e) {
   } catch (final RpcException e) {
   }
 }
예제 #3
0
 @Override
 public void send(final OtpErlangPid pid, final Object msg) {
   try {
     tryConnect();
     rpcHelper.send(localNode, pid, msg);
   } catch (final SignatureException e) {
   } catch (final RpcException e) {
   }
 }
예제 #4
0
 @Override
 public void cast(
     final OtpErlangObject gleader,
     final String module,
     final String fun,
     final String signature,
     final Object... args0)
     throws RpcException {
   tryConnect();
   try {
     rpcHelper.rpcCast(
         localNode, data.getNodeName(), false, gleader, module, fun, signature, args0);
   } catch (final SignatureException e) {
     throw new RpcException(e);
   }
 }
예제 #5
0
 @Override
 public void async_call_result(
     final IRpcResultCallback cb,
     final String m,
     final String f,
     final String signature,
     final Object... args)
     throws RpcException {
   final OtpErlangAtom gleader = new OtpErlangAtom("user");
   try {
     rpcHelper.rpcCastWithProgress(
         cb, localNode, data.getNodeName(), false, gleader, m, f, signature, args);
   } catch (final SignatureException e) {
     throw new RpcException(e);
   }
 }
예제 #6
0
 @Override
 public void async_call_cb(
     final IRpcCallback cb,
     final int timeout,
     final OtpErlangObject gleader,
     final String module,
     final String fun,
     final String signature,
     final Object... args)
     throws RpcException {
   tryConnect();
   try {
     rpcHelper.makeAsyncCbCall(
         localNode, data.getNodeName(), cb, timeout, gleader, module, fun, signature, args);
   } catch (final SignatureException e) {
     throw new RpcException(e);
   }
 }
예제 #7
0
public class ErlRuntime implements IErlRuntime, IRpcSite {
  private static final String COULD_NOT_CONNECT_TO_BACKEND =
      "Could not connect to backend! Please check runtime settings.";
  private static final int EPMD_PORT = 4369;
  public static int DEFAULT_TIMEOUT;

  {
    setDefaultTimeout();
  }

  private static final int MAX_RETRIES = 15;
  public static final int RETRY_DELAY =
      Integer.parseInt(System.getProperty("erlide.connect.delay", "400"));
  private static final Object connectLock = new Object();
  private static final RpcHelper rpcHelper = RpcHelper.getInstance();

  public enum State {
    CONNECTED,
    DISCONNECTED,
    DOWN
  }

  private State state;
  private final RuntimeData data;
  private OtpNode localNode;
  private final Object localNodeLock = new Object();
  private boolean reported;
  private final IProcess process;
  private final boolean connectOnce;
  private final IProvider<IProcess> processProvider;
  private final OtpNodeStatus statusWatcher;
  private OtpMbox eventBox;
  private boolean stopped;

  public ErlRuntime(
      final String name,
      final String cookie,
      final IProvider<IProcess> processProvider,
      final boolean reportWhenDown,
      final boolean longName,
      final boolean connectOnce) {
    state = State.DISCONNECTED;
    this.processProvider = processProvider;
    process = processProvider.get();
    this.connectOnce = connectOnce;
    stopped = false;
    data = new RuntimeData();
    data.setLongName(longName);
    data.setCookie(cookie);
    data.setNodeName(name);
    data.setReportErrors(reportWhenDown);

    statusWatcher =
        new OtpNodeStatus() {
          @Override
          public void remoteStatus(final String node, final boolean up, final Object info) {
            if (node.equals(data.getNodeName())) {
              if (up) {
                ErlLogger.debug("Node %s is up", data.getNodeName());
                connectRetry();
              } else {
                ErlLogger.debug("Node %s is down: %s", data.getNodeName(), info);
                state = State.DOWN;
              }
            }
          }
        };
    startLocalNode();
    // if (epmdWatcher.isRunningNode(name)) {
    // connect();
    // }
  }

  @Override
  public void start() {
    // TODO Auto-generated method stub

  }

  public void startLocalNode() {
    boolean nodeCreated = false;
    synchronized (localNodeLock) {
      int i = 0;
      do {
        try {
          i++;
          localNode = ErlRuntime.createOtpNode(data.getCookie(), data.hasLongName());
          localNode.registerStatusHandler(statusWatcher);
          nodeCreated = true;
        } catch (final IOException e) {
          ErlLogger.error("ErlRuntime could not be created (%s), retrying %d", e.getMessage(), i);
          try {
            localNodeLock.wait(300);
          } catch (final InterruptedException e1) {
          }
        }
      } while (!nodeCreated && i < 10);
    }
  }

  @Override
  public String getNodeName() {
    return data.getNodeName();
  }

  private boolean connectRetry() {
    int tries = MAX_RETRIES;
    boolean ok = false;
    while (!ok && tries > 0) {
      ErlLogger.debug("# ping..." + getNodeName() + " " + Thread.currentThread().getName());
      ok = localNode.ping(getNodeName(), RETRY_DELAY + (MAX_RETRIES - tries) * RETRY_DELAY % 3);
      tries--;
    }
    return ok;
  }

  @Override
  public void async_call_result(
      final IRpcResultCallback cb,
      final String m,
      final String f,
      final String signature,
      final Object... args)
      throws RpcException {
    final OtpErlangAtom gleader = new OtpErlangAtom("user");
    try {
      rpcHelper.rpcCastWithProgress(
          cb, localNode, data.getNodeName(), false, gleader, m, f, signature, args);
    } catch (final SignatureException e) {
      throw new RpcException(e);
    }
  }

  @Override
  public IRpcFuture async_call(
      final OtpErlangObject gleader,
      final String module,
      final String fun,
      final String signature,
      final Object... args0)
      throws RpcException {
    tryConnect();
    try {
      return rpcHelper.sendRpcCall(
          localNode, data.getNodeName(), false, gleader, module, fun, signature, args0);
    } catch (final SignatureException e) {
      throw new RpcException(e);
    }
  }

  @Override
  public IRpcFuture async_call(
      final String module, final String fun, final String signature, final Object... args0)
      throws RpcException {
    return async_call(new OtpErlangAtom("user"), module, fun, signature, args0);
  }

  @Override
  public void async_call_cb(
      final IRpcCallback cb,
      final int timeout,
      final String module,
      final String fun,
      final String signature,
      final Object... args)
      throws RpcException {
    async_call_cb(cb, timeout, new OtpErlangAtom("user"), module, fun, signature, args);
  }

  @Override
  public void async_call_cb(
      final IRpcCallback cb,
      final int timeout,
      final OtpErlangObject gleader,
      final String module,
      final String fun,
      final String signature,
      final Object... args)
      throws RpcException {
    tryConnect();
    try {
      rpcHelper.makeAsyncCbCall(
          localNode, data.getNodeName(), cb, timeout, gleader, module, fun, signature, args);
    } catch (final SignatureException e) {
      throw new RpcException(e);
    }
  }

  @Override
  public OtpErlangObject call(
      final int timeout,
      final OtpErlangObject gleader,
      final String module,
      final String fun,
      final String signature,
      final Object... args0)
      throws RpcException {
    if (stopped) {
      return null;
    }
    tryConnect();
    OtpErlangObject result;
    try {
      result =
          rpcHelper.rpcCall(
              localNode,
              data.getNodeName(),
              false,
              gleader,
              module,
              fun,
              timeout,
              signature,
              args0);
    } catch (final SignatureException e) {
      throw new RpcException(e);
    }
    return result;
  }

  @Override
  public OtpErlangObject call(
      final int timeout,
      final String module,
      final String fun,
      final String signature,
      final Object... args0)
      throws RpcException {
    return call(timeout, new OtpErlangAtom("user"), module, fun, signature, args0);
  }

  @Override
  public void cast(
      final OtpErlangObject gleader,
      final String module,
      final String fun,
      final String signature,
      final Object... args0)
      throws RpcException {
    tryConnect();
    try {
      rpcHelper.rpcCast(
          localNode, data.getNodeName(), false, gleader, module, fun, signature, args0);
    } catch (final SignatureException e) {
      throw new RpcException(e);
    }
  }

  @Override
  public void cast(
      final String module, final String fun, final String signature, final Object... args0)
      throws RpcException {
    cast(new OtpErlangAtom("user"), module, fun, signature, args0);
  }

  private void tryConnect() throws RpcException {
    synchronized (connectLock) {
      switch (state) {
        case DISCONNECTED:
          reported = false;
          if (connectRetry()) {
            state = State.CONNECTED;
          } else if (connectOnce) {
            state = State.DOWN;
          } else {
            state = State.DISCONNECTED;
          }
          break;
        case CONNECTED:
          break;
        case DOWN:
          try {
            if (process != null) {
              process.terminate();
            }
          } catch (final DebugException e) {
            ErlLogger.info(e);
          }
          // TODO restart it??
          // process =
          if (!stopped) {
            final String msg = reportRuntimeDown(data.getNodeName());
            throw new RpcException(msg);
          }
      }
    }
  }

  private String reportRuntimeDown(final String peer) {
    final String fmt = "Backend '%s' is down";
    final String msg = String.format(fmt, peer);
    if (data.getReportErrors() && !reported) {
      final String user = System.getProperty("user.name");

      String msg1;
      if (connectOnce) {
        msg1 =
            "It is likely that your network is misconfigured or uses 'strange' host names.\n"
                + "Please check the "
                + "Window->preferences->erlang->network page for hints about that."
                + "\n\n"
                + "Also, check if you can create and connect two erlang nodes on your machine\n"
                + "using \"erl -name foo1\" and \"erl -name foo2\".";
      } else {
        msg1 =
            "If you didn't shut it down on purpose, it is an "
                + "unrecoverable error, please restart Eclipse. ";
      }

      final String bigMsg =
          msg
              + "\n\n"
              + msg1
              + "\n\n"
              + "If an error report named '"
              + user
              + "_<timestamp>.txt' has been created in your home directory,\n "
              + "please consider reporting the problem. \n"
              + (SystemConfiguration.hasFeatureEnabled("erlide.ericsson.user")
                  ? ""
                  : "http://www.assembla.com/spaces/erlide/support/tickets");
      MessageReporter.showError(bigMsg, ReporterPosition.CORNER);
      reported = true;
    }
    return msg;
  }

  @Override
  public boolean isAvailable() {
    return state == State.CONNECTED;
  }

  public static String createJavaNodeName() {
    final String fUniqueId = ErlRuntime.getTimeSuffix();
    return "jerlide_" + fUniqueId;
  }

  public static String createJavaNodeName(final String hostName) {
    return createJavaNodeName() + "@" + hostName;
  }

  static String getTimeSuffix() {
    String fUniqueId;
    fUniqueId = Long.toHexString(System.currentTimeMillis() & 0xFFFFFFF);
    return fUniqueId;
  }

  public static OtpNode createOtpNode(final String cookie, final boolean longName)
      throws IOException {
    OtpNode node;
    final String hostName = HostnameUtils.getErlangHostName(longName);
    if (Strings.isNullOrEmpty(cookie)) {
      node = new OtpNode(createJavaNodeName(hostName));
    } else {
      node = new OtpNode(createJavaNodeName(hostName), cookie);
    }
    debugPrintCookie(node.cookie());
    return node;
  }

  private static void debugPrintCookie(final String cookie) {
    final int len = cookie.length();
    final String trimmed = len > 7 ? cookie.substring(0, 7) : cookie;
    ErlLogger.debug("using cookie '%s...'%d (info: '%s')", trimmed, len, cookie);
  }

  @Override
  public void send(final OtpErlangPid pid, final Object msg) {
    try {
      tryConnect();
      rpcHelper.send(localNode, pid, msg);
    } catch (final SignatureException e) {
    } catch (final RpcException e) {
    }
  }

  @Override
  public void send(final String fullNodeName, final String name, final Object msg) {
    try {
      tryConnect();
      rpcHelper.send(localNode, fullNodeName, name, msg);
    } catch (final SignatureException e) {
    } catch (final RpcException e) {
    }
  }

  @Override
  public OtpMbox createMbox(final String name) {
    return localNode.createMbox(name);
  }

  @Override
  public OtpMbox createMbox() {
    return localNode.createMbox();
  }

  @Override
  public void stop() {
    // close peer too?
    stopped = true;
    localNode.close();
  }

  @Override
  public RpcResult call_noexception(
      final String m, final String f, final String signature, final Object... args) {
    return call_noexception(DEFAULT_TIMEOUT, m, f, signature, args);
  }

  @Override
  public RpcResult call_noexception(
      final int timeout,
      final String m,
      final String f,
      final String signature,
      final Object... args) {
    try {
      final OtpErlangObject result = call(timeout, m, f, signature, args);
      return new RpcResult(result);
    } catch (final RpcException e) {
      return RpcResult.error(e.getMessage());
    }
  }

  @Override
  public void async_call_cb(
      final IRpcCallback cb,
      final String m,
      final String f,
      final String signature,
      final Object... args)
      throws RpcException {
    async_call_cb(cb, DEFAULT_TIMEOUT, m, f, signature, args);
  }

  @Override
  public OtpErlangObject call(
      final String m, final String f, final String signature, final Object... a)
      throws RpcException {
    return call(DEFAULT_TIMEOUT, m, f, signature, a);
  }

  @Override
  public void send(final String name, final Object msg) {
    try {
      tryConnect();
      rpcHelper.send(localNode, data.getNodeName(), name, msg);
    } catch (final SignatureException e) {
    } catch (final RpcException e) {
    }
  }

  @Override
  public void connect() {
    final String label = getNodeName();
    ErlLogger.debug(label + ": waiting connection to peer...");
    try {
      wait_for_epmd();
      eventBox = createMbox("rex");

      if (waitForCodeServer()) {
        ErlLogger.debug("connected!");
      } else {
        ErlLogger.error(COULD_NOT_CONNECT_TO_BACKEND);
      }

    } catch (final BackendException e) {
      ErlLogger.error(e);
      ErlLogger.error(COULD_NOT_CONNECT_TO_BACKEND);
    } catch (final Exception e) {
      ErlLogger.error(e);
      ErlLogger.error(COULD_NOT_CONNECT_TO_BACKEND);
    }
  }

  private OtpMbox getEventBox() {
    return eventBox;
  }

  @Override
  public OtpErlangPid getEventPid() {
    final OtpMbox theEventBox = getEventBox();
    if (theEventBox == null) {
      return null;
    }
    return theEventBox.self();
  }

  private void wait_for_epmd() throws BackendException {
    wait_for_epmd("localhost");
  }

  private void wait_for_epmd(final String host) throws BackendException {
    // If anyone has a better solution for waiting for epmd to be up, please
    // let me know
    int tries = 50;
    boolean ok = false;
    do {
      Socket s;
      try {
        s = new Socket(host, EPMD_PORT);
        s.close();
        ok = true;
      } catch (final IOException e) {
      }
      try {
        Thread.sleep(100);
        // ErlLogger.debug("sleep............");
      } catch (final InterruptedException e1) {
      }
      tries--;
    } while (!ok && tries > 0);
    if (!ok) {
      final String msg =
          "Couldn't contact epmd - erlang backend is probably not working\n"
              + "  Possibly your host's entry in /etc/hosts is wrong ("
              + host
              + ").";
      ErlLogger.error(msg);
      throw new BackendException(msg);
    }
  }

  private boolean waitForCodeServer() {
    try {
      OtpErlangObject r;
      int i = 30;
      boolean gotIt = false;
      do {
        r = call("erlang", "whereis", "a", "code_server");
        gotIt = !(r instanceof OtpErlangPid);
        if (!gotIt) {
          try {
            Thread.sleep(200);
          } catch (final InterruptedException e) {
          }
        }
        i--;
      } while (gotIt && i > 0);
      if (gotIt) {
        ErlLogger.error("code server did not start in time for %s", getNodeName());
        return false;
      }
      ErlLogger.debug("code server started");
      return true;
    } catch (final Exception e) {
      ErlLogger.error("error starting code server for %s: %s", getNodeName(), e.getMessage());
      return false;
    }
  }

  private static void setDefaultTimeout() {
    final String t = System.getProperty("erlide.rpc.timeout", "9000");
    if ("infinity".equals(t)) {
      DEFAULT_TIMEOUT = RpcHelper.INFINITY;
    } else {
      try {
        DEFAULT_TIMEOUT = Integer.parseInt(t);
      } catch (final Exception e) {
        DEFAULT_TIMEOUT = 9000;
      }
    }
  }

  @Override
  public boolean isStopped() {
    return stopped || !isAvailable();
  }

  @Override
  public OtpMbox getEventMbox() {
    return eventBox;
  }

  @Override
  public IRpcSite getRpcSite() {
    return this;
  }

  @Override
  public RuntimeData getRuntimeData() {
    return data;
  }

  @Override
  public RuntimeInfo getRuntimeInfo() {
    return data.getRuntimeInfo();
  }
}