/** * 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.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; import kilim.Pausable; import erjang.BIF; import erjang.EAtom; import erjang.EInternalPID; import erjang.EInternalPort; import erjang.EObject; import erjang.EPort; import erjang.EProc; import erjang.ERT; import erjang.ESeq; import erjang.ESmall; import erjang.EString; import erjang.ETask; import erjang.ETuple; import erjang.ETuple2; import erjang.ETuple3; import erjang.driver.EDriver; import erjang.driver.EDriverInstance; import erjang.driver.EDriverTask; import erjang.driver.EExecDriverTask; import erjang.driver.EFDDriverTask; import erjang.driver.ESpawnDriverTask; import erjang.driver.tcp_inet.TCPINet; /** * */ public class ErlPort { static Logger log = Logger.getLogger("erjang.port"); public static final EAtom am_fd = EAtom.intern("fd"); private static final ByteBuffer EMPTY_BYTEBUFFER = ByteBuffer.allocate(0); public static EAtom am_spawn = EAtom.intern("spawn"); static EAtom am_spawn_driver = EAtom.intern("spawn_driver"); static EAtom am_spawn_executable = EAtom.intern("spawn_executable"); @BIF static EObject port_connect(EProc proc, EObject arg_port, EObject arg_pid) throws Pausable { EInternalPort iport; EInternalPID ipid; if ((iport=arg_port.testInternalPort()) == null) { EAtom port_name = arg_port.testAtom(); EObject resolved; if (port_name != null && (resolved=ERT.whereis(port_name)) != ERT.am_undefined && (iport=resolved.testInternalPort()) != null) { // ok // } else { throw ERT.badarg(arg_port, arg_pid); } } if ((ipid=arg_pid.testInternalPID()) == null) { EAtom pid_name = arg_pid.testAtom(); EObject resolved; if (pid_name != null && (resolved=ERT.whereis(pid_name)) != ERT.am_undefined && (ipid=resolved.testInternalPID()) != null) { // ok // } else { throw ERT.badarg(arg_port, arg_pid); } } // TODO: what if port or pid args are already dead? iport.set_owner(ipid); ipid.task().link_to(iport); return ERT.TRUE; } @BIF static EObject port_command(EProc proc, EObject port, EObject data) throws Pausable { EInternalPort p = port.testInternalPort(); if (log.isLoggable(Level.FINER)) log.finer("port_command "+port+", "+data); if (p == null) { port = ERT.whereis(port); if (port == ERT.am_undefined) port = null; else p = port.testInternalPort(); } List<ByteBuffer> ovec = new ArrayList<ByteBuffer>(); if (p == null || !data.collectIOList(ovec)) { if (log.isLoggable(Level.WARNING)) { log.warning("collect failed! or p==null: "+p); } throw ERT.badarg(port, data); } ByteBuffer[] out = new ByteBuffer[ovec.size()]; ovec.toArray(out); if (log.isLoggable(Level.FINE)) { log.fine("EVEC: "); TCPINet.dump_buffer(out); } // log.finer("packing "+data+"::"+data.getClass().getName()+" -> "+ovec); p.command(proc.self_handle(), out); return ERT.TRUE; } /* TODO: worry about the options argument */ @BIF static EObject port_command(EProc proc, EObject port, EObject data, EObject options) throws Pausable { EInternalPort p = port.testInternalPort(); if (log.isLoggable(Level.FINE)) log.fine("port_command "+port+", "+data); if (p == null) { port = ERT.whereis(port); if (port == ERT.am_undefined) port = null; else p = port.testInternalPort(); } List<ByteBuffer> ovec = new ArrayList<ByteBuffer>(); if (p == null || !data.collectIOList(ovec)) { if (log.isLoggable(Level.FINE)) { log.fine("collect failed! or p==null: "+p); } throw ERT.badarg(port, data); } ByteBuffer[] out = new ByteBuffer[ovec.size()]; ovec.toArray(out); EDriverInstance.dump_buffer(log, "EVEC: ", out); // log.fine("packing "+data+"::"+data.getClass().getName()+" -> "+ovec); p.command(proc.self_handle(), out); return ERT.TRUE; } @BIF static EObject port_control(EProc proc, EObject port, EObject operation, EObject data) throws Pausable { try { return port_control0(proc, port, operation, data); } catch (RuntimeException e) { e.printStackTrace(); throw e; } catch (Error e) { e.printStackTrace(); throw e; } } static EObject port_control0(EProc proc, EObject port, EObject operation, EObject data) throws Pausable { EInternalPort p = port.testInternalPort(); if (p == null) { port = ERT.whereis(port); if (port == ERT.am_undefined) port = null; p = port.testInternalPort(); } ESmall op = operation.testSmall(); List<ByteBuffer> ovec = new ArrayList<ByteBuffer>(); if (p == null || op == null || !data.collectIOList(ovec)) { throw ERT.badarg(port, operation, data); } ByteBuffer cmd = flatten(ovec); // TODO: improve exception handling/wrapping here so we get // ErlangException types only! return p.control(proc, op.value, cmd); } private static ByteBuffer flatten(List<ByteBuffer> ovec) { if (ovec.size() == 0) { return EMPTY_BYTEBUFFER; } else if (ovec.size() == 1) { return ovec.get(0); } int len = 0; for (int i = 0; i < ovec.size(); i++) { len += ovec.get(i).remaining(); } ByteBuffer res = ByteBuffer.allocate(len); for (ByteBuffer bb : ovec) { res.put(bb); } res.rewind(); return res; } @BIF static EObject port_call(EProc proc, EObject port, EObject operation, EObject data) throws Pausable { EInternalPort p = port.testInternalPort(); if (p == null) { port = ERT.whereis(port); if (port == ERT.am_undefined) port = null; p = port.testInternalPort(); } ESmall op = operation.testSmall(); if (p == null || op == null) { throw ERT.badarg(port, operation, data); } // TODO: improve exception handling/wrapping here so we get // ErlangException types only! return p.call(proc, op.value, data); } @BIF static EPort open_port(EProc proc, EObject portName, EObject portSetting) throws Pausable { ETuple t; if ((t = portName.testTuple()) == null) throw ERT.badarg(portName, portSetting); ETask<? extends EPort> task = null; ETuple2 name; ETuple3 name3; if ((name = ETuple2.cast(t)) != null) { EAtom am = name.elem2.testAtom(); EString command = (am == null) ? (EString)EString.make(name.elem2) : EString.fromString(am.getName()); if (name.elem1 == am_spawn) { EDriver drv = ERT.find_driver(command); if (drv == null) { task = new EExecDriverTask(proc, name, command, portSetting); } else { task = new ESpawnDriverTask(proc, drv, command, portSetting); } } else if (name.elem1 == am_spawn_driver) { EDriver drv = ERT.find_driver(command); if (drv == null) { throw ERT.badarg(portName, portSetting); } task = new ESpawnDriverTask(proc, drv, command, portSetting); } else if (name.elem1 == am_spawn_executable) { task = new EExecDriverTask(proc, name, command, portSetting); } } else if ((name3 = ETuple3.cast(portName)) != null && name3.elem1 == am_fd) { ESmall in = name3.elem2.testSmall(); ESmall out = name3.elem3.testSmall(); if (in == null || out == null) throw ERT.badarg(portName, portSetting); // log.finer("creating fd driver in="+in+"; out="+out); task = new EFDDriverTask(proc, in.value, out.value, portSetting); } if (task != null) { // link this proc and the driver task proc.link_to(task); ERT.run(task); return task.self_handle(); } throw ERT.badarg(portName, portSetting); } @BIF static public EObject port_close(EProc proc, EObject port) throws Pausable { EPort p; if ((p = port.testPort()) == null) { EObject obj = ERT.whereis(port); if (obj == ERT.am_undefined || ((p = obj.testPort()) == null)) { throw ERT.badarg(port); } } if (!p.isOpen()) { throw ERT.badarg(port); } proc.unlink(p); p.close(); return ERT.TRUE; } @BIF static public ESeq ports() { return EDriverTask.all_ports(); } @BIF static public EObject port_info(EProc proc, EObject a1, EObject a2) { EPort p = a1.testPort(); EAtom spec = a2.testAtom(); if (p==null || spec==null) throw ERT.badarg(); EObject info = p.port_info(spec); //log.finer(""+proc.self_handle()+"::port_info ("+a1+") => "+info); return info; } @BIF static public EObject port_set_data(EObject port, EObject data) { EPort p = id_or_name2port(port); if (p == null) { throw ERT.badarg(port, data); } p.set_data(data); return data; } @BIF static public EObject port_get_data(EObject port) { EPort p = id_or_name2port(port); if (p == null) { throw ERT.badarg(port); } return p.get_data(); } private static EPort id_or_name2port(EObject port) { EPort p = port.testPort(); if (p != null) return p; EObject p2 = ERT.whereis(port); // p2 is ERT.am_undefined if not found return p2.testPort(); } }