Visualizzazione post con etichetta Java. Mostra tutti i post
Visualizzazione post con etichetta Java. Mostra tutti i post

07 dicembre 2010

URLConnection : richiesta http + login

E' da tanto tempo che non scrivo un post sul mio blog, trovo l'occasione grazie ad un client che stò sviluppando che permette di eseguire il login su un sito mediante classi standard java.net, anzichè utilizzare librerie note come quella di apache.

In particolare in questo post vedremo come creare un wrapper per eseguire connessioni tramite l'oggetto URLConnection.
Quella che vedremo di seguito è una classe di utilità per la gestione delle richieste http con parametri post e/o invio delle informazioni presenti nei cookies.

Ecco il codice sorgente della classe :

In dipendenza a questa classe è necessario definire la classe ResponseWrapper :
Questa classe è ha il compito di incapsulare le informazioni recuperate mediante la richiesta http, in particolare l'html contenuto e la lista di cookies generati dal sito.

Definiamo adesso un metodo per eseguire il login a cui passiamo username, password e cookies generati dal sito


Come utilizzare questo metodo è Semplice :

Spero che la classe vi possa rendere le cose più semplici.
Alla prossima!

26 febbraio 2010

FileUtils : funzionalità aggiuntive per la gestione dei files in Java

Tramite questo post metto a disposizione l'implementazione di una classe di utilità che implementa alcune funzionalità per la gestione dei Files in Java.

In dettagli le funzionalità implementate sono le seguenti :

- Cancellare i files più vecchi di un certo numero di giorni da una cartella
- Creare una cartella nel caso in cui non esista
- Recuperare l'elenco dei files da una cartella ordinandoli per data crescente / decrescente.

Il codice qui di seguito :
package it.fritzzz.utils;

import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

/***
* Classe di utilità per la gestione dei files
* @author a.franzi
*
*/
public class FileUtils {

/***
* Ordina e restituisce i files per data di ultima modifica
* @param directory : directory su cui eseguire il list del files
* @param asc : boolean (true = dal più lontano al più recente ; false = il contrario)
* @return : List di nomi files
*/
public static List listFilesOfDirOrderByLastModifiedDate(final File directory,boolean asc){
if (directory!=null && directory.isDirectory()) {
String[] children = directory.list();
if (children!=null && children.length>0){
List childList = Arrays.asList(children);
Collections.sort(childList,new Comparator() {

public int compare(Object o1, Object o2) {
if (o1 !=null && o2 !=null){
File file1 = new File(directory, (String)o1);
File file2 = new File(directory, (String)o2);

if (file1.lastModified()==file2.lastModified()){
return 0;
}else if (file1.lastModified()>file2.lastModified()){
return 1;
}else if (file1.lastModified()<file2.lastModified()){
return -1;
}
}
return 0;
}
});
return childList;
}
}
return null;
}


/***
* Metodo per rimuovere i files troppo vecchi da una determinata cartella
* @param folderName : nome della cartella
* @param numberOfDays : numero di giorni di tolleranza
* @return : boolean true se tutto è andato a buon fine
*/
public static boolean removeTooOldFilesFromDir(String folderName, int numberOfDays){
boolean ok = true;

try{
File dir = new File(folderName);

if (dir.isDirectory()){
File[] filesIntoDir = dir.listFiles();
if (filesIntoDir!=null && filesIntoDir.length> 0){
for (int i = 0 ; i < filesIntoDir.length; i++){
if (filesIntoDir[i].isFile()){
long lastUpdateOfFile = filesIntoDir[i].lastModified();
Calendar cal = Calendar.getInstance();
cal.setTimeInMillis(lastUpdateOfFile);
Calendar today = Calendar.getInstance();
today.add(Calendar.DAY_OF_YEAR, -numberOfDays);
if (cal.before(today)){
boolean currDelete = filesIntoDir[i].delete();
if (!currDelete){
System.out.println("Problem removing too old file : "+filesIntoDir[i].getAbsolutePath());
ok = false;
}else{
System.out.println("Removed too old file : "+filesIntoDir[i].getAbsolutePath());
}
}
}
}
}else{
throw new IOException("nessun file presente nella cartella : "+folderName);
}

}else{
throw new IOException("il path : "+folderName+" non è una directory");
}
}catch(IOException ex){
System.out.println("Eccezione : "+ex.getMessage());
ok = false;
}


return ok;
}

/***
* Metodo per creare una cartella nel caso non esiste gia'
* @param path : path
* @return : boolean
*/
public static boolean createFolderIfNotExits(String path){
File folder = new File(path);
if (!folder.exists()){
return folder.mkdirs();
}else{
System.out.println("la cartella : "+path+" esiste gia'");
}
return false;
}
}

14 febbraio 2010

Usare JDBC per connettersi a Mysql da Java

Introduzione
Lo scopo di questo tutorial è quello di eseguire i seguenti passi :
- Scaricare e installare un database MySQL
- Preparare il database per essere usato tramite i JDBC
- Scrivere e testare la nostra prima applicazione che usa i JDBC

Che cosa è JDBC ?
JDBC (Java DataBase Connectivity), è un connettore per database che consente l'accesso alle basi di dati da qualsiasi programma scritto con il linguaggio di programmazione Java, indipendentemente dal tipo di DBMS utilizzato. È costituita da una API, raggruppata nel package java.sql, che serve ai client per connettersi a un database. Fornisce metodi per interrogare e modificare i dati. È orientata ai database relazionali ed è Object Oriented. La piattaforma Java 2 Standard Edition contiene le API JDBC, insieme all'implementazione di un bridge JDBC-ODBC, che permette di connettersi a database relazionali che supportino ODBC. Questo driver è in codice nativo e non in Java.

Per poter utilizzare MySQL dalle vostre applicazioni Java è necessario scaricare il Connector/J dal seguente link.

Cosa è MySQL ?
MySQL è un Relational database management system (RDBMS), composto da un client con interfaccia a caratteri e un server, entrambi disponibili sia per sistemi Unix come GNU/Linux che per Windows, anche se prevale un suo utilizzo in ambito Unix.
Dal 1996 supporta la maggior parte della sintassi SQL e si prevede in futuro il pieno rispetto dello standard ANSI. Possiede delle interfacce per diversi linguaggi, compreso un driver ODBC, due driver Java e un driver per Mono e .NET.

Per poter installare MySQL è necessario scaricare il pacchetto adatto al vostro Sistema Operativo dal seguente link.

Adesso abbiamo tutto il necessario per poter utilizzare MySQL all' interno della nostra applicazione java.


CONFIGURARE TOMCAT e MYSQL per il recupero della connessione

Prima di procedere, è necessario copiare il Driver JDBC (Connector) nella cartella : $CATALINA_HOME/lib.

In secondo luogo nel caso si tratti di un applicazione WEB è possibile definire un datasource nel file di configurazione del server context.xml:
<Resource name="jdbc/TestDB" auth="Container" type="javax.sql.DataSource"
maxActive="100" maxIdle="30" maxWait="10000"
username="javauser" password="javadude" driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/javatest?autoReconnect=true"/>


Adesso tramite l'applicazione ed in particolare con il seguente pezzo di codice sarà possibile recuperare una connessione :

/***
* Classe di utilità per gestione connessioni
* @author alessandrofranzi
*
*/
public class DBAccess {
private static Logger logger = Logger.getRootLogger();

public DBAccess() {
}

/***
* Recupera la connessione
* @return : connessione
* @throws DatabaseException : eccezione personalizzata
*/
public static Connection getConnection() throws DatabaseException{
try {
InitialContext ic = new InitialContext();
DataSource ds = (DataSource) ic.lookup("java:comp/env/jdbc/TestDB");
return ds.getConnection();
} catch (NamingException e){
System.out.println("Eccezione : "+e.getMessage());
} catch (SQLException e){
System.out.println("Eccezione : "+e.getMessage());
} catch (Exception e){
System.out.println("Eccezione : "+e.getMessage());
}
}
}

01 settembre 2009

Da ArrayList a vettore in Java

Mi capita spessisimo di dover integrare componenti scritte da me con vecchie componenti che utilizzano ancora semplici vettori.
Personalmente tendo ad usare spessissimo gli ArrayList, ma mi capita altrettanto spesso di dimenticare come convertire gli stessi in vettori.
Ecco come si fà :
(MyClass[]) mioArrayList.toArray(new MyClass[0])


Nel caso volessimo avere almeno un elemento per il vettore generato la sinstassi sarebbe la seguente :
(MyClass[]) mioArrayList.toArray(new MyClass[1])

22 giugno 2009

Richieste HTTP GET, POST, REST con Android

Dopo il tutorial su cos'è rest ecco un' implementazione per poter utilizzare REST sulla piattaforma Android.

In particolare ciò che ho creato è una classe di utilità in cui sono presenti una serie di metodi utili per l'esecuzionedi richieste di tipo GET e POST ed un wrapper mediante il quale è possibile eseguire una chiamata REST modellata in base al tipo di method che gli si passa in input.

public class RestUtils {
private final static String METHOD_POST = "post";
private final static String METHOD_GET= "get";
private final static String TAG= "RestUtils";

/***
* Esegue una richiesta REST
*
* @param host : hostname
* @param method : methodo (GET,POST supportati per ora)
* @param path : percorso
* @param parameters : parametri
* @return : Stringa contenente la response
* @throws RestRequestException : Eccezione personalizzata
*/
public static String doRESTRequest (String host, String method, String path, Map<String,String> parameters)
throws RestRequestException{

return doRESTRequest(host, "80", method, path, parameters);
}

/***
* Esegue una richiesta REST
*
* @param host : hostname
* @param port : porta
* @param method : methodo (GET,POST supportati per ora)
* @param path : percorso
* @param parameters : parametri
* @return : Stringa contenente la response
* @throws RestRequestException : Eccezione personalizzata
*/
public static String doRESTRequest (String host, String port, String method, String path, Map<String,String> parameters)
throws RestRequestException{

String returnString = null;
try{

String url = host+":"+port+path;
Log.i(TAG,METHOD_GET+" Request - URL : "+url );

if (METHOD_GET.equalsIgnoreCase(method)){
returnString = doGETRequest(url,parameters);
}else if (METHOD_POST.equalsIgnoreCase(method)){
returnString = doPOSTRequest(url, parameters);
}
}catch(GetRequestException e){
throw new RestRequestException(e.getMessage());
}catch(PostRequestException e){
throw new RestRequestException(e.getMessage());
}

return returnString;

}

/***
* Metodo per eseguire una request di tipo GET
* @param url : url della request
* @return : stringa contenente la response
* @throws GetRequestException : Eccezione modellata
*/
public static String doGETRequest(String url,Map<String, String> parameters) throws GetRequestException{
String paramStr = composeParametersForGetRequest(parameters);

if (paramStr!=null && paramStr.length() > 0 ) url = url +"?"+paramStr;
String websiteData = null;

try {
Log.i(TAG,"GetRequest - url : "+url);
DefaultHttpClient client = new DefaultHttpClient();
URI uri = new URI(url);
HttpGet method = new HttpGet(uri);
HttpResponse res = client.execute(method);
InputStream data = res.getEntity().getContent();
websiteData = parseISToString(data);
} catch (ClientProtocolException e) {
throw new GetRequestException(e.getMessage());
} catch (IOException e) {
throw new GetRequestException(e.getMessage());
} catch (URISyntaxException e) {
throw new GetRequestException(e.getMessage());
}finally{
Log.i(TAG,"GetRequest - Request & Response completed");
}
return websiteData;
}

public static String doPOSTRequest(String url,Map<String, String> parameters)
throws PostRequestException{

String returnString = null;
// Creo un nuovo HttpClient e l'Header del post
HttpClient httpclient = new DefaultHttpClient();
HttpPost httppost = new HttpPost(url);
httppost.setHeader(HTTP.CONTENT_TYPE, "application/x-www-form-urlencoded");

try {
// aggiungo i dati alla richiesta
List<NameValuePair> nameValuePairs = composeParametersForPostRequest(parameters);
httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs));

// Eseguo la richiesta HTTP
HttpResponse response = httpclient.execute(httppost);
if (response.getStatusLine().getStatusCode() == 200) {
returnString = EntityUtils.toString(response.getEntity());
response = null;

}
} catch (ClientProtocolException e) {
throw new PostRequestException(e.getMessage());
} catch (IOException e) {
throw new PostRequestException(e.getMessage());
}

return returnString;

}

public static List<NameValuePair> composeParametersForPostRequest(Map<String,String> parameters){

List<String> chiavi = new ArrayList<String>(parameters.keySet());
List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>();

for (int i = 0 ; i < chiavi.size(); i++){
String chiave = chiavi.get(i);
nameValuePairs.add(new BasicNameValuePair(chiave, parameters.get(chiave)));
}

return nameValuePairs;
}

/***
* Metodo per comporre la parte di URL relativa ai parametri
* @param parameters : mappa di parametri
* @return : Stringa contenente i parametri concatenati
*/
public static String composeParametersForGetRequest(Map<String, String> parameters){
String paramStr = "";
List<String> chiavi = new ArrayList<String>(parameters.keySet());

for (int i = 0 ; i < chiavi.size(); i++){
String chiave = chiavi.get(i);
paramStr += chiave+"=";
paramStr += URLEncoder.encode(parameters.get(chiave));
paramStr += "&";
}
return paramStr;
}
}


Innanzitutto noterete che mancano alcune componenti per cui non potrete compilare questa classe:
- Metodi per la conversione da Stringa ad InputStream e viceversa che potrete trovare su questo post.
- Eccezioni personalizzate...potete ricrearvele voi, l'importante è che estandano Exception.

Adesso analizziamo i metodi :
- doRESTRequest : ci sono 2 overload utilizzabili uno in cui è possibile utilizzare una porta diversa da quella di default (80). Tramite questo metodo è possibile eseguire qualsiasi tipo di richiesta REST in base al parametro method che può essere "get" o "post". Gli altri parametri sono facilmente intuibili e troverete un esempio più avanti.
- doGETRequest : è un metodo che dato un url ed una mappa di parametri esegue una richiesta di tipo GET e restituisce una stringa contenente la response.
- doPOSTRequest : è un metodo che dato un url e una mappa di parametri esegue una richiesta di tipo POST e restituisce una stringa contenente la response.
- composeParametersForPostRequest : metodo per comporre i parametri in maniera adeguata per poter eseguire una richiesta di tipo POST.
- composeParametersForGetRequest : metodo per comporre i parametri in maniera adeguata per completare l'url per una richiesta di tipo GET.

Vediamo un esempio di utilizzo della classe di utilità per una richiesta di tipo POST. L'esempio utilizza le API REST di Yahoo per eseguire una ricerca :

Map<String,String> paramz = new HashMap<String,String>();
paramz.put("appid", "YahooDemo");
paramz.put("query", "umbrella");
paramz.put("results", "10");
try {
String retString = RestUtils.doRESTRequest("http://api.search.yahoo.com", "POST", "/WebSearchService/V1/webSearch", paramz);
Log.i(TAG,"Return : "+retString);
} catch (RestRequestException e) {
Log.e(TAG,"Eccezione : ",e);
}


Spero che questo post vi sia stato di aiuto.
Accetto domande e proponimenti a riguardo.

String ed InputStream in Java

Premetto che non sono dotato di ottima memoria, quindi mi ritrovo ogni volta a riscrivere funzioni di conversione per ogni genere di Type in ogni genere di linguaggio di programmazione. Naturalmente ogni volta dimentico di come si convertano i diversi type... un esempio di conversione che avrò implementato milioni di volte ma che ogni volta devo riscrivere da zero è quella da Stringa ad InputStream e viceversa.

Per cui per mia comodità e per vostra necessità ecco 2 metodi con i quali potrete convertire questi due tipi di dati Java.

Da InputStream a String

public static String parseISToString(InputStream is){
java.io.DataInputStream din = new java.io.DataInputStream(is);
StringBuffer sb = new StringBuffer();
try{
String line = null;
while((line=din.readLine()) != null){
sb.append(line+"\n");
}
}catch(Exception ex){
ex.getMessage();
}finally{
try{
is.close();
}catch(Exception ex){}
}
return sb.toString();
}


Da String a InputStream

public java.io.InputStream parseStringToIS(String str){
if(str==null) return null;
str = str.trim();
java.io.InputStream in = null;
try{
in = new java.io.ByteArrayInputStream(str.getBytes("UTF-8"));
}catch(Exception ex){}
return in;
}


Spero che questo breve post vi sia d'aiuto spessissime volte.
Alla prossima

31 marzo 2009

Recuperare Charset di default della Java Virtual Machine (JVM)

A volte in fase di compilazione si può incorrere in problemi di charset, specialmente se si sviluppa in ambienti eterogenei (Linux, Windows, Unix).
A volte si incorre in classi Java compilate con virtual machine che utilizzano charset diverse, causando problemi nelle stringhe che contengono caratteri speciali.
Il metodo mediante cui è possibile riconoscere il charset utilizzato dalla Virtual Machine per compilare è il seguente.

Questo esempio presenta una sezione compatibile con la versione 1.4 della jdk ed un altra con la 1.5. Per poter eseguire lo script è necessario commentare la sezione non interessata.
public class Hello {
public static void main(String args[]) throws Exception{
// not crossplateform safe
System.out.println(System.getProperty("file.encoding"));
// jdk1.4
System.out.println(
new java.io.OutputStreamWriter(
new java.io.ByteArrayOutputStream()).getEncoding()
);
// jdk1.5
System.out.println(java.nio.charset.Charset.defaultCharset().name());
}
}

L'output sarà del tipo:
>java Hello
Cp1252
Cp1252
windows-1252

15 marzo 2009

Padding di una stringa in Java

Questo post ha come scopo quello di illustrare una possibile implementazione della funzionalità di padding per le stringhe in Java.
In questo esempio la funzionalità di padding è una funzione generica in cui è possibile specificare come parametri di input le seguenti informazioni :
- Stringa di cui effettuare il padding
- Carattere o stringa con cui eseguire il padding
- Numero di caratteri della stringa di output
- Orientamento del padding (sinistra, destra)
Ecco l'implementazione:
public class Padding {

public static final int PADDING_LEFT = 0;
public static final int PADDING_RIGHT = 1;

public static String padString(String str, String paddingChar, int lngth, int paddingSide) {
if (str == null) {
str = "";
}

if (str.length() < lngth) {
for (int k = str.length(); k < lngth; k++) {
if (paddingSide == PADDING_LEFT)
str = paddingChar + str;
else if (paddingSide == PADDING_RIGHT)
str = str + paddingChar;
else throw new IllegalArgumentException("paddingSide argument wrong!");
}
}
return str;
}
}

Di seguito un esempio di un possibile utilizzo di questo metodo :
String padded = Padding.padString("Hello World","-",50,Padding.PADDING_LEFT);

03 marzo 2009

Java : Eseguire Jar o Web Application in modalità debug

Capita spesso di dover rintracciare determinati errori o comportamenti anomali della propria applicazione. Ritorna utilissimo utilizzare l'esecuzione in modalità debug rendendo possibile tramite il proprio IDE l'esecuzione passo passo di una determinato blocco di codice, controllare lo stato delle variabili in un determinato stato del sistema, controllare lo stack trace, valutare determinate espressioni personalizzate e modificare a runtime il valore delle variabili.
Con un utilizzo corretto e avanzato di questa modalità è possibile rintracciare e risolvere i problemi delle nostre applicazioni in pochissimi istanti.

Non starò a spiegare come eseguire il debug in maniera ottimale. Questo post ha il semplice scopo di voler aiutare gli utenti a configurare il proprio ide, la propria web application o applicazione stand alone ad essere eseguita in modalità debug.
Il primo passo da eseguire è quello di lanciare l'application server o l'applicazione con i seguenti parametri :
-Xdebug -Xrunjdwp:server=y,transport=dt_socket,address=4142,suspend=n

Ad esempio se volessi lanciare il mio container, in questo caso un Oc4j:

java -XX:PermSize=128m -Xmx512m -Xdebug -Xrunjdwp:server=y,transport=dt_socket,address=4142,suspend=n -jar oc4j.jar -userThreads

Se invece volessi lanciare il jar della mia applicazione:
javaw -jar -Xmx512M -Xdebug -Xrunjdwp:server=y,transport=dt_socket,address=4142,suspend=n -jar externalEPG.jar

Dopodichè è necessario configurare il proprio IDE per poter comunicare con la virtual machine ed eseguire il codice in debug.
N.B : E' necessario che eclipse utilizzi la stessa virtual machine utilizzata dall'applicazione.
E' necessario andare nel menù Run -> Open Debug dialog...
Settare le impostazioni come nell'immagine:



E' necessario quindi scegliere il progetto da cui eclipse andrà recuperare i relativi sources, la connection type: Socket (metodo con cui eclipse si connette alla VirtualMachine) e la porta che abbiamo impostato precedentemente nei parametri passati alla VM (nel nostro caso 4142).
Adesso è necessario solamente avviare il debug mediante il corrispondente pulsante e switchare la modalità di visualizzazione in quella di debug. Il risultato che otterrete sarà il seguente:



Molti non adorano "Debuggare" ma altri riescono a coglierne a pieno la potenza. Buon divertimento!

18 gennaio 2009

Socket in Java (tcp e udp)

Molti sistemi distribuiti ed applicazioni sono costruiti e basati semplicemente sul modello message-oriented del livello di trasporto.
In fase di progettazione del livello di trasporto è stata prestata molta attenzione alle standardizzazioni delle interfaccie per permettere ai programmatori di utilizzare l'intera suite di protocolli mediante un semplice set di primitive.
Un classico esempio sono le interfaccie socket introdotte in Berkley Unix.

Concettualmente un socket è un endpoint di comunicazione tramite cui un applicazione può scrivere dati che vengono inviati tramite la rete e tramite cui dati in ingresso possono essere letti.
Consideriamo le primitive :
1. SOCKET : crea un nuovo endpoint di comunicazione
2. BIND : attacca un indirizzo locale ad un socket
3. LISTEN : annuncia l'approvazione di accettare la connessione
4. ACCEPT : blocca il chiamante finchè non arriva una richiesta di connessione
5. CONNECT : tenta attivamente di stabilire una connessione
6. SEND : invia dati mediante la connessione
7. RECEIVE : riceve dati dalla connessione
8. CLOSE : rilascia la connessione

I server generalmente eseguono le prime 4 nell'ordine dato:
1. Il sistema operativo locale riserva la risorsa per garantire l'invio e la ricezione di messaggi per il protocollo specificato
2. Dice al S.O. che il server vuole ricevere dati solo su quell'indirizzo e su quella porta
3. E' chiamata solo in caso di comunicazione orientata alla connessione, serve a dire al S.O di allocare abbastanza buffer per un numero massimo di connessioni.
4. Quando arriva una richiesta il S.O. crea un nuovo socket con le stesse proprietà di quello originale e lo restituisce al chiamante.

Guardiamo il Client:
1. Va creato il SOCKET utilizzando la primitiva
5. La primitiva CONNECTION richiede che il chiamante specifichi l'indirizzo del livello di trasporto al quale la richiesta di connessione deve essere inviata. Il client si blocca finchè la richiesta è completata. Quindi le 2 parti possono iniziare a scambiare dati mediante le primitive READ e RECEIVE.

8. La chiusura è simmetrica e può avvenire sia lato client che lato server.

Vediamo un esempio di implementazione in cui avvengono i seguenti passi :
- Il client legge una linea dallo standard input (inFromUser stream) , e lo invia al server mediante socket (outToServer stream)
- Il server legge la linea dal socket
- Il server converte la line in uppercase e la restituisce al client
- Il client legge e stampa la linea modificata mediante il socket (inFromServer stream)

IMPLEMENTAZIONE TCP
Nella versione TCP dell'implementazione dei socket distinguiamo le seguenti caratteristiche peculiari:
Poichè il TCP fornisce un affidabile e ordinato trasferimento di bytes tra client e server (orientato alla connessione),
è necessario che il processo server sia il primo ad essere eseguito, in maniera tale da creare il socket (door) che accetta la connessione da parte dei client.
Quando i client creano il socket, il client TCP stabilisce una connessione con il server TCP il quale crea un nuovo socket per la comunicazione con il client. Questo permette di avere una comunicazione simultanea con più client.
Ecco l'implementazione del server :
/***
* Classe per la gestione del server
*
* @author Alessandro Franzi
*
*/
public class TCPServer {
public static final int PORT = 6768;

public static void main(String[] args){
String fraseClient;
String fraseMaiuscola;
try{
// creo il socket di benvenuto
ServerSocket welcomeSocket = new ServerSocket(PORT);

// ciclo infinitamente (server)
while (true){

// aspetto il socket per il contatto con il client
Socket connectionSocket = welcomeSocket.accept();

System.out.println("Accettata connessione socket");
System.out.println("Il client è connesso dall'ip : "+connectionSocket.getRemoteSocketAddress());

//creo uno stream di input attaccato al socket
BufferedReader inFromClient = new BufferedReader(new InputStreamReader(connectionSocket.getInputStream()));

DataOutputStream outToClient = new DataOutputStream(connectionSocket.getOutputStream());

// leggo la frase del client
fraseClient = inFromClient.readLine();

// trasformo la frase in maiuscolo
fraseMaiuscola = fraseClient.toUpperCase();

System.out.println("frase da inviare : "+fraseMaiuscola);

outToClient.writeBytes(fraseMaiuscola+'\n');

}
}catch (IOException e) {
System.out.println("Errore :"+e.getMessage());
}
}
}


Questa invece è l'implementazione del client :
/***
* Classe client TCP
*
* @author Alessandro Franzi
*
*/
public class TCPClient {

private static final String HOSTNAME = "1.46.193.82";
public static final int PORT = 6768;

public static void main(String[] args){
String host ="";
int port = PORT;
String frase ="";
String fraseModificata;

if (args.length==0){
System.out.println("Parametri di default.");
System.out.println("Host : "+HOSTNAME+" Porta : "+PORT);
host = HOSTNAME;
}else{
if (args.length==1){
host = args[0];
}
if (args.length==2){
host = args[0];
port = new Integer(args[1]).intValue();
}
}
// dichiaro un buffer Reader
BufferedReader inFromUser = new BufferedReader(new InputStreamReader(System.in));

try{
// connetto il socket locale

Socket clientSocket = new Socket(host,port);

System.out.println("Connessione accettata dal server ");
// crea un outputStream connesso allo stram di output del socket
DataOutputStream outToServer = new DataOutputStream(clientSocket.getOutputStream());

// creo un buffer di lettura dal server
BufferedReader inFromServer = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));


// prelevo la frase dallo standard input
frase = inFromUser.readLine();

// passo la frase al server
outToServer.writeBytes(frase + '\n');

// recupero la frase modificata dal server
fraseModificata = inFromServer.readLine();

System.out.println(fraseModificata);

// chiudo il socket
clientSocket.close();

}catch(UnknownHostException e){
System.out.println("Host sconosciuto : "+e.getMessage());
}catch(IOException e){
System.out.println("Problema con il server : "+e.getMessage());
}

}
}


IMPLEMENTAZIONE UDP
Il protocollo UDP non fornisce una connessione tra client e server, il server infatti esplicitamente si attacca ad un indirizzo IP ed una porta di un client del destinatario. Il server deve estrarre l'indirizzo IP e la porta del mittende ricavandoli dal datagram ricevuto.
Vediamo l'implementazione del server UDP:
public class UDPServer {
public static final int PORT = 6769;

public static void main(String[] args) {

try {

// creo il socket
DatagramSocket serverSocket = new DatagramSocket(PORT);

byte[] sendData = new byte[1024];
byte[] receiveData = new byte[1024];

while (true){

// istanzio il datagramma in input
DatagramPacket receivePacket = new DatagramPacket(receiveData,receiveData.length);

// ricevo il datagramma dal socket
serverSocket.receive(receivePacket);

// recupero la frase
String frase = new String(receivePacket.getData());

// indirizzo ip
InetAddress iPAddress = receivePacket.getAddress();

// porta
int port = receivePacket.getPort();

// modifico la porta
String fraseModificata = frase.toUpperCase();

// trasformo in byte
sendData = fraseModificata.getBytes();

// creo un datagramma per inviare al client
DatagramPacket sendPacket = new DatagramPacket(sendData,sendData.length,iPAddress,port);

//scrivo il datagramma sul socket
serverSocket.send(sendPacket);
}
}catch(SocketException e){
System.out.println("Problemi nell'apertura del socket "+e.getMessage());
}catch(IOException e){
System.out.println("Problemi di I/O : "+e.getMessage());
}
}

}


Ecco invece l'implementazione del corrispettivo client
/***
* Classe per la gestione del client UDP
*
* @author Alessandro Franzi
*
*/
public class UDPClient {
public static final String HOSTNAME = "localhost";
public static final int PORT = 6769;

public static void main(String[] args) {
String host ="";
int port = PORT;

if (args.length==0){
System.out.println("Parametri di default.");
System.out.println("Host : "+HOSTNAME);
host = HOSTNAME;
}else{
if (args.length==1){
host = args[0];
}
if (args.length==2){
host = args[0];
port = new Integer(args[1]).intValue();
}
}

// buffer di input del client
BufferedReader inFromUser = new BufferedReader(new InputStreamReader(System.in));

try{

// creo il socket client
DatagramSocket clientSocket = new DatagramSocket();

// creo l'indirizzo ip
InetAddress IPAddress = InetAddress.getByName(host);

byte[] sendData = new byte[1024];
byte[] receiveData = new byte[1024];

String frase = inFromUser.readLine();

sendData = frase.getBytes();

// creo un datagramma con i dati da inviare, lunghezza, ip e porta
DatagramPacket sendPacket = new DatagramPacket(sendData,sendData.length,IPAddress,port);

// invio il datagramma al server
clientSocket.send(sendPacket);

// credo un pacchetto
DatagramPacket receivePacket = new DatagramPacket(receiveData,receiveData.length);

clientSocket.receive(receivePacket);

// frase ricevuta dal server
String fraseModificata = new String(receivePacket.getData());

System.out.println("Frase ricevuta : "+fraseModificata);

// chiudo il socket
clientSocket.close();
}catch(SocketException e){
System.out.println("Problemi con il socket : "+e.getMessage());
}catch(UnknownHostException e){
System.out.println("Host sconosciuto : "+e.getMessage());
}catch (IOException e){
System.out.println("I/O exception : "+e.getMessage());
}
}
}


Spero che questi esempi siano sufficienti a chiarire il funzionamento dei socket in Java. Per qualsiasi perplessità o chiarimento non esitate a contattarmi. Alla prossima.

12 dicembre 2008

Ordinare ArrayList in Java

In Java è indispensabile a volte dover ordinare collezioni di oggetti in maniera "custom". Se ad esempio avessimo un ArrayList di stringhe e volessimo ordinare tale collezione in ordine lessicologico, basterebbe richiamare il metodo : Collections.sort(arrayList);.
Tale operazione però su altri tipi di oggetti non funziona, poichè bisogna definire i criteri in base ai quali tale collezione andrebbe ordinata.

Ci sono 2 possibili approcci per la risoluzione di questo genere di problematiche.

Modificare la classe facendole implementare l'interfaccia Comparable
E' necessario che l'oggetto che inseriamo all'interno della collection implementi l'interfaccia Comparable e quindi bisogna definire il metodo compareTo che rappresenta il metodo richiamato dal Collections.sort per poter stabilire con quale criterio ordinare.
Vediamo un esempio :
public class MyClass implements Comparable {
public int ordine;
....
public int compareTo(Object o) {
if (o!=null){
MyClass objOfMyClass = (MyClass)o;
// qui ci arriva solo se i titoli sono uguali o un titolo è nullo quindi confronto i codici edizione
if (ordine>objOfMyClass.ordine){
return 1;
}else{
if (ordine==objOfMyClass.ordine){
return 0;
}
}
}
return -1;
}
}

Per poter ordinare L'ArrayList quindi basta richiamare il metodo:
List arrayList = new ArrayList ();
arrayList.add(myObj1);
arrayList.add(myObj2);
.....
Collections.sort(arrayList);


Creare una classe che implementa Comparator
Questo metodo necessita la creazione di una classe che funge da "Comparator" tra 2 oggetti. E' necessario quindi che tale classe implementi il metodo compare mediante cui il metodo Collection.sort riesce a stabilire l'ordine richiamandolo con i 2 oggetti che intende confrontare.
Vediamo un esempio :
public class OrderComparator implements Comparator {
public int compare(Object obj1, Object obj2) {
if (obj1!=null && obj1!=null){
MyClass objOfMyClass1 = (MyClass)obj1;
MyClass objOfMyClass2 = (MyClass)obj2;
if (objOfMyClass1.ordine>objOfMyClass2.ordine){
return 1;
}else{
if (objOfMyClass1.ordine==objOfMyClass2.ordine){
return 0;
}
}
}
return -1;
}
}

Per poter ordinare L'ArrayList quindi basta richiamare il metodo:
List arrayList = new ArrayList ();
arrayList.add(myObj1);
arrayList.add(myObj2);
.....
Collections.sort(arrayList,new OrderComparator () );

Naturalmente è possibile definire una serie di comparator da utilizzare quando e come si vuole, in maniera tale da poter avere a disposizione diversi criteri di ordinamento per uno stesso tipo di Classe.

10 dicembre 2008

Assert in Java : cosa sono e come configurare Eclipse o MyEclipse per poterle utilizzare

Mi è capitato in questi giorni di aver a che fare con CheckStyle, un progetto openSource che ho scaricato da sourceForge ed ho importato come Java Project nel mio Eclipse.
Mi sono ritrovato davanti ad una serie di errori provocati da alcune istruzioni del tipo assert line != null; la cui sintassi mi era del tutto sconosciuta.
Non ero l'unico a cui era sconosciuta... anche il mio Eclipse segnava tutte le occorrenza di questa keyword con un errore.

Cosa sono le assert?
Le assert sono istruzioni che verificano la verità di una condizione booleana, e provocano la terminazione del programma (mediante il lancio di un AssertionError) nel caso in cui tale condizione risulti falsa. Ad esempio l'istruzione:
assert a + b > 0;

provocherà la terminazione del programma qualora la somma dei valori a e b sia uguale o inferiore a zero. In prima istanza, la semantica dell'assert si riconduce ad una forma compatta per espressioni del tipo:
if(!(a+b>0))
throw new AssertException();

Da un punto di vista tecnico, il costrutto delle assert prevede la possibilità di disabilitare in blocco il controllo delle condizioni: come vedremo più avanti, le assert vengono usate essenzialmente in fase di test e debugging: durante la normale esecuzione, è possibile disabilitarle, eliminando in tal modo l'overhead legato alla loro gestione. Ma esiste anche una differenza assai sottile sul piano filosofico, che rende l'assert un qualcosa di completamente diverso da qualunque altro costrutto presente in Java. Contrariamente a quanto avviene con i costrutti standard dei linguaggi imperativi, una assert non rappresenta un ordine, ma un punto di vista: essa indica una condizione che il programmatore ritiene debba essere vera in un determinato momento dell'esecuzione di un programma. La violazione di tale condizione causerà la terminazione del programma, dal momento che si è verificato qualcosa che il programmatore non aveva previsto. Mai e in nessun caso una assert potrà contenere direttive che influenzino la normale esecuzione del programma.

Come configurare Eclipse o MyEclipse per compilare le assert
Nella versione 3.1 o successive di Eclipse (Per MyEclipse dalla versione 6.0), è necessario accedere al menù Window -> Preferences -> Java -> Compiler e settare per quanto riguarda il gruppo JDK Compliance il Compiler Compliance Level su 1.4 o 5.0 in base alla versione desiderata e selezionare le seguenti voci :

Compiler Compliance Level: 1.4 (o 5.0)
Use default compliance settings : non Selezionata
Generated .class files compatibility: 1.4 (o 5.0)
Source compatibility: 1.4 (o 5.0)
Disallow identifiers called 'assert': Error
.....

In questo modo eclipse dopo aver eseguito il rebuild del progetto riconoscerà tali costrutti e vi permetterà di utilizzarli e compilarli.

25 novembre 2008

Come clonare un oggetto Java

Un giorno mi sono imbattuto in un problema alquanto strano, in cui magari si sarà imbattuto chi, come me, ha iniziato a programmare in linguaggi come C o C++ in cui la referenza agli oggetti e il relativo passaggio parametri per valore o per riferimento essendo esplicito era facilmente gestibile.
In Java tutti gli oggetti vengono passati per riferimento, tranne alcuni come le Stringhe ad esempio, per cui ci si può imbattere nell'utilizzo di uno stesso bean in più contesti. Il problema si presenta nel caso in cui venga modificato il valore di qualche proprietà del bean in una parte remota del codice, quindi in tutti i contesti lo stesso viene utilizzato presenterà tali informazioni variate. In alcuni casi è necessario quindi eseguire una copia dell'oggetto. Per questo ci torna utile il metodo Clone.

Il metodo clone per gli Object è di tipo protected, quindi richiamare il metodo clone su un qualsiasi oggetto porta ad un problema di compilazione. E' necessario in primo luogo ridefinire il metodo clone , in secondo luogo implementare l'interfaccia Clobeable in modo tale da non incorrere in un eccezione di tipo CloneNotSupportedException. Il codice corretto risulta quindi:
class A implements Cloneable {
private int x;
public A(int i) {
x = i;
}
public Object clone() {
try {
return super.clone();
}
catch (CloneNotSupportedException e) {
throw new InternalError(e.toString());
}
}
public int getx() {
return x;
}
}

public class CloneDemo3 {
public static void main(String args[])
throws CloneNotSupportedException {
A obj1 = new A(37);
A obj2 = (A)obj1.clone();
System.out.println(obj2.getx());
}
}



09 novembre 2008

Contare righe di codice di un progetto Java

Potrebbe tornare utile, forse più per questioni di esaltazione personale che per reale utilità, uno script mediante il quale avere un elenco delle righe di codice di un determinato progetto.

Il seguente script oltre a fornire un elenco completo dei files, con relativo numero di codice, esegue anche la somma degli stessi.
find . \( -name '*.java' -o -name '*.jsp' \) -print0 | xargs -0 wc -l

E' possibile modificare il riferimento al path cambiando il "." dopo il comando con qualsiasi percorso assoluto o relativo.
E' inoltre possibile adattarlo a qualsiasi linguaggio, cambiando i filtri relativi all'estensione del file "*.java" o "*.jsp".

07 novembre 2008

Come creare un Singleton in Java o Php

Il Singleton è un design pattern creazionale, il suo scopo è assicurare che una classe abbia una sola istanza e fornire un punto di accesso globale a tale istanza. Questo può essere usato ad esempio per caricare delle informazione da database e gestirle durante tutto il flusso dell'applicazione senza doverle necessariamente ricaricare. Un suo utilizzo ponderato può renderne l'utilizzo davvero vantaggioso.

Guardiamo un esempio di Singleton in Java. Il codice presenta un metodo opzionale : invalidate
che invalida la prima istanza della classe, forzandone la creazione di una nuova istanza nel successivo accesso alla classe.
public class Singleton {
private static Singleton instance;

public static Singleton getInstance(){
if (instance == null) instance = new Singleton();
return instance;
}

public static void invalidate(){
instance = null;
// si possono invalidare eventuali altre proprietà della classe
}

private Singleton(){
// qui si eseguono le eventuali operazioni di inizializzazione delle informazioni necessarie
}
}

Ecco un implementazione alternativa in PHP.
/**
* Semplice implementazione del design pattern singleton.
* Data la struttura sintattica di PHP non è possibile sfruttare l'ereditarietà
* per poter riutilizzare il pattern, se non utilizzando piccoli stratagemmi sintattici.
*/
class MySingleton
{
/**
* La variabile statica privata che conterrà l'istanza univoca
* della nostra classe.
*/
private static $instance = null;

/**
* Il costruttore in cui ci occuperemo di inizializzare la nostra
* classe. E' opportuno specificarlo come privato in modo che venga
* visualizzato automaticamente un errore dall'interprete se si cerca
* di istanziare la classe direttamente.
*/
private function __construct()
{
// ... codice ...
}

/**
* Il metodo statico che si occupa di restituire l'istanza univoca della classe.
* per facilitare il riutilizzo del codice in altre situazioni, si frutta la
* costante __CLASS__ che viene valutata automaticamente dall'interprete con il
* nome della classe corrente (ricordo che "new $variabile" crea un'istanza della classe
* il cui nome è specificato come stringa all'interno di $variabile)
*/
public static function getInstance()
{
if(self::$instance == null)
{
$c = __CLASS__;
self::$instance = new $c;
}

return self::$instance;
}
}

02 novembre 2008

java.lang.IllegalThreadStateException problema restart di un thread java

In alcuni casi è necessario dover gestire un numero di thread paralleli considerevole.
Si noti che in queste situazioni si presenta un deterioramento delle prestazioni dovuto principalmente al costo computazione di creazione e distruzione dei thread.
In questi casi è opportuno sviluppare una strategia tale da riuscire a riutilizzare i thread senza crearne altri. Il problema è che in Java, dopo aver creato un thread non è possibile riavviare la propria esecuzione mediante il metodo start().

Ecco un esempio di come creare un thread, escogitando un metodo per poterne gestire il "re"start.
public class MyThread extends Thread{ 
public Integer semaforoThread = new Integer(0);
// metodo run
public void run(){
try{
while(true){
// codice da far eseguire al thread
....

synchronized(semaforoThread){
semaforoThread.wait();
}
}
}catch(InterruptedException e){
System.out.println("Problemi con il thread : "+e.getMessage());
}
}
// metodo per far ripartire il thread
public void restart(){
synchronized (this.semaforoThread) {
this.semaforoThread.notify();
}
}
}

Questo è il codice per poter utilizzare tale thread :
MyThread thread = new MyThread();
thread.start();
....
// si intende restartare il thread
thread.restart();

13 ottobre 2008

Ottenere il numero di righe recuperate da un ResultSet

Una domanda che prima o poi uno sviluppatore Java si pone è : come posso recuperare il numero di risultati di una query da un java.sql.ResultSet.
La risposta potrebbe sembrare non banale, ma lo è in quanto di default i ResultSet vengono istanziati in maniera sequenziale, ma ciò può essere modificato mediante un opportuna dichiarazione, con cui è possibile accedere ai dati in maniera non sequenzioale. E' possibile infatti muoversi anche in senso opposto, oppure accedere direttamente ad una riga.

Vediamo un esempio di codice:
PreparedStatement pstmt4 = medpConnection.prepareStatement(
"SELECT * FROM USERS",
ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_READ_ONLY);

Vediamo ora come si può recuperare il numero di righe ottenute come risultato dalla query:
ResultSet rs4 =  pstmt4.executeQuery();
rs4.last();
int numberList = rs4.getRow();
rs4.beforeFirst();

In questo modo si posiziona il ResultSet in ultima riga ottenuta, si recupera il numero della riga e si riposiziona il ResultSet nella posizione iniziale, cioè prima del primo risultato.
In tal modo è possibile utilizzare il ResultSet in un semplicissimo ciclo del tipo:

while (rs4.next()) {
...
}

10 ottobre 2008

Eseguire un unica istanza di un'applicazione Java

Tempo fa mi sono imbattuto nel dilemma su come gestire in Java l'esecuzione di una sola istanza di una determinata applicazione, la rete mi è stata d'aiuto proponendomi una serie di soluzioni.
Una soluzione trovata è quella di recuperare tale informazione dall'elenco dei Thread in esecuzione, ma a parer mio è la peggiore.

Un altra soluzione è quella di istanziare un Server Socket su una determinata porta nel main dell'applicazione, nel caso in cui tale porta sia stata già Bindata, significa che si tratta della seconda istanza della stessa applicazione.

Una terza la soluzione, la migliore a pare mio è quella Unix Like in cui si esegue il lock su di un file durante l'esecuzione e sbloccare il lock al termine della stessa.

Di seguito l'esempio sul mio esempio di implementazione :

public class JustOneLock {
    private String appName;
    private File file;
    private FileChannel channel;
    private FileLock lock;

    public JustOneLock(String appName) {
        this.appName = appName;
    }

    public boolean isAppActive() {
        try {
            file = new File(System.getProperty("user.home"), appName + ".tmp");
            channel = new RandomAccessFile(file, "rw").getChannel();
            try {
                lock = channel.tryLock();
            } catch (OverlappingFileLockException e) {
                // il file è già loccato
                closeLock();
                return true;
            }

            if (lock == null) {
                closeLock();
                return true;
            }

            Runtime.getRuntime().addShutdownHook(new Thread() {
                    // distrugge il lock quando la JVM viene chiusa
                    public void run() {
                        closeLock();
                        deleteFile();
                    }
                });
            return false;
        }
        catch (Exception e) {
            closeLock();
            return true;
        }
    }

    // metodo per chiudere il lock sul file
    private void closeLock() {
        try { 
            lock.release();  
        }catch (Exception e) {  
            
        }
        try { 
            channel.close(); 
        }
        catch (Exception e) {  
        }
    }
    
    // metodo per cancellare il file di lock
    private void deleteFile() {
        try { 
            file.delete(); 
        }
        catch (Exception e) { 
        }
    }
}


Vediamo ora come utilizzare tale classe :
JustOneLock ua = new JustOneLock("JustOneId");
 
if (ua.isAppActive()) {
    System.out.println("Applicazione già in esecuzione. ");
}else {
    // caso in cui si tratta della prima istanza dell'applicazione
        System.out.println("Applicazione eseguita");
}

Spero che questo esempio sia stato utile. Alla prossima

09 ottobre 2008

Centrare a video una componente AWT o SWING

Ecco un semplice hint per centrare a video un qualsiasi componente Swing o Awt come ad esempio un frame.
public static void centra(Component componente){
Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
componente.setLocation((int) (dim.getWidth() - componente.getWidth()) / 2,
(int) (dim.getHeight() - componente.getHeight()) / 2);
}


N.B. Il meotod accetta un oggetto di tipo Component che non è altro che la classe da cui derivano le componenti Awt e Swing.
Si potrebbe quindi creare un metodo apposito per ogni tipo di componente, altrimenti accettare in input un Component e gestire in maniera generica ogni tipo di oggetto che ne deriva.

06 ottobre 2008

Server RMI + Registry + Client Rmi

Java Remote Method Invocation (Java RMI) non è altro che una tecnologia Java per la gestione degli oggetti distribuiti. Nell' RMI i metodi di un oggetto remoto possono essere invocati dal client ubicato anche su un altra istanza di Java Virtual Machine.
La potenza è proprio nel fatto di poter recuperare un istanza ben precisa di oggetto senza avere a disposizione l'implementazione di tale classe e invocarne i metodi mediante lo stub messo a disposizione dal server.

Ho utilizzato RMI per un progetto universitario e la mia difficoltà è stata quella di recuperare informazioni chiare e dettagliate su come realizzare un Server in grado di istanziare un Registry.
Il Registry non è altro che una sorta di elenco telefonico che converte una determinata stringa (ES: rmi://localhost/nomeOggetto ) in una vera e propria istanza di tale oggetto remoto.
Esaminiamo i package che ci fornisce Java per tale architettura:
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;

Passiamo alla vera e propria implementazione :
public class Runner{
public static int DEFAULT_PORT = 6669;

// main
public static void main(String[] args) throws RemoteException{
int port = DEFAULT_PORT;
if (args.length>0){
try{
port = Integer.parseInt(args[0]);
}catch(NumberFormatException ex){
System.out.println("Formato utilizzato per il numero di porta non valido ");
}
}
try{
Registry registry = LocateRegistry.getRegistry(port);
bindObjectInRegistry(registry);
}catch(RemoteException ex){
System.out.println("Registry non raggiungibile ");
try{
Registry registry = LocateRegistry.createRegistry(port);
System.out.println("Creato registry in ascolto sulla porta : "+port);
bindObjectInRegistry(registry);
}catch(RemoteException e){
System.out.println("Problemi nel creare il registry");
}
}
}
/***
* Metodo che esegue il bind di un oggetto su un registry passatogli in input
*
* @param registry : registry allocato
* @throws RemoteException : eccezione remota
*/
private static void bindObjectInRegistry(Registry registry) throws RemoteException{
CentralizedSrvIF remoteReference =
(CentralizedSrvIF) UnicastRemoteObject.exportObject(new CentralizedSrv());
registry.rebind(CentralizedSrvIF.SHAREDOBJECTNAME, remoteReference);
System.out.println("Oggetto registrato");
}
}

Osserviamo che il server tenta di linkarsi al registry e nel caso in cui non trova un istanza dello stesso lo istanzia su una porta di default (nel caso in cui non venga specificata da riga di comando).
Attenzione CentralizedSrvIF non è altro che l'implementazione dell'interfaccia dell'oggetto remoto come da esempio :
public interface CentralizedSrvIF extends Remote{
// nome dell'oggetto remoto
public static final String SHAREDOBJECTNAME ="chatRoomManager";
// metodo per entrare in una chatRoom
public UserID[] joinChat(String chatRoomName, String userName, Integer port) throws UserAlreadyExistException,RemoteException,UnknownHostException;
// metodo per abbandonare la chatroom
public void leaveChat(String chatRoomName, UserID theUser) throws RemoteException;
}

La relativa implementazione è una classica implementazione custom dei metodi specificati nell'interfaccia.
Passiamo ora all'implementazione del client :
public class ChatClient {
public static final String SHAREDOBJECTNAME ="chatRoomManager";

try{

// connetto al server e recupero l'oggetto
String name = "rmi://"+host+":"+portRmi+"/"+SHAREDOBJECTNAME;
System.out.println("Bindato oggetto : "+name);
Remote obj = Naming.lookup(name);
CentralizedSrvIF chatServer = (CentralizedSrvIF)obj;
System.out.println("Connesso al server");
....
// chiamo il metodo remoto
UserID[] sdd = chatServer.joinChat(chatRoomName, userName, new Integer(port));
System.out.println("Connesso alla chatRoom : "+chatRoomName);
// rebindo l'oggetto salvandone le modifiche
Naming.rebind(name, obj);
}catch(MalformedURLException e){
System.out.println("Problema con l'url : "+e.getMessage());
}catch(IOException e){
System.out.println("Problema in input/output :"+e.getMessage());
}catch(NotBoundException e){
System.out.println("Problema con il lookup : "+e.getMessage());
}catch(UserAlreadyExistException ex){
System.out.println("Problema : "+ex.getMessage());
}
}
}

Prestare particolare attenzione al metodo Rebind, con esso si sostituisce l'istanza bindata mediante il registry, sovrascrivendo lo stato dell'oggetto con il nuovo stato dell'istanza corrente.
Spero che questo esempio sia servito a chiarire i vostri dubbi nei confronti dell' RMI.