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