org.acplt.oncrpc.OncRpcClient Maven / Gradle / Ivy
Show all versions of remotetea-oncrpc Show documentation
/*
* $Header: /home/harald/repos/remotetea.sf.net/remotetea/src/org/acplt/oncrpc/OncRpcClient.java,v 1.4 2009/02/25 19:15:23 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.net.InetAddress;
/**
* The abstract OncRpcClient
class is the foundation for
* protcol-specific ONC/RPC clients. It encapsulates protocol-independent
* functionality, like port resolving, if no port was specified for the
* ONC/RPC server to contact. This class also provides the method skeleton,
* for instance for executing procedure calls.
*
* In order to communicate with an ONC/RPC server, you need to create an
* ONC/RPC client, represented by classes derived from OncRpcClient
.
* The most generic way to generate an ONC/RPC client is as follows: use
* {@link #newOncRpcClient(InetAddress, int, int, int) newOncRpcClient(...)}
* and specify:
*
*
* - the host (of class InetAddress) where the ONC/RPC server resides,
*
- the ONC/RPC program number of the server to contact,
*
- the program's version number,
*
- and finally the IP protocol to use when talking to the server. This can be
* either {@link OncRpcProtocols#ONCRPC_UDP} or
* {@link OncRpcProtocols#ONCRPC_TCP}.
*
*
* The next code snippet shows how to create an ONC/RPC client, which can
* communicate over UDP/IP with the ONC/RPC server for program number
* 0x49678
on the same host (by coincidence, this is the program
* number of the ACPLT/KS protocol).
*
*
* OncRpcClient client;
* try {
* client = OncRpcClient.newOncRpcClient(
* InetAddress.getByName("localhost"),
* 0x49678, 1,
* OncRpcProtocols.ONCRPC_UDP);
* } catch ( OncRpcProgramNotRegisteredException e ) {
* System.out.println("ONC/RPC program server not found");
* System.exit(0);
* } catch ( OncRpcException e ) {
* System.out.println("Could not contact portmapper:");
* e.printStackTrace(System.out);
* System.exit(0);
* } catch ( IOException e ) {
* System.out.println("Could not contact portmapper:");
* e.printStackTrace(System.out);
* System.exit(0);
* }
*
*
* This code snippet also shows exception handling. The most common error
* you'll see is probably an {@link OncRpcProgramNotRegisteredException}, in
* case no such program number is currently registered at the specified host.
* An {@link OncRpcProgramNotRegisteredException} is a subclass of
* {@link OncRpcException} with a detail of
* {@link OncRpcException#RPC_PROGNOTREGISTERED}.
* In case no ONC/RPC portmapper is available at the specified host, you'll
* get an {@link OncRpcTimeoutException} instead (which is again a subclass of
* OncRpcException
with a detail of
* {@link OncRpcException#RPC_TIMEDOUT}).
* You might also get an IOException when using TCP/IP and the server
* can not be contacted because it does not accept new connections.
*
*
Instead of calling
* {@link #newOncRpcClient(InetAddress, int, int, int) OncRpcClient.newOncRpcClient(...)}
* you can also directly create objects of classes
* {@link OncRpcTcpClient} and {@link OncRpcUdpClient} if you know at compile
* time which kind of IP protocol you will use.
*
*
With a client proxy in your hands, you can now issue ONC/RPC calls. As
* a really, really simple example -- did I say "simple example"? -- we start
* with the famous ONC/RPC ping call. This call sends no parameters and expects
* no return from an ONC/RPC server. It is just used to check whether a server
* is still responsive.
*
*
* System.out.print("pinging server: ");
* try {
* client.call(0, XdrVoid.XDR_VOID, XdrVoid.XDR_VOID);
* } catch ( OncRpcException e ) {
* System.out.println("method call failed unexpectedly:");
* e.printStackTrace(System.out);
* System.exit(1);
* }
* System.out.println("server is alive.");
*
*
* By definition, the ONC/RPC ping call has program number 0 and expects
* no parameters and replies with no result. Thus we just specify an empty
* parameter and result in the form of the static {@link XdrVoid#XDR_VOID}
* object, when calling the ping procedure in the server using the
* {@link #call(int, XdrAble, XdrAble) call(...)} method.
*
*
For more complex and sometimes more useful ONC/RPC calls, you will need
* to write appropriate ONC/RPC parameter and reply classes. Unfortunately,
* at this time there's no compiler available to compile .x
files
* into appropriate Java classes. Well -- surely a lot of people will now associate
* completely wrong things with "x files", but in our case there's no new age
* or whatever mumbo jumbo involved, but definitions of XDR data structures
* instead.
*
* For the next example, let's pretend our server provides the answer to all
* questions when called with procedure number 42. Let's also pretend that
* this ONC/RPC call expects a question in form of a string and returns the
* answer as an integer. So we need to define two classes, one for the call's
* parameters and one for the reply. But let us first examine the class
* containing a call's parameters:
*
*
* class Parameters implements XdrAble {
* public String question;
*
* public void xdrEncode(XdrEncodingStream xdr)
* throws OncRpcException, IOException {
* xdr.xdrEncodeString(question);
* }
* public void xdrDecode(XdrDecodingStream xdr)
* throws OncRpcException, IOException {
* question = xdr.xdrDecodeString();
* }
* }
*
*
* The Parameters
class implements {@link XdrAble}, so instances
* of it can be sent and received over the network using Sun's XDR protocol.
* What exactly is sent over the wire is up to the two methods
* {@link XdrAble#xdrEncode xdrEncode(...)} and
* {@link XdrAble#xdrDecode xdrDecode(...)}. The xdrEncode
method
* is responsible to "encode" the data to be sent over the network. On the
* other side, xdrDecode
is responsible to restore an object's
* state from the data received over the network. In our example, these methods
* either send or receive a string.
*
*
The class defining the reply of our the-answer-to-all-questions ONC/RPC
* call is now straightforward:
*
*
* class Answer implements XdrAble {
* public int definitiveAnswer;
*
* public void xdrEncode(XdrEncodingStream xdr)
* throws OncRpcException, IOException {
* xdr.xdrEncodeInt(definitiveAnswer);
* }
* public void xdrDecode(XdrDecodingStream xdr)
* throws OncRpcException, IOException {
* definitiveAnswer = xdr.xdrDecodeInt();
* }
* }
*
*
* Finally, to ask a question, you need to create the parameter object and
* fill it with the parameters to be sent. Then create the object later receiving
* the reply. Finally issue the ONC/RPC call:
*
*
* Parameters parameters = new Parameters();
* parameters.question = "What is the final answer to all our questions?";
*
* Answer answer = new Answer();
*
* try {
* client.call(42, parameters, answer);
* } catch ( OncRpcException e ) {
* } catch ( IOException e ) {
* }
* System.out.println(parameters.question);
* System.out.println("And the answer is: " + answer.definitiveAnswer);
*
*
* When you do not need the client proxy object any longer, you should
* return the resources it occupies to the system. Use the {@link #close}
* method for this.
*
*
* client.close();
* client = null; // Hint to the garbage (wo)man
*
*
* {@link OncRpcClientAuth Authentication} can be done as follows: just
* create an authentication object and hand it over to the ONC/RPC client
* object.
*
*
* OncRpcClientAuth auth = new OncRpcClientAuthUnix(
* "[email protected]",
* 42, 1001, new int[0]);
* client.setAuth(auth);
*
*
* The {@link OncRpcClientAuthUnix} authentication AUTH_UNIX
will
* handle shorthand credentials (of type AUTH_SHORT
) transparently.
* If you do not set any authentication object after creating an ONC/RPC client
* object, AUTH_NONE
is used automatically.
*
* TCP-based ONC/RPC clients also support call batching (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);
*
*
* @see OncRpcPortmapClient
* @see OncRpcTcpClient
* @see OncRpcUdpClient
* @see OncRpcClientAuth
*
* @version $Revision: 1.4 $ $Date: 2009/02/25 19:15:23 $ $State: Exp $ $Locker: $
* @author Harald Albrecht
*/
public abstract class OncRpcClient {
/**
* Constructs an OncRpcClient
object (the generic part). If
* no port number is given (that is, port
is 0
),
* then a port lookup using the portmapper at host
is done.
*
* @param host Host address where the desired ONC/RPC server resides.
* @param program Program number of the desired ONC/RPC server.
* @param version Version number of the desired ONC/RPC server.
* @param port port number of the listening server component.
* @param protocol {@link OncRpcProtocols Protocol} to be used for
* ONC/RPC calls. This information is necessary, so port lookups through
* the portmapper can be done.
*
* @throws OncRpcException if an ONC/RPC error occurs.
* @throws IOException if an I/O error occurs.
*/
protected OncRpcClient(InetAddress host,
int program, int version,
int port,
int protocol)
throws OncRpcException, IOException {
//
// Set up the basics...
//
this.host = host;
this.program = program;
this.version = version;
//
// Initialize the message identifier with some more-or-less random
// value.
//
long seed = System.currentTimeMillis();
xid = ((int) seed) ^ ((int) (seed >>> 32));
//
// If the port number of the ONC/RPC server to contact is not yet
// known, try to find it out. For this we need to contact the portmap
// process at the given host and ask it for the desired program.
//
// In case of tunneling through the HTTP protocol, we accept a port
// number of zero and do not resolve it. This task is left up to
// the other end of the HTTP tunnel (at the web server).
//
if ( (port == 0) && (protocol != OncRpcProtocols.ONCRPC_HTTP) ) {
//
// FIX: We now use the same protocol to query the remote portmapper
// as we're told to use for later communicating with the remote
// RPC server.
//
OncRpcPortmapClient portmap = new OncRpcPortmapClient(host, protocol);
try {
port = portmap.getPort(program, version, protocol);
} finally {
portmap.close();
}
}
this.port = port;
}
/**
* Creates a new ONC/RPC client object, which can handle the requested
* protocol
.
*
* @param host Host address where the desired ONC/RPC server resides.
* @param program Program number of the desired ONC/RPC server.
* @param version Version number of the desired ONC/RPC server.
* @param protocol {@link OncRpcProtocols Protocol} to be used for
* ONC/RPC calls.
*
* @return A new instance of a concrete specialisation of class {@link OncRpcClient}.
*
* @throws OncRpcException if an ONC/RPC error occurs.
* @throws IOException if an I/O error occurs.
*/
public static OncRpcClient newOncRpcClient(InetAddress host,
int program, int version,
int protocol)
throws OncRpcException, IOException {
return newOncRpcClient(host, program, version, 0, protocol);
}
/**
* Creates a new ONC/RPC client object, which can handle the requested
* protocol
.
*
* @param host Host address where the desired ONC/RPC server resides.
* @param program Program number of the desired ONC/RPC server.
* @param version Version number of the desired ONC/RPC server.
* @param port Port number of the ONC/RPC server. Specifiy 0
* if this is not known and the portmap process located at host should
* be contacted to find out the port.
* @param protocol {@link OncRpcProtocols Protocol} to be used for
* ONC/RPC calls.
*
* @return A new instance of a concrete specialisation of class {@link OncRpcClient}.
*
* @throws OncRpcException if an ONC/RPC error occurs.
* @throws IOException if an I/O error occurs.
*/
public static OncRpcClient newOncRpcClient(InetAddress host,
int program, int version,
int port,
int protocol)
throws OncRpcException, IOException {
//
// Now we need to create a protocol client object, which will know
// how to create the network connection and how to send and receive
// data to and from it.
//
switch ( protocol ) {
case OncRpcProtocols.ONCRPC_UDP:
return new OncRpcUdpClient(host, program, version, port);
case OncRpcProtocols.ONCRPC_TCP:
return new OncRpcTcpClient(host, program, version, port);
default:
throw(new OncRpcException(OncRpcException.RPC_UNKNOWNPROTO));
}
}
/**
* 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 {
}
/**
* Calls a remote procedure on an ONC/RPC server.
*
* The OncRpcUdpClient
uses a similar timeout scheme as
* the genuine Sun C implementation of ONC/RPC: it starts with a timeout
* of one second when waiting for a reply. If no reply is received within
* this time frame, the client doubles the timeout, sends a new request
* and then waits again for a reply. In every case the client will wait
* no longer than the total timeout set through the
* {@link #setTimeout(int)} method.
*
* @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 result The object receiving the result of the procedure call.
*
* @throws OncRpcException if an ONC/RPC error occurs.
*/
public synchronized void call(int procedureNumber,
XdrAble params, XdrAble result)
throws OncRpcException {
//
// Use the default version number as specified for this client.
//
call(procedureNumber, version, params, result);
}
/**
* Calls a remote procedure on an ONC/RPC server.
*
* @param procedureNumber Procedure number of the procedure to call.
* @param versionNumber Protocol version number.
* @param parameters 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 abstract void call(int procedureNumber, int versionNumber,
XdrAble parameters, XdrAble result)
throws OncRpcException;
/**
* Set the timeout 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 indicated batched calls, for which no reply message
* is expected.
*
* @param milliseconds Timeout in milliseconds. A timeout of zero indicates
* batched calls.
*/
public void setTimeout(int milliseconds) {
if ( milliseconds < 0 ) {
throw(new IllegalArgumentException("timeouts can not be negative."));
}
timeout = milliseconds;
}
/**
* Retrieve the current timeout set for remote procedure calls. A timeout
* of zero indicates batching calls (no reply message is expected).
*
* @return Current timeout.
*/
public int getTimeout() {
return timeout;
}
/**
* Returns the program number specified when creating this client.
*
* @return ONC/RPC program number.
*/
public int getProgram() {
return program;
}
/**
* Returns the version number specified when creating this client.
*
* @return ONC/RPC version number of ONC/RPC program.
*/
public int getVersion() {
return version;
}
/**
* Returns the IP address of the server's host this client is connected to.
*
* @return IP address of host.
*/
public InetAddress getHost() {
return host;
}
/**
* Returns port number of the server this client is connected to.
*
* @return port number of ONC/RPC server.
*/
public int getPort() {
return port;
}
/**
* Sets the authentication to be used when making ONC/RPC calls.
*
* @param auth Authentication protocol handling object encapsulating
* authentication information.
*/
public void setAuth(OncRpcClientAuth auth) {
this.auth = auth;
}
/**
* Returns the current authentication.
*
* @return Authentication protocol handling object encapsulating
* authentication information.
*/
public OncRpcClientAuth getAuth() {
return auth;
}
/**
* 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 abstract void setCharacterEncoding(String 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 abstract String getCharacterEncoding();
/**
* Create next message identifier. Message identifiers are used to match
* corresponding ONC/RPC call and reply messages.
*/
protected void nextXid() {
xid++;
}
/**
* Internet address of the host where the ONC/RPC server we want to
* communicate with is located at.
*/
protected InetAddress host;
/**
* Timeout (in milliseconds) for communication with an ONC/RPC server.
* ONC/RPC calls through the {@link #call(int, XdrAble, XdrAble)} method
* will throw an exception if no answer from the ONC/RPC server is
* received within the timeout time span.
*/
protected int timeout = 30000;
/**
* Program number of the ONC/RPC server to communicate with.
*/
protected int program;
/**
* Version number of the ONC/RPC server to communicate with.
*/
protected int version;
/**
* Port number at which the ONC/RPC server can be contacted.
*/
protected int port;
/**
* The message id (also sometimes known as "transaction id") used for
* the next call message.
*/
protected int xid;
/**
* Authentication protocol object to be used when issuing ONC/RPC calls.
*/
protected OncRpcClientAuth auth;
}
// End of OncRpcClient.java