1/*
2 * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package sun.rmi.transport;
27
28import java.io.IOException;
29import java.io.ObjectInput;
30import java.io.ObjectOutput;
31import java.rmi.Remote;
32import java.rmi.RemoteException;
33import java.rmi.server.ObjID;
34import java.rmi.server.RMIClientSocketFactory;
35import java.rmi.server.RMIServerSocketFactory;
36import java.util.Arrays;
37import sun.rmi.transport.tcp.TCPEndpoint;
38
39/**
40 * NOTE: There is a JDK-internal dependency on the existence of this
41 * class and its getClientSocketFactory method in the implementation
42 * of javax.management.remote.rmi.RMIConnector.
43 **/
44public class LiveRef implements Cloneable {
45    /** wire representation for the object*/
46    private final Endpoint ep;
47    private final ObjID id;
48
49    /** cached connection service for the object */
50    private transient Channel ch;
51
52    /** flag to indicate whether this ref specifies a local server or
53     * is a ref for a remote object (surrogate)
54     */
55    private final boolean isLocal;
56
57    /**
58     * Construct a "well-known" live reference to a remote object
59     * @param isLocal If true, indicates this ref specifies a local
60     * server in this address space; if false, the ref is for a remote
61     * object (hence a surrogate or proxy) in another address space.
62     */
63    public LiveRef(ObjID objID, Endpoint endpoint, boolean isLocal) {
64        ep = endpoint;
65        id = objID;
66        this.isLocal = isLocal;
67    }
68
69    /**
70     * Construct a new live reference for a server object in the local
71     * address space.
72     */
73    public LiveRef(int port) {
74        this((new ObjID()), port);
75    }
76
77    /**
78     * Construct a new live reference for a server object in the local
79     * address space, to use sockets of the specified type.
80     */
81    public LiveRef(int port,
82                   RMIClientSocketFactory csf,
83                   RMIServerSocketFactory ssf)
84    {
85        this((new ObjID()), port, csf, ssf);
86    }
87
88    /**
89     * Construct a new live reference for a "well-known" server object
90     * in the local address space.
91     */
92    public LiveRef(ObjID objID, int port) {
93        this(objID, TCPEndpoint.getLocalEndpoint(port), true);
94    }
95
96    /**
97     * Construct a new live reference for a "well-known" server object
98     * in the local address space, to use sockets of the specified type.
99     */
100    public LiveRef(ObjID objID, int port, RMIClientSocketFactory csf,
101                   RMIServerSocketFactory ssf)
102    {
103        this(objID, TCPEndpoint.getLocalEndpoint(port, csf, ssf), true);
104    }
105
106    /**
107     * Return a shallow copy of this ref.
108     */
109    public Object clone() {
110        try {
111            LiveRef newRef = (LiveRef) super.clone();
112            return newRef;
113        } catch (CloneNotSupportedException e) {
114            throw new InternalError(e.toString(), e);
115        }
116    }
117
118    /**
119     * Return the port number associated with this ref.
120     */
121    public int getPort() {
122        return ((TCPEndpoint) ep).getPort();
123    }
124
125    /**
126     * Return the client socket factory associated with this ref.
127     *
128     * NOTE: There is a JDK-internal dependency on the existence of
129     * this method in the implementation of
130     * javax.management.remote.rmi.RMIConnector.
131     **/
132    public RMIClientSocketFactory getClientSocketFactory() {
133        return ((TCPEndpoint) ep).getClientSocketFactory();
134    }
135
136    /**
137     * Return the server socket factory associated with this ref.
138     */
139    public RMIServerSocketFactory getServerSocketFactory() {
140        return ((TCPEndpoint) ep).getServerSocketFactory();
141    }
142
143    /**
144     * Export the object to accept incoming calls.
145     */
146    public void exportObject(Target target) throws RemoteException {
147        ep.exportObject(target);
148    }
149
150    public Channel getChannel() throws RemoteException {
151        if (ch == null) {
152            ch = ep.getChannel();
153        }
154        return ch;
155    }
156
157    public ObjID getObjID() {
158        return id;
159    }
160
161    Endpoint getEndpoint() {
162        return ep;
163    }
164
165    public String toString() {
166        String type;
167
168        if (isLocal)
169            type = "local";
170        else
171            type = "remote";
172        return "[endpoint:" + ep + "(" + type + ")," +
173            "objID:" + id + "]";
174    }
175
176    public int hashCode() {
177        return id.hashCode();
178    }
179
180    public boolean equals(Object obj) {
181        if (obj != null && obj instanceof LiveRef) {
182            LiveRef ref = (LiveRef) obj;
183
184            return (ep.equals(ref.ep) && id.equals(ref.id) &&
185                    isLocal == ref.isLocal);
186        } else {
187            return false;
188        }
189    }
190
191    public boolean remoteEquals(Object obj) {
192        if (obj != null && obj instanceof LiveRef) {
193            LiveRef ref = (LiveRef) obj;
194
195            TCPEndpoint thisEp = ((TCPEndpoint) ep);
196            TCPEndpoint refEp = ((TCPEndpoint) ref.ep);
197
198            RMIClientSocketFactory thisClientFactory =
199                thisEp.getClientSocketFactory();
200            RMIClientSocketFactory refClientFactory =
201                refEp.getClientSocketFactory();
202
203            /**
204             * Fix for 4254103: LiveRef.remoteEquals should not fail
205             * if one of the objects in the comparison has a null
206             * server socket.  Comparison should only consider the
207             * following criteria:
208             *
209             * hosts, ports, client socket factories and object IDs.
210             */
211            if (thisEp.getPort() != refEp.getPort() ||
212                !thisEp.getHost().equals(refEp.getHost()))
213            {
214                return false;
215            }
216            if ((thisClientFactory == null) ^ (refClientFactory == null)) {
217                return false;
218            }
219            if ((thisClientFactory != null) &&
220                !((thisClientFactory.getClass() ==
221                   refClientFactory.getClass()) &&
222                  (thisClientFactory.equals(refClientFactory))))
223            {
224                return false;
225            }
226            return (id.equals(ref.id));
227        } else {
228            return false;
229        }
230    }
231
232    public void write(ObjectOutput out, boolean useNewFormat)
233        throws IOException
234    {
235        boolean isResultStream = false;
236        if (out instanceof ConnectionOutputStream) {
237            ConnectionOutputStream stream = (ConnectionOutputStream) out;
238            isResultStream = stream.isResultStream();
239            /*
240             * Ensure that referential integrity is not broken while
241             * this LiveRef is in transit.  If it is being marshalled
242             * as part of a result, it may not otherwise be strongly
243             * reachable after the remote call has completed; even if
244             * it is being marshalled as part of an argument, the VM
245             * may determine that the reference on the stack is no
246             * longer reachable after marshalling (see 6181943)--
247             * therefore, tell the stream to save a reference until a
248             * timeout expires or, for results, a DGCAck message has
249             * been received from the caller, or for arguments, the
250             * remote call has completed.  For a "local" LiveRef, save
251             * a reference to the impl directly, because the impl is
252             * not reachable from the LiveRef (see 4114579);
253             * otherwise, save a reference to the LiveRef, for the
254             * client-side DGC to watch over.  (Also see 4017232.)
255             */
256            if (isLocal) {
257                ObjectEndpoint oe =
258                    new ObjectEndpoint(id, ep.getInboundTransport());
259                Target target = ObjectTable.getTarget(oe);
260
261                if (target != null) {
262                    Remote impl = target.getImpl();
263                    if (impl != null) {
264                        stream.saveObject(impl);
265                    }
266                }
267            } else {
268                stream.saveObject(this);
269            }
270        }
271        // All together now write out the endpoint, id, and flag
272
273        // (need to choose whether or not to use old JDK1.1 endpoint format)
274        if (useNewFormat) {
275            ((TCPEndpoint) ep).write(out);
276        } else {
277            ((TCPEndpoint) ep).writeHostPortFormat(out);
278        }
279        id.write(out);
280        out.writeBoolean(isResultStream);
281    }
282
283    public static LiveRef read(ObjectInput in, boolean useNewFormat)
284        throws IOException, ClassNotFoundException
285    {
286        Endpoint ep;
287        ObjID id;
288
289        // Now read in the endpoint, id, and result flag
290        // (need to choose whether or not to read old JDK1.1 endpoint format)
291        if (useNewFormat) {
292            ep = TCPEndpoint.read(in);
293        } else {
294            ep = TCPEndpoint.readHostPortFormat(in);
295        }
296        id = ObjID.read(in);
297        boolean isResultStream = in.readBoolean();
298
299        LiveRef ref = new LiveRef(id, ep, false);
300
301        if (in instanceof ConnectionInputStream) {
302            ConnectionInputStream stream = (ConnectionInputStream)in;
303            // save ref to send "dirty" call after all args/returns
304            // have been unmarshaled.
305            stream.saveRef(ref);
306            if (isResultStream) {
307                // set flag in stream indicating that remote objects were
308                // unmarshaled.  A DGC ack should be sent by the transport.
309                stream.setAckNeeded();
310            }
311        } else {
312            DGCClient.registerRefs(ep, Arrays.asList(new LiveRef[] { ref }));
313        }
314
315        return ref;
316    }
317}
318