/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.jini.jeri.connection; import com.sun.jini.jeri.internal.mux.MuxServer; import com.sun.jini.logging.Levels; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.Collection; import java.util.logging.Level; import java.util.logging.LogRecord; import java.util.logging.Logger; import net.jini.core.constraint.InvocationConstraints; import net.jini.io.UnsupportedConstraintException; import net.jini.jeri.InboundRequest; import net.jini.jeri.RequestDispatcher; import net.jini.jeri.ServerEndpoint; /** * Provides server-side connection management using the <a * href="{@docRoot}/net/jini/jeri/connection/doc-files/mux.html">Jini * extensible remote invocation (Jini ERI) multiplexing protocol</a> * to frame and multiplex requests and responses over connections. * * <p>A <code>ServerConnectionManager</code> is created by a * connection-based {@link ServerEndpoint} implemention to manage * connections. The {@link #handleConnection handleConnection} method * is used to manage connections for a particular {@link * ServerConnection}. * * <p>Each <i>session</i> of the Jini ERI multiplexing protocol is * mapped to a new request. Request data is read as the data received * for the session, and response data is written as the data sent for * the session. * * @author Sun Microsystems, Inc. * @since 2.0 * * @com.sun.jini.impl * * This implementation uses the {@link Logger} named * <code>net.jini.jeri.connection.ServerConnectionManager</code> to * log information at the following levels: * * <p><table summary="Describes what is logged by * ServerConnectionManager to its logger at various logging levels" * border=1 cellpadding=5> * * <tr> <th> Level <th> Description * * <tr> <td> {@link Levels#HANDLED HANDLED} <td> * I/O exception initiating handling of a new request on a connection * * <tr> <td> {@link Levels#HANDLED HANDLED} <td> * I/O exception initiating multiplexing on a new connection * * </table> * * <p>This implementation uses the {@link Logger} named * <code>net.jini.jeri.connection.mux</code> to log information at the * following levels: * * <p><table summary="Describes what is logged by * ServerConnectionManager to the mux logger at various logging * levels" border=1 cellpadding=5> * * <tr> <th> Level <th> Description * * <tr> <td> {@link Level#WARNING WARNING} <td> unexpected exception * during asynchronous I/O processing, or thread creation failure * * <tr> <td> {@link Levels#HANDLED HANDLED} <td> I/O exception during * asynchronous I/O processing * * <tr> <td> {@link Level#FINEST FINEST} <td> detailed implementation * activity * * </table> **/ public final class ServerConnectionManager { /** * ServerConnectionManager logger. */ private static final Logger logger = Logger.getLogger("net.jini.jeri.connection.ServerConnectionManager"); /** * Creates a new <code>ServerConnectionManager</code>. **/ public ServerConnectionManager() { } /** * Request dispatcher wrapper around the request dispatcher passed to * handleConnection (i.e., the request dispatcher created by the runtime). */ private static final class Dispatcher implements RequestDispatcher { /** * The request dispatcher passed to handleConnection. */ private final RequestDispatcher dispatcher; /** * The connection passed to handleConnection. */ private final ServerConnection c; Dispatcher(RequestDispatcher dispatcher, ServerConnection c) { this.dispatcher = dispatcher; this.c = c; } /** * Calls processRequestData on the connection, passing the streams * from the specified inbound request, to obtain the request handle, * wraps the mux inbound request, connection and handle in an inbound * request, checks for accept permission, and calls dispatch on the * underlying dispatcher. If an IOException is thrown, catch it. It * is assumed that the caller will always abort the request (even on * normal return). */ public void dispatch(InboundRequest req) { try { InboundRequest sreq = new Inbound(req, c, c.processRequestData( req.getRequestInputStream(), req.getResponseOutputStream())); sreq.checkPermissions(); dispatcher.dispatch(sreq); } catch (IOException e) { if (logger.isLoggable(Levels.HANDLED)) { logThrow(logger, "dispatchRequest", "{0} throws", new Object[]{c}, e); } } } } /** * Inbound request wrapper around the inbound request created by the mux. */ private static final class Inbound implements InboundRequest { /** * The inbound request created by the mux. */ private final InboundRequest req; /** * The connection on which the inbound request originates. */ private final ServerConnection c; /** * The inbound request handle. */ private final InboundRequestHandle handle; Inbound(InboundRequest req, ServerConnection c, InboundRequestHandle handle) { this.req = req; this.c = c; this.handle = handle; } /* delegate to both the underlying request and the connection */ public void populateContext(Collection context) { req.populateContext(context); c.populateContext(handle, context); } /* pass-through to the connection */ public InvocationConstraints checkConstraints( InvocationConstraints constraints) throws UnsupportedConstraintException { return c.checkConstraints(handle, constraints); } /* pass-through to the connection */ public void checkPermissions() { c.checkPermissions(handle); } /* pass-through to the underlying request */ public InputStream getRequestInputStream() { return req.getRequestInputStream(); } /* pass-through to the underlying request */ public OutputStream getResponseOutputStream() { return req.getResponseOutputStream(); } /* pass-through to underlying request */ public void abort() { req.abort(); } } /** * Subclass wrapper around MuxServer for inbound connections. */ private static final class InboundMux extends MuxServer { /** * The inbound connection. */ private final ServerConnection c; /** * Constructs an instance from the connection's streams. */ private InboundMux(ServerConnection c, RequestDispatcher dispatcher) throws IOException { super(c.getOutputStream(), c.getInputStream(), dispatcher); this.c = c; } /** * Constructs an instance from the connection's channel. */ private InboundMux(ServerConnection c, RequestDispatcher dispatcher, boolean ignore) throws IOException { super(c.getChannel(), dispatcher); this.c = c; } /** * Constructs an instance from the connection. */ static void create(ServerConnection c, RequestDispatcher dispatcher) { RequestDispatcher d = new Dispatcher(dispatcher, c); try { if (c.getChannel() == null) { new InboundMux(c, d).start(); } else { new InboundMux(c, d, true).start(); } } catch (IOException e) { if (logger.isLoggable(Levels.HANDLED)) { logThrow(logger, "handleConnection", "{0} throws", new Object[]{c}, e); } try { c.close(); } catch (IOException ee) { } } } /** * Close the connection, so that the provider is notified. */ protected void handleDown() { try { c.close(); } catch (IOException e) { } } } /** * Log a throw. */ private static void logThrow(Logger logger, String method, String msg, Object[] args, Exception e) { LogRecord lr = new LogRecord(Levels.HANDLED, msg); lr.setLoggerName(logger.getName()); lr.setSourceClassName(ServerConnectionManager.class.getName()); lr.setSourceMethodName(method); lr.setParameters(args); lr.setThrown(e); logger.log(lr); } /** * Starts handling requests received on the specified newly * accepted connection, dispatching them to the specified request * dispatcher asynchronously, and returns immediately. * * <p>The Jini ERI multiplexing protocol is started on the * connection (as the server). As each request is received, the * {@link ServerConnection#processRequestData processRequestData} * method of the connection will be invoked with the request input * stream and the response output stream of the {@link * InboundRequest} created for the request, to obtain a handle for * the request. The {@link ServerConnection#checkPermissions * checkPermissions} method of the connection is then invoked with * that handle, and if it returns normally, * <code>dispatcher</code> is invoked with the * <code>InboundRequest</code>. All of this processing is * performed using the same security context in force when this * <code>handleConnection</code> method was invoked. The {@link * InboundRequest#checkPermissions checkPermissions}, {@link * InboundRequest#checkConstraints checkConstraints}, and {@link * InboundRequest#populateContext populateContext} methods of each * <code>InboundRequest</code> created are implemented by * delegating to the corresponding method of the connection * passing the handle for the request and the other arguments (if * any). * * <P>The implementation may close the connection if it determines * that the client has closed its side of the connection, if there * is an unrecoverable problem with the connection, or for other * implementation-specific reasons. The caller is responsible for * closing the connection when the {@link * net.jini.jeri.ServerEndpoint.ListenHandle#close close} method * of the associated {@link * net.jini.jeri.ServerEndpoint.ListenHandle ListenHandle} is * invoked. * * @param conn the server connection * * @param dispatcher the request dispatcher to use to dispatch * requests received on the specified connection * * @throws NullPointerException if <code>conn</code> or * <code>dispatcher</code> is <code>null</code> **/ public void handleConnection(ServerConnection conn, RequestDispatcher dispatcher) { if (conn == null || dispatcher == null) { throw new NullPointerException(); } InboundMux.create(conn, dispatcher); } }