Package org.astrogrid.samp.client

# Class HubConnector

java.lang.Object
org.astrogrid.samp.client.HubConnector

Direct Known Subclasses:
  `GuiHubConnector`

public class HubConnector extends Object
Manages a client's connection to SAMP hubs. Normally SAMP client applications will use one instance of this class for as long as they are running. It provides the following services:

- Keeps track of other registered clients
- Keeps track of hubs shutting down and starting up
- Manages client metadata and subscription information across hub reconnections
- Facilitates provision of callback services by the client
- Implements simple MTypes such as `samp.app.ping`.
- Optionally looks out for hubs starting up and connects automatically when they do

This object provides a `getConnection()` method which provides the currently active `HubConnection` object if one exists or can be acquired. The `HubConnection` can be used for direct calls on the running hub, but in some cases similar methods with additional functionality exist in this class:

`declareMetadata`
`declareSubscriptions`
  These methods not only make the relevant declarations to the existing hub connection, if one exists, but will retain the metadata and subscriptions information and declare them to other connections if the hub connection is terminated and restarted (with either the same or a different hub) over the lifetime of this object.
`callAndWait`
  Provides identical semantics to the similarly named `HubConnection` method, but communicates with the hub asynchronously and fakes the synchrony at the client end. This is more robust and almost certainly a better idea.
`call`
`callAll`
  Convenience methods to make asynchronous calls without having to worry about registering handlers which match up message tags.

It is good practice to call `setActive(false)` when this object is finished with; however if it is not called explicitly, any open connection will unregister itself on object finalisation or JVM termination, as long as the JVM shuts down cleanly.

### Examples

Here is an example of what use of this class might look like:

       // Construct a connector
       ClientProfile profile = DefaultClientProfile.getProfile();
       HubConnector conn = new HubConnector(profile)

       // Configure it with metadata about this application
       Metadata meta = new Metadata();
       meta.setName("Foo");
       meta.setDescriptionText("Application that does stuff");
       conn.declareMetadata(meta);

       // Prepare to receive messages with specific MType(s)
       conn.addMessageHandler(new AbstractMessageHandler("stuff.do") {
           public Map processCall(HubConnection c, String senderId, Message msg) {
               // do stuff
           }
       });

       // This step required even if no custom message handlers added.
       conn.declareSubscriptions(conn.computeSubscriptions());

       // Keep a look out for hubs if initial one shuts down
       conn.setAutoconnect(10);

       // Broadcast a message
       conn.getConnection().notifyAll(new Message("stuff.event.doing"));

A real example, including use of the GUI hooks, can be found in the `HubMonitor` client source code.

### Backwards Compatibility Note

This class does less than it did in earlier versions; the functionality which is no longer here can now be found in the `GuiHubConnector` class instead.

Since:
  15 Jul 2008
Author:
  Mark Taylor

## Constructor Summary

Constructors
Constructor
Description
`HubConnector(ClientProfile profile)`
Constructs a HubConnector based on a given profile instance.
`HubConnector(ClientProfile profile, TrackedClientSet clientSet)`
Constructs a HubConnector based on a given profile instance using a custom client set implementation.

## Method Summary

Modifier and Type
Method
Description
`void`
`addMessageHandler(MessageHandler handler)`
Adds a MessageHandler to this connector, which allows it to respond to incoming messages.
`void`
`addResponseHandler(ResponseHandler handler)`
Adds a ResponseHandler to this connector, which allows it to receive replies from messages sent asynchronously.
`void`
`call(String recipientId, Map msg, ResultHandler resultHandler, int timeout)`
Sends a message asynchronously to a single client, making a callback on a supplied ResultHandler object when the result arrives.
`void`
`callAll(Map msg, ResultHandler resultHandler, int timeout)`
Sends a message asynchronously to all subscribed clients, making callbacks on a supplied ResultHandler object when the results arrive.
`Response`
`callAndWait(String recipientId, Map msg, int timeout)`
Sends a message synchronously to a client, waiting for the response.
`Subscriptions`
`computeSubscriptions()`
Works out the subscriptions map for this connector.
`void`
`configureConnection(HubConnection connection)`
Configures a connection with a hub in accordance with the state of this object.
`protected void`
`connectionChanged(boolean isConnected)`
Method which is called every time this connector changes its connection status (from disconnected to connected, or vice versa).
`protected HubConnection`
`createConnection()`
Invoked by this class to create a hub connection.
`String`
`createTag(Object owner)`
Generates a new `msgTag` for use with this connector.
`void`
`declareMetadata(Map meta)`
Declares the metadata for this client.
`void`
`declareSubscriptions(Map subscriptions)`
Declares the MType subscriptions for this client.
`protected void`
`disconnect()`
Unregisters from the currently connected hub, if any.
`Map`
`getClientMap()`
Returns a map which keeps track of other clients currently registered with the hub to which this object is connected, including their currently declared metadata and subscriptions.
`protected TrackedClientSet`
`getClientSet()`
Returns the tracked client set implementation which is used to keep track of the currently registered clients.
`HubConnection`
`getConnection()`
If necessary attempts to acquire, and returns, a connection to a running hub.
`Metadata`
`getMetadata()`
Returns this client's own metadata.
`Subscriptions`
`getSubscriptions()`
Returns this client's own subscriptions.
`boolean`
`isActive()`
Indicates whether this connector is active or not.
`boolean`
`isConnected()`
Indicates whether this connector is currently registered with a running hub.
`void`
`removeMessageHandler(MessageHandler handler)`
Removes a previously-added MessageHandler to this connector.
`void`
`removeResponseHandler(ResponseHandler handler)`
Removes a ResponseHandler from this connector.
`void`
`setActive(boolean active)`
Sets whether this connector is active or not.
`void`
`setAutoconnect(int autoSec)`
Sets the interval at which this connector attempts to connect to a hub if no connection currently exists.

### Methods inherited from class java.lang.Object

`clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait`

## Constructor Details

### HubConnector

public HubConnector(ClientProfile profile)
Constructs a HubConnector based on a given profile instance. A default client set implementation is used.

Parameters:
  `profile` - profile implementation

### HubConnector

public HubConnector(ClientProfile profile, TrackedClientSet clientSet)
Constructs a HubConnector based on a given profile instance using a custom client set implementation.

Parameters:
  `profile` - profile implementation
  `clientSet` - object to keep track of registered clients

## Method Details

### setAutoconnect

public void setAutoconnect(int autoSec)
Sets the interval at which this connector attempts to connect to a hub if no connection currently exists. Otherwise, a connection will be attempted whenever `getConnection()` is called.

Parameters:
  `autoSec` - number of seconds between attempts; <=0 means no automatic connections are attempted

### declareMetadata

public void declareMetadata(Map meta)
Declares the metadata for this client. This declaration affects the current connection and any future ones.

Parameters:
  `meta` - `Metadata`-like map

### getMetadata

public Metadata getMetadata()
Returns this client's own metadata.

Returns:
  metadata

### declareSubscriptions

public void declareSubscriptions(Map subscriptions)
Declares the MType subscriptions for this client. This declaration affects the current connection and any future ones.

Note that this call must be made, with a subscription list which includes the various hub administrative messages, in order for this connector to act on those messages (for instance to update its client map and so on). For this reason, it is usual to call it with the `subs` argument given by the result of calling `computeSubscriptions()`.

Parameters:
  `subscriptions` - `Subscriptions`-like map

### getSubscriptions

public Subscriptions getSubscriptions()
Returns this client's own subscriptions.

Returns:
  subscriptions

### computeSubscriptions

public Subscriptions computeSubscriptions()
Works out the subscriptions map for this connector. This is based on the subscriptions declared by for any `MessageHandler`s installed in this connector as well as any MTypes which this connector implements internally. The result of this method is usually a suitable value to pass to `declareSubscriptions(java.util.Map)`. However you might wish to remove some entries from the result if there are temporarily unsubscribed services.

Returns:
  subscription list for MTypes apparently implemented by this connector

### addMessageHandler

public void addMessageHandler(MessageHandler handler)
Adds a MessageHandler to this connector, which allows it to respond to incoming messages. Note that this does not in itself update the list of subscriptions for this connector; you may want to follow it with a call to

        declareSubscriptions(computeSubscriptions());

Parameters:
  `handler` - handler to add

### removeMessageHandler

public void removeMessageHandler(MessageHandler handler)
Removes a previously-added MessageHandler to this connector. Note that this does not in itself update the list of subscriptions for this connector; you may want to follow it with a call to

        declareSubscriptions(computeSubscriptions());

Parameters:
  `handler` - handler to remove

### addResponseHandler

public void addResponseHandler(ResponseHandler handler)
Adds a ResponseHandler to this connector, which allows it to receive replies from messages sent asynchronously.

Note however that this class's `callAndWait` method can provide a synchronous facade for fully asynchronous messaging, which in many cases will be more convenient than installing your own response handlers to deal with asynchronous replies.

Parameters:
  `handler` - handler to add

### removeResponseHandler

public void removeResponseHandler(ResponseHandler handler)
Removes a ResponseHandler from this connector.

Parameters:
  `handler` - handler to remove

### setActive

public void setActive(boolean active)
Sets whether this connector is active or not. If set false, any existing connection will be terminated (the client will unregister) and autoconnection attempts will be suspended. If set true, if there is no existing connection an attempt will be made to register, and autoconnection attempts will begin if applicable.

Parameters:
  `active` - whether this connector should be active
See Also:
  - `setAutoconnect(int)`

### isActive

public boolean isActive()
Indicates whether this connector is active or not.

Returns:
  true if this connector is willing to connect

### callAndWait

public Response callAndWait(String recipientId, Map msg, int timeout) throws SampException
Sends a message synchronously to a client, waiting for the response. If more seconds elapse than the value of the `timeout` parameter, an exception will result.

The semantics of this call are, as far as the caller is concerned, identical to that of the similarly named `HubConnection` method. However, in this case the client communicates with the hub asynchronously and internally simulates the synchrony for the caller, rather than letting the hub do that. This is more robust and almost certainly a better idea.

Parameters:
  `recipientId` - public-id of client to receive message
  `msg` - `Message`-like map
  `timeout` - timeout in seconds, or <=0 for no timeout
Returns:
  response
Throws:
  `SampException`

### call

public void call(String recipientId, Map msg, ResultHandler resultHandler, int timeout) throws SampException
Sends a message asynchronously to a single client, making a callback on a supplied ResultHandler object when the result arrives. The `ResultHandler.done()` method will be called after the result has arrived or the timeout elapses, whichever happens first.

This convenience method allows the user to make an asynchronous call without having to worry registering message handlers and matching message tags.

Parameters:
  `recipientId` - public-id of client to receive message
  `msg` - `Message`-like map
  `resultHandler` - object called back when response arrives or timeout is exceeded
  `timeout` - timeout in seconds, or <=0 for no timeout
Throws:
  `SampException`

### callAll

public void callAll(Map msg, ResultHandler resultHandler, int timeout) throws SampException
Sends a message asynchronously to all subscribed clients, making callbacks on a supplied ResultHandler object when the results arrive. The `ResultHandler.done()` method will be called after all the results have arrived or the timeout elapses, whichever happens first.

This convenience method allows the user to make an asynchronous call without having to worry registering message handlers and matching message tags.

Parameters:
  `msg` - `Message`-like map
  `resultHandler` - object called back when response arrives or timeout is exceeded
  `timeout` - timeout in seconds, or <=0 for no timeout
Throws:
  `SampException`

### isConnected

public boolean isConnected()
Indicates whether this connector is currently registered with a running hub. If true, the result of `getConnection()` will be non-null.

Returns:
  true if currently connected to a hub

### getConnection

public HubConnection getConnection() throws SampException
If necessary attempts to acquire, and returns, a connection to a running hub. If there is an existing connection representing a registration with a hub, it is returned. If not, and this connector is active, an attempt is made to connect and register, followed by a call to `configureConnection`, is made.

Note that if `setActive(false)` has been called, null will be returned.

Returns:
  hub connection representing configured registration with a hub if a hub is running; if not, null
Throws:
  `SampException` - in the case of some unexpected error

### configureConnection

public void configureConnection(HubConnection connection) throws SampException
Configures a connection with a hub in accordance with the state of this object. The hub is made aware of how to perform callbacks on the registered client, and any current metadata and subscriptions are declared.

Parameters:
  `connection` - connection representing registration with a hub
Throws:
  `SampException`

### getClientMap

public Map getClientMap()
Returns a map which keeps track of other clients currently registered with the hub to which this object is connected, including their currently declared metadata and subscriptions. Map keys are public IDs and values are `Client`s.

This map is `synchronized` which means that to iterate over any of its views you must synchronize on it. When the map or any of its contents changes, it will receive a `Object.notifyAll()`.

To keep itself up to date, the client map reads hub status messages. These will only be received if `declareSubscriptions(computeSubscriptions())` has been called. Hence, this method should only be called after `declareSubscriptions(java.util.Map)` has been called. If this order is not observed, a warning will be emitted through the logging system.

Returns:
  id->Client map

### getClientSet

protected TrackedClientSet getClientSet()
Returns the tracked client set implementation which is used to keep track of the currently registered clients.

Returns:
  client set implementation

### createConnection

protected HubConnection createConnection() throws SampException
Invoked by this class to create a hub connection. The default implementation just calls `profile.register()`.

Returns:
  new hub connection
Throws:
  `SampException`

### disconnect

protected void disconnect()
Unregisters from the currently connected hub, if any. Performs any associated required cleanup.

### connectionChanged

protected void connectionChanged(boolean isConnected)
Method which is called every time this connector changes its connection status (from disconnected to connected, or vice versa). The default implementation does nothing, but it may be overridden by subclasses wishing to be informed of these events.

Parameters:
  `isConnected` - true if we've just registered; false if we've just unregistered

### createTag

public String createTag(Object owner)
Generates a new `msgTag` for use with this connector. It is guaranteed to return a different value on each invocation. It is advisable to use this method whenever a message tag is required to prevent clashes.

Parameters:
  `owner` - object to identify caller (not really necessary - may be null)
Returns:
  unique tag for this connector