PDA

View Full Version : [JSP] applicazioni web e i database


nazkul
16-08-2007, 18:25
Ciao, nella applicazione web che sto realizzando memorizzo dei dati (nome e cognome di un login) su un database mysql. Il dubbio che ho è che questa applicazione verrà consegnata al docente del corso, che poi verificherà se il tutto funziona.
Quando il docente lo testerà sul suo computer non avrà sicuramente lo stesso nome utente e password usati da me nel codice Java per creare il database, inserire dati, chiudere la connessione ecc... quindi mi domando come dovrei fare per far funziona rel'applicazione anche su altri computer. Per caso nelle specifiche devo mettere che per l'uso è necessario oltre ad aver installato ovviamente mysql, che deve essere creato un utente "tizio" con password"pwd" per esempio?

Il database non va inserito nel file web.xml? Cioè, nel mio caso all'inizio il database può essere anche vuoto, ma se fosse con alcuni elementi, come dovrebbe essere consegnato il database? Io no credo che vada nel .war, ma non ho affatto le idee chiare.

Ringrazio anticipatamente chiunque mi possa fornire qualche consiglio.
:)

cionci
17-08-2007, 15:58
Puoi dare un dump al tuo professore per creare sia utente che tabelle, in questo modo ci penserà il professore a creare il tutto.
In alternativa puoi creare una piccola applicazione che permette di configurare le tua applicazione ed immettere i dati del server database (indirizzo, username e password). Ovviamente l'applicazione si occuperà di scrivere questi dati nel file di configurazione della tua applicazione e di creare le tabelle con i dati di partenza.

nazkul
17-08-2007, 21:07
Ciao, grazie per la risposta, almeno per questo aspetto ho le idee un po più chiare.
Ma per quanto riguarda l'applicazione nel suo complesso, devo dichiarare da qualche parte l'uso del database? Nel senso: io in giro leggo che bisogna inserire del codice xml sul server.xml di Tomcat, poi leggo che va metto del codice anche sul deployment descriptor ( il web.xml ); poi leggo del Connector/J che da qualche parte va indicato che si deve usare com.sql.jdbc.Driver.
Premetto che come applicazione stand-alone l'accesso al database mi riesce, poi però se passo a fare l'applicazione web, pur mettendo anche com.sql.jdbc.Driver nella stessa posizione scelta nell'esempio stand-alone, con l'applicazione web non fa, la console di Eclipse dice che si pianta su quel driver.
Io non so assolutamente dove sbattere la testa. Se qualcuno mi sa indicare un filo logico da seguire gliene sarei grato.
:)

morskott
17-08-2007, 21:08
e se crei un file xml con i dati di configurazione?
Del tipo<?xml version="1.0" encoding="UTF-8"?>
<DBConf>
<driver>Mio.Driver</driver>
<url>jdbc:myURL</url>
<usn>cicillo</usn>
<psw>cacace</psw>
</DBConf>e poi con un (DOM/SAX)parser te li parsi nel ConnectionManager, che è sulla falsa riga dipublic class ConnectionManager{
private static ConnectionManager conman=null; //usato per il pattern Singleton

private ConnectionManager(){
SqlConfig pars=new SqlConfig(new File("mioFile.xml")); //ho supposto che la classe XMLParser (che ti devi creare tu) prenda come parametro il file XML
Class.forName(pars.getDriver());
}

public static ConnectionManager getConnectionManager(){
if (conman==null) conman=new ConnectionManager(); //inizializzazione lazy, per caricare il driver sql il piu tardi possibile
return conman;
}

public syncronized Connection getConnection(){ //metodo syncronized per gestire la concorrenza di piu thread sull'unica istanza del ConnectionManager
SqlConfig pars=new SqlConfig(new File("mioFile.xml"));
return DriverManager.getConnection(pars.getURL(),pars.getUserName(),pars.getPassword());
}

public syncronized void close(Connection conn){ //Metodo che puo sembrare inutile (puoi fare un conn.close() anche fuori dal ConnectionManager), ma che rende banale l'estenzione usando un pooling di connessioni a disposizione
conn.close();
}
}dove XMLParser è il parser che ti sei creato e il ConnectionManager è facilissimo estenderlo inserendoci la gestione di pooling di connessioni.
Attenzione, non mi son preoccupato di gestire le dovute eccezioni.

nazkul
17-08-2007, 21:31
Ciao, grazie anche a te per l'idea; è che io di parser non ne so nulla, purtroppo ho una scadenza per la consegna e quindi mi tocca far leva sulle cose che so "meno peggio" :D
Casomai stavo pensando se poteva essere meglio un doc xml al posto del database perché da quello che leggo in giro mi pare di dover fare una montagna di operazioni per far funzionare il tutto, anche se credo che essendo comunque dei sistemi di persistenza dati più o meno vadano fatti presente lo stesso in file tipo web.xml.
Purtroppo mi hanno parlato solo di pattern come mvc, dei filter, del front controller, tutti concetti, tutte scelte progettuali, poi per quanto riguarda l'implementazione non so da che parte farmi.
Grazie mille.
:)

morskott
17-08-2007, 22:04
Allora, ti posto i sources dei parser che ho fatto io, il mio file xml è del tipo<?xml version="1.0"?>
<sqlConfig>
<driver driverName="org.apache.derby.jdbc.EmbeddedDriver" />
<connectionUrl url="jdbc:derby:#DB_NAME#" />
<creationUrl creationUrl="jdbc:derby:#DB_NAME#;create=true" />
</sqlConfig>e i 2 file di parsing sonoimport java.io.*;
import org.w3c.dom.*;
import org.xml.sax.SAXException;
import javax.xml.parsers.*;

public class SqlConfig {
private String url;
private String driver;
private String creationUrl;

public SqlConfig(String xmlFile){
XMLParser parser=new XMLParser();
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser saxParser;
try {
saxParser = factory.newSAXParser();
saxParser.parse( new File(xmlFile), parser );
this.url=parser.getUrl();
this.driver=parser.getDriver();
this.creationUrl=parser.getCreationUrl();
} catch (SAXException ex) {
ex.printStackTrace();
} catch (ParserConfigurationException ex) {
ex.printStackTrace();
}catch (IOException ex) {
ex.printStackTrace();
}
}

public String getDriver(){
return this.driver;
}

public String getUrl(){
return this.url;
}

public String getCreationUrl(){
return this.creationUrl;
}
}epublic class XMLParser extends DefaultHandler{
private String driver="";
private String url="";
private String creationUrl="";

public void startElement(String uri,String localName,String qName,Attributes attrs) throws SAXException{
if (qName.equals("driver")){
if (attrs != null) {
for (int i = 0; i < attrs.getLength(); i++) {
String aName = attrs.getLocalName(i); // Attr name
if ("".equals(aName)) aName = attrs.getQName(i);
if (aName.equals("driverName"))
this.driver=attrs.getValue(i);
}
}
}else{
if (qName.equals("connectionUrl")){
if (attrs != null) {
for (int i = 0; i < attrs.getLength(); i++) {
String aName = attrs.getLocalName(i); // Attr name
if ("".equals(aName)) aName = attrs.getQName(i);
if (aName.equals("url"))
this.url=attrs.getValue(i);
}
}
}else{
if (qName.equals("creationUrl")){
if (attrs != null) {
for (int i = 0; i < attrs.getLength(); i++) {
String aName = attrs.getLocalName(i); // Attr name
if ("".equals(aName)) aName = attrs.getQName(i);
if (aName.equals("creationUrl"))
this.creationUrl=attrs.getValue(i);
}
}
}else{
//do nothing
}
}
}
}

public String getDriver(){
return this.driver;
}

public String getUrl(){
return this.url;
}

public String getCreationUrl(){
return this.creationUrl;
}
} naturalmente devi modificare il nome dei parametri (non è che io sia granchè come coder di parsers, sono un autodidatta in parsing XML, se cerchi in rete ne troverai 3000 fatti meglio, cmq è una buon punto di partenza), io dal ConnectionManager chiamo la classe SqlConfig che a sua volta usa la classe XMLParser (forse un livello di indirezione di troppo).

nazkul
18-08-2007, 09:18
Dunque, grazie per il codice.
Vediamo un po, perché sono testone a capire :D
Questi 3 file .xml mi servono per trasportare un db su un'altra postazione giusto? servono per ricreare stessa situazione che nel mio pc, compreso utente e password che poi a livello di codice Java servono per accedere alla base di dati. Ma dove dovrei mettere questi 3 file? Mi vanno dentro il progetto immagino, ma c'è una collocazione obbligatoria? tipo il file web.xml deve stare in un punto ben preciso, roba così intendo.
E come si lanciano? cioè da come la capisco io questi dovranno essere i primi a essere eseguiti.
E il db mysql dove viene gestito nel tuo codice? comunque devo fare un file dump?
Grazie per i consigli e la pazienza.
:)

cionci
18-08-2007, 09:31
Ad occhio mi sembra che serva solamente a trasportare le impostazioni di connessione ;)
Ma MySQL nel tuo progetto è un requisito oppure hai scelto te di utilizzarlo ?

nazkul
18-08-2007, 10:05
No non è un requisito, è una scelta perché il progetto in pratica consiste in un gioco, di cui mi sono state fornite le librerie, e io devo pensare a tutto ciò che lo fa funzionare se fosse un'applicazione web; siccome simulo una specie di registrazione dei nuovi utenti e poi ho una pagina di login, mi serve un sistema di persistenza dati e avevo pensato di utilizzare mysql, tanto da quello che ho capito o mysql o documento xml.
:)

cionci
18-08-2007, 10:14
Ci sono anche altre alternative...ad esempio un JavaDB embedded.
In questo modo sarà la tua web application a far partire il database.

Il problema principale di MySQL è che non è detto che il tuo prof abbia un server mysql per provarlo ;)

nazkul
18-08-2007, 10:29
Si ma ha spiegato che basta mettere nella relazione tutto ciò che è necessario per far funzionare l'applicazione, poi ci pensa lui a metterlo se non ce l'ha. Almeno da questo punto di vista non ci sono vincoli :)

cionci
18-08-2007, 10:33
Allora va benissimo ;)
Gli fai il dump delle tabelle ed in cima ci metti anche il comando GRANT per creare l'utente.
Ovviamnete gli dici di importare il dump come root.

nazkul
18-08-2007, 11:27
OK, ma per quanto riguarda dover dire al deployment descriptor che c'è un database, poi ho letto in modo un po sintetico che pare vada impostato anche server.xml di tomcat per usare il db, sono cose che ho interpretato male oppure vanno fatte? o basta fare una classe di accesso al db che usi connector/J e poi il dump per l'esportazione?
Il dubbio mi è venuto in quanto a fare un normale programmino java che usa il connector/J ha funzionato, adesso mi segnala errore all'istruzione Class.forName(com.sl.jdbc.Driver).
Il connector l'ho messo nel progetto, anche se dubito che a questo punto sia nel path giusto, però non fa. Da qui mi è venuto da pensare che vadano fatti ulteriori passaggi.
Grazie mille
:)

morskott
18-08-2007, 13:23
Il file xml serve per poter cambiare il db senza dover ricompilare l'applicazione, in pratica gli dici sia che db utilizzare, tramite il driver, l'url con il quale connettersi e le credenziali d'accesso. Poi naturalmente nella macchina del tuo prof ci dev'essere un'istanza del DB con le tabelle create e accessibili dalle credenziali fornite.

nazkul
18-08-2007, 14:35
Ciao, dunque facendo un po il punto della situazione, che sono duro a capire, come il granito :D
Io ho trovato questa roba:

http://www.mokabyte.it/2005/03/j2ee-datasources.htm

dove in pratica, con troppa sintesi in rapporto a quanto ne so, pare sia detto che bisogna configurare Tomcat per usare il mio database, e poi devo anche mettero mano al file web.xml della mia applicazione.

La via che mi indichi tu morskott mi pare di capire che permetta di scavalcare questa procedura, correggimi se mi sto sbagliando.
Quello che non capisco è:
io mi trovo con 3 file .xml come quelli che mi hai passato te, ovviamente correggendo nome utente, password ... per adattarli ai miei parametri di configurazione.
Ma dove li metto questi 3 file xml? e come faccio in modo che siano diciamo letti da non so chi in modo tale che facciano il loro lavoro?

Scusate se sono un po noioso, progettare così con la sola arte dell'arrangiarsi è veramente brutto, purtroppo se non chiedo non so da che parte mettere le mani per portare in fondo questo progetto.:cry:
Grazie per la pazienza:)

morskott
18-08-2007, 14:59
Si, la mia soluzione andrebbe a scavalcare quella procedura, anche perchè non ne conoscevo l'esistenza ;) .
La soluzione che mi hai linkato è meglio della mia, e anche un po piu semplice, e presuppone una modifica ai file di configurazione dell'application server non troppo difficile.
La mia soluzione è un po piu "fatta in casa", in cui per ottenere un oggetto Connection hai bisogno di un ConnectionManager che a sua volta ha bisogno dei parametri di accesso al DB (è lui, e solo lui, che sa come accedere al db), queste info possono essere hard coded (cioè messe direttamente nel codice, e ha come vantaggio la semplicità ma come svantaggio che in caso di cambio del db o di credenziali devi cambiare il sorgente e ricompilare), oppure possono esser messi in un file di configurazione e parsati per passarli al ConnectionManager (leggermente piu complesso da implementare ma portabilità su diversi DB molto superiore).
Per "far trovare" il file xml dall'applicazione potresti fare una System.out.println((new File(.)).getPath()) per sapere la dir di lavoro per poi mettere il file là e ripescarlo con un File confFile=new File("mioFile.xml")(naturlamente la System.out la fai solo la prima volta (per "debug"), poi il percorso (relativo) sarà sempre quello).

Anch'io ho dovuto praticare la sana arte dell'arrangiarsi quando è toccato per il mio progetto!!!!! ;)

nazkul
18-08-2007, 15:40
La soluzione del link che ho inserito però impone che in qualsiasi macchina verrà esportato il mio progetto, dovrà essere modificato il file server.xml di tomcat giusto?

Se scegliessi la tua soluzione, quelle 2 righe di codice che hai inserito chi le dovrebbe fare?
Ho una servlet ServletNewUser.java, che delega il lavoro al javaBeanNewUser che per inserire un nuovo utente con i valori ricevuti dalle form della pagina jsp delega il tutto a un oggetto della classe ElaboraDB.java. Credo che sia quest'ultima classe a dover fare ciò giusto?
Grazie :)

morskott
18-08-2007, 18:26
Allora, uno scetch di "ElaboraDB.java" potrebbe essere (faccio il caso di un inserimento di un utente)public class ElaboraDB{

//suppongo che la tabella utente abbia come campi: UserName, Password, nome e cognome
public boolean store(Utente ut) throws SQLException{
ConnectionManager conman=ConnectionManager.getConnectionManager(); //prendo l'istanza del ConnectionManager per prendermi la connessione
Connection conn=conman.getConnection(); //chiedo al ConnectionManager di darmi una connessione, io ElaboraDB non so come faccia
//con la connessione che mi son preso inserisco nel db la tupla relativa all'utente
PreparedStatement ps=conn.prepareStatement("insert into utente values(?,?,?,?)");
ps.setString(1,ut.getUserName());
ps.setString(2,ut.getPassword());
ps.setString(3,ut.getNome());
ps.setString(4,ut.getCognome());
int ris=ps.executeUpdate();
ps.close();
conman.close(conn); //dico al ConnectionManager che la Connection non mi serve piu
return ris==1;
}
}e il ConnectionManager è il pezzo di codice che ti ho scritto sopra.

Poi metti il file xml con la configurazione nella dir che ti dice il System.out e dovresti essere a posto.

nazkul
19-08-2007, 11:35
Ciao, non capisco i significati dei tag url nel documento xml; avevo pensato di fare il mio file Dati.xml in questo modo:


<?xml version="1.0"?>
<sqlConfig>
<driver driverName="com.sql.jdbc.Driver" />
<connectionUrl url="jdbc:mysql://localhost/gioco" />
<creationUrl creationUrl="jdbc:mysql://localhost/gioco;create=true" />
<usn>nomeUtente</usn>
<psw>password</psw>
</sqlConfig>


Il driver è quello di Connector/J, e nell'applicazione stand alone avevo usato quella stringa e funzionava per cui sono tranquillo almeno lì.
connectionUrl e creationUrl avevo pensato di farli in quel modo ma non sono affatto sicuro; erano più o meno le stringhe usate nell'applicazione stand alone. Che mi consigli?
Grazie
:)

morskott
19-08-2007, 18:20
la "creationUrl" la usavo io per creare un db usando il db embedded in java, non credo ti serva per il progetto (il db te lo dovresti già creare prima della prima esecuzione), il tag connectionUrl è l'url che passi al DriverManager per farti dare l'oggetto Connection "DriverManager.getConnection(url,usn,psw)".
Per l'usn e psw nel formato che hai usato pur essendo corretto al 100% per l'xml il codice del mio parser non lo parsa (non so io come farlo, non è che non si puo fare), dovresti mettere "<usn user="blah" />" e "<psw pass="bleh" />" oppure migliorare (e passarmelo) il parser.

nazkul
20-08-2007, 14:29
Sto piano piano lavorando al codice per adattarlo alla mia applicazione.
Mi è venuto un dubbio:
Il pattern Singleton adottato per il connection manager, siccome realizza una sola istanza, mi verrebbe da pensare che sia necessario mettere synchronized solo nel metodo getConnectionManager() che è l'unico metodo che usa il costruttore, secondo appunto lo schema del pattern.
Gli altri metodi non capisco se è veramente necessario metterli synchronized, forse ho le idee un pochetto confuse.
Chiarimenti in merito?
Grazie
:)

cionci
20-08-2007, 15:51
Il metodo statico che ritorna l'istanza del singleton va messo synchronized...
Per gli altri dipende da cosa fanno: immaginati comunque che ha più thread che accedono alla stessa istanza di classe.

nazkul
20-08-2007, 16:03
Ciao, beh in pratica per come è stato portato avanti lo schema del progetto, c'è questa classe che si occupa della connessione e poi un'altra classe che ha un riferimento al gestore della connessione e quando gli ha fatto la connessione poi la seconda classe fa delle query.
Ci sono i metodi connetti() e disconnetti() in pratica; è che in definitiva la risposta che può essere data su tali metodi credo valga a questo punto anche per la classe che fa le query, perchè io ho solo scisso i compiti di connessione da quelli di interrogazione, ma il lavoro sulla risorsa condivisa c'è in entrambe le classi.
Per come conosco io il multithreading sarei per mettere tutto synchronized, solo che mi confonde molto questa storia della singola istanza; se non fosse necessario, oltre che poco corretto anche se funzionante, sarebbe di sicuro poco performante.

cionci
20-08-2007, 16:18
Dipende da come hai sviluppato la cosa, comunque in teoria la concorrenza sull'accesso al database dovrebbe essere gestita dal dbms stesso. Quindi se ci sono dati di stato condivisi nella classe hai bisogno di sincronizzare, altrimenti probabilmente no.

nazkul
20-08-2007, 16:54
Lo schema delle classi è così:

ConnectionManager

private static ConnectionManager conMan=null;

costruttore privato

metodo getConnectionManager per realizzare una sola istanza

metodo getConn
{
...
Drivermanager.getConnection(.....);
....
}

metodo closeConn()


Mentre la classe delle query:

connectionManager cm; //pensa alla connessione

public synchronized Vector eseguiQuery(String query)

public synchronized boolean eseguiAggiornamento(String query)

disconnetti //si appoggia a connectionManager



Di solito c'è da sincronizzare se ci sono variabili di istanza, che qui è solo l'oggetto di connessione del Singleton.

Tra l'altro nel ragionare mi è venuto un ulteriore dubbio:
ma nelle classi che implementano il Singleton si possono mettere altre variabili di istanza oltre alla variabile public static che rappresenta l'unica istanza dell'oggetto? sennò nei metodi si rischia di dover creare più volte un oggetto ogni volta che mi serve un parametro che mi deve fornire, invece fattorizzerei mettendo fuori dai metodi. Non so se ho spiegato bene.
grazie:)