1 /*
   2  * Copyright (c) 1996, 2001, 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 package sun.rmi.transport;
  26 
  27 import java.io.*;
  28 import java.util.*;
  29 import java.rmi.RemoteException;
  30 import java.rmi.server.UID;
  31 import sun.rmi.server.MarshalInputStream;
  32 import 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  */
  40 class 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 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 refList = (List) incomingRefTable.get(ep);
  74 
  75         if (refList == null) {
  76             refList = new ArrayList();
  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      * Add references to DGC table (and possibly send dirty call).
  86      * RegisterRefs now calls DGCClient.referenced on all
  87      * refs with the same endpoint at once to achieve batching of
  88      * calls to the DGC
  89      */
  90     void registerRefs() throws IOException {
  91         if (!incomingRefTable.isEmpty()) {
  92             Set entrySet = incomingRefTable.entrySet();
  93             Iterator iter = entrySet.iterator();
  94             while (iter.hasNext()) {
  95                 Map.Entry entry = (Map.Entry) iter.next();
  96                 Endpoint ep = (Endpoint) entry.getKey();
  97                 List refList = (List) entry.getValue();
  98                 DGCClient.registerRefs(ep, refList);
  99             }
 100         }
 101     }
 102 
 103     /**
 104      * Indicate that an ack is required to the distributed
 105      * collector.
 106      */
 107     void setAckNeeded() {
 108         dgcAckNeeded = true;
 109     }
 110 
 111     /**
 112      * Done with input stream for remote call. Send DGC ack if necessary.
 113      * Allow sending of ack to fail without flagging an error.
 114      */
 115     void done(Connection c) {
 116         /*
 117          * WARNING: The connection c may have already been freed.  It
 118          * is only be safe to use c to obtain c's channel.
 119          */
 120 
 121         if (dgcAckNeeded) {
 122             Connection conn = null;
 123             Channel ch = null;
 124             boolean reuse = true;
 125 
 126             DGCImpl.dgcLog.log(Log.VERBOSE, "send ack");
 127 
 128             try {
 129                 ch = c.getChannel();
 130                 conn = ch.newConnection();
 131                 DataOutputStream out =
 132                     new DataOutputStream(conn.getOutputStream());
 133                 out.writeByte(TransportConstants.DGCAck);
 134                 if (ackID == null) {
 135                     ackID = new UID();
 136                 }
 137                 ackID.write((DataOutput) out);
 138                 conn.releaseOutputStream();
 139 
 140                 /*
 141                  * Fix for 4221173: if this connection is on top of an
 142                  * HttpSendSocket, the DGCAck won't actually get sent until a
 143                  * read operation is attempted on the socket.  Calling
 144                  * available() is the most innocuous way of triggering the
 145                  * write.
 146                  */
 147                 conn.getInputStream().available();
 148                 conn.releaseInputStream();
 149             } catch (RemoteException e) {
 150                 reuse = false;
 151             } catch (IOException e) {
 152                 reuse = false;
 153             }
 154             try {
 155                 if (conn != null)
 156                     ch.free(conn, reuse);
 157             } catch (RemoteException e){
 158                 // eat exception
 159             }
 160         }
 161     }
 162 }