All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.acplt.oncrpc.OncRpcTcpClient Maven / Gradle / Ivy

There is a newer version: 1.1.6
Show newest version
/*
 * $Header: /home/harald/repos/remotetea.sf.net/remotetea/src/org/acplt/oncrpc/OncRpcTcpClient.java,v 1.5 2005/11/11 21:04:30 haraldalbrecht Exp $
 *
 * Copyright (c) 1999, 2000
 * Lehrstuhl fuer Prozessleittechnik (PLT), RWTH Aachen
 * D-52064 Aachen, Germany.
 * All rights reserved.
 *
 * This library is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Library General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this program (see the file LICENSE.txt for more
 * details); if not, write to the Free Software Foundation, Inc.,
 * 675 Mass Ave, Cambridge, MA 02139, USA.
 */

package org.acplt.oncrpc;

import java.io.IOException;
import java.io.InterruptedIOException;
import java.net.InetAddress;
import java.net.Socket;

/**
 * ONC/RPC client which communicates with ONC/RPC servers over the network
 * using the stream-oriented protocol TCP/IP.
 *
 * @version $Revision: 1.5 $ $Date: 2005/11/11 21:04:30 $ $State: Exp $ $Locker:  $
 * @author Harald Albrecht
 */
public class OncRpcTcpClient extends OncRpcClient {

    /**
     * Constructs a new OncRpcTcpClient object, which connects
     * to the ONC/RPC server at host for calling remote procedures
     * of the given { program, version }.
     *
     * 

Note that the construction of an OncRpcTcpClient * object will result in communication with the portmap process at * host. * * @param host The host where the ONC/RPC server resides. * @param program Program number of the ONC/RPC server to call. * @param version Program version number. * * @throws OncRpcException if an ONC/RPC error occurs. * @throws IOException if an I/O error occurs. */ public OncRpcTcpClient(InetAddress host, int program, int version) throws OncRpcException, IOException { this(host, program, version, 0, 0); } /** * Constructs a new OncRpcTcpClient object, which connects * to the ONC/RPC server at host for calling remote procedures * of the given { program, version }. * *

Note that the construction of an OncRpcTcpClient * object will result in communication with the portmap process at * host if port is 0. * * @param host The host where the ONC/RPC server resides. * @param program Program number of the ONC/RPC server to call. * @param version Program version number. * @param port The port number where the ONC/RPC server can be contacted. * If 0, then the OncRpcUdpClient object will * ask the portmapper at host for the port number. * * @throws OncRpcException if an ONC/RPC error occurs. * @throws IOException if an I/O error occurs. */ public OncRpcTcpClient(InetAddress host, int program, int version, int port) throws OncRpcException, IOException { this(host, program, version, port, 0); } /** * Constructs a new OncRpcTcpClient object, which connects * to the ONC/RPC server at host for calling remote procedures * of the given { program, version }. * *

Note that the construction of an OncRpcTcpClient * object will result in communication with the portmap process at * host if port is 0. * * @param host The host where the ONC/RPC server resides. * @param program Program number of the ONC/RPC server to call. * @param version Program version number. * @param port The port number where the ONC/RPC server can be contacted. * If 0, then the OncRpcUdpClient object will * ask the portmapper at host for the port number. * @param bufferSize Size of receive and send buffers. In contrast to * UDP-based ONC/RPC clients, messages larger than the specified * buffer size can still be sent and received. The buffer is only * necessary to handle the messages and the underlaying streams will * break up long messages automatically into suitable pieces. * Specifying zero will select the default buffer size (currently * 8192 bytes). * * @throws OncRpcException if an ONC/RPC error occurs. * @throws IOException if an I/O error occurs. */ public OncRpcTcpClient(InetAddress host, int program, int version, int port, int bufferSize) throws OncRpcException, IOException { this(host, program, version, port, bufferSize, -1); } /** * Constructs a new OncRpcTcpClient object, which connects * to the ONC/RPC server at host for calling remote procedures * of the given { program, version }. * *

Note that the construction of an OncRpcTcpClient * object will result in communication with the portmap process at * host if port is 0. * * @param host The host where the ONC/RPC server resides. * @param program Program number of the ONC/RPC server to call. * @param version Program version number. * @param port The port number where the ONC/RPC server can be contacted. * If 0, then the OncRpcUdpClient object will * ask the portmapper at host for the port number. * @param bufferSize Size of receive and send buffers. In contrast to * UDP-based ONC/RPC clients, messages larger than the specified * buffer size can still be sent and received. The buffer is only * necessary to handle the messages and the underlaying streams will * break up long messages automatically into suitable pieces. * Specifying zero will select the default buffer size (currently * 8192 bytes). * @param timeout Maximum timeout in milliseconds when connecting to * the ONC/RPC server. If negative, a default implementation-specific * timeout setting will apply. Note that this timeout only applies * to the connection phase, but not to later communication. * * @throws OncRpcException if an ONC/RPC error occurs. * @throws IOException if an I/O error occurs. */ public OncRpcTcpClient(InetAddress host, int program, int version, int port, int bufferSize, int timeout) throws OncRpcException, IOException { // // Construct the inherited part of our object. This will also try to // lookup the port of the desired ONC/RPC server, if no port number // was specified (port = 0). // super(host, program, version, port, OncRpcProtocols.ONCRPC_TCP); // // Let the host operating system choose which port (and network // interface) to use. Then set the buffer sizes for sending and // receiving UDP datagrams. Finally set the destination of packets. // if ( bufferSize == 0 ) { bufferSize = 8192; // default setting } if ( bufferSize < 1024 ) { bufferSize = 1024; } // // Note that we use this.port at this time, because the superclass // might have resolved the port number in case the caller specified // simply 0 as the port number. // socketHelper = new OncRpcTcpSocketHelper(); socket = socketHelper.connect(host, this.port, timeout); socket.setTcpNoDelay(true); if ( socketHelper.getSendBufferSize() < bufferSize ) { socketHelper.setSendBufferSize(bufferSize); } if ( socketHelper.getReceiveBufferSize() < bufferSize ) { socketHelper.setReceiveBufferSize(bufferSize); } // // Create the necessary encoding and decoding streams, so we can // communicate at all. // sendingXdr = new XdrTcpEncodingStream(socket, bufferSize); receivingXdr = new XdrTcpDecodingStream(socket, bufferSize); } /** * Close the connection to an ONC/RPC server and free all network-related * resources. Well -- at least hope, that the Java VM will sometimes free * some resources. Sigh. * * @throws OncRpcException if an ONC/RPC error occurs. */ public void close() throws OncRpcException { if ( socket != null ) { try { socket.close(); } catch ( IOException e ) { } socket = null; } if ( sendingXdr != null ) { try { sendingXdr.close(); } catch ( IOException e ) { } sendingXdr = null; } if ( receivingXdr != null ) { try { receivingXdr.close(); } catch ( IOException e ) { } receivingXdr = null; } } /** * Calls a remote procedure on an ONC/RPC server. * *

Please note that while this method supports call batching by * setting the communication timeout to zero * (setTimeout(0)) you should better use * {@link #batchCall} as it provides better control over when the * batch should be flushed to the server. * * @param procedureNumber Procedure number of the procedure to call. * @param versionNumber Protocol version number. * @param params The parameters of the procedure to call, contained * in an object which implements the {@link XdrAble} interface. * @param result The object receiving the result of the procedure call. * * @throws OncRpcException if an ONC/RPC error occurs. */ public synchronized void call(int procedureNumber, int versionNumber, XdrAble params, XdrAble result) throws OncRpcException { Refresh: for ( int refreshesLeft = 1; refreshesLeft >= 0; --refreshesLeft ) { // // First, build the ONC/RPC call header. Then put the sending // stream into a known state and encode the parameters to be // sent. Finally tell the encoding stream to send all its data // to the server. Then wait for an answer, receive it and decode // it. So that's the bottom line of what we do right here. // nextXid(); OncRpcClientCallMessage callHeader = new OncRpcClientCallMessage(xid, program, versionNumber, procedureNumber, auth); OncRpcClientReplyMessage replyHeader = new OncRpcClientReplyMessage(auth); // // Send call message to server. If we receive an IOException, // then we'll throw the appropriate ONC/RPC (client) exception. // Note that we use a connected stream, so we don't need to // specify a destination when beginning serialization. // try { socket.setSoTimeout(transmissionTimeout); sendingXdr.beginEncoding(null, 0); callHeader.xdrEncode(sendingXdr); params.xdrEncode(sendingXdr); if ( timeout != 0 ) { sendingXdr.endEncoding(); } else { sendingXdr.endEncoding(false); } } catch ( IOException e ) { throw(new OncRpcException(OncRpcException.RPC_CANTSEND, e.getLocalizedMessage())); } // // Receive reply message from server -- at least try to do so... // In case of batched calls we don't need no stinkin' answer, so // we can do other, more interesting things. // if ( timeout == 0 ) { return; } try { // // Keep receiving until we get the matching reply. // while ( true ) { socket.setSoTimeout(timeout); receivingXdr.beginDecoding(); socket.setSoTimeout(transmissionTimeout); // // First, pull off the reply message header of the // XDR stream. In case we also received a verifier // from the server and this verifier was invalid, broken // or tampered with, we will get an // OncRpcAuthenticationException right here, which will // propagate up to the caller. If the server reported // an authentication problem itself, then this will // be handled as any other rejected ONC/RPC call. // try { replyHeader.xdrDecode(receivingXdr); } catch ( OncRpcException e ) { // // ** SF bug #1262106 ** // // We ran into some sort of trouble. Usually this will have // been a buffer underflow. Whatever, end the decoding process // and ensure this way that the next call has a chance to start // from a clean state. // receivingXdr.endDecoding(); throw(e); } // // Only deserialize the result, if the reply matches the // call. Otherwise skip this record. // if ( replyHeader.messageId == callHeader.messageId ) { break; } receivingXdr.endDecoding(); } // // Make sure that the call was accepted. In case of unsuccessful // calls, throw an exception, if it's not an authentication // exception. In that case try to refresh the credential first. // if ( !replyHeader.successfullyAccepted() ) { receivingXdr.endDecoding(); // // Check whether there was an authentication // problem. In this case first try to refresh the // credentials. // if ( (refreshesLeft > 0) && (replyHeader.replyStatus == OncRpcReplyStatus.ONCRPC_MSG_DENIED) && (replyHeader.rejectStatus == OncRpcRejectStatus.ONCRPC_AUTH_ERROR) && (auth != null) && auth.canRefreshCred() ) { continue Refresh; } // // Nope. No chance. This gets tough. // throw(replyHeader.newException()); } try { result.xdrDecode(receivingXdr); } catch ( OncRpcException e ) { // // ** SF bug #1262106 ** // // We ran into some sort of trouble. Usually this will have // been a buffer underflow. Whatever, end the decoding process // and ensure this way that the next call has a chance to start // from a clean state. // receivingXdr.endDecoding(); throw(e); } // // Free pending resources of buffer and exit the call loop, // returning the reply to the caller through the result // object. // receivingXdr.endDecoding(); return; } catch ( InterruptedIOException e ) { // // In case our time run out, we throw an exception. // throw(new OncRpcTimeoutException()); } catch ( IOException e ) { // // Argh. Trouble with the transport. Seems like we can't // receive data. Gosh. Go away! // throw(new OncRpcException(OncRpcException.RPC_CANTRECV, e.getLocalizedMessage())); } } // for ( refreshesLeft ) } /** * Issues a batched call for a remote procedure to an ONC/RPC server. * Below is a small example (exception handling ommited for clarity): * *

     * OncRpcTcpClient client = new OncRpcTcpClient(
     *     InetAddress.getByName("localhost"),
     *     myprogramnumber, myprogramversion,
     *     OncRpcProtocols.ONCRPC_TCP);
     * client.callBatch(42, myparams, false);
     * client.callBatch(42, myotherparams, false);
     * client.callBatch(42, myfinalparams, true);
     * 
* * In the example above, three calls are batched in a row and only be sent * all together with the third call. Note that batched calls must not expect * replies, with the only exception being the last call in a batch: * *
     * client.callBatch(42, myparams, false);
     * client.callBatch(42, myotherparams, false);
     * client.call(43, myfinalparams, myfinalresult);
     * 
* * @param procedureNumber Procedure number of the procedure to call. * @param params The parameters of the procedure to call, contained * in an object which implements the {@link XdrAble} interface. * @param flush Make sure that all pending batched calls are sent to * the server. * * @throws OncRpcException if an ONC/RPC error occurs. */ public synchronized void batchCall(int procedureNumber, XdrAble params, boolean flush) throws OncRpcException { // // First, build the ONC/RPC call header. Then put the sending // stream into a known state and encode the parameters to be // sent. Finally tell the encoding stream to send all its data // to the server. We don't then need to wait for an answer. And // we don't need to take care of credential refreshes either. // nextXid(); OncRpcClientCallMessage callHeader = new OncRpcClientCallMessage(xid, program, version, procedureNumber, auth); // // Send call message to server. If we receive an IOException, // then we'll throw the appropriate ONC/RPC (client) exception. // Note that we use a connected stream, so we don't need to // specify a destination when beginning serialization. // try { socket.setSoTimeout(transmissionTimeout); sendingXdr.beginEncoding(null, 0); callHeader.xdrEncode(sendingXdr); params.xdrEncode(sendingXdr); sendingXdr.endEncoding(flush); } catch ( IOException e ) { throw(new OncRpcException(OncRpcException.RPC_CANTSEND, e.getLocalizedMessage())); } } /** * Set the timout for remote procedure calls to wait for an answer from * the ONC/RPC server. If the timeout expires, * {@link #call(int, XdrAble, XdrAble)} will raise a * {@link java.io.InterruptedIOException}. The default timeout value is * 30 seconds (30,000 milliseconds). The timeout must be > 0. * A timeout of zero indicates a batched call, for which no reply message * is expected. * * @param milliseconds Timeout in milliseconds. A timeout of zero indicates * batched calls. */ public void setTimeout(int milliseconds) { super.setTimeout(milliseconds); } /** * Set the timeout used during transmission of data. If the flow of data * when sending calls or receiving replies blocks longer than the given * timeout, an exception is thrown. The timeout must be > 0. * * @param milliseconds Transmission timeout in milliseconds. */ public void setTransmissionTimeout(int milliseconds) { if ( milliseconds <= 0 ) { throw(new IllegalArgumentException("transmission timeout must be > 0")); } transmissionTimeout = milliseconds; } /** * Retrieve the current timeout used during transmission phases (call and * reply phases). * * @return Current transmission timeout. */ public int getTransmissionTimeout() { return transmissionTimeout; } /** * Set the character encoding for (de-)serializing strings. * * @param characterEncoding the encoding to use for (de-)serializing strings. * If null, the system's default encoding is to be used. */ public void setCharacterEncoding(String characterEncoding) { receivingXdr.setCharacterEncoding(characterEncoding); sendingXdr.setCharacterEncoding(characterEncoding); } /** * Get the character encoding for (de-)serializing strings. * * @return the encoding currently used for (de-)serializing strings. * If null, then the system's default encoding is used. */ public String getCharacterEncoding() { return receivingXdr.getCharacterEncoding(); } /** * TCP socket used for stream-oriented communication with an ONC/RPC * server. */ private Socket socket; /** * Socket helper object supplying missing methods for JDK 1.1 * backwards compatibility. So much for compile once, does not run * everywhere. */ private OncRpcTcpSocketHelper socketHelper; /** * XDR encoding stream used for sending requests via TCP/IP to an ONC/RPC * server. */ protected XdrTcpEncodingStream sendingXdr; /** * XDR decoding stream used when receiving replies via TCP/IP from an * ONC/RPC server. */ protected XdrTcpDecodingStream receivingXdr; /** * Timeout during the phase where data is sent within calls, or data is * received within replies. */ protected int transmissionTimeout = 30000; } // End of OncRpcTcpClient.java




© 2015 - 2024 Weber Informatics LLC | Privacy Policy