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 import java.net.UnknownHostException;
24 
25 /**
26  * Represents an OTP node. It is used to connect to remote nodes or accept
27  * incoming connections from remote nodes.
28  *
29  * <p>
30  * When the Java node will be connecting to a remote Erlang, Java or C node, it
31  * must first identify itself as a node by creating an instance of this class,
32  * after which it may connect to the remote node.
33  *
34  * <p>
35  * When you create an instance of this class, it will bind a socket to a port so
36  * that incoming connections can be accepted. However the port number will not
37  * be made available to other nodes wishing to connect until you explicitely
38  * register with the port mapper daemon by calling {@link #publishPort()}.
39  * </p>
40  *
41  * <pre>
42  * OtpSelf self = new OtpSelf(&quot;client&quot;, &quot;authcookie&quot;); // identify self
43  * OtpPeer other = new OtpPeer(&quot;server&quot;); // identify peer
44  *
45  * OtpConnection conn = self.connect(other); // connect to peer
46  * </pre>
47  *
48  */
49 public class OtpSelf extends OtpLocalNode {
50     private final OtpServerTransport sock;
51     private final OtpErlangPid pid;
52 
53     /**
54      * <p>
55      * Create a self node using the default cookie. The default cookie is found
56      * by reading the first line of the .erlang.cookie file in the user's home
57      * directory. The home directory is obtained from the System property
58      * "user.home".
59      * </p>
60      *
61      * <p>
62      * If the file does not exist, an empty string is used. This method makes no
63      * attempt to create the file.
64      * </p>
65      *
66      * @param node
67      *            the name of this node.
68      *
69      * @exception IOException
70      *                in case of server transport failure
71      *
72      */
OtpSelf(final String node)73     public OtpSelf(final String node) throws IOException {
74         this(node, defaultCookie, 0);
75     }
76 
77     /**
78      * <p>
79      * Create a self node using the default cookie and custom transport factory.
80      * The default cookie is found by reading the first line of the
81      * .erlang.cookie file in the user's home directory. The home directory is
82      * obtained from the System property "user.home".
83      * </p>
84      *
85      * <p>
86      * If the file does not exist, an empty string is used. This method makes no
87      * attempt to create the file.
88      * </p>
89      *
90      * @param node
91      *            the name of this node.
92      *
93      * @param transportFactory
94      *            the transport factory to use when creating connections.
95      *
96      * @exception IOException
97      *                in case of server transport failure
98      *
99      */
OtpSelf(final String node, final OtpTransportFactory transportFactory)100     public OtpSelf(final String node,
101             final OtpTransportFactory transportFactory) throws IOException {
102         this(node, defaultCookie, 0, transportFactory);
103     }
104 
105     /**
106      * Create a self node.
107      *
108      * @param node
109      *            the name of this node.
110      *
111      * @param cookie
112      *            the authorization cookie that will be used by this node when
113      *            it communicates with other nodes.
114      *
115      * @exception IOException
116      *                in case of server transport failure
117      */
OtpSelf(final String node, final String cookie)118     public OtpSelf(final String node, final String cookie) throws IOException {
119         this(node, cookie, 0);
120     }
121 
122     /**
123      * Create a self node.
124      *
125      * @param node
126      *            the name of this node.
127      *
128      * @param cookie
129      *            the authorization cookie that will be used by this node when
130      *            it communicates with other nodes.
131      *
132      * @param transportFactory
133      *            the transport factory to use when creating connections.
134      *
135      * @exception IOException
136      *                in case of server transport failure
137      */
OtpSelf(final String node, final String cookie, final OtpTransportFactory transportFactory)138     public OtpSelf(final String node, final String cookie,
139             final OtpTransportFactory transportFactory) throws IOException {
140         this(node, cookie, 0, transportFactory);
141     }
142 
143     /**
144      * Create a self node.
145      *
146      * @param node
147      *            the name of this node.
148      *
149      * @param cookie
150      *            the authorization cookie that will be used by this node when
151      *            it communicates with other nodes.
152      *
153      * @param port
154      *            the port number you wish to use for incoming connections.
155      *            Specifying 0 lets the system choose an available port.
156      *
157      * @exception IOException
158      *                in case of server transport failure
159      */
OtpSelf(final String node, final String cookie, final int port)160     public OtpSelf(final String node, final String cookie, final int port)
161             throws IOException {
162         super(node, cookie);
163 
164         sock = createServerTransport(port);
165 
166         if (port != 0) {
167             this.port = port;
168         } else {
169             this.port = sock.getLocalPort();
170         }
171 
172         pid = createPid();
173     }
174 
175     /**
176      * Create a self node.
177      *
178      * @param node
179      *            the name of this node.
180      *
181      * @param cookie
182      *            the authorization cookie that will be used by this node when
183      *            it communicates with other nodes.
184      *
185      * @param port
186      *            the port number you wish to use for incoming connections.
187      *            Specifying 0 lets the system choose an available port.
188      *
189      * @param transportFactory
190      *            the transport factory to use when creating connections.
191      *
192      * @exception IOException
193      *                in case of server transport failure
194      */
OtpSelf(final String node, final String cookie, final int port, final OtpTransportFactory transportFactory)195     public OtpSelf(final String node, final String cookie, final int port,
196             final OtpTransportFactory transportFactory) throws IOException {
197         super(node, cookie, transportFactory);
198 
199         sock = createServerTransport(port);
200 
201         if (port != 0) {
202             this.port = port;
203         } else {
204             this.port = sock.getLocalPort();
205         }
206 
207         pid = createPid();
208     }
209 
210     /**
211      * Get the Erlang PID that will be used as the sender id in all "anonymous"
212      * messages sent by this node. Anonymous messages are those sent via send
213      * methods in {@link OtpConnection OtpConnection} that do not specify a
214      * sender.
215      *
216      * @return the Erlang PID that will be used as the sender id in all
217      *         anonymous messages sent by this node.
218      */
pid()219     public OtpErlangPid pid() {
220         return pid;
221     }
222 
223     /**
224      * Make public the information needed by remote nodes that may wish to
225      * connect to this one. This method establishes a connection to the Erlang
226      * port mapper (Epmd) and registers the server node's name and port so that
227      * remote nodes are able to connect.
228      *
229      * <p>
230      * This method will fail if an Epmd process is not running on the localhost.
231      * See the Erlang documentation for information about starting Epmd.
232      *
233      * <p>
234      * Note that once this method has been called, the node is expected to be
235      * available to accept incoming connections. For that reason you should make
236      * sure that you call {@link #accept()} shortly after calling
237      * {@link #publishPort()}. When you no longer intend to accept connections
238      * you should call {@link #unPublishPort()}.
239      *
240      * @return true if the operation was successful, false if the node was
241      *         already registered.
242      *
243      * @exception java.io.IOException
244      *                if the port mapper could not be contacted.
245      */
publishPort()246     public boolean publishPort() throws IOException {
247         if (getEpmd() != null) {
248             return false; // already published
249         }
250 
251         OtpEpmd.publishPort(this);
252         return getEpmd() != null;
253     }
254 
255     /**
256      * Unregister the server node's name and port number from the Erlang port
257      * mapper, thus preventing any new connections from remote nodes.
258      */
unPublishPort()259     public void unPublishPort() {
260         // unregister with epmd
261         OtpEpmd.unPublishPort(this);
262 
263         // close the local descriptor (if we have one)
264         try {
265             if (super.epmd != null) {
266                 super.epmd.close();
267             }
268         } catch (final IOException e) {/* ignore close errors */
269         }
270         super.epmd = null;
271     }
272 
273     /**
274      * Accept an incoming connection from a remote node. A call to this method
275      * will block until an incoming connection is at least attempted.
276      *
277      * @return a connection to a remote node.
278      *
279      * @exception java.io.IOException
280      *                if a remote node attempted to connect but no common
281      *                protocol was found.
282      *
283      * @exception OtpAuthException
284      *                if a remote node attempted to connect, but was not
285      *                authorized to connect.
286      */
accept()287     public OtpConnection accept() throws IOException, OtpAuthException {
288         OtpTransport newsock = null;
289 
290         while (true) {
291             try {
292                 newsock = sock.accept();
293                 return new OtpConnection(this, newsock);
294             } catch (final IOException e) {
295                 try {
296                     if (newsock != null) {
297                         newsock.close();
298                     }
299                 } catch (final IOException f) {/* ignore close errors */
300                 }
301                 throw e;
302             }
303         }
304     }
305 
306     /**
307      * Open a connection to a remote node.
308      *
309      * @param other
310      *            the remote node to which you wish to connect.
311      *
312      * @return a connection to the remote node.
313      *
314      * @exception java.net.UnknownHostException
315      *                if the remote host could not be found.
316      *
317      * @exception java.io.IOException
318      *                if it was not possible to connect to the remote node.
319      *
320      * @exception OtpAuthException
321      *                if the connection was refused by the remote node.
322      */
connect(final OtpPeer other)323     public OtpConnection connect(final OtpPeer other) throws IOException,
324             UnknownHostException, OtpAuthException {
325         return new OtpConnection(this, other);
326     }
327 }
328