org.apache.vinci.transport.BaseClient Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jVinci Show documentation
Show all versions of jVinci Show documentation
This is a non-standard protocol for higher efficiency than
SOAP, used by the base UIMA Collection processing manager for supporting
networked deployment. See UIMA-AS as a more modern alternative supporting
more standard protocols.
/*
* 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 org.apache.vinci.transport;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketTimeoutException;
import org.apache.vinci.debug.Debug;
/**
* Class for conjuring a Vinci service by host/port (that is, without interaction with the naming
* service). Usually you want to use VinciClient, which extends this class to invoke a service by
* (qualified) name.
*
* Provides generic "send/recieve/sendAndReceive" for communicating arbitrary (transportable)
* document models, and also specific "rpc" methods for more convenient support of the VinciFrame
* document model.
*/
public class BaseClient {
static public final int DEFAULT_SOCKET_TIMEOUT = 120000;
static public final int DEFAULT_CONNECT_TIMEOUT = 30000;
private String host = null;
private int port = 0;
private TransportableFactory factory;
private Socket socket = null;
private InputStream is = null;
private OutputStream os = null;
private KeyValuePair header = null;
private int socketTimeout = DEFAULT_SOCKET_TIMEOUT;
private int connectTimeout = DEFAULT_CONNECT_TIMEOUT;
private boolean retry = true;
/**
* Open up the service at the specified host and port, using a VinciFrame factory.
*
* @param h
* The hostname/ip address of the machine running the service.
* @param p
* The port on which the service runs.
* @throws IOException
* if the underlying socket fails to connect
*
* @pre h != null
* @pre p ≥ 0
* @pre p < 65536
*/
public BaseClient(String h, int p) throws IOException {
this(h, p, VinciFrame.getVinciFrameFactory());
}
/**
* Open up the service at the specified host and port, using a VinciFrame factory.
*
* @param h
* The hostname/ip address of the machine running the service.
* @param p
* The port on which the service runs.
* @param connect_timeout
* The number of milliseconds that will elapse before a connect attempt fails.
*
* @throws IOException
* if the underlying socket fails to connect
*
* @pre h != null
* @pre p ≥ 0
* @pre p < 65536
*/
public BaseClient(String h, int p, int connect_timeout) throws IOException {
this(h, p, VinciFrame.getVinciFrameFactory(), connect_timeout);
}
/**
* Open up the service at the specified host and port.
*
* @param h
* The hostname/ip address of the machine running the service.
* @param p
* The port on which the service runs.
* @param f
* A factory for creating documents of the desired type.
* @throws IOException
* if the underlying socket fails to connect.
*
* @pre h != null
* @pre f != null
* @pre p ≥ 0
* @pre p < 65536
*/
public BaseClient(String h, int p, TransportableFactory f) throws IOException {
this.factory = f;
this.host = h;
this.port = p;
open(host, port);
}
/**
* Open up the service at the specified host and port, using the specified connect timeout.
*
* @param h
* The hostname/ip address of the machine running the service.
* @param p
* The port on which the service runs.
* @param f
* A factory for creating documents of the desired type.
* @param timeout
* The number of milliseconds that will elapse before a connect attempt fails.
* @throws IOException
* if the underlying socket fails to connect.
*
* @pre h != null
* @pre f != null
* @pre p ≥ 0
* @pre p < 65536
* @pre timeout > 0
*/
public BaseClient(String h, int p, TransportableFactory f, int timeout) throws IOException {
this.factory = f;
this.host = h;
this.port = p;
connectTimeout = timeout;
open(host, port);
}
/**
* Create a base client without establishing a connection. This is useful for client classes which
* extend this class and which to perform their own connection establishment. Uses a VinciFrame
* factory.
*/
public BaseClient() {
this(VinciFrame.getVinciFrameFactory());
}
/**
* Create a base client without establishing a connection. This is useful for client classes which
* extend this class and which to perform their own connection establishment.
*
* @param f
* A factory for creating documents of the desired type.
*
* @pre f != null
*/
public BaseClient(TransportableFactory f) {
this.factory = f;
}
/**
* Create a base client without establishing a connection. This is useful for client classes which
* extend this class and which to perform their own connection establishment.
*
* @param f
* A factory for creating documents of the desired type.
* @param timeout
* The number of milliseconds that will elapse before a connect attempt fails.
*
* @pre f != null
*/
public BaseClient(TransportableFactory f, int timeout) {
this.factory = f;
this.connectTimeout = timeout;
}
/**
* Get the hostname/ip address to which this client is connected.
* @return -
*/
public String getHost() {
return host;
}
public int getPort() {
return port;
}
/**
* Get the socket used for the connection.
* @return -
*/
protected Socket getSocket() {
return socket;
}
/**
* Get the default timeout value for the socket (0 indicates never timeout, which is the default,
* but generally NOT a good setting).
* @return -
*/
public int getSocketTimeout() {
return socketTimeout;
}
/**
* Set the timeout value used for connect timeouts. Note that if you use one of the
* connection-inducing constructors, then this method has no effect unless a subsequent connection
* attempt is made.
*
* @param timeout
* The number of milliseconds that will elapse before a connect attempt fails.
*/
public void setConnectTimeout(int timeout) {
connectTimeout = timeout;
}
/**
* Set the transportable factory used by this client.
* @param f -
*/
public void setTransportableFactory(TransportableFactory f) {
factory = f;
}
/**
* Takes a transportable object, sends it across the connection, then retrieves the response and
* returns it.
*
* @param in
* The query frame.
* @return A transportable representing the result, its specific type determined by the factory
* provided through BaseClient's constructor.
* @exception IOException
* thrown by the underlying socket IO (e.g. due to connection timeout).
* @exception ServiceException
* thrown if the server threw ServiceException or returned an ErrorFrame.
*
* @pre in != null
* @pre getHost() != null
*/
public Transportable sendAndReceive(Transportable in) throws IOException, ServiceException {
try {
try {
if (!isOpen()) {
open();
}
return sendAndReceiveWork(in, factory);
} catch (SocketTimeoutException e) {
// Don't retry on socket timeout - service might be overloaded
throw e;
} catch (IOException e) {
if (retry) {
reopen(e);
return sendAndReceiveWork(in, factory);
} else {
throw e;
}
}
} catch (SocketTimeoutException e) {
// Close on timeout to make sure buffers are cleared if client reused
close();
throw e;
}
}
/**
* Takes a transportable object, sends it across the connection, then retrieves the response and
* returns it.
*
* @param in
* The query frame.
* @param timeout
* The timeout value to use in place of this client's default timeout setting.
* @return A transportable representing the result, its specific type determined by the factory
* provided through BaseClient's constructor.
* @throws IOException
* thrown by the underlying socket IO (e.g. due to connection timeout).
* @throws ServiceException
* thrown if the server threw ServiceException or returned an ErrorFrame.
*
* @pre getHost() != null
*/
public Transportable sendAndReceive(Transportable in, int timeout) throws IOException,
ServiceException {
try {
try {
if (!isOpen()) {
open();
}
return sendAndReceiveWork(in, factory, timeout);
} catch (SocketTimeoutException e) {
// Don't retry on socket timeout - service might be overloaded
throw e;
} catch (IOException e) {
if (retry) {
reopen(e);
return sendAndReceiveWork(in, factory, timeout);
} else {
throw e;
}
}
} catch (SocketTimeoutException e) {
// Close on timeout to make sure buffers are cleared if client reused
close();
throw e;
}
}
/**
* Same as sendAndReceive(Transportable) except the provided factory is used to create the return
* document in place of the default factory.
*
*
* @pre f != null
* @pre in != null
*
* @pre getHost() != null
* @param in -
* @param f
* The factory to used to create the return document.
* @return -
* @throws IOException -
* @throws ServiceException -
*/
public Transportable sendAndReceive(Transportable in, TransportableFactory f) throws IOException,
ServiceException {
try {
try {
if (!isOpen()) {
open();
}
return sendAndReceiveWork(in, f);
} catch (SocketTimeoutException e) {
// Don't retry on socket timeout - service might be overloaded
throw e;
} catch (IOException e) {
if (retry) {
reopen(e);
return sendAndReceiveWork(in, f);
} else {
throw e;
}
}
} catch (SocketTimeoutException e) {
// Close on timeout to make sure buffers are cleared if client reused
close();
throw e;
}
}
/**
* Same as sendAndReceive(Transportable, timeout) except the provided factory is used to create
* the return document in place of the default factory.
*
* @param in -
* @param f The factory to used to create the return document.
* @param timeout -
* @return -
* @throws IOException -
* @throws ServiceException -
* @pre in != null
* @pre f != null
* @pre getHost() != null
*/
public Transportable sendAndReceive(Transportable in, TransportableFactory f, int timeout)
throws IOException, ServiceException {
try {
try {
if (!isOpen()) {
open();
}
return sendAndReceiveWork(in, f, timeout);
} catch (SocketTimeoutException e) {
// Don't retry on socket timeout - service might be overloaded
throw e;
} catch (IOException e) {
if (retry) {
reopen(e);
return sendAndReceiveWork(in, f, timeout);
} else {
throw e;
}
}
} catch (SocketTimeoutException e) {
// Close on timeout to make sure buffers are cleared if client reused
close();
throw e;
}
}
/**
* Same as sendAndReceive(Transportable) except for return type. Syntactic sugar method for the
* case where return result is known to be VinciFrame (eliminates the need for casting in the
* typical usage case).
*
* @return A VinciFrame representing the service result.
*
* @pre query != null
* @pre getHost() != null
* @param query -
* @return -
* @throws IOException -
* @throws ServiceException -
*/
public VinciFrame rpc(Transportable query) throws IOException, ServiceException {
return (VinciFrame) sendAndReceive(query);
}
/**
* Same as sendAndReceive(Transportable, timeout) except for return type. Syntactic sugar method
* for the case where return result is known to be VinciFrame (eliminates the need for casting in
* the typical usage case).
* @param query -
* @param timeout -
* @return A VinciFrame representing the service result.
* @throws IOException -
* @throws ServiceException -
*
* @pre query != null
* @pre getHost() != null
*/
public VinciFrame rpc(Transportable query, int timeout) throws IOException, ServiceException {
return (VinciFrame) sendAndReceive(query, timeout);
}
/**
* Support for 1/2 transaction RPC. This allows interaction with an asynchronous ("receive only")
* service, or for the sender to simply do something else before coming back and receiving the
* result (though at the risk of timeouts!).
*
* @param in
* The Transportable to send.
* @throws IOException
* Thrown by the underlying transport layer.
*
* @pre in != null
* @pre getHost() != null
* @pre os != null
*/
public void send(Transportable in) throws IOException {
try {
try {
if (!isOpen()) {
open();
}
in.toStream(os);
os.flush();
} catch (SocketTimeoutException e) {
// Don't retry on socket timeout - service might be overloaded
throw e;
} catch (IOException e) {
if (retry) {
reopen(e);
in.toStream(os);
os.flush();
} else {
throw e;
}
}
} catch (SocketTimeoutException e) {
// Close on timeout to make sure buffers are cleared if client reused
close();
throw e;
}
}
/**
* The other 1/2 of the split RPC. This allows for interaction with an asynchronous "publish only"
* service, or simply picks up a result queried for earlier via send().
*
* @return The Transportable requested.
* @throws IOException
* Thrown by the underlying transport layer, or the socket is closed.
* @throws ServiceException
* Thrown if the remote server responded with an error frame.
*
* @pre is != null
*/
public Transportable receive() throws IOException, ServiceException {
if (!isOpen()) {
throw new IOException("Socket not open");
}
Transportable out = factory.makeTransportable();
header = out.fromStream(is);
if (header != null && header.key.equals(TransportConstants.ERROR_KEY)) {
throw new ServiceException(header.getValueAsString(), out);
}
return out;
}
/**
* Close the connection. Using the Client object after this will throw an exception.
*/
public void close() {
if (isOpen()) {
try {
socket.close();
is = null;
os = null;
socket = null;
} catch (IOException e) {
Debug.reportException(e, "Could not close connection.");
}
}
}
public boolean isOpen() {
return socket != null && os != null && is != null;
}
/**
* Set connection restablishment on IOException to on/off, default is ON. This way, by default,
* BaseClient attempts to reopen a connection at most once if it receives an IOException which can
* happen, for example, from the connection timing out.
* @param to -
*/
public void setRetry(boolean to) {
retry = to;
}
/**
* Fetch the header of the last Transportable received.
* @return -
*/
public KeyValuePair getHeader() {
return header;
}
/**
* Convenience method for "one-shot" or "single-query" connections.
*
* @pre host_name != null
* @pre f != null
* @pre p ≥ 0
* @pre p < 65536
* @param in -
* @param host_name -
* @param p -
* @param f -
* @return -
* @throws IOException -
* @throws ServiceException -
*/
static public Transportable sendAndReceive(Transportable in, String host_name, int p,
TransportableFactory f) throws IOException, ServiceException {
BaseClient tempClient = new BaseClient(host_name, p, f);
tempClient.setRetry(false);
try {
return tempClient.sendAndReceive(in);
} finally {
tempClient.close();
}
}
/**
* Convenience method for "one-shot" or "single-query" connections with socket timeout support.
*
* @pre in != null
* @pre host_name != null
* @pre f != null
* @pre p ≥ 0
* @pre p < 65536
* @param in -
* @param host_name -
* @param p -
* @param f -
* @param socket_timeout -
* @return -
* @throws IOException -
* @throws ServiceException -
*/
static public Transportable sendAndReceive(Transportable in, String host_name, int p,
TransportableFactory f, int socket_timeout) throws IOException, ServiceException {
BaseClient tempClient = new BaseClient(host_name, p, f);
tempClient.setSocketTimeout(socket_timeout);
tempClient.setRetry(false);
try {
return tempClient.sendAndReceive(in);
} finally {
tempClient.close();
}
}
/**
* Convenience method for "one-shot" or "single-query" connections with socket timeout support
* & connect timeout support.
*
* @pre in != null
* @pre host_name != null
* @pre f != null
* @pre p ≥ 0
* @pre p < 65536
* @param in -
* @param host_name -
* @param p -
* @param f -
* @param socket_timeout -
* @param connect_timeout -
* @return -
* @throws IOException -
* @throws ServiceException -
*/
static public Transportable sendAndReceive(Transportable in, String host_name, int p,
TransportableFactory f, int socket_timeout, int connect_timeout) throws IOException,
ServiceException {
BaseClient tempClient = new BaseClient(host_name, p, f, connect_timeout);
tempClient.setSocketTimeout(socket_timeout);
tempClient.setRetry(false);
try {
return tempClient.sendAndReceive(in);
} finally {
tempClient.close();
}
}
/**
* Convenience method for "one-shot" or "single-query" connections. Same as sendAndReceive except
* uses VinciFrame factory so return type is known to be VinciFrame.
*
* @pre in != null
* @pre host_name != null
* @pre p ≥ 0
* @pre p < 65536
* @param in -
* @param host_name -
* @param p -
* @return -
* @throws IOException -
* @throws ServiceException -
*/
static public VinciFrame rpc(Transportable in, String host_name, int p) throws IOException,
ServiceException {
return (VinciFrame) sendAndReceive(in, host_name, p, VinciFrame.getVinciFrameFactory());
}
/**
* Convenience method for "one-shot" or "single-query" connections. Same as sendAndReceive except
* uses VinciFrame factory so return type is known to be VinciFrame.
*
* @pre host_name != null
* @pre in != null
* @pre p ≥ 0
* @pre p < 65536
* @param in -
* @param host_name -
* @param p -
* @param socket_timeout -
* @return -
* @throws IOException -
* @throws ServiceException -
*/
static public VinciFrame rpc(Transportable in, String host_name, int p, int socket_timeout)
throws IOException, ServiceException {
return (VinciFrame) sendAndReceive(in, host_name, p, VinciFrame.getVinciFrameFactory(),
socket_timeout);
}
/**
* Convenience method for "one-shot" or "single-query" connections. Same as sendAndReceive except
* uses VinciFrame factory so return type is known to be VinciFrame.
*
* @pre host_name != null
* @pre in != null
* @pre p ≥ 0
* @pre p < 65536
* @param in -
* @param host_name -
* @param p -
* @param socket_timeout -
* @param connect_timeout -
* @return -
* @throws IOException -
* @throws ServiceException -
*/
static public VinciFrame rpc(Transportable in, String host_name, int p, int socket_timeout,
int connect_timeout) throws IOException, ServiceException {
return (VinciFrame) sendAndReceive(in, host_name, p, VinciFrame.getVinciFrameFactory(),
socket_timeout, connect_timeout);
}
/**
* @pre in != null
* @pre f != null
* @pre getHost() != null
* @pre os != null
* @pre is != null
* @param in -
* @param f -
* @return -
* @throws IOException -
* @throws ServiceException -
*/
protected Transportable sendAndReceiveWork(Transportable in, TransportableFactory f)
throws IOException, ServiceException {
in.toStream(os);
os.flush();
Transportable out = f.makeTransportable();
header = out.fromStream(is);
if (header != null && header.key.equals(TransportConstants.ERROR_KEY)) {
throw new ServiceException(header.getValueAsString(), out);
}
return out;
}
/**
* @pre in != null
* @pre f != null
* @pre socket != null
* @param in -
* @param f -
* @param timeout -
* @return -
* @throws IOException -
* @throws ServiceException -
*/
protected Transportable sendAndReceiveWork(Transportable in, TransportableFactory f, int timeout)
throws IOException, ServiceException {
socket.setSoTimeout(timeout);
try {
return sendAndReceiveWork(in, f);
} finally {
// Restore default timeout setting.
socket.setSoTimeout(socketTimeout);
}
}
/**
* @pre e != null
* @pre getHost() != null
* @param e -
* @throws IOException -
*/
protected void reopen(Exception e) throws IOException {
Debug.p("Trying to reopen connection due to exception: " + e.getMessage());
// Make sure connection is closed.
close();
open();
}
/**
* Connects the client to the specified host and port.
*
* @param h
* The hostname/ip address of the server to connect to.
* @param p
* The port to connect to.
*
* @throws IOException
* Thrown by underlying Socket open() call.
*
* @pre h != null
* @pre p ≥ 0
* @pre p < 65536
*/
protected final void open(String h, int p) throws IOException {
this.host = h;
this.port = p;
open();
}
/**
* (Re)connects the client to a previously specified host and port. Should only be called if this
* client has been previously closed via a call to "close".
*
* @throws IOException
* Thrown by underlying Socket open() call.
*
* @pre getHost() != null
*/
public final void open() throws IOException {
socket = new Socket();
InetAddress addr = InetAddress.getByName(host);
InetSocketAddress socketAddress = new InetSocketAddress(addr, port);
socket.connect(socketAddress, connectTimeout);
socket.setTcpNoDelay(true); // needed to avoid delays with Linux/loopback
socket.setSoTimeout(socketTimeout);
socket.setKeepAlive(isSocketKeepAliveEnabled());
is = new BufferedInputStream(socket.getInputStream());
os = new BufferedOutputStream(socket.getOutputStream());
}
/**
* Make this client use an already established socket connection. If you use this open method,
* then setRetry is set to false. Resetting it to true will cause problems since the client does
* not know how to reopen the connection.
*
* @param use_me
* The socket to use.
*
* @exception IOException
* Thrown by underlying Socket open() call.
*
* @pre use_me != null
*/
public void open(Socket use_me) throws IOException {
setRetry(false);
socket = use_me;
socket.setSoTimeout(socketTimeout);
socket.setKeepAlive(isSocketKeepAliveEnabled());
is = new BufferedInputStream(socket.getInputStream());
os = new BufferedOutputStream(socket.getOutputStream());
}
/**
* Set the timeout value used by the underlying socket. Default is 2 minutes.
* @param millis -
* @throws IOException -
*/
public void setSocketTimeout(int millis) throws IOException {
if (socket != null) {
socket.setSoTimeout(millis);
}
socketTimeout = millis;
}
/**
* Gets whether keepAlive should be turned on for client sockets.
* Always returns true for BaseClient. Can be overridden in
* subclasses.
*
*@return whether socket keepAlive should be turned on
*/
protected boolean isSocketKeepAliveEnabled() {
return true;
}
} // end class BaseClient
© 2015 - 2024 Weber Informatics LLC | Privacy Policy