/**
* This file is part of Erjang - A JVM-based Erlang VM
*
* Copyright (c) 2009 by Trifork
*
* 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 erjang.m.erlang;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;
import kilim.Pausable;
import erjang.BIF;
import erjang.EAbstractNode;
import erjang.EAtom;
import erjang.EFun;
import erjang.EHandle;
import erjang.EInternalPID;
import erjang.EInternalPort;
import erjang.ENode;
import erjang.EObject;
import erjang.EPeer;
import erjang.EProc;
import erjang.ERT;
import erjang.ERef;
import erjang.ESeq;
import erjang.ESmall;
import erjang.ETuple;
import erjang.ETuple2;
import erjang.ErlangError;
import erjang.Import;
import erjang.NotImplemented;
import erjang.BIF.Type;
import erjang.driver.EDriverTask;
/**
* BIFs supporting distribution
*/
public class ErlDist {
static Logger log = Logger.getLogger("erjang.dist");
private static DistEntry this_dist_entry = null;
private static final EAtom am_net_kernel = EAtom.intern("net_kernel");
private static final EAtom am_Noname = EAtom.intern("Noname");
// initialization from node_tables
static {
this_dist_entry = new DistEntry(am_Noname, (EInternalPort) null);
}
/** distribution traps */
@Import(module="net_kernel", fun="connect", arity=1)
public static EFun net_kernel__connect__1;
@Import(module="erlang", fun="dsend", arity=2)
static EFun dsend2_trap;
@Import(module="erlang", fun="dsend", arity=3)
static EFun dsend3_trap;
@Import(module="erlang", fun="dlink", arity=1)
static EFun dlink1_trap;
@Import(module="erlang", fun="dunlink", arity=1)
static EFun dunlink1_trap;
@Import(module="erlang", fun="dmonitor_node", arity=3)
static EFun dmonitor_node3_trap;
@Import(module="erlang", fun="dgroup_leader", arity=2)
static EFun dgroup_leader2_trap;
@Import(module="erlang", fun="dexit", arity=2)
static EFun dexit2_trap;
@Import(module="erlang", fun="dmonitor_p", arity=2)
static EFun dmonitor_p2_trap;
static EAtom erts_is_alive = ERT.FALSE;
@BIF
public static final EObject is_alive() {
return erts_is_alive;
}
@BIF
public static EObject nodes(EObject node) {
throw new NotImplemented();
}
@BIF
public static EObject monitor_node(EProc proc, EObject node, EObject flag, EObject opts) throws Pausable {
EAtom aname = node.testAtom();
EAtom aflag = flag.testAtom();
ESeq sopts = opts.testSeq();
if (aname == null || aflag == null || sopts == null) {
throw ERT.badarg(node, flag);
}
EAbstractNode n = EPeer.get(aname);
if (n == null) {
return dmonitor_node3_trap.invoke(proc, new EObject[]{aname, aflag, opts});
} else {
n.monitor_node(proc.self_handle(), aflag==ERT.TRUE);
return ERT.TRUE;
}
}
@BIF
public static EObject monitor_node(EProc proc, EObject node, EObject flag) throws Pausable {
return monitor_node(proc, node, flag, ERT.NIL);
}
@BIF
public static EObject dist_exit(EObject a1, EObject a2, EObject a3) {
throw new NotImplemented();
}
@BIF
static public EAtom node() {
EAtom val = ERT.getLocalNode().node();
return val;
}
@BIF
static public EAtom node(EObject name) {
if (!ERT.getLocalNode().isALive()) {
return ENode.am_nonode_at_nohost;
}
ERef ref;
if ((ref=name.testReference()) != null)
return ref.node();
EHandle handle;
if ((handle=name.testHandle()) != null)
return handle.node();
throw ERT.badarg(name);
}
@BIF(type = Type.GUARD, name = "node")
static public EAtom node$p(EObject name) {
if (!ERT.getLocalNode().isALive()) {
return ENode.am_nonode_at_nohost;
}
EHandle handle;
if ((handle=name.testHandle()) != null) {
return handle.node();
}
ERef ref;
if ((ref=name.testReference()) != null) {
return ref.node();
}
return null;
}
@BIF
public static EObject setnode(EObject arg_node, EObject arg_creation)
throws Pausable
{
int creation;
ESmall cr = arg_creation.testSmall();
EAtom node = arg_node.testAtom();
if (cr == null || node == null || cr.value > 3 || !is_node_name_atom(node)) {
if (log.isLoggable(Level.FINE)) log.fine("cr="+cr+"; node="+node+"; is_name="+is_node_name_atom(node));
throw ERT.badarg(arg_node, arg_creation);
}
EObject net_kernel = ERT.whereis(am_net_kernel);
EInternalPID nk = net_kernel.testInternalPID();
if (nk == null) {
throw new ErlangError(EAtom.intern("no_net_kernel"));
}
nk.set_dist_entry(this_dist_entry);
// nk.add_flag( F_DISTRIBUTION );
set_this_node(node, cr.value);
erts_is_alive = ERT.TRUE;
EAbstractNode n = EPeer.get(node);
n.node_up(null/*??*/, ERT.NIL);
return ERT.TRUE;
}
private static void set_this_node(EAtom node, int value) {
ERT.getLocalNode().set(node, value);
}
private static Pattern node_name_regex = Pattern.compile("([a-zA-Z0-9_]|-)+@[^@]+");
public static boolean is_node_name_atom(EAtom node) {
if (node_name_regex.matcher(node.getName()).matches()) {
return true;
} else {
return false;
}
}
/**********************************************************************
** Allocate a dist entry, set node name install the connection handler
** setnode_3({name@host, Creation}, Cid, {Type, Version, Initial, IC, OC})
** Type = flag field, where the flags are specified in dist.h
** Version = distribution version, >= 1
** IC = in_cookie (ignored)
** OC = out_cookie (ignored)
**
** Note that in distribution protocols above 1, the Initial parameter
** is always NIL and the cookies are always the atom '', cookies are not
** sent in the distribution messages but are only used in
** the handshake.
**
***********************************************************************/
@BIF
public static EObject setnode(EObject node_arg, EObject cid_arg, EObject type_arg)
throws Pausable
{
//System.err.println("SETNODE("+node_arg+", "+cid_arg+", "+type_arg+")");
EAtom node;
int creation = 0;
ETuple2 tup;
if ((tup=ETuple2.cast(node_arg)) != null) {
node = tup.elem1.testAtom();
ESmall sm = tup.elem2.testSmall();
if (node == null || sm==null || !is_node_name_atom(node))
throw ERT.badarg(node_arg, cid_arg, type_arg);
creation = sm.value;
} else if ((node=node_arg.testAtom()) != null && is_node_name_atom(node)) {
// ok
} else {
throw ERT.badarg(node_arg, cid_arg, type_arg);
}
/** first arg is ok */
EInternalPort port = cid_arg.testInternalPort();
if (port == null) {
throw ERT.badarg(node_arg, cid_arg, type_arg);
}
ETuple t = type_arg.testTuple();
if (t.arity() != 4) {
throw ERT.badarg(node_arg, cid_arg, type_arg);
}
ESmall flags = t.elm(1).testSmall();
ESmall version = t.elm(2).testSmall();
if (flags == null || version == null) {
throw ERT.badarg(node_arg, cid_arg, type_arg);
}
EPeer n = EPeer.get_or_create(node, creation, port, flags.value, version.value);
EDriverTask task = port.task();
if (task != null) {
task.node(n);
/*TODO:
send_nodes_mon_msgs(BIF_P,
am_nodeup,
BIF_ARG_1,
flags & DFLAG_PUBLISHED ? am_visible : am_hidden,
NIL);
*/
n.node_up(null/*??*/, ERT.NIL);
return ERT.TRUE;
} else {
return ERT.FALSE;
}
}
}