1/*
2 * Copyright (c) 1996, 2017, 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 */
25package sun.rmi.transport;
26
27import java.io.*;
28import java.util.*;
29import java.rmi.RemoteException;
30import java.rmi.server.UID;
31import sun.rmi.server.MarshalInputStream;
32import sun.rmi.runtime.Log;
33
34/**
35 * Special stream to keep track of refs being unmarshaled so that
36 * refs can be ref-counted locally.
37 *
38 * @author Ann Wollrath
39 */
40class ConnectionInputStream extends MarshalInputStream {
41
42    /** indicates whether ack is required for DGC */
43    private boolean dgcAckNeeded = false;
44
45    /** Hashtable mapping Endpoints to lists of LiveRefs to register */
46    private Map<Endpoint, List<LiveRef>> incomingRefTable = new HashMap<>(5);
47
48    /** identifier for gc ack*/
49    private UID ackID;
50
51    /**
52     * Constructs a marshal input stream using the underlying
53     * stream "in".
54     */
55    ConnectionInputStream(InputStream in) throws IOException {
56        super(in);
57    }
58
59    void readID() throws IOException {
60        ackID = UID.read((DataInput) this);
61    }
62
63    /**
64     * Save reference in order to send "dirty" call after all args/returns
65     * have been unmarshaled.  Save in hashtable incomingRefTable.  This
66     * table is keyed on endpoints, and holds objects of type
67     * IncomingRefTableEntry.
68     */
69    void saveRef(LiveRef ref) {
70        Endpoint ep = ref.getEndpoint();
71
72        // check whether endpoint is already in the hashtable
73        List<LiveRef> refList = incomingRefTable.get(ep);
74
75        if (refList == null) {
76            refList = new ArrayList<LiveRef>();
77            incomingRefTable.put(ep, refList);
78        }
79
80        // add ref to list of refs for endpoint ep
81        refList.add(ref);
82    }
83
84    /**
85     * Discard the saved incoming refs so there is nothing to register
86     * when {@code registerRefs} is called.
87     */
88    void discardRefs() {
89        incomingRefTable.clear();
90    }
91
92    /**
93     * Add references to DGC table (and possibly send dirty call).
94     * RegisterRefs now calls DGCClient.referenced on all
95     * refs with the same endpoint at once to achieve batching of
96     * calls to the DGC
97     */
98    void registerRefs() throws IOException {
99        if (!incomingRefTable.isEmpty()) {
100            for (Map.Entry<Endpoint, List<LiveRef>> entry :
101                     incomingRefTable.entrySet()) {
102                DGCClient.registerRefs(entry.getKey(), entry.getValue());
103            }
104        }
105    }
106
107    /**
108     * Indicate that an ack is required to the distributed
109     * collector.
110     */
111    void setAckNeeded() {
112        dgcAckNeeded = true;
113    }
114
115    /**
116     * Done with input stream for remote call. Send DGC ack if necessary.
117     * Allow sending of ack to fail without flagging an error.
118     */
119    void done(Connection c) {
120        /*
121         * WARNING: The connection c may have already been freed.  It
122         * is only be safe to use c to obtain c's channel.
123         */
124
125        if (dgcAckNeeded) {
126            Connection conn = null;
127            Channel ch = null;
128            boolean reuse = true;
129
130            DGCImpl.dgcLog.log(Log.VERBOSE, "send ack");
131
132            try {
133                ch = c.getChannel();
134                conn = ch.newConnection();
135                DataOutputStream out =
136                    new DataOutputStream(conn.getOutputStream());
137                out.writeByte(TransportConstants.DGCAck);
138                if (ackID == null) {
139                    ackID = new UID();
140                }
141                ackID.write((DataOutput) out);
142                conn.releaseOutputStream();
143
144                /*
145                 * Fix for 4221173: if this connection is on top of an
146                 * HttpSendSocket, the DGCAck won't actually get sent until a
147                 * read operation is attempted on the socket.  Calling
148                 * available() is the most innocuous way of triggering the
149                 * write.
150                 */
151                conn.getInputStream().available();
152                conn.releaseInputStream();
153            } catch (RemoteException e) {
154                reuse = false;
155            } catch (IOException e) {
156                reuse = false;
157            }
158            try {
159                if (conn != null)
160                    ch.free(conn, reuse);
161            } catch (RemoteException e){
162                // eat exception
163            }
164        }
165    }
166}
167