1 /* 2 * %CopyrightBegin% 3 * 4 * Copyright Ericsson AB 2000-2016. All Rights Reserved. 5 * 6 * Licensed under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 * 18 * %CopyrightEnd% 19 */ 20 package com.ericsson.otp.erlang; 21 22 import java.io.IOException; 23 24 /** 25 * <p> 26 * Maintains a connection between a Java process and a remote Erlang, Java or C 27 * node. The object maintains connection state and allows data to be sent to and 28 * received from the peer. 29 * </p> 30 * 31 * <p> 32 * Once a connection is established between the local node and a remote node, 33 * the connection object can be used to send and receive messages between the 34 * nodes. 35 * </p> 36 * 37 * <p> 38 * The various receive methods are all blocking and will return only when a 39 * valid message has been received or an exception is raised. 40 * </p> 41 * 42 * <p> 43 * If an exception occurs in any of the methods in this class, the connection 44 * will be closed and must be reopened in order to resume communication with the 45 * peer. 46 * </p> 47 * 48 * <p> 49 * The message delivery methods in this class deliver directly to 50 * {@link OtpMbox mailboxes} in the {@link OtpNode OtpNode} class. 51 * </p> 52 * 53 * <p> 54 * It is not possible to create an instance of this class directly. 55 * OtpCookedConnection objects are created as needed by the underlying mailbox 56 * mechanism. 57 * </p> 58 */ 59 public class OtpCookedConnection extends AbstractConnection { 60 protected OtpNode self; 61 62 /* 63 * The connection needs to know which local pids have links that pass 64 * through here, so that they can be notified in case of connection failure 65 */ 66 protected Links links = null; 67 68 /* 69 * Accept an incoming connection from a remote node. Used by {@link 70 * OtpSelf#accept() OtpSelf.accept()} to create a connection based on data 71 * received when handshaking with the peer node, when the remote node is the 72 * connection intitiator. 73 * 74 * @exception java.io.IOException if it was not possible to connect to the 75 * peer. 76 * 77 * @exception OtpAuthException if handshake resulted in an authentication 78 * error 79 */ 80 // package scope OtpCookedConnection(final OtpNode self, final OtpTransport s)81 OtpCookedConnection(final OtpNode self, final OtpTransport s) 82 throws IOException, OtpAuthException { 83 super(self, s); 84 this.self = self; 85 links = new Links(25); 86 start(); 87 } 88 89 /* 90 * Intiate and open a connection to a remote node. 91 * 92 * @exception java.io.IOException if it was not possible to connect to the 93 * peer. 94 * 95 * @exception OtpAuthException if handshake resulted in an authentication 96 * error. 97 */ 98 // package scope OtpCookedConnection(final OtpNode self, final OtpPeer other)99 OtpCookedConnection(final OtpNode self, final OtpPeer other) 100 throws IOException, OtpAuthException { 101 super(self, other); 102 this.self = self; 103 links = new Links(25); 104 start(); 105 } 106 107 // pass the error to the node 108 @Override deliver(final Exception e)109 public void deliver(final Exception e) { 110 self.deliverError(this, e); 111 return; 112 } 113 114 /* 115 * pass the message to the node for final delivery. Note that the connection 116 * itself needs to know about links (in case of connection failure), so we 117 * snoop for link/unlink too here. 118 */ 119 @Override deliver(final OtpMsg msg)120 public void deliver(final OtpMsg msg) { 121 final boolean delivered = self.deliver(msg); 122 123 switch (msg.type()) { 124 case OtpMsg.linkTag: 125 if (!delivered) { 126 try { 127 // no such pid - send exit to sender 128 super.sendExit(msg.getRecipientPid(), msg.getSenderPid(), 129 new OtpErlangAtom("noproc")); 130 } catch (final IOException e) { 131 } 132 } 133 break; 134 default: 135 break; 136 } 137 138 return; 139 } 140 141 /* 142 * send to pid 143 */ 144 @SuppressWarnings("resource") send(final OtpErlangPid from, final OtpErlangPid dest, final OtpErlangObject msg)145 void send(final OtpErlangPid from, final OtpErlangPid dest, 146 final OtpErlangObject msg) throws IOException { 147 // encode and send the message 148 sendBuf(from, dest, new OtpOutputStream(msg)); 149 } 150 151 /* 152 * send to remote name dest is recipient's registered name, the nodename is 153 * implied by the choice of connection. 154 */ 155 @SuppressWarnings("resource") send(final OtpErlangPid from, final String dest, final OtpErlangObject msg)156 void send(final OtpErlangPid from, final String dest, 157 final OtpErlangObject msg) throws IOException { 158 // encode and send the message 159 sendBuf(from, dest, new OtpOutputStream(msg)); 160 } 161 162 @Override close()163 public void close() { 164 super.close(); 165 breakLinks(); 166 } 167 168 @Override finalize()169 protected void finalize() { 170 close(); 171 } 172 173 /* 174 * this one called by dying/killed process 175 */ exit(final OtpErlangPid from, final OtpErlangPid to, final OtpErlangObject reason)176 void exit(final OtpErlangPid from, final OtpErlangPid to, 177 final OtpErlangObject reason) { 178 try { 179 super.sendExit(from, to, reason); 180 } catch (final Exception e) { 181 } 182 } 183 184 /* 185 * this one called explicitely by user code => use exit2 186 */ exit2(final OtpErlangPid from, final OtpErlangPid to, final OtpErlangObject reason)187 void exit2(final OtpErlangPid from, final OtpErlangPid to, 188 final OtpErlangObject reason) { 189 try { 190 super.sendExit2(from, to, reason); 191 } catch (final Exception e) { 192 } 193 } 194 link(final OtpErlangPid from, final OtpErlangPid to)195 void link(final OtpErlangPid from, final OtpErlangPid to) 196 throws OtpErlangExit { 197 try { 198 super.sendLink(from, to); 199 } catch (final IOException e) { 200 throw new OtpErlangExit("noproc", to); 201 } 202 } 203 unlink(final OtpErlangPid from, final OtpErlangPid to, final long unlink_id)204 void unlink(final OtpErlangPid from, final OtpErlangPid to, final long unlink_id) { 205 try { 206 super.sendUnlink(from, to , unlink_id); 207 } catch (final IOException e) { 208 } 209 } 210 unlink_ack(final OtpErlangPid from, final OtpErlangPid to, final long unlink_id)211 void unlink_ack(final OtpErlangPid from, final OtpErlangPid to, final long unlink_id) { 212 try { 213 super.sendUnlinkAck(from, to , unlink_id); 214 } catch (final IOException e) { 215 } 216 } 217 node_link(OtpErlangPid local, OtpErlangPid remote, boolean add)218 synchronized void node_link(OtpErlangPid local, OtpErlangPid remote, boolean add) 219 { 220 if (add) { 221 links.addLink(local, remote, true); 222 } 223 else { 224 links.removeLink(local, remote); 225 } 226 } 227 228 /* 229 * When the connection fails - send exit to all local pids with links 230 * through this connection 231 */ breakLinks()232 synchronized void breakLinks() { 233 if (links != null) { 234 final Link[] l = links.clearLinks(); 235 236 if (l != null) { 237 final int len = l.length; 238 239 for (int i = 0; i < len; i++) { 240 // send exit "from" remote pids to local ones 241 self.deliver(new OtpMsg(OtpMsg.exitTag, l[i].remote(), l[i] 242 .local(), new OtpErlangAtom("noconnection"))); 243 } 244 } 245 } 246 } 247 } 248