• Non ci sono risultati.

Il Package sun.rmi

N/A
N/A
Protected

Academic year: 2021

Condividi "Il Package sun.rmi"

Copied!
31
0
0

Testo completo

(1)

Capitolo 3

Il Package sun.rmi

In questo capitolo viene data una descrizione sufficientemente dettagliata delle principali classi e interfacce che, all’interno del package sun.rmi, vengono utilizzate nel meccanismo di invocazione di metodo remoto tra client e server, tralasciando, invece, quelle che servono a svolgere funzioni di log, a costituire il meccanismo rmic per la creazione delle classi stub e quelle relative alla runtime di RMI.

Il package sun.rmi contiene, a sua volta, altri sei package, ciascuno con classi e interfacce specifiche per svolgere determinati servizi: sun.rmi.rmic, sun.rmi.runtime, sun.rmi.log, sun.rmi.server, sun.rmi.registry e sun.rmi.transport.

(2)

3.1 Il package sun.rmi.registry

Questo package contiene la sola classe RegistryImpl, la quale può essere eseguita generando un oggetto registry.

Un oggetto registry è presente su ogni VM che permette delle connessioni ai server. In particolare, un registry contiene un database che mappa nomi con oggetti remoti.

3.1.1 La classe RegistryImpl

La classe RegistryImpl implementa l’interfaccia Registry del package java.rmi.registry, definendo il comportamento dei suoi metodi.

public class RegistryImpl extends java.rmi.server.RemoteServer implements Registry { private Hashtable bindings = new Hashtable(101);

private static Hashtable allowedAccessCache = new Hashtable(3); private static RegistryImpl registry;

private static ObjID id = new ObjID(ObjID.REGISTRY_ID); private static ResourceBundle resources = null;

public RegistryImpl(int port,

RMIClientSocketFactory csf, RMIServerSocketFactory ssf) throws RemoteException { //... }

public RegistryImpl(int port)

throws RemoteException { //...

}

private void setup(UnicastServerRef uref)

throws RemoteException { //...

(3)

public Remote lookup(String name)

throws RemoteException, NotBoundException { //...

}

public void bind(String name, Remote obj)

throws RemoteException, AlreadyBoundException, AccessException {

//... }

public void unbind(String name)

throws RemoteException, NotBoundException, AccessException { //...

}

public void rebind(String name, Remote obj)

throws RemoteException, AccessException { //...

}

public String[] list()

throws RemoteException { //...

}

public static void checkAccess(String op)

throws AccessException { //...

}

public static ObjID getID() {

//... }

private static String getTextResource(String key) {

//... }

public static void main(String args[]) {

//... } }

(4)

Il metodo lookup restituisce l’oggetto remoto specificato dal parametro

name nel registry.

Col metodo bind viene associato il nome dato dal parametro name all’oggetto remoto dato da obj, mentre col metodo unbind viene fatta l’operazione opposta. Tramite il metodo rebind, invece, si può associare il nome name all’oggetto obj, rimpiazzando un legame già esistente. Il metodo list permette di ottenere una lista di tutti i nomi presenti nel registry.

Il metodo checkAccess (che viene richiamato ogni qualvolta si esegue una bind, una rebind o una unbind) verifica che il chiamante abbia accesso alle operazioni indicate dal suo argomento.

Occorre infine notare la presenza di un metodo main. Perciò è possibile avviare un registry (specificando eventualmente il numero di porta sulla linea di comando) eseguendo il comando: java sun.rmi.registry.RegistryImpl (dopo ovviamente aver compilato il file col comando javac e aver ottenuto lo stub col comando rmic). Ciò è del tutto analogo a quanto avviene quando si esegue il comando: rmiregistry.

3.2 Principali classi del package

sun.rmi.server

Il package sun.rmi.server implementa varie interfacce e classi che supportano il livello riferimento remoto lato client e lato server del meccanismo di invocazione di metodo remoto. Contiene anche ulteriori classi relative all’attivazione di oggetti persistenti, che comunque non vengono trattate in questa discussione.

(5)

3.2.1 La classe MarshalInputStream

E’ un’estensione della classe java.io.ObjectInputStream[9]; essa permette di leggere un oggetto inviato su uno stream scritto con la corrispondente classe MarshalOutputStream. In particolare, quello letto sarà un riferimento alla classe che definisce l’oggetto (con un URL associato), tramite il quale si tenterà di caricare la classe in questione, adoperando il primo caricatore di classe presente sullo stack o il caricatore di classe di contesto del thread corrente.

public class MarshalInputStream extends ObjectInputStream { //...

public Runnable getDoneCallback(Object key) {

//... }

public void setDoneCallback(Object key, Runnable callback) {

//... }

public void done() {

//... }

public void close() throws IOException {

//... }

protected Class resolveClass(ObjectStreamClass classDesc) throws IOException, ClassNotFoundException { //...

} }

(6)

Il metodo getDoneCallback restituisce una callback (registrata precedentemente con setDoneCallback) individuata con la chiave key oppure restituisce il valore null se non è stata ancora registrata alcuna callback con la chiave key.

Il metodo done indica che tutte le callback registrate devono essere eseguite. Quando questo metodo ritorna, non ci sono più chiamate registrate. Esso viene invovato implicitamente dal metodo close(), prima che quest’ultimo richiami il corrispondente metodo della superclasse.

Infine, col metodo resolveClass viene acquisita la locazione da cui caricare la classe specificata.

3.2.2 La classe MarshalOutputStream

Estende la classe java.io.ObjectOutputStream[10] per aggiungere funzionalità relative al marshaling dei riferimenti ad oggetti remoti. Questa classe deve essere usata quando è necessario serializzare oggetti remoti o oggetti che contengono riferimenti ad oggetti remoti.

La classe MarshalOutputStream mappa oggetti remoti con il corrispondente stub remoto e incapsula la locazione dalla quale caricare le classi stub.

public class MarshalOutputStream extends ObjectOutputStream { //...

protected final Object replaceObject(Object obj)

throws IOException { //...

}

protected void annotateClass(Class cl)

throws IOException { //...

} //...

(7)

protected void annotateProxyClass(Class cl)

throws IOException { //...

}

protected void writeLocation(String location)

throws IOException { //...

} }

Il metodo replaceObject serve a contenere le istanze di Remote che devono essere serializzate come oggetti proxy.

Il metodo annotateClass serve a serializzare una locazione dalla quale caricare la classe specificata; annotateProxyClass ha lo stesso comportamento del precedente metodo.

Il metodo writeLocation scrive nello stream la locazione della classe.

3.2.3 La classe RemoteProxy

Questa classe contiene metodi generali per ottenere stub e skeleton (solo per le versioni precedenti alla JDK 1.2) per gli oggetti remoti.

public final class RemoteProxy

extends java.rmi.server.RemoteStub { public static RemoteStub getStub(Remote object,

RemoteRef ref)

throws StubNotFoundException { //...

}

public static RemoteStub getStub(String classname, RemoteRef ref)

(8)

public static RemoteStub getStub(String classname, Class fromClass, RemoteRef ref) throws StubNotFoundException { //... }

public static RemoteStub getStub(String stubname, int id, String host, int port) throws RemoteException { //...

}

public static RemoteStub getStub(String stubname, int id, String host, int port, RMIClientSocketFactory csf) throws RemoteException { //...

} //...

static Class getRemoteClass(Class cl)

throws ClassNotFoundException { //...

} }

Il primo metodo getStub trova un oggetto RemoteStub corrispondente all’oggetto remoto specificato dal parametro object. Il secondo, invece, crea un oggetto RemoteStub per la classe specificata dall’argomento classname, inizializzandolo con il riferimento remoto specificato da ref. Infine, il terzo metodo getStub aggiunge al precedente, un parametro (fromclass) che serve a indicare da dove caricare la classe stub, qualora questa non sia disponibile localmente; il comportamento seguito è quello di trovare la classe, crearne un’istanza e successivamente settare il riferimento remoto.

Il metodo getRemoteClass trova la classe o la superclasse che implementa l’interfaccia remota.

(9)

3.2.4 La classe UnicastRef

Tale classe implementa l’interfaccia RemoteRef e rappresenta il livello riferimento remoto dal lato client.

Tra gli altri, contiene il metodo invoke, tramite il quale, il client può invocare un metodo di un oggetto remoto posto sul server.

public class UnicastRef implements RemoteRef { protected LiveRef ref;

public UnicastRef() { }

public UnicastRef(LiveRef liveRef) { //...

}

public Object invoke(Remote obj,

java.lang.reflect.Method method, Object[] params, long opnum)

throws Exception { //...

}

protected static void marshalValue(Class type, Object value, ObjectOutput out)

throws IOException { //...

}

protected static Object unmarshalValue(Class type, ObjectInput in)

throws IOException, ClassNotFoundException { //...

}

public RemoteCall newCall(RemoteObject obj, Operation[] ops, int opnum, long hash)

throws RemoteException { //...

}

private void free(RemoteCall call, boolean reuse)

throws RemoteException { //...

(10)

public void done(RemoteCall call) throws RemoteException { //... } //... }

Il metodo invoke permette al lato client di invocare un metodo di un oggetto remoto. Ciò che viene restituito è il risultato del metodo invocato o l’eventuale eccezione da esso sollevata, oppure una RemoteException, nel caso l’invocazione di metodo fallisca. Quando viene eseguito il metodo invoke, in particolare, viene aperta una nuova connessione, servendosi del riferimento remoto ref di tipo LiveRef (classe presente nel package sun.rmi.transport che vedremo in seguito) e invocando il metodo getChannel; dopodiché, viene settato al valore false il booleano alreadyFreed, usato per indicare che la connessione non può essere riusata. Successivamente, viene creato il contesto della chiamata, usando un oggetto StreamRemoteCall e, quindi, viene effettuato il marshal dei parametri. A questo punto, può essere inoltrata la chiamata remota, invocando il metodo executeCall() di RemoteCall. Quando questo ritorna, si può eseguire l’unmarshal del valore di ritorno e liberare la connessione, riportando al valore true il booleano alreadyFreed. Nel caso in cui il valore di ritorno è un’eccezione sollevata dal server, questa viene rigettata nel contesto del client e quindi gestita. In particolare, nell’ambito della gestione delle eccezioni, occorre distinguere il caso in cui si ha una RemoteException proveniente dal server, da quello in cui l’eccezione in questione è generata dal metodo invoke stesso (quindi nel client).

I metodi marshalValue e unmarshalValue vengono richiamati all’interno del metodo invoke per effettuare il marshal dei parametri e l’unmarshal del valore di ritorno.

Il metodo newCall crea un appropriato oggetto call, per una nuova chiamata sull’oggetto specificato dal parametro obj.

(11)

Col metodo free è possibile liberare una connessione, mentre il metodo done va invocato solo quando invoke ritorna con successo (cioè senza aver sollevato eccezioni) un risultato allo stub. Esso permette al riferimento remoto di “ripulire” una connessione che potrà essere riusata.

3.2.5 La classe UnicastServerRef

Implementa il comportamento del livello riferimento remoto lato server per gli oggetti remoti esportati.

public class UnicastServerRef extends UnicastRef

implements ServerRef, Dispatcher { private transient Map methodTable = null;

private static Map methodTableCache = new WeakHashMap(11); public UnicastServerRef() { }

public UnicastServerRef(LiveRef ref) {

//... }

public UnicastServerRef(int port) {

//... }

public RemoteStub exportObject(Remote impl, Object data) throws RemoteException { //...

}

public RemoteStub exportObject(Remote impl, Object data, boolean permanent)

throws RemoteException { //...

}

public String getClientHost()

throws ServerNotActiveException { //...

(12)

public void dispatch(Remote obj, RemoteCall call)

throws IOException { //...

} //...

protected RemoteRef getClientRef() {

//... }

private static Map getMethodTable(Class remoteClass) {

//... }

//... }

Il metodo exportObject serve ad esportare un oggetto remoto. In particolare, crea uno stub basato sul tipo dell’oggetto impl e lo inizializza con l’opportuno riferimento remoto, mentre il metodo getClientHost restituisce l’hostname del client corrente.

Il metodo dispatch è quello che viene invocato dal lato server per effettuare una chiamata di metodo remoto. In particolare, viene inizialmente letto il call header remoto, estraendo lo stream per comunicare col client; successivamente, legge il nome del metodo da invocare e gli eventuali parametri su cui effettua l’unmarshal, quindi invoca il metodo in questione. Una volta ottenuto il valore di ritorno o l’eventuale eccezione sollevata, ne effettua il marshal e restituisce lo stream al client.

Il metodo getClientRef restituisce il riferimento remoto del client, mentre col metodo getMethodTable viene restituita la tabella contenente i metodi della classe remota individuata dal parametro remoteClass.

(13)

3.3 Principali classi del package

sun.rmi.transport

Contiene tutte quelle classi e interfacce che servono ad implementare il livello trasporto e che permettono di creare delle connessioni tra host client e host server; mediante queste classi avviene la comunicazione, non solo relativamente all’invocazione di metodo remoto, ma anche per ciò che concerne lo scambio di messaggi nel meccanismo di garbage collection distribuita.

Il package sun.rmi.transport contiene, a sua volta, altri due package: sun.rmi.transport.tcp, al quale appartengono classi relative alla comunicazione col protocollo TCP/IP e sun.rmi.transport.proxy, che contiene classi relative alla comunicazione con HTTP.

3.3.1 La classe Transport

La classe astratta Transport fornisce l’astrazione del livello trasporto, per consentire la comunicazione tra VM differenti.

public abstract class Transport {

public abstract Channel getChannel(Endpoint ep); public abstract void free(Endpoint ep);

public void exportObject(Target target)

throws RemoteException { //...

}

protected abstract void

checkAcceptPermission(AccessControlContext acc); public boolean serviceCall(final RemoteCall call)

{

//... }

(14)

Il metodo getChannel restituisce un oggetto Channel che genera connessioni con l’endpoint specificato dal parametro ep. Un Channel è un oggetto che crea e gestisce connessioni di un determinato tipo (generalmente di tipo TCP) in un particolare spazio di indirizzi.

Contrariamente al precedente, il metodo free rimuove il Channel che genera connessioni con l’endpoint ep.

Il metodo exportObject permette di esportare l’oggetto remoto, individuato all’interno della ObjectTable dal parametro target, rendendolo disponibile per eventuali chiamate provenienti dai client.

Il metodo serviceCall viene invocato dai thread che ricevono sulla connessione un messaggio indicante l’inizio di una invocazione di metodo remoto; il comportamento di default è quello di ricercare e quindi chiamare l’oggetto Dispatcher. Quindi, una volta che il livello trasporto ha ricevuto una chiamata remota proveniente da un client, col metodo serviceCall viene passata questa chiamata al livello superiore (il livello riferimento remoto); il risultato di tale chiamata verrà poi posto sullo stream di uscita (output stream) della connessione. Il valore restituito dal metodo è un valore booleano che, se vale true indica che l’invocazione remota è avvenuta senza errori (quindi il livello trasporto potrà riusare la connessione), mentre se assume il valore false indica che si è verificato un errore durante la chiamata remota e pertanto la connessione dovrà essere eliminata.

3.3.2 La classe ObjectTable

Questa classe descrive la tabella degli oggetti, cioè una tabella che mappa identificatori di oggetti (i nomi degli oggetti remoti) con il loro Target (un Target è un oggetto contenente informazioni relative all’oggetto remoto, come l’ObjID, il dispatcher, lo stub, etc.) nello spazio degli indirizzi in cui è presente.

(15)

public final class ObjectTable { private ObjectTable() {}

static Target getTarget(ObjID id) {

//... }

public static Target getTarget(Remote impl) {

//... }

public static RemoteStub getStub(Remote impl)

throws NoSuchObjectException { //...

}

public static boolean unexportObject(Remote obj, boolean force)

throws java.rmi.NoSuchObjectException { //...

}

static void putTarget(Target target)

throws ExportException { //...

}

private static void removeTarget(Target target) {

//... }

//... }

I metodi getTarget ricercano nella ObjectTable l’oggetto remoto passato come parametro e restituiscono il Target ad esso associato.

Il metodo getStub, invece, restituisce lo stub dell’oggetto passato come parametro. Ciò può essere fatto solo dopo che l’oggetto remoto è stato esportato

(16)

Il metodo unexportObject rimuove l’oggetto obj dalla runtime di RMI, in modo che non sia più disponibile per le chiamate provenienti dai client. In particolare, se il parametro force assume il valore true, allora l’oggetto viene rimosso anche se ci sono chiamate pendenti o addirittura in corso, altrimenti, esso verrà rimosso solo quando non ci sono più chiamate pendenti o in corso.

Il metodo putTarget aggiunge un Target nella ObjectTable, mentre il metodo removeTarget lo elimina dalla tabella.

3.3.3 La classe DGCClient

Questa classe implementa il lato client del sistema di garbage collection distribuita. Quando un LiveRef (un riferimento remoto) è associato ad una VM, esso deve essere registrato con la DGCClient per partecipare alla garbage collection. Nel momento in cui viene registrato il primo riferimento remoto ad un dato oggetto, occorre fare una dirty call verso il lato server del DGC, il quale risponde inviando un lease period per garantire che l’oggetto remoto non verrà eliminato durante tale lasso di tempo. Fintanto che esistono i riferimenti ad oggetti remoti su un particolare server, il lato client del DGC invierà periodicamente delle dirty call per rinnovare il lease period.

La classe DGCClient tiene traccia localmente delle istanze di LiveRef registrate, mediante riferimenti fantasma e quando un riferimento remoto ad un dato oggetto diventa eliminabile localmente, invia una clean call al lato server del DGC, per indicare che il client non ha più bisogno di riferire quell’oggetto.

(17)

3.4 Principali classi del package

sun.rmi.transport.tcp

Contiene una serie di classi per implementare il comportamento TCP del livello trasporto (in particolare classi che rappresentano il comportamento di un canale, di un endpoint e di una connessione) e per realizzare il multiplexing di una connessione: data una connessione fisica ad un endpoint remoto, è possibile associare a questa, un insieme di connessioni virtuali.

3.4.1 La classe TCPTransport

Questa classe rappresenta l’implementazione basata sui socket TCP del livello trasporto di RMI.

public class TCPTransport extends Transport implements Runnable {

//...

TCPTransport(LinkedList epList) { //...

}

public void shedConnectionCaches() { //...

}

public Channel getChannel(Endpoint ep) { //...

}

public void free(Endpoint ep) { //...

}

public void exportObject(Target target)

throws RemoteException { //...

(18)

public void run() {

//... }

private static void closeSocket(Socket sock) {

//... }

void handleMessages(Connection conn, boolean persistent) {

//... }

public static String getClientHost()

throws ServerNotActiveException { //...

} //... }

Il metodo shedConnectionCaches permette di chiudere tutte le connessioni di ogni canale relativo a questo oggetto TCPTransport.

Il metodo getChannel restituisce un canale (un oggetto Channel) che genera una connessione all’endpoint specificato dal parametro ep. Un Channel è un oggetto che crea e gestisce connessioni di un certo tipo in un dato spazio degli indirizzi. Nel caso in cui non possano essere generate connessioni all’endpoint specificato, getChannel restituisce il valore null.

Contrariamente al precedente, il metodo free rimuove il canale che genera connessioni all’endpoint specificato da ep.

Il metodo exportObject permette di esportare l’oggetto individuato all’interno della ObjectTable dal parametro target, in modo che possa accettare chiamate provenienti dai client.

Il metodo run() accetta delle connessioni al server e crea per ognuna di esse, un thread che si pone in ascolto per ricevere le chiamate provenienti dal relativo client.

(19)

Col metodo closeSocket viene chiuso il socket specificato dal parametro sock e nel contempo viene eliminata l’eventuale eccezione che ha portato all’evento di chiusura del socket stesso.

Il metodo handleMessages serve a gestire appropriatamente i messaggi che arrivano al lato server del livello trasporto tramite lo stream di ingresso (InputStream); se si verifica un’eccezione durante la gestione dei messaggi, il socket viene chiuso. I messaggi sono discriminati dal valore intero op letto dall’header posto nello stream; in particolare, si distingue tra una chiamata RMI proveniente da un client, un “ping” per verificare lo stato della connessione e un “acknowledge” relativo ai messaggi inviati per la garbage collection distribuita (DGCAck).

Infine, il metodo getClientHost restituisce l’host del client relativo alla connessione del thread corrente; se non c’è una connessione attiva per il thread corrente, viene sollevata una ServerNotActiveException.

3.4.2 La classe TCPChannel

Questa classe, che implementa l’interfaccia sun.rmi.transport.Channel, rappresenta l’implementazione di un canale per la comunicazione RMI basato sui socket.

public class TCPChannel implements Channel { /** endpoint for this channel */

private final TCPEndpoint ep;

/** transport for this channel */ private final TCPTransport tr; /** list of cached connections */

(20)

private Reaper reaper = null;

/** using multiplexer (for bi-directional applet communication */ private boolean usingMultiplexer = false;

/** connection multiplexer, if used */

private ConnectionMultiplexer multiplexer = null; /** connection acceptor (should be in TCPTransport) */ private ConnectionAcceptor acceptor;

TCPChannel(TCPTransport tr, TCPEndpoint ep) { //...

}

public Endpoint getEndpoint() { //...

}

private void checkConnectPermission()

throws SecurityException { //...

}

public Connection newConnection() throws RemoteException { //...

}

private Connection createConnection()

throws RemoteException { //...

}

public void free(Connection conn, boolean reuse) { //...

}

private void writeTransportHeader(DataOutputStream out)

throws RemoteException { //... } synchronized void useMultiplexer(ConnectionMultiplexer newMultiplexer) { //... }

void acceptMultiplexConnection(Connection conn) { //...

} //... }

(21)

Il costruttore crea un canale TCP per un dato endpoint (quello fornito col parametro ep), mentre col metodo getEndpoint viene restituito l’endpoint del canale in questione.

Col metodo checkConnectPermission viene verificato se il chiamante attuale ha i privilegi sufficienti per fare una connessione all’endpoint remoto; nel caso in cui esso non possieda i requisiti per usare il canale, verrà sollevata una SecurityException.

Il metodo newConnection fornisce una connessione ad un endpoint, ricercandola in una lista di connessioni “idle”, mentre il metodo createConnection crea una nuova connessione all’endpoint remoto per il canale in questione.

Il metodo free libera la connessione (specificata dall’argomento conn) generata da questo canale. In particolare, se il parametro reuse assume il valore

true, la connessione potrà essere riusata per un’altra chiamata di metodo remoto.

Infine, il metodo useMultiplexer permette di usare uno specifico oggetto ConnectionMultiplexer per ottenere delle connessioni virtuali multiple da associare al canale in questione.

3.4.3 La classe TCPConnection

Tale classe implementa l’interfaccia sun.rmi.transport.Connection e serve ad ottenere una connessione relativamente ad un particolare oggetto Channel.

public class TCPConnection implements Connection { private Socket socket;

private Channel channel;

private InputStream in = null; private OutputStream out = null;

(22)

TCPConnection(TCPChannel ch, Socket s,

InputStream in, OutputStream out) { //...

}

TCPConnection(TCPChannel ch, InputStream in,

OutputStream out) { //... } TCPConnection(TCPChannel ch, Socket s) { //... }

public OutputStream getOutputStream() throws IOException { //...

}

public void releaseOutputStream() throws IOException { //...

}

public InputStream getInputStream() throws IOException { //...

}

public void releaseInputStream() { //...

}

public boolean isReusable() { //...

}

boolean expired(long time) { //...

}

public boolean isDead() { //...

}

public void close() throws IOException { //...

}

public Channel getChannel() { //...

} }

(23)

Il primo costruttore è usato per creare una connessione che accetti chiamate (quindi sarà una connessione di input). La seconda forma di costruttore viene usata dalle sottoclassi di TCPConnection quando si vuole creare una connessione, avendo già a disposizione gli stream di ingresso e uscita, mentre quando questi non si posseggono, ma si ha a disposizione il socket, viene usata la terza forma di costruttore.

I metodi getOutputStream e releaseOutputStream permettono di ottenere e di rilasciare uno stream di uscita, mentre le funzioni analoghe per lo stream di ingresso vengono svolte dai metodi getInputStream e releaseInputStream.

Il metodo isReusable determina se la connessione può essere usata per operazioni multiple.

Il metodo booleano expired restituisce il valore true se il timeout della connessione è scattato.

Col metodo isDead è possibile verificare se la connessione è ancora attiva e connessa al server. Quando la connessione risulta da “troppo tempo” idle (cioè non viene usata), viene effettuato un “ping” verso il server; più precisamente, col termine “troppo tempo” si intende un tempo più lungo del “round-trip time” dell’ultimo ping. Occorre notare che, questo metodo può erroneamente indicare una connessione “morta” come “attiva”, ma non succederà mai che una connessione “attiva” venga dichiarata “morta”.

Infine, il metodo close viene usato per chiudere una connessione, mentre il metodo getChannel restituisce il canale relativo alla connessione.

3.4.4 La classe ConnectionMultiplexer

Questa classe gestisce il multiplexing di connessioni virtuali multiple tra un endpoint e un altro, attraverso una data connessione fisica a quell’endpoint.

(24)

connessione fisica e necessiterà un oggetto col quale essere informati circa nuove connessioni virtuali aperte.

final class ConnectionMultiplexer {

/** object to notify for new connections from remote endpoint */ private TCPChannel channel;

/** input stream for underlying single connection */ private InputStream in;

/** output stream for underlying single connection */ private OutputStream out;

/** true if underlying connection originated from this endpoint (used for generating unique connection IDs) */ private boolean orig;

/** layered stream for reading formatted data from underlying connection */ private DataInputStream dataIn;

/** layered stream for writing formatted data to underlying connection */ private DataOutputStream dataOut;

/** table holding currently open connection IDs and related info */ private Hashtable connectionTable = new Hashtable(7); /** number of currently open connections */

private int numConnections = 0;

/** maximum allowed open connections */

private final static int maxConnections = 256; /** ID of last connection opened */

private int lastID = 0x1001;

/** true if this mechanism is still alive */ private boolean alive = true;

public ConnectionMultiplexer( TCPChannel channel, InputStream in, OutputStream out, boolean orig) { //...

(25)

public void run() throws IOException { //...

}

public synchronized TCPConnection openConnection()

throws IOException { //...

}

public void shutDown() { //...

}

void sendRequest(MultiplexConnectionInfo info, int len) throws IOException { //...

}

void sendTransmit(MultiplexConnectionInfo info, byte buf[], int off, int len)

throws IOException { //...

}

void sendClose(MultiplexConnectionInfo info)

throws IOException { //...

}

void sendCloseAck(MultiplexConnectionInfo info)

throws IOException { //...

}

protected void finalize() throws Throwable { //...

} }

Il costruttore crea un nuovo oggetto ConnectionMultiplexer usando la coppia di stream (di input e di output) e il Channel passati come parametri.

Il metodo run viene richiamato da un opportuno thread appositamente creato quando occorre effettuare il demultiplexing delle connessioni. In

(26)

chiusura di una connessione virtuale, l’acknowledge di avvenuta chiusura della connessione, la richiesta e la trasmissione di un pacchetto di dati da e verso l’endpoint remoto.

Il metodo openConnection crea una nuova connessione virtuale (generando per essa un identificatore unico tra i 32768 possibili) che viene aggiunta in un’opportuna tabella, quindi informa l’endpoint remoto della nuova connessione.

Col metodo shutDown viene fatto lo shut down di tutte le connessioni virtuali e viene anche chiusa la connessione fisica sottostante.

Il metodo sendRequest viene usato per inviare una richiesta di dati sulla connessione all’endpoint remoto, mentre col metodo sendTransmit viene inviato un pacchetto di dati sulla connessione.

Il metodo sendClose informa l’endpoint remoto che la connessione è stata chiusa, mentre con sendCloseAck, l’endpoint remoto fornisce un acknowledge di avvenuta chiusura della connessione.

3.4.5 La classe MultiplexConnectionInfo

Questa classe serve a creare un oggetto che contenga informazioni sulla connessione virtuale gestita da un oggetto ConnectionMultiplexer. In particolare, essa contiene un intero che identifica univocamente la connessione (id), uno stream di ingresso (in) e uno di uscita (out) per leggere e scrivere dati nella connessione e un valore booleano (closed) che quando assume il valore true indica che la connessione è stata chiusa.

class MultiplexConnectionInfo {

/** integer that uniquely identifies this connection */ int id;

/** input stream for reading from connection */ MultiplexInputStream in = null;

(27)

/** output stream for writing to connection */ MultiplexOutputStream out = null;

/** true if this connection has been closed */ boolean closed = false;

MultiplexConnectionInfo(int id) { //... } } 3.4.6 La classe MultiplexInputStream

Permette di descrivere la ricezione di dati su una connessione gestita da un oggetto ConnectionMultiplexer. In particolare, essa si occupa di richiedere dei byte di dati, non appena si libera spazio nel suo buffer interno.

final class MultiplexInputStream extends InputStream { /** object managing multiplexed connection */ private ConnectionMultiplexer manager;

/** information about the connection this is the input stream for */ private MultiplexConnectionInfo info;

/** input buffer */ private byte buffer[];

/** number of real data bytes present in buffer */ private int present = 0;

/** current position to read from in input buffer */ private int pos = 0;

/** pending number of bytes this stream has requested */ private int requested = 0;

/** true if this connection has been disconnected */ private boolean disconnected = false;

(28)

/** level at which more data is requested when read past */ private int waterMark;

/** data structure for holding reads of one byte */ private byte temp[] = new byte[1];

MultiplexInputStream( ConnectionMultiplexer manager, MultiplexConnectionInfo info, int bufferLength) { //...

}

public synchronized int read() throws IOException { //...

}

public synchronized int read(byte b[], int off, int len) throws IOException { //...

}

public int available() throws IOException { //...

}

public void close() throws IOException { //...

}

void receive(int length, DataInputStream in)

throws IOException { //... } void disconnect() { //... } }

Osserviamo innanzitutto che, tra gli altri, è presente un membro lock per consentire il corretto accesso alle variabili condivise. In particolare, i metodi send del manager (cioè dell’oggetto ConnectionMultiplexer che gestisce la risorsa in questione) non possono essere invocati mentre si è in possesso del lock, in quanto questi potrebbero bloccarsi, non riuscendo a svuotare i buffer della connessione fisica sottostante, qualora questi ultimi siano pieni.

(29)

Il metodo read senza parametri permette di leggere un byte dalla connessione, mentre l’altro metodo read legge un certo numero di byte (acquisendo la risorsa con lock) e restituisce un intero che indica il numero di byte letti o il valore -1 se si è raggiunta la fine dello stream.

Il metodo available restituisce il numero di byte attualmente disponibili per la lettura.

Col metodo close si chiude la connessione, mentre il metodo disconnect permette di disconnettere lo stream in questione dalla connessione.

Infine, il metodo receive permette di ricevere i byte trasmessi dalla connessione all’endpoint remoto.

3.4.7 La classe MultiplexOutputStream

Questa classe permette di realizzare l’invio di dati su una connessione gestita da un oggetto ConnectionMultiplexer. I dati scritti vengono bufferizzati, finchè il buffer interno non si riempie o finchè non viene invocato il metodo flush(); dopodiché il pacchetto di byte viene inoltrato verso l’endpoint remoto, tenendo presente che mai verranno inoltrati più byte di quelli richiesti (ciò per prevenire un overflow sul buffer del ricevente).

final class MultiplexOutputStream extends OutputStream { /** object managing multiplexed connection */

private ConnectionMultiplexer manager; /** information about the connection this is the output stream for */ private MultiplexConnectionInfo info; /** output buffer */

private byte buffer[];

/** current position to write to in output buffer */ private int pos = 0;

(30)

/** true if this connection has been disconnected */ private boolean disconnected = false;

/** lock acquired to access shared

variables: requested & disconnected */ private Object lock = new Object();

MultiplexOutputStream( ConnectionMultiplexer manager, MultiplexConnectionInfo info, int bufferLength) { //...

}

public synchronized void write(int b) throws IOException { //...

}

public synchronized void write(byte b[], int off, int len) throws IOException { //...

}

public synchronized void flush() throws IOException { //...

}

public void close() throws IOException { //...

}

void request(int num) { //...

}

void disconnect() { //...

}

private void push() throws IOException { //...

} }

Anche in questa classe è presente un membro lock per consentire il corretto accesso alle variabili condivise.

I due metodi write permettono di scrivere rispettivamente un byte e un insieme di byte sulla connessione.

(31)

Col metodo flush si inviano tutti i dati scritti nello stream verso l’endpoint remoto e a tal proposito viene usato il metodo push che deposita i byte sulla connessione, bloccandosi quando il buffer ricevente è pieno.

Il metodo request viene usato per conoscere il numero di byte richiesti dalla connessione.

Infine, il metodo close permette di chiudere la connessione, mentre col metodo disconnect si provvede a disconnettere lo stream di uscita dalla connessione.

Riferimenti

Documenti correlati

Architettura di Java Remote Method Invocation (RMI).. Il

Note that some keys default to this value every listing, namely the keys which can be used on individual listings only.... Regarding the parameters, please keep in mind

Nel caso in cui la richiesta di connessione non soddisfi le condizioni di nessuna politica, resta la Politica di Accesso Remoto di Default le cui condizioni sono sempre soddisfatte

 In Java il tipo base degli array può essere qualunque, sia primitivo sia riferimento.  Sono possibili “array

Per scrivere dati in un file si costruisce un oggetto di tipo PrintWriter , fornendo il nome del file:. PrintWriter out =

(b) Per ogni regione, selezionare il prezzo medio al litro, il numero medio di litri di vino esportati per provincia, e la percentuale di litri di vino esportati da ogni

Thus, the elements of any collection class can be accessed through the methods defined by Iterator, with similar cycles for different data structures, e.g.. sets

Plus pr´ ecis´ ement, il faut documenter le package dans sa globalit´ e, tous les fichiers de donn´ ees, toutes les fonctions publiques et toutes les invisibles (G´ en´ e-