Newsgroup: it.comp.java
Titolo: Problema Servlet + Postgres

Salve ragazzi, avrei un problemino.
Il seguente codice
-------------------------------------
persTO = new PersonTO(id,nome,email);
persDAO = new PersonDAO();
persDAO.insert(persTO);
-------------------------------------
Inserito nell'override del metodo doGet di una servlet, mi genera una
JavaNullException su Tomcat. Premetto che le classi DAO del mio
progetto funzionano alla perfezione, mi danno problemi solamente
quando le inserisco all'interno di una servlet.
Sto sbagliando qualcosa?
Ciao a tutti
Alberto

AlbertZ ha scritto:

...sě, sicuramente stai sbagliando a non darci qualche dettaglio in 
piů...mica abbiamo la sfera di cristallo.
Almeno lo stack trace...
Lucio
-- 
Lucio Benfante
JUG Padova           
http://www.parancoe.org
 ...have a look at it!
www.jugpadova.it
 
http://www.jugevents.org


Questo è l'ABC del debugging.
  1. Leggi sullo stack trace quale è la riga del tuo codice che genera 
la NullPointerException.
  2. Vai a vedere la linea nel codice sorgente.
  3. Individua tutte le variabili il cui valore è letto nella riga 
incriminata.
  3. Per ogni variabile aggiungi un istruzione del tipo:
     System.err.println("nomevariabile:" + variabile);
Per esempio se hai:
    pippo = pluto + this.paperino + beppe.getCiccio()
Aggiungi:
   System.err.println("pluto:" + pluto);
   System.err.println("this.paperino:" + paperino);
   System.err.println("beppe:" + beppe);
   System.err.println("beppe.getCiccio():" + beppe.getCiccio());
Nota: non c'e' bisogno una riga per pippo perché quella variabile viene 
solamente scritta e non letta.
Nota 2: beppe.getCiccio() non è una variabile, ma ci siamo capiti lo stesso.
Ricompili.
Avvii l'applicazione.
Aspetti che generi l'eccezione.
Vai a leggerti prima appena prima del messaggio dell'eccezione quale 
variabile è null.
-- 
Andrea Francia
alias rm='trash' #use trash command instead of removing
rm -Rfv file     #put the file in the KDE trashcan
http://www.andreafrancia.it/trash

stack stack :D
-- 
Davide Consonni

Questo è il codice di errore che mi genera Tomcat:
------------------------------------------------------------------------
java.lang.NullPointerException
dao.PersonDAO.insert(PersonDAO.java:31)
servlet.HomeServlet.doGet(HomeServlet.java:41)
javax.servlet.http.HttpServlet.service(HttpServlet.java:689)
javax.servlet.http.HttpServlet.service(HttpServlet.java:802)
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:
39)
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:
25)
java.lang.reflect.Method.invoke(Method.java:597)
org.apache.catalina.security.SecurityUtil$1.run(SecurityUtil.java:244)
java.security.AccessController.doPrivileged(Native Method)
javax.security.auth.Subject.doAsPrivileged(Subject.java:517)
org.apache.catalina.security.SecurityUtil.execute(SecurityUtil.java:
276)
org.apache.catalina.security.SecurityUtil.doAsPrivilege(SecurityUtil.java:
162)
------------------------------------------------------------------------
Spiego brevemente la cosa, il mio è un progetto che prende in ingresso
dei parametri inseriti da una pagina html e li va ad inserire in un DB
postgres. Quando uso le classi DAO e TO nel Main per inserire un
elemento funziona tutto alla perfezione, il problema c'è solamente
quando faccio la chiamata alle classi DAO e TO all'interno della
servlet.
Non riesco proprio a capire da che cosa possa dipendere..........
Alberto

 dao.PersonDAO.insert(PersonDAO.java:31)
...visto che l'errore si verifica all'interno del tuo DAO, devi proprio 
mostrarcene il codice, se vuoi avere qualche suggerimento.
Ipotesi: sicuro che il driver JDBC sia stato caricato?
Ciao


    Lucio
si esatto posta il metodo in PersonDAO.java intorno alla linea 31
-- 
Davide Consonni

Il codice della Classe DAO funziona!!
Se volete non ho nessun problema a postarlo comunque!
Per quanto riguarda il driver JDBC ho messo il file .jar di postgres
nella cartella lib della web application. E' sufficiente fare così?

 Il codice della Classe DAO funziona!!
Con questo atteggiamento non troverai mai il baco.
Nella linea 31 arriva una variabile 'null', segui le istruzioni che ti 
ho dato e troverai qual'e'.


 Il codice della Classe DAO funziona!!
Cosa te lo fa pensare?

AlbertZ wrote:

questo dipende dall'application server e da come è configurato, non è detto
che debbano prendere le librerie solo perchè lo copi dentro la directory
lib.
-- 
Davide Consonni

Codice della classe DAO:
-------------------------------------------
package dao;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import to.PersonTO;
public class PersonDAO extends BaseDAO {
	private static String insertSQL =
		"insert into person(id,nome,email) values(?,?,?)";
	public int insert(PersonTO pers){
		int ret = 0;
		Connection conn = null;
		PreparedStatement prepStat = null;
		conn = this.getConnection();
		try {
			prepStat = conn.prepareStatement(insertSQL);
			prepStat.setLong(1, pers.getId());
			prepStat.setString(2, pers.getNome());
			prepStat.setString(3, pers.getEmail());
			ret = prepStat.executeUpdate();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		finally {
			try {
				prepStat.close();
				conn.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		return ret;
	}
}
-------------------------------------------
Nel metodo Main che ho usato per testare le classi DAO e TO ho provato
ad inserire dei dati all'interno del DB e questi vengono inseriti
senza problemi.

eh ma tu fai 
           prepStat.close();
alla riga 31, ma non è detto che sia non nullo.
ad esempio se alla linea 
conn = this.getConnection();
tira un'eccezione allora prepStat è null e tu chiami un metodo su null.
quindi controlla se la connessione è null o no e dovresti fare in modo di
trappare meglio il codice.
-- 
Davide Consonni

ma sei sicuro che dipenda da quello il problema?
Se fosse quello mi darebbe ugualmente problemi anche nel main no?

 Se fosse quello mi darebbe ugualmente problemi anche nel main no?
non sapendo come funziona  this.getConnection(); non posso dirlo.
puoi controllare se arrivato al metodo la connessione è null o no ?
-- 
Davide Consonni

Ecco come funziona il metodo getConnection:
---------------------------------------------------------------------
package dao;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class BaseDAO {
	public Connection getConnection(){
		Connection conn = null;
		String url;
		String user;
		String password;
		String driverName;
		url = "jdbc:postgresql://localhost/DB_JAVA4";
		user = " *** ";
		password = " *** ";
		driverName = "org.postgresql.Driver";
		try {
			Class.forName(driverName);
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		try {
			conn = DriverManager.getConnection(url,user,password);
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return conn;
	}
}
---------------------------------------------------------------------

 Ecco come funziona il metodo getConnection:
Finché non trovi quale variabile è null nella riga 31 l'errore non lo 
trovi piu'.



Il problema è nella gestione delle eccezioni.
Quei due catch autogenerati non vanno bene.
Quando capita un eccezione durante la chiamata a
	DriverManager.getConnection()
Semplicemente il tuo metodo restiutisce il valore di conn che è null.
Quando accadono l'eccezioni il metodo BaseDAO.getConnection() non è piu' 
in grado di restituire un risultato corretto. Dato che è così dovrebbe 
generare un'eccezione. Questo si ridurrebbe a scrivere il codice così:
public class DaoInitializationException extends Exception {
	...
}
public class BaseDAO {
	public Connection getConnection() throws DaoInitializationException {
		Connection conn = null;
		String url;
		String user;
		String password;
		String driverName;
		url = "jdbc:postgresql://localhost/DB_JAVA4";
		user = " *** ";
		password = " *** ";
		driverName = "org.postgresql.Driver";
		try {
			Class.forName(driverName);
		} catch (ClassNotFoundException e) {
			throw new DaoInitializationException(e);
		}
		try {
			conn = DriverManager.getConnection(url,user,password);
		} catch (SQLException e) {
			throw new DaoInitializationException(e);
		}
		return conn;
	}
}
Altro problema di design: dato che probabilmente getConnection() è usata 
solo dalle sotto classi perché è public e non protected. Come linea 
guida dovresti cercare di mantenere i metodi piu' privati possibili, 
questo diminuisce la possibilità che ci sia un alto accoppiamento tra i 
diversi moduli del software.



Altro problema di design che viene fuori solo dopo aver visto il codice 
di PersonDAO e BaseDAO:
Praticamente apri e chiudi la connessione per ogni operazione che fai.
Solitamente questa č un'operazione considerata costosta e in piů ti 
porta a dover gestire i casi eccezionali di inizializzazione della 
connessione tutte le volte che fai l'insert.
Uno schema migliore potrebbe essere il seguente:
  - inizializzo una volta il DAO controllando se ci sono eccezioni 
dovute alla connessioni al database o al caricamento della classe Driver.
  - utilizzo la funzione insert tutte le volte che voglio.
  - chiudo la connessione.
Il tuo DAP diventerebbe cosě:
class BaseDAO {
   private Connnection connection = null;
   /* Inizializza connection*/
   public init() throws DaoInitilizationException
   {
         String url;
         String user;
         String password;
         String driverName;
         url = "jdbc:postgresql://localhost/DB_JAVA4";
         user = " *** ";
         password = " *** ";
         driverName = "org.postgresql.Driver";
         try {
             Class.forName(driverName);
         } catch (ClassNotFoundException e) {
             throw new DaoInitializationException(e);
         }
         try {
             connection = DriverManager.getConnection(url,user,password);
         } catch (SQLException e) {
             throw new DaoInitializationException(e);
         } 	
   }
   /* Funzione usata dalle classi che ereditano da BaseDao.
   protected Connection getConnection() {
     if(connection==null) {
        throw new IllegalStateException("DAo not initiliazide yet. Call 
init() first");
     }
     return connection;
   }
   /* Libera le risorse, in particolare chiude la connection
   public close() throws SQLException {
     connection.close();
   }
}
Il PersonDAO riamne uguale solo che devi togliere la chiamata a 
conn.close().
Nel metodo che inizializza tutto, (non so qual'č, dove lo metti? nella 
main? ) devi scrivere:
   PersonDAO personDAO = new PersonDAO();
   personDAO.init();
   ...
   // uso di personDAO.insert per tutte le volte che vuoi
   ...
   personDAO.close();
Poi il codice anche cosě puň essere migliorato, per esempio sarebbe 
meglio gestire l'inizializzazione della Connection usando un pattern di 
Dependency Injection ma č meglio non mettere troppa carne al fuoco.


eh si, la gestione delle eccezioni fa rivista per forza.
e mi gioco 20 fish virtuali sulla connessione a null :D
-- 
Davide Consonni

In questi giorni ho fatto tutti i JunitTestCase di tutte le funzioni
per accedere al DB...Funzionano tutti quanti e nemmeno uno restituisce
il valore NULL.
Mi chiedevo...non è che c'è qualcosa da sbloccare nel database
Postgres o Tomcat affinchè questi due possano colloquiare?

Tra l'altro ho fatto pure un search su google ed ho visto che
purtroppo questo problema è diffuso...non è capitato solo a me:
http://forum.studentidia.org/index.php?showtopic=6173

 il valore NULL.
Se in produzione di restituisce *null* e nei test non ti restituisce 
*null* vuol dire che i test non coprono il caso della produzione.
Se non propaghi le eccezioni ti sarà difficile capire dove sta il problema.
-- 
Andrea Francia
http://www.andreafrancia.it/

 
...il fatto che sia diffuso fra studenti...non ne fa un problema diffuso :)
Albertz...quello che devi fare è individuare il punto del TUO codice che 
genera la prima eccezione (e se non ne capisci la causa, magari 
riportarcene lo stacktrace). Quello che ci hai mostrato finora è lo 
stacktrace dell'ultima eccezione, sicuramente causata da qualcos'altro.
Dire "ho fatto tutti i JUnit test case..." è un po' come dire "sulla mia 
macchina funziona..."...non s'ha da fare :), e sicuramente non aiuta a 
risolvere il problema!
