001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: *
015: * See the License for the specific language governing permissions and
016: * limitations under the License.
017: */
018:
019: /**
020: * @author Mikhail A. Markov
021: * @version $Revision: 1.1.2.2 $
022: */package org.apache.harmony.rmi.server;
023:
024: import java.io.IOException;
025: import java.net.ServerSocket;
026: import java.net.Socket;
027: import java.rmi.server.RMIFailureHandler;
028: import java.rmi.server.RMISocketFactory;
029: import java.security.AccessController;
030: import java.util.Collections;
031: import java.util.HashSet;
032: import java.util.Hashtable;
033: import java.util.Set;
034:
035: import org.apache.harmony.rmi.common.CreateThreadAction;
036: import org.apache.harmony.rmi.common.RMILog;
037: import org.apache.harmony.rmi.internal.nls.Messages;
038: import org.apache.harmony.rmi.transport.Endpoint;
039: import org.apache.harmony.rmi.transport.proxy.HttpInboundSocket;
040: import org.apache.harmony.rmi.transport.proxy.HttpServerConnection;
041: import org.apache.harmony.rmi.transport.tcp.TcpServerConnection;
042:
043: /**
044: * Manager waiting for client connections and initiating communication with
045: * them.
046: *
047: * @author Mikhail A. Markov
048: * @version $Revision: 1.1.2.2 $
049: */
050: public class ServerConnectionManager implements Runnable {
051:
052: // Client host for RemoteServer.getClientHost() method
053: static ThreadLocal clientHost = new ThreadLocal();
054:
055: /*
056: * Table of local endpoints (keys) and appropriate mapped connection
057: * manager (values).
058: */
059: private static Hashtable localEps = new Hashtable();
060:
061: // ServerSocket where this manager waits for connections
062: private ServerSocket ss;
063:
064: // Local Endpoint for this connection
065: private Endpoint ep;
066:
067: // List of accepted(active) connections
068: private Set conns = Collections.synchronizedSet(new HashSet());
069:
070: // Number of in-progress calls to the objects in this table.
071: private int activeCallsNum = 0;
072:
073: // lock object for working with active calls
074: private class CallsLock {
075: }
076:
077: private Object callsLock = new CallsLock();
078:
079: /*
080: * Default wait time after 5 consecutive failed accept attempts
081: * if RMIFailureHandler is not set.
082: */
083: private static final long defaultFailureDelay = 3000;
084:
085: // Number of failed accepts attempts.
086: private long failedAcceptsNum = 0;
087:
088: // Log for logging transport-layer activity
089: static final RMILog transportLog = RMILog.getTransportLog();
090:
091: /**
092: * Constructs ServerConnectionManager and creates ServerSocket.
093: *
094: * @param sref server-side handle for exported remote object
095: *
096: * @throws IOException if any I/O error occurred during opening ServerSocket
097: */
098: private ServerConnectionManager(Endpoint localEp)
099: throws IOException {
100: ep = localEp;
101: ss = ep.createServerSocket();
102: }
103:
104: /**
105: * Returns ServerConnectionManager corresponding to the given ep. If such a
106: * manager does not exist creates it and put record to the table.
107: *
108: * @param ep Endpoint to get ServerConnectionManager for
109: *
110: * @return found (created) ServerConnectionManager
111: *
112: * @throws IOException and and I/O error occurred during manager creation
113: */
114: public static synchronized ServerConnectionManager getMgr(
115: Endpoint ep) throws IOException {
116: ServerConnectionManager mgr;
117: Endpoint tmpl = null;
118:
119: if (ep.getPort() != 0) {
120: tmpl = Endpoint.createTemplate(ep);
121: mgr = (ServerConnectionManager) localEps.get(tmpl);
122:
123: if (mgr != null) {
124: // verify that we can listen on the Endpoint's port
125: SecurityManager sm = System.getSecurityManager();
126:
127: if (sm != null) {
128: sm.checkListen(ep.getPort());
129: }
130: return mgr;
131: }
132: }
133: mgr = new ServerConnectionManager(ep);
134: ((Thread) AccessController.doPrivileged(new CreateThreadAction(
135: mgr, "ServerConnectionManager[" //$NON-NLS-1$
136: + mgr.getEndpoint() + "]", true))).start(); //$NON-NLS-1$
137: if (tmpl == null) {
138: tmpl = Endpoint.createTemplate(ep);
139: }
140: localEps.put(tmpl, mgr);
141: return mgr;
142: }
143:
144: /**
145: * Returns the string representation of the client's host for RMI calls
146: * which are processed in the current thread (this method is intended to be
147: * called by RemoteServer.getClientHost() method).
148: *
149: * @return string representation of the client's host for RMI calls which
150: * are processed in the current thread
151: */
152: public static String getClientHost() {
153: return (String) clientHost.get();
154: }
155:
156: /**
157: * Returns true if there are in-progress calls to remote objects
158: * associated with this manager.
159: *
160: * @return true if there are in-progress calls to remote object
161: * associated with this manager
162: */
163: public boolean hasActiveCalls() {
164: synchronized (callsLock) {
165: return (activeCallsNum != 0);
166: }
167: }
168:
169: /**
170: * Returns server's endpoint.
171: *
172: * @return server's endpoint;
173: */
174: public Endpoint getEndpoint() {
175: return ep;
176: }
177:
178: /**
179: * Starts waiting for incoming remote calls. When connection from remote
180: * is accepted, separate thread to process remote call is spawned. Waits
181: * for connections until this thread will not be interrupted.
182: */
183: public void run() {
184: while (!Thread.interrupted()) {
185: try {
186: Socket s = ss.accept();
187: startConnection(s);
188: failedAcceptsNum = 0;
189: } catch (Exception ex) {
190: RMIFailureHandler rfh = RMISocketFactory
191: .getFailureHandler();
192:
193: if (rfh != null) {
194: if (rfh.failure(ex)) {
195: return;
196: }
197: } else {
198: // We will try to immediately accept another client again,
199: // but if we have a bad client which fails our accept tries
200: // for a number of times, we should sleep for a while.
201: if (failedAcceptsNum >= 5) {
202: try {
203: Thread.sleep(defaultFailureDelay);
204: } catch (InterruptedException ie) {
205: return;
206: }
207: failedAcceptsNum = 0;
208: }
209: }
210: }
211: }
212: }
213:
214: /**
215: * Stops specified connection with remote client: closes opened Socket,
216: * stops appropriate thread and removes this connection from the list of
217: * active connections.
218: *
219: * @param conn connection to be stopped
220: */
221: public void stopConnection(ServerConnection conn) {
222: conn.close();
223: conns.remove(conn);
224: }
225:
226: /**
227: * Increase the number of active calls by one.
228: */
229: protected void addActiveCall() {
230: synchronized (callsLock) {
231: ++activeCallsNum;
232: ExportManager.addActiveCall();
233: }
234: }
235:
236: /**
237: * Decrease the number of active calls by one.
238: */
239: protected void removeActiveCall() {
240: synchronized (callsLock) {
241: --activeCallsNum;
242: ExportManager.removeActiveCall();
243: }
244: }
245:
246: /*
247: * Starts separate thread communicating with remote client to process
248: * remote call.
249: *
250: * @param s Socket connected with remote client
251: *
252: * @return connection to the remote client
253: *
254: * @throws IOException if any I/O error occurred while starting connection
255: */
256: private ServerConnection startConnection(Socket s)
257: throws IOException {
258: ServerConnection conn;
259:
260: if (s instanceof HttpInboundSocket) {
261: conn = new HttpServerConnection(s, this );
262: } else {
263: conn = new TcpServerConnection(s, this );
264: }
265: conns.add(conn);
266:
267: /*
268: * Start the thread in non-system group
269: * (see comment for CreateThreadAction class).
270: */
271: Thread connThread = (Thread) AccessController
272: .doPrivileged(new CreateThreadAction(conn,
273: "Call from " + conn.ep, true, //$NON-NLS-1$
274: false));
275: connThread.start();
276:
277: if (transportLog.isLoggable(RMILog.VERBOSE)) {
278: // rmi.log.10A=Accepted {0}
279: transportLog.log(RMILog.VERBOSE, Messages.getString(
280: "rmi.log.10A", conn)); //$NON-NLS-1$
281: }
282: return conn;
283: }
284: }
|