Esercizi
- Home
- Esercizi del capitolo 17 e dell'appendice G
Esercizi del capitolo 17 e dell'appendice G
Java Database
Connectivity e package
java.util
Gli esercizi pratici di questo capitolo utilizzeranno come database Apache Derby, ma ovviamente è
possibile utilizzare anche altri database se il lettore ha le conoscenze giuste. Potete trovare
nell'appendice J, un'introduzione all'installazione e l'uso di questo RDBMS. L'integrazione delle
nostre applicazioni con Derby, ovviamente aggiungerà complessità. Inoltre troverete anche esercizi
che riguardano gli argomenti trattati nella appendice G. Questi esercizi hanno una numerazione
diversa che inizia con la lettera G.
Di seguito trovate gli esercizi del capitolo.
Per ogni esercizio, cliccando sulla traccia potete vedere la relativa soluzione.
Gli esercizi caratterizzati dall'icona sono
considerati i più complessi relativamente agli argomenti trattati.
Se preferite lavorare offline, è possibile scaricare tutti gli esercizi e le relative
soluzioni in formato PDF nella sezione download.
-
Esercizio 17.a) JDBC, Vero o Falso:
1.
Connection
è solo un'interfaccia.
2. Un'applicazione JDBC è indipendente dal database solo se si parametrizzano le stringhe relative al driver, lo URL di connessione, lo username e la password.
3. Se si inoltra ad un particolare database un comando non standard SQL 2, questo comando funzionerà solo su quel database. In questo modo si perde l'indipendenza dal database, a meno di controlli o parametrizzazioni.
4. Per eliminare un record bisogna utilizzare il metodoexecuteQuery
.
5. Per aggiornare un record bisogna utilizzare il metodoexecuteUpdate
.
6.CallableStatement
è una sottointerfaccia diPreparedStatement
.PreparedStatement
è una sottointerfaccia diStatement
.
7. Per eseguire una stored procedure bisogna utilizzare il metodoexecute
.
8. L'auto-commit è impostato atrue
per default.
9. In ambienti enterprise dove si eseguono applicazioni in contesti Java EE,DataSource
va utilizzato in luogo diDriver
.
10. Il tipoCHAR
di SQL si mappa al tipochar
di Java.
Soluzione
1. Vero.
2. Vero.
3. Vero.
4. Falso, per eliminare un record bisogna utilizzare il metodoexecuteUpdate
.
5. Vero.
6. Vero.
7. Vero.
8. Vero.
9. Vero.
10. Falso, il tipoCHAR
di SQL si mappa al tipoString
di Java. -
Esercizio 17.b)
Creare una classe chiamata
DBGenerator
che crea con istruzioni DDL, un database eseguendo le istruzioni contenute nei fileDBConfig.txt
eDBScript.txt
forniti nella cartella 17.b del file del codice del libro. In particolare le istruzioni del fileDBConfig.txt
sono le seguenti:
db.url=jdbc:derby:Phonebook;create=true db.user= db.pwd=
Dovremo leggeremo questo file utilizzando un oggetto di tipo
Properties
, visto che contiene coppie di tipo chiave-valore. Notiamo che non sono stati definiti valori per username e password, quindi il programma non dovrà leggerle (sono state riportate solo per rendere l'idea del file di configurazione). Notare anche che la "url" per connettersi al database è leggermente diversa da quella che ci si potrebbe aspettare rispetto agli altri database. Infatti questa sintassi, una volta letta con JDBC, creerà uno schema chiamatoPhonebook
(in italiano "rubrica"), come visto già nel paragrafo 17.2.4.
Poi il programma dovrà eseguire le seguenti istruzioni che sono contenute nel fileDBScript.txt
:CREATE TABLE Contact (Name VARCHAR(50), Phone VARCHAR(20), Address VARCHAR(255), PRIMARY KEY (Name)) INSERT INTO Contact (Name, Phone, Address) VALUES ('Claudio De Sio Cesari', '131313131313', '13, Java Street') INSERT INTO Contact (Name, Phone, Address) VALUES ('Stevie Wonder', '1010101010', '10, Music Avenue') INSERT INTO Contact (Name, Phone, Address) VALUES ('Gennaro Capuozzo', '1111111111', '1, Four Days of Naples Square')
Questo file potrebbe essere letto ottenendo un'oggetto
Stream
chiamando il metodolist
della classeFiles
(leggere la documentazione).
Una volta creato il database, dovrebbe essere creata la cartellaRubrica
all'interno della cartella di dove è stato lanciato il file. Per controllare il contenuto del database, è possibile utilizzare il programma IQ (vedi Appendice K), oppure procedere con il prossimo esercizio.
Soluzione
Creare una classe chiamata
DBGenerator
che crea con istruzioni DDL, un database eseguendo le istruzioni contenute nei fileDBConfig.txt
eDBScript.txt
forniti nella cartella 17.b del file del codice del libro. In particolare le istruzioni del fileDBConfig.txt
sono le seguenti:
db.url=jdbc:derby:Phonebook;create=true db.user= db.pwd=
Dovremo leggeremo questo file utilizzando un oggetto di tipoProperties
, visto che contiene coppie di tipo chiave-valore. Notiamo che non sono stati definiti valori perusername
epassword
, quindi il programma non dovrà leggerle (sono state riportate solo per rendere l'idea del file di configurazione). Notare anche che la "url" per connettersi al database è leggermente diversa da quella che ci si potrebbe aspettare rispetto agli altri database. Infatti questa sintassi, una volta letta con JDBC, creerà uno schema chiamatoPhonebook
(in italiano "rubrica"), come visto già nel paragrafo 17.2.4.
Poi il programma dovrà eseguire le seguenti istruzioni che sono contenute nel fileDBScript.txt
:
CREATE TABLE Contact (Name VARCHAR(50), Phone VARCHAR(20), Address VARCHAR(255), PRIMARY KEY (Name)) INSERT INTO Contact (Name, Phone, Address) VALUES ('Claudio De Sio Cesari', '131313131313', '13, Java Street') INSERT INTO Contact (Name, Phone, Address) VALUES ('Stevie Wonder', '1010101010', '10, Music Avenue') INSERT INTO Contact (Name, Phone, Address) VALUES ('Gennaro Capuozzo', '1111111111', '1, Four Days of Naples Square')
Questo file potrebbe essere letto ottenendo un'oggettoStream
chiamando il metodo list della classeFiles
(leggere la documentazione). Una volta creato il database, dovrebbe essere creata la cartellaRubrica
all'interno della cartella di dove è stato lanciato il file. Per controllare il contenuto del database, è possibile utilizzare il programma IQ (vedi Appendice K), oppure procedere con il prossimo esercizio. -
Esercizio 17.c)
Partendo dalla soluzione dell'esercizio precedente, creare una classe chiamata
DBExplorer
, che definisce un metodo chiamatoprintContacts
che stampa il contenuto della tabellaContact
.
Soluzione
Tutte le affermazioni sono corrette.
-
Esercizio 17.d)
Partendo dall'esercizio 15.q e dal database creato nell'esercizio 17.b e 17.c, creare una classe
GestoreDB
che implementa l'interfacciaGestoreSerializzazione
. In particolare:
1. Implementare il metodogetContatti
per recuperare i contatti dal database.
2. Dal momento che tale metodo (ed anche gli altri che implementeremo nei prossimi esercizi) lancerà l'eccezioneSQLException
, bisogna modificare l' interfacciaGestoreSerializzazione
in modo tale che rimanga compatibile con la dichiarazione dei metodi anche delle altre implementazioni (GestoreFile
eGestoreFileNIO2
).
3. Di conseguenza bisognerà apportare anche delle modifiche alla classeRubricaCLI
affinché compili.
4. Modificare il fileconfig.properties
in modo tale che venga caricata la classeGestoreDB
per gestire la serializzazione.
5. Testare il corretto funzionamento dell'applicazione (per ora deve funzionare solo il caricamento dei contatti dal databaseRubrica
).
Soluzione
Modifichiamo l'interfaccia
GestoreSerializzazione
per gestire una eccezione generica (soluzione più veloce):
package rubrica.integrazione; import java.util.*; import rubrica.dati.*; import rubrica.util.*; public interface GestoreSerializzazione<T extends Dato> { void inserisci(T dato) throws Exception; T recupera(String id) throws Exception; void modifica(T dato) throws Exception; void rimuovi(String id) throws Exception; List<T> getContatti() throws Exception; }
Possiamo implementare la classeGestioneDB
nel seguente modo:
package rubrica.integrazione; import java.util.*; import rubrica.dati.*; import rubrica.eccezioni.*; import rubrica.util.*; import java.sql.*; import java.io.*; public class GestoreDB implements GestoreSerializzazione<Contatto> { private Properties p; private static final String CONFIGURATION_FILE ="DBConfig.txt"; public GestoreDB() { p = new Properties(); try (InputStream fis = new FileInputStream(CONFIGURATION_FILE);) { p.load(fis); } catch (IOException exc) { exc.printStackTrace(); } } @Override public void inserisci(Contatto contatto) throws ContattoEsistenteException, SQLException { try (Connection connection = DriverManager.getConnection(p.getProperty("db.url")); Statement cmd = connection.createStatement();) { String insert ="INSERT INTO CONTATTO (NOME, TELEFONO, INDIRIZZO) "+ "VALUES ('"+ contatto.getNome() +"', '"+ contatto.getNumeroDiTelefono() + "', '"+ contatto.getIndirizzo()+"')"; int result = cmd.executeUpdate(insert); } } @Override public Contatto recupera(String nome) throws ContattoInesistenteException, ContattoEsistenteException { return null; } @Override public void modifica(Contatto contatto) throws ContattoInesistenteException, ContattoEsistenteException, SQLException { } @Override public void rimuovi(String nome) throws ContattoInesistenteException, ContattoEsistenteException, SQLException { } @Override public List<Contatto> getContatti() throws Exception { List<Contatto> contatti = new ArrayList<Contatto>(); try (Connection conn = DriverManager.getConnection(p.getProperty("db.url")); Statement stmt = conn.createStatement(); ResultSet res = stmt.executeQuery("SELECT * FROM CONTATTO")) { while (res.next()) { contatti.add(new Contatto(res.getString("NOME"), res.getString("TELEFONO"), res.getString("INDIRIZZO"))); } } catch (SQLException e) { e.printStackTrace(); } return contatti; } }
Abbiamo anche modificato il fileconfig.properties
per far sì che venga caricata la classeGestoreDB
come gestore di serializzazione:
rubrica.ser=GestoreDB
L'output attuale dell'applicazione sarà:
Sto caricando la classe GestoreDB Lista dei contatti in Rubrica: 1 Claudio De Sio Cesari 2 Stevie Wonder 3 Gennaro Capuozzo Scrivi '/v' e il nome del contatto per visualizzarne i dettagli Scrivi '/i' per inserire un nuovo contatto Scrivi '/t' per terminare il programma
-
Esercizio 17.e)
Partendo dall'esercizio 17.d, implementare il metodo
recupera
e testare il corretto funzionamento dell'applicazione.
Soluzione
Potremmo implementare il metodo
recupera
nel seguente modo:
@Override public Contatto recupera(String nome) throws ContattoInesistenteException { Contatto contatto = null; try (Connection conn = DriverManager.getConnection(p.getProperty("db.url")); Statement stmt = conn.createStatement(); ResultSet res = stmt.executeQuery( "SELECT * FROM CONTATTO WHERE NOME = '" + nome + "'")) { if (res.next()) { contatto = new Contatto(res.getString("NOME"), res.getString("TELEFONO"), res.getString("INDIRIZZO")); } } catch (SQLException e) { e.printStackTrace(); } catch (NomeVuotoException e) { throw new ContattoInesistenteException( "Non esistono contatti senza nome"); } return contatto; }
-
Esercizio 17.f)
Partendo dall'esercizio 17.e, implementare il metodo
inserisci
e testare il corretto funzionamento dell'applicazione.
Soluzione
Potremmo implementare il metodo
inserisci
nel seguente modo:
@Override public void inserisci(Contatto contatto) throws ContattoEsistenteException, SQLException { try (Connection connection = DriverManager.getConnection(p.getProperty("db.url")); Statement cmd = connection.createStatement();) { String insert ="INSERT INTO CONTATTO (NOME, TELEFONO, INDIRIZZO) "+ "VALUES ('"+ contatto.getNome() +"', '"+ contatto.getNumeroDiTelefono() + "', '"+ contatto.getIndirizzo()+"')"; int result = cmd.executeUpdate(insert); } catch (SQLException e) { throw new ContattoEsistenteException("Contatto già presente"); } }
-
Esercizio 17.g)
Partendo dall'esercizio 17.f, implementare il metodo
modifica
e testare il corretto funzionamento dell'applicazione.
Soluzione
Potremmo implementare il metodo
modifica
nel seguente modo:
@Override public void modifica(Contatto contatto) throws ContattoInesistenteException, SQLException { try (Connection conn = DriverManager.getConnection(p.getProperty("db.url")); Statement cmd = conn.createStatement();) { String update = String.format( "UPDATE CONTATTO SET TELEFONO = '%s', INDIRIZZO = '%s' " + "WHERE NOME = '%s'", contatto.getNumeroDiTelefono(), contatto.getIndirizzo(), contatto.getNome()); int result = cmd.executeUpdate(update); if (result == 0) { throw new ContattoInesistenteException( "Non esiste un contatto con nome " + contatto.getNome()); } } }
-
Esercizio 17.h)
Partendo dall'esercizio 17.g, implementare il metodo
rimuovi
e testare il corretto funzionamento dell'applicazione.
Soluzione
Potremmo implementare il metodo
rimuovi
nel seguente modo:
@Override public void rimuovi(String nome) throws ContattoInesistenteException, SQLException { try (Connection conn = DriverManager.getConnection(p.getProperty("db.url")); Statement cmd = conn.createStatement();) { String delete = String.format("DELETE FROM CONTATTO WHERE NOME = '%s'", nome); int result = cmd.executeUpdate(delete); if (result == 0) { throw new ContattoInesistenteException( "Non esiste un contatto con nome " + nome); } } } }
-
Esercizio G.a) Package
java.util
, Vero o Falso:1. La classe
Properties
estendeHashtable
ma consente di salvare su un file le coppie chiave-valore rendendole persistenti.
2. La classeLocale
astrae il concetto di "zona".
3. La classeResourceBundle
rappresenta un file di properties che permette di gestire l'internazionalizzazione. Il rapporto tra nome del file eLocale
specificato per individuare tale file, permetterà di gestire la configurazione della lingua delle nostre applicazioni.
4. L'output del seguente codice:sarà:StringTokenizer st = new StringTokenizer( "Il linguaggio object oriented Java", "t", false); while (st.hasMoreTokens()) { System.out.println(st.nextToken()); }
5. Il seguente codice non è valido:Il linguaggio objec t orien t ed Java
Pattern p = Pattern.compile("\bb"); Matcher m = p.matcher("blablabla..."); boolean b = m.find(); System.out.println(b);
6. La classe
Preferences
permette di gestire file di configurazione con un file XML.
7. La classeFormatter
definisce il metodoprintf
.
8. L'espressione regolare[a]
è un quantificatore (greedy quantifier).
9. Il seguente codice:Date d = new Date(); d.now();
crea un oggetto
Date
con la data attuale.
10. UnSimpleNumberFormat
può formattare e analizzare qualsiasi valuta grazie alla specifica di unLocale
.
Soluzione
1. Vero.
2. Vero.
3. Vero.
4. Falso, tutte le "t" non dovrebbero esserci.
5. Falso, è valido ma stamperàfalse
. Affinché stampitrue
l'espressione si deve modificare in "\\bb
".
6. Falso, il modo in cui vengono immagazzinati i dati persistenti è dipendente dalla piattaforma. Un oggettoProperties
invece può usare anche file di configurazione in formato XML.
7. Falso, la classePrintStream
definisce il metodoprintf
, la classeFormatter
definisce il metodoformat
.
8. Falso, è un gruppo di caratteri.
9. Falso, la classeDate
non ha un metodonow
. La prima riga da sola avrebbe adempiuto al compito richiesto.
10. Falso, la classeSimpleNumberFormat
semplicemente non esiste. Ma esiste la classeSimpleDateFormat
. -
Esercizio G.b)
Creare una classe
Traduttore
che espone un metodotraduci
per tradurre un numero limitato di parole dall'inglese all'italiano e viceversa, utilizzando un resource bundle.
Soluzione
Il listato della classe
Traduttore
potrebbe essere come il seguente::
import java.util.Locale; import java.util.ResourceBundle; public class Traduttore { private LinguaEnum lingua; private ResourceBundle resources; public Traduttore (LinguaEnum lingua) { this.lingua = lingua; String chiaveLingua = lingua.getChiave(); Locale locale = new Locale(chiaveLingua); resources = ResourceBundle.getBundle("risorse.vocabolario", locale); } public String traduci(ParoleEnum testo) { String traduzione = resources.getString(testo.getChiave()); return traduzione; } public void setLingua(LinguaEnum lingua) { this.lingua = lingua; } public LinguaEnum getLingua() { return lingua; } }
Si noti che per semplificare il nostro esercizio abbiamo deciso di limitare il numero di parole da tradurre mediante l'enumerazioneParoleEnum
:
public enum ParoleEnum { LIBRO("libro"), TEMPO("tempo"), CASA("casa"); private String chiave; private ParoleEnum(String chiave) { this.chiave = chiave; } public String getChiave() { return chiave; } }
Abbiamo anche limitato il numero di lingue supportate tramite l'enumerazioneLingueEnum
:
public enum LinguaEnum { ITALIANO("it", "Italiano"), INGLESE("en", "Inglese"); String chiave; String descrizione; LinguaEnum(String chiave, String descrizione) { this.chiave = chiave; this.descrizione = descrizione; } public String getChiave() { return chiave; } public String toString() { return descrizione; } }
Inoltre abbiamo creato nella cartellarisorse
(inserita nella cartella dei sorgenti scaricabili: https://www.nuovojava.it) i file di properties che ci servivano:vocabolario_it.properties
:
libro=libro casa=casa tempo=tempo
evocabolario_en.properties
:
libro=book casa=home tempo=time
-
Esercizio G.c)
Creare una classe
TestTraduttore
per testare la correttezza delle traduzioni.
Soluzione
Il listato potrebbe essere il seguente:
public class TestTraduttore { public static void main(String args[]) { Traduttore traduttore = new Traduttore(LinguaEnum.INGLESE); String parolaTradotta = traduttore.traduci(ParoleEnum.LIBRO); System.out.println(parolaTradotta); } }
L'output sarà:
book
potremmo anche usare la classeScanner
e rendere interattivo il programma. -
Esercizio G.d)
Creare una classe
StringUtils
che dichiara solo metodi statici (e che quindi è inutile istanziare). Essa deve esporre un metodo ricerca, che tramite espressioni regolari deve ricercare tutte le parole in un testo (specificato in input) che iniziano con un certo carattere, e restituirle all'interno di una lista.
Soluzione
Il listato potrebbe essere il seguente:
import java.util.ArrayList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; public class StringUtils { public static List<String> ricerca(String testo, char iniziale) { List<String> list = new ArrayList<>(); Pattern pattern = Pattern.compile("\\b"+iniziale+"[a-zA-Z]+\\b"); Matcher matcher = pattern.matcher(testo); while (matcher.find()) { list.add(matcher.group()); } return list; } }
-
Esercizio G.e)
Creare una classe
TestStringUtils
per testare la correttezza del metodoricerca
.
Soluzione
Il listato della classe
TestStringUtils
potrebbe essere il seguente:
import java.util.List; public class TestStringUtils { public static void main(String args[]) { List<String> list = StringUtils.ricerca( "The smile of dawn arrived early May " + "she carried a gift from her home " + "the night shed a tear to tell her of fear " + "and of sorrow and pain she'll never outgrow ", 't'); for (String string : list) { System.out.println(string); } } }
L'output del codice precedente è:
the tear to tell
-
Esercizio G.f)
Creare un semplice programma che simuli il lancio di un dado. Eseguendo il programma sarà stampato un numero casuale da 1 a 6. Si utilizzi la documentazione ufficiale per trovare un modo per generare numeri casuali.
Suggerimento: basta un'unica classe con un metodo main contenente un unico statement.
Soluzione
La classe giusta da utilizzare è la classe
java.util.Random
, e il suo metodonextInt
che prende in input un limite superiore (escluso), e che ha come limite inferiore0
(incluso). Basta sommargli il valore1
e il gioco è fatto. Il listato potrebbe essere il seguente:
import java.util.Random; public class LancioDeiDadi { public static void main(String args[]) { System.out.println("Lancio il dado... " + (1 + new Random().nextInt(6)) + "!"); } }
-
Esercizio G.g)
Riprendiamo il discorso iniziato nell'esercizio 5.w, e implementato nell'esercizio 14.q, introdotto nell'approfondimento 5.7.
Nel caso d'uso dell'autenticazione, un utente inseriva uno username e una password per autenticarsi nel sistema. Ma chi ha inserito nel sistema le credenziali per permettere all'utente di autenticarsi con username e password? La risposta la troviamo nel diagramma dei casi d'uso, riportato nuovamente nella figura G.g.1.
Figura G.p.1 - Use case diagram aggiornato di Logos.
Il caso d'uso configura utenti, è il caso d'uso che questo esercizio richiede di implementare. Anche questo caso d'uso deve essere inteso come un piccolo programma funzionante, indipendentemente da Logos, in modo tale da poterlo eventualmente riutilizzare in altri programmi. Per poter immagazzinare le coppie username e password, utilizzare un file di properties. Tenere presente che oltre a username e password, la classe
Utente
deve avere anche altre proprietà da configurare, come il nome e il ruolo.
Riutilizzare il codice degli esercizi 14.q e degli altri esercizi correlati.
Soluzione
Riutilizziamo le classi
Utente
:
package com.claudiodesio.autenticazione; public class Utente { private String nome; private String username; private String password; public Utente(String n, String u, String p) { this.nome = n; this.username = u; this.password = p; } public String getNome() { return nome; } public void setNome(String nome) { this.nome = nome; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } }
La classeAmministratore
:
package com.claudiodesio.autenticazione; public class Amministratore extends Utente { public Amministratore (String nome, String username, String password) { super(nome, username, password); } }
La classeCommesso
:
package com.claudiodesio.autenticazione; public class Commesso extends Utente{ public Commesso(String nome, String username, String password) { super(nome, username, password); } }
Poi modifichiamo la classeProfiliUtenti
in modo tale da gestire i profili degli utenti:
package com.claudiodesio.autenticazione; import java.util.*; import java.io.*; public class ProfiliUtenti { private static ProfiliUtenti instance; private Properties properties; private ProfiliUtenti() { properties = new Properties(); try { loadProperties(); } catch (IOException e) { e.printStackTrace(); System.exit(1); } } public void loadProperties() throws IOException { try (FileInputStream inputStream = new FileInputStream("config.properties");) { properties.load(inputStream); } } public static ProfiliUtenti getInstance() { if (instance == null) { instance = new ProfiliUtenti(); } return instance; } public void inserisciUtente(String[] args) throws IOException { String ruolo = args[0]; String nome= args[1]; String username = args[2]; String password = args[3]; // Se non si specifica il ruolo Amministratore // verrà inserito sempre un Commesso Utente utente = (ruolo.equals("Amministratore") ? new Amministratore(nome, username, password): new Commesso(nome, username, password)); String valore = nome +","+ ruolo+","+ password; properties.setProperty(username, valore); try (FileOutputStream fos = new FileOutputStream("config.properties")) { properties.store(fos,"File di configurazione"); } System.out.println("Inserita la proprietà: " + username + "=" + valore); } }
In particolare abbiamo aggiunto un metodo per aggiungere profili utente, che ci permette di aggiungere nel file di properties una riga la cui chiave è lo username, e il cui valore è una lista con le altre informazioni dell'utente separate da virgole. Infine con la seguente classe che prende da riga di comando dei valori da inserire, viene eseguita l'applicazione:
package com.claudiodesio.autenticazione; import java.io.*; public class EsercizioGG { public static void main(String args[]) throws IOException { if (args.length != 4) { System.out.println("Specificare ruolo, nome, username, password"); System.exit(1); } ProfiliUtenti.getInstance().inserisciUtente(args); } }
Eseguendo l'applicazione, per esempio senza specificare argomenti di riga di comando, otterremo il seguente output:
java com.claudiodesio.autenticazione.EsercizioGG Specificare ruolo, nome, username, password
invece specificando i seguenti argomenti:
java com.claudiodesio.autenticazione.EsercizioGG Amministratore Claudio desio xxxxxxxx Inserita la proprietà: desio=Claudio,Amministratore,xxxxxxxx
otterremo che nel file di properties troveremo il seguente contenuto:
#File di configurazione #Sun Mar 25 19:10:31 CEST 2018 desio=Claudio,Amministratore,xxxxxxxx
-
Esercizio G.h)
Tenendo presente la soluzione dell'esercizio precedente, modificare il codice dell'esercizio 6.z ed in particolare la classe
ProfiliUtenti
, in modo tale da caricare gli oggettiUtente
a partire dal file di properties.
Soluzione
Una possibile soluzione risiede nella modifica della classe
ProfiliUtenti
nel seguente modo:
package com.claudiodesio.autenticazione; import java.util.*; import java.io.*; public class ProfiliUtenti { private static ProfiliUtenti instance; private Properties properties; private ProfiliUtenti() { properties = new Properties(); try { loadProperties(); } catch (IOException e) { e.printStackTrace(); System.exit(1); } } public static ProfiliUtenti getInstance() { if (instance == null) { instance = new ProfiliUtenti(); } return instance; } public void loadProperties() throws IOException { try (FileInputStream inputStream = new FileInputStream("config.properties");) { properties.load(inputStream); } } public void inserisciUtente(String[] args) throws IOException { String ruolo = args[0]; String nome= args[1]; String username = args[2]; String password = args[3]; // Se non si specifica il ruolo Amministratore // verrà inserito sempre un Commesso Utente utente = (ruolo.equals("Amministratore") ? new Amministratore(nome, username, password): new Commesso(nome, username, password)); String valore = nome +","+ ruolo+","+ password; properties.setProperty(username, valore); try (FileOutputStream fos = new FileOutputStream("config.properties")) { properties.store(fos,"File di configurazione"); } System.out.println("Inserita la proprietà: " + username + "=" + valore); } public Utente getUtente(String username) { String valore = (String)properties.getProperty(username); if (valore == null) { return null; } String [] campi = valore.split(","); Utente utente = (campi[1].equals("Amministratore") ? new Amministratore(campi[0], username, campi[2]): new Commesso(campi[0], username, campi[2])); return utente; } }
In particolare abbiamo creato il metodogetUtente
che andiamo a richiamare dalla classeAutenticazione
opportunamente modificata:
package com.claudiodesio.autenticazione; import java.util.Scanner; public class Autenticazione { public void login() { boolean autorizzato = false; Scanner scanner = new Scanner(System.in); do { Stampa.richiediUsername(); String username = scanner.nextLine(); Utente utente = ProfiliUtenti.getInstance().getUtente(username); if (utente != null) { Stampa.richiediPassword(); String password = scanner.nextLine(); if (verificaPassword(utente, password)) { Stampa.auguraBenvenuto(utente.getNome()); autorizzato = true; } else { Stampa.autenticazioneFallita(); } } else { Stampa.usernameInesistente(); } } while (!autorizzato); } private boolean verificaPassword(Utente utente, String password) { boolean trovato = false; if (password != null) { if (password.equals(utente.getPassword())) { trovato = true; } } return trovato; } }
Infine il metodomain
lo abbiamo spostato nella classeEsercizioGH
:
package com.claudiodesio.autenticazione; public class EsercizioGH { public static void main(String args[]) { Autenticazione autenticazione = new Autenticazione(); autenticazione.login(); } }
Eseguendo l'applicazione tutto funziona come prima:
Inserisci username. Pippo Utente non trovato! Inserisci username. desio Inserisci password. yyyyyyyy Autenticazione fallita Inserisci username. desio Inserisci password. xxxxxxxx Benvenuto Claudio
-
Esercizio G.i)
Quali tra le seguenti affermazioni sono corrette?
1. Un oggettoFormatter
permette di immagazzinare coppie chiave-valore come un oggettoProperties
, senza il bisogno di dover specificare un file da utilizzare per l'immagazzinamento dei dati.
2. Un oggettoSystem.out
è di tipojava.io.PrintStream
.
3. La classejava.io.PrintStream
definisce il metodoprintf
che sfrutta il metodo format della classeFormatter
.
4. La classeFormatter
definisce un overload del metodoformat
.
5. La classeFormatter
permette di formattare anche in base a oggettiLocale
.
Soluzione
Tutte le affermazioni sono vere.
-
Esercizio G.j)
Quali tra le seguenti affermazioni sono corrette riguardo la classe
Preferences
?
1. Un oggettoPreferences
permette di immagazzinare coppie chiave-valore come un oggettoProperties
, senza il bisogno di dover specificare un file da utilizzare per l'immagazzinamento dei dati.
2. Un oggettoPreferences
non usa il metodosetProperty
.
3. La classePreferences
estendeHashtable
.
4. La classePreferences
si istanzia senza poter utilizzare costruttori, ma metodi che restituiscono istanze. Infatti è addirittura una classe astratta.
5. La classePreferences
definisce metodi per inserire specifici tipi di dati come valore.
Soluzione
Tutte le affermazioni sono vere tranne la numero 3. Infatti la classePreferences
non estendeHashtable
, ma è una classe astratta che estendeObject
. È sufficiente leggere la documentazione delle API Java. -
Esercizio G.k)
Quali tra le seguenti affermazioni sono corrette riguardo le regular expression?
1. La classeMatcher
definisce il metodofind
che ritorna la stringa che coincide con l'espressione booleana diMatcher
.
2. La classeMatcher
si istanzia senza poter utilizzare costruttori, ma metodi che restituiscono istanze.
3. Un oggettoPattern
definisce i metodistart
eend
.
4. All'interno di stringhe Java, il simbolo\
deve essere ripetuto, per evitare che il compilatore lo consideri il prefisso di un carattere di escape.
5. Un "greedy quantifier" è un simbolo che simboleggia la molteplicità di occorrenze coincidenti con un certo pattern.
Soluzione
Le affermazioni corrette sono le numero 2, 4 e 5. La numero 1 è falsa perché il metodo
find
non ritorna una stringa ma un booleano (il metodofind
viene in fatti usato solitamente come condizione di un ciclo).
La numero 3 invece è falsa perché i metodistart
eend
non appartengono alla classePattern
ma vengono dichiarati dalla classeMatcher
.