Newsgroup: it.comp.java
Titolo: problemi con client-server e objectInputStream e objcetOutputStream

salve a tutti,
ho qualche problema con un progetto che sto realizzando. Si tratta di
un'applicazione client server che si poggia su database access.
Riscontro dei problemi nell'invio di oggetti fra client e server
utilizzando gli objectOutputStream e objectInputStream.
Il tutto è fatto con le socket e senza rmi.
In pratica ho una classe in cui è contenuta l'interfaccia grafica con
2 campi: username e password. Sull'action listener del bottone ok viene
chiamato il costruttore della classe Utente passando i due valori e poi
viene richiamato il metodo Login() sempre della classe utente!
===============================================================================
username = usert.getText();
password = passt.getText();
Utente verifica = new Utente(username,password);
verifica.Login();
===============================================================================
Questo metodo della classe utente nel package client dovrebbe spedire
l'oggetto al server appunto con un objectOutputStream questo è il
codice:
===============================================================================
public Utente Login(){
	try{
		String address = "127.0.0.1";
		Socket sock = new Socket(address,port);
		outSt=sock.getOutputStream();
		dout = new DataOutputStream(outSt);
		dout.writeInt(1);
		ObjectOutputStream oos = new
ObjectOutputStream(sock.getOutputStream());
		oos.writeObject(new Utente(this.Username,this.Password));
		System.out.println("Oggetto inviato al server");
		ObjectInputStream ois = new ObjectInputStream(sock.getInputStream());
		Utente utenteRicevuto = (Utente) ois.readObject();
		return utenteRicevuto;
	}catch(Exception e){
		System.out.println(""+e);
		return null;
	}
}
===============================================================================
Questo è il pezzo di codice della classe server interessata:
===============================================================================
public Server (ServerSocket s)throws IOException{
	System.out.println("Creata nuova istanza della classe server");
	this.sock = s.accept();
	Thread t = new Thread(this);
	t.start();
}
public void run(){
System.out.println("nuovo thread");
try{
	inSt= this.sock.getInputStream();
	din= new DataInputStream(inSt);
    int i= din.readInt();
    System.out.println("Codice letto: "+i);//funziona fin qui!
    switch(i){
	case 1: // funzione di login
		ObjectInputStream ois = new ObjectInputStream(sock.getInputStream());
		Utente utenteRicevuto = (Utente) ois.readObject();
		utenteRicevuto.Login();
		oout.writeObject(utenteRicevuto);
	break;
===============================================================================
Questo è il del metodo login della classe utente nel package server
che dovrebbe accedere al db e controllare se esiste un utente con i
dati inseriti!
===============================================================================
public Utente Login(){
	try{
		Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
		Connection conn = DriverManager.getConnection("jdbc:odbc:Test");
		Statement st = conn.createStatement();
		PreparedStatement pstmt = conn.prepareStatement("SELECT * FROM
Utenti WHERE Username = ?AND Password = ?");
		pstmt.setString(1, this.Username);
		pstmt.setString(2, this.Password);
		ResultSet rs = pstmt.executeQuery();
		rs.next();
		Utente ut = new Utente();
		ut.IdUtente = rs.getInt("IdUtente");
		ut.Username = rs.getString("Username");
		ut.Password = rs.getString("Password");
		ut.Cognome = rs.getString("Cognome");
		ut.Nome = rs.getString("Nome");
		conn.close();
		return ut;
	}catch(ClassNotFoundException e){
		return null;
	}catch(SQLException se){
		return null;
		}
	catch(NullPointerException npe){
		System.out.println(""+npe);
		return null;
		}
	}
===============================================================================
Il tutto funziona fin quando il server legge l'intero che corrisponde
al codice dell'azione da eseguire. Dopo mi parte un loop infinito fin
quando non si esaurisce la memoria della jvm!
Dove è l'errore cosa sbaglio? Scusate gli errori banali ma sono alle
prime armi!
Ciao e grazie,
Luigi

 
Non puoi mescolare l'uso di ObjectStream con DataStream. Hai due soluzioni:
- usare solo ObjectStream passando l'intero come oggetto (new Integer(x))
- usare solo DataStream serializzando l'oggetto prima di passarlo e 
deserializzandolo in arrivo (vedi ad esempio qui: 
http://tinyurl.com/m3jdg
 )
Ciao,
     Bruno
P.S.
Il JavaDay č qui! Il 7 Luglio a Torino!
http://www.javaday.it
-- 
| Bruno Bossola
| A Java Software Engineer :-)
| 
http://digilander.iol.it/bbossola
| Coordinatore del JUG Torino - 
http://www.jugtorino.it

 deserializzandolo in arrivo (vedi ad esempio qui: 
http://tinyurl.com/m3jdg
 )
Ciao e grazie per la tua risposta!
ho seguito il tuo consiglio e ho tolto tutti i DataStream e deciso di
utilizzare solo objectStream.
Quando invio l'integer mi funziona e viene letto, tutto ok!
Quando invece provo ad inviare un oggetto della classe utente ricevo un
errore quando faccio il cast!
In pratica facendo
Utente daVerificare = (Utente)oin.readObject();
viene generata un'eccezione ClassCastException.
Sono sicuro che l'oggetto venga inviato sul server perchè ho provato a
leggere un oggetto generico e a stamparlo e mi viene stampato.
Ho scritto
Object o = oin.readObject();
System.out.println("Oggetto letto: "+o.toString());
e va tutto ok. Pensavo che il problema fosse dovuto alle due classi "un
pò" diverse allora ho provato a fare una nuova classe da utilizzare
solo per il trasferimento e perfettamente uguale sia sul client sia sul
server. Ho instanziato un oggetto e l'ho trasmesso. Quando ho fatto il
cast mi viene generato lo stesso errore!
Qualche suggerimento?
Ciao e grazie,
Luigi

 > ...pensavo che il problema fosse dovuto alle due classi
 > "un pò" diverse allora ho provato a fare una nuova classe
 > da utilizzare > solo per il trasferimento e perfettamente
 > uguale sia sul client sia sul server.
 >
Le classi presenti sul server e sul client devono essere _uguali_, 
ovvero deve essere la stessa classe. Inoltre la classe deve essere 
serializzabile.
Non vedo problemi nell'approccio. Posta il codice e lo stacktrace, vediamo.
Ciao,
     Bruno
P.S.
Il JavaDay è qui! Il 7 Luglio a Torino!


 Non vedo problemi nell'approccio. Posta il codice e lo stacktrace, vediamo.
Ciao,
grazie ancora per il tuo interesse!
Provo a spiegare la mia situazione così forse è più semplice. Il
programma lo sto realizzando con eclipse ed ho un package client ed uno
server ed in entrambi la classe Utente. Per poter inviare semplicemente
tutti i dati relativi ad un utente ho pensato di usare la
serializzazione tuttavia i metodi delle due classi sono diversi. Faccio
un piccolo esempio. Il metodo login della classe utente sul client non
dovrebbe far altro che inviare un oggetto di tipo utente al server con
i campi user e pwd settati con i valori inseriti dall'utente nella
finestra; il metodo login della stessa classe sul server, invece,
dovrebbe accedere al database e controllare l'esattezza per poi
restituire un oggetto di tipo utente con i valori presi dal db.
Credi sia una soluzione giusta?
Alla luce di quanto mi hai detto nell'ultimo post ho creato un'altra
classe perfettamente uguale sia nel package cliente sia in quello
server chiamandola UtenteDaInviare. L'idea sarebbe quella di utilizzare
oggetti di questa classe solo per il trasporto da un processo
all'altro. Credi sia conveniente?
Il serialVersionUID = 1 l'ho impostato io perchè quello generato
automaticamente da eclipse era diverso per la stessa classe ma
posizionata in un pkg diverso! E se introducessi un terzo ppakage per
le classi comuni sia al client  che al server?
in ogni caso ricevo ancora lo stesso identico errore. Ti posto il
codice di quest'ultima versione!
Classe server (pkg server):
===============================================================================
package server;
import java.net.*;
import java.io.*;
public class Server implements Runnable {
	final static int port = 7777;
	public  Socket sock =null;
	private ObjectInputStream oin;
	private ObjectOutputStream oout;
	private InputStream inSt;
	private OutputStream outSt;
	public static void main(String args[])throws IOException{
	ServerSocket ss= new ServerSocket(port);
	while(true){
		System.out.println("Prima di accept");
		Server serv = new Server(ss);
		}
	}
public Server (ServerSocket s)throws IOException{
	System.out.println("Creata nuova istanza della classe server");
	this.sock = s.accept();
	Thread t = new Thread(this);
	t.start();
}
public void run(){
System.out.println("nuovo thread");
try{
	inSt= this.sock.getInputStream();
	oin = new ObjectInputStream(inSt);
	Integer codiceLetto = new Integer(0);
	codiceLetto = (Integer) oin.readObject();
	outSt = this.sock.getOutputStream();
	oout = new ObjectOutputStream(outSt);
	 int i = Integer.parseInt(codiceLetto.toString());
   switch(i){
	case 1: // funzione di login
		UtenteDaInviare daVerificare = new UtenteDaInviare();
		daVerificare = (UtenteDaInviare)oin.readObject();
		/*non ho messo l'atro codice perchè qui non ci arriva mai
		l'idea cmq è quella di creare un oggetto utente, chiamare il metodo
login
		e scrivere sullo stream di output un nuovo UtenteDaInviare creato a
partire dall'utente
		risultato del login.
			*/
	break;
	//poi dobbiamo scriver le altre funzionalità del server
    		}
    }
catch(ClassCastException ce){
	System.out.println("Eccezione server: "+ce);
	ce.printStackTrace();
}
catch(Exception e){
	System.out.println("Eccezione server: "+e);
	e.printStackTrace();
}
	}
}
===============================================================================
classe utente (pkg server):
===============================================================================
package server;
import java.io.*;
import java.sql.*;
public class Utente implements Serializable{
	private static final long serialVersionUID = 1;
	private int IdUtente;
	private String Username;
	private String Password;
	private String Cognome;
	private String Nome;
	private int Matricola;
	private int Livello;
	private int Tipo;
public Utente(String u, String p)
	{
		Username = u;
		Password = p;
System.out.println("Creato nuovo oggetto utente da costruttore 2");
	}
public Utente()
	{}
public Utente Login(){
	try{
		System.out.println("Sono nel metodo login (classe utente, pkg
server)");
		Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
		Connection conn = DriverManager.getConnection("jdbc:odbc:Test");
		Statement st = conn.createStatement();
		PreparedStatement pstmt = conn.prepareStatement("SELECT * FROM
Utenti WHERE Username = ? AND Password = ?");
		pstmt.setString(1, this.Username);
		pstmt.setString(2, this.Password);
		ResultSet rs = pstmt.executeQuery();
		rs.next();
		Utente ut = new Utente();
		ut.Username = rs.getString("Username");
		ut.Password = rs.getString("Password");
		if
((ut.Username.equals(this.Username))&&(ut.Password.equals(this.Password)
)){
			conn.close();
			return ut;}
		else{
			conn.close();
			return null;}
	}catch(ClassNotFoundException ce){
		ce.printStackTrace();
		return null;
	}catch(SQLException se){
		se.printStackTrace();
		return null;
		}
	catch(NullPointerException npe){
		System.out.println(""+npe);
		npe.printStackTrace();
		return null;
		}catch(Exception e){
			System.out.println(""+e);
			e.printStackTrace();
			return null;}
}
public String getNome(){
	return Nome;
	}
public String getCognome(){
	return Cognome;}
public String getUser(){
	return Username;}
public String getPwd(){
	return Password;}
public int getTipo(){
	return Tipo;}
}
===============================================================================
Classe UtenteDaInviare (pkg server e client) cambia solo il nome del
package nella prima riga!
===============================================================================
package client;
import java.io.*;
public class UtenteDaInviare implements Serializable {
	/**
	 *
	 */
	private static final long serialVersionUID = 1;
	private int IdUtente;
	private String Username;
	private String Password;
	private String Cognome;
	private String Nome;
	private int Matricola;
	private int Livello;
	private int Tipo;
	public UtenteDaInviare(String u, String p)
	{
		Username = u;
		Password = p;
	}
	public UtenteDaInviare() {
	}}
===============================================================================
Classe utente (pkg client)
===============================================================================
package client;
import java.io.*;
import java.net.Socket;
public class Utente implements Serializable {
	private static final long serialVersionUID = 1;
	private int IdUtente;
	private String Username;
	private String Password;
	private String Cognome;
	private String Nome;
	private int Matricola;
	private int Livello;
	private int Tipo;
	final static int port = 7777;
	private InputStream inSt;
	private OutputStream outSt;
	private ObjectInputStream oin;
	private ObjectOutputStream oout;
	//private DataOutputStream dout;
	//private DataInputStream din;
	public Utente(String u, String p)
	{
		Username = u;
		Password = p;
	}
	public Utente()
	{
		Username = null;
		Password = null;
		Cognome = null;
		Nome = null;
		Matricola = -1;
		Livello = -1;
		Tipo = -1;
	}
	public Utente Login(){
	try{
		String address = "127.0.0.1";
		Socket sock = new Socket(address,port);
		outSt=sock.getOutputStream();
		oout = new ObjectOutputStream(outSt);
		oout.writeObject(new Integer(1));
		UtenteDaInviare daInviare = new
UtenteDaInviare(this.Username,this.Password);
		oout.writeObject(daInviare);
		inSt=sock.getInputStream();
		oin = new ObjectInputStrwam(inSt);
		UtenteDaInviare ricevuto = (UtenteDaInviare) oin.readObject();
		//altre cose varie e poi ritorna un oggetto di tipo Utente;
		return null;
	}catch(Exception e){
		System.out.println("Eccezione classe utente: "+e);
		e.printStackTrace();
		return null;
	}
}
public String getNome(){
	return Nome;
	}
public String getCognome(){
	return Cognome;}
public String getUser(){
	return Username;}
public String getPwd(){
	return Password;}
public int getTipo(){
	return Tipo;}
}
===============================================================================
Classe Gui (pkg client):
===============================================================================
package client;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Gui {
	String username, password;
	JFrame f = new JFrame("Login");
	JTextField usert=new JTextField(15);
	JPasswordField passt  = new JPasswordField(15);
	JButton ok=new JButton("OK");
	JButton an=new JButton("ANNULLA");
	JLabel userl= new JLabel("User Name:");
	JLabel passl= new JLabel("Password:");
public static void main(String[] args) {
	Gui prova = new Gui();
	prova.LoginGUI();
	}
public void LoginGUI(){
	f.setSize(300,150);
	f.setResizable(false);
	f.getContentPane().setLayout(new FlowLayout());
	f.getContentPane().add(userl);
	f.getContentPane().add(usert);
	f.getContentPane().add(passl);
	f.getContentPane().add(passt);
	f.getContentPane().add(an);
	f.getContentPane().add(ok);
	f.setVisible(true);
	f.setLocationRelativeTo(null);
	ok.addActionListener(new OkListener());
	an.addActionListener(new AnnListener());
	}
public class OkListener implements ActionListener
{
	public void actionPerformed(ActionEvent arg0) {
		if(usert.getText().equals("") ||passt.getText().equals(""))
			JOptionPane.showMessageDialog(f,"Digitare nome utente e password!");
		else{
			username = usert.getText();
			password = passt.getText();//il metodo corretto è getPassword ma
restituisce un array di caratteri
			Utente verifica = new Utente(username,password);
			Utente risultato = verifica.Login();
			f.setVisible(false);
			if (risultato !=null){
				JOptionPane.showMessageDialog(f,"Connessione effettuata
correttamente!");
				//qui scrivere cosa fare dopo il login andato a buon fine
				}
			else{
				JOptionPane.showMessageDialog(f,"Nome utente o password non
corretti!");
				usert.setText("");
				passt.setText("");
				f.setVisible(true);
			}
		}
	}
}
public class AnnListener implements ActionListener
{
	public void actionPerformed(ActionEvent arg0)
	{
		System.exit(-1);
	}
}
}
===============================================================================
Lo stacktrace è il seguente:
java.lang.ClassCastException
	at server.Server.run(Server.java:55)
	at java.lang.Thread.run(Unknown Source)
dove la riga 55 è: daVerificare = (UtenteDaInviare)oin.readObject();
Un'ultima domanda: quale potrebbe essere una soluzione per evitare il
passaggio per la classe UtenteDaInviare?
E' la mia prima esperienza con Java, non so se sto seguendo la strada
giusta e scusate se ci sono errori banalissimi!
Intanto ciao e grazie mille per l'interesse!
Luigi

La soluzione di usare un value object (cosi si chiama) per veicolare le 
informazioni fra client e server è corretta. La classe di quell'oggetto 
deve pero' restare nello stesso package (il package è parte integrante 
della classe!). Definisciti quindi, come già hai intuito, un package 
common, ad esempio, in cui metti queste classi "di trasporto": è l'unico 
modo per farlo funzionare.


 modo per farlo funzionare.
Ciao,
volevo solo farti sapere che la soluzione suggerita funziona alla
grande e finalmente sono riuscito a fare tutto come volevo!
Ciao e grazie,
Luigi