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 atrueper default.
9. In ambienti enterprise dove si eseguono applicazioni in contesti Java EE,DataSourceva utilizzato in luogo diDriver.
10. Il tipoCHARdi SQL si mappa al tipochardi 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 tipoCHARdi SQL si mappa al tipoStringdi Java. -
Esercizio 17.b)
Creare una classe chiamata
DBGeneratorche crea con istruzioni DDL, un database eseguendo le istruzioni contenute nei fileDBConfig.txteDBScript.txtforniti nella cartella 17.b del file del codice del libro. In particolare le istruzioni del fileDBConfig.txtsono le seguenti:
db.url=jdbc:derby:Rubrica;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 chiamatoRubrica.
Poi il programma dovrà eseguire le seguenti istruzioni che sono contenute nel fileDBScript.txt:CREATE TABLE CONTATTO (NOME VARCHAR(50), TELEFONO VARCHAR(20), INDIRIZZO VARCHAR(255), PRIMARY KEY (NOME)) INSERT INTO CONTATTO (NOME, TELEFONO, INDIRIZZO) VALUES ('Claudio De Sio Cesari', '131313131313', '13, Java Street') INSERT INTO CONTATTO (NOME, TELEFONO, INDIRIZZO) VALUES ('Stevie Wonder', '1010101010', '10, Music Avenue') INSERT INTO CONTATTO (NOME, TELEFONO, INDIRIZZO) VALUES ('Gennaro Capuozzo', '1111111111', '1, Four Days of Naples Square')
Questo file potrebbe essere letto ottenendo un'oggetto
Streamchiamando il metodolistdella classeFiles(leggere la documentazione).
Una volta creato il database, dovrebbe essere creata la cartellaRubricaall'interno della cartella di dove è stato lanciato il file. Per controllare il contenuto del database, è possibile utilizzare il programma IJ (vedi Appendice K), oppure procedere con il prossimo esercizio.
Soluzione
La classe
DBGeneratorpotrebbe essere la seguente:
import java.sql.*; import java.io.InputStream; import java.io.FileInputStream; import java.io.IOException; import java.util.Properties; import java.util.stream.*; import java.nio.file.*; public class DBGenerator { private Properties p; private static final String CONFIGURATION_FILE ="DBConfig.txt"; private static final String SCRIPT_FILE ="DBScript.txt"; public DBGenerator ( ) throws Exception { p = new Properties(); try (InputStream fis = new FileInputStream(CONFIGURATION_FILE);) { p.load(fis); createDB(); } catch (IOException exc) { exc.printStackTrace(); } } public void createDB() throws Exception { try (Connection conn = DriverManager.getConnection(p.getProperty("db.url")); Stream<String> stream = Files.lines(Paths.get(SCRIPT_FILE)); Statement stmt = conn.createStatement()) { stream.forEach(line-> executeUpdate(stmt, line)); System.out.println("Database created!"); } catch (SQLException e) { e.printStackTrace(); } } private void executeUpdate(Statement stmt, String cmd) { try { stmt.executeUpdate(cmd); } catch (SQLException exc) { exc.printStackTrace(); } } public static void main(String args[]) throws Exception { new DBGenerator(); } }
-
Esercizio 17.c)
Partendo dalla soluzione dell'esercizio precedente, creare una classe chiamata
DBExplorer, che definisce un metodo chiamatoprintContactsche stampa il contenuto della tabellaCONTATTO.
Soluzione
La classe
DBExplorerpotrebbe essere la seguente:
import java.sql.*; import java.io.InputStream; import java.io.FileInputStream; import java.io.IOException; import java.util.Properties; import java.util.stream.*; import java.nio.file.*; public class DBExplorer { private Properties p; private static final String CONFIGURATION_FILE ="DBConfig.txt"; public DBExplorer ( ) throws Exception { p = new Properties(); try (InputStream fis = new FileInputStream(CONFIGURATION_FILE);) { p.load(fis); } catch (IOException exc) { exc.printStackTrace(); } printContacts(); } private void printContacts() { try (Connection conn = DriverManager.getConnection(p.getProperty("db.url")); Statement stmt = conn.createStatement(); ResultSet res = stmt.executeQuery("SELECT * FROM CONTATTO")) { while (res.next()) { System.out.printf("%s\nTelefono: %s\nIndirizzo: %s\n\n", res.getString("NOME"), res.getString("TELEFONO"), res.getString("INDIRIZZO")); } } catch (SQLException e) { e.printStackTrace(); } } public static void main(String args[]) throws Exception { new DBExplorer(); } }
-
Esercizio 17.d)
Partendo dall'esercizio 15.q e dal database creato nell'esercizio 17.b e 17.c, creare una classe
GestoreDBche implementa l'interfacciaGestoreSerializzazione. In particolare:
1. Implementare il metodogetContattiper 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' interfacciaGestoreSerializzazionein modo tale che rimanga compatibile con la dichiarazione dei metodi anche delle altre implementazioni (GestoreFileeGestoreFileNIO2).
3. Di conseguenza bisognerà apportare anche delle modifiche alla classeRubricaCLIaffinché compili.
4. Modificare il fileconfig.propertiesin modo tale che venga caricata la classeGestoreDBper 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
GestoreSerializzazioneper 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 classeGestioneDBnel 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.propertiesper far sì che venga caricata la classeGestoreDBcome 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
recuperae testare il corretto funzionamento dell'applicazione.
Soluzione
Potremmo implementare il metodo
recuperanel 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
inseriscie testare il corretto funzionamento dell'applicazione.
Soluzione
Potremmo implementare il metodo
inseriscinel 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
modificae testare il corretto funzionamento dell'applicazione.
Soluzione
Potremmo implementare il metodo
modificanel 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
rimuovie testare il corretto funzionamento dell'applicazione.
Soluzione
Potremmo implementare il metodo
rimuovinel 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
PropertiesestendeHashtablema consente di salvare su un file le coppie chiave-valore rendendole persistenti.
2. La classeLocaleastrae il concetto di "zona".
3. La classeResourceBundlerappresenta un file di properties che permette di gestire l'internazionalizzazione. Il rapporto tra nome del file eLocalespecificato 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
Preferencespermette di gestire file di configurazione con un file XML.
7. La classeFormatterdefinisce il metodoprintf.
8. L'espressione regolare[a]è un quantificatore (greedy quantifier).
9. Il seguente codice:Date d = new Date(); d.now();
crea un oggetto
Datecon la data attuale.
10. UnSimpleNumberFormatpuò 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é stampitruel'espressione si deve modificare in "\\bb".
6. Falso, il modo in cui vengono immagazzinati i dati persistenti è dipendente dalla piattaforma. Un oggettoPropertiesinvece può usare anche file di configurazione in formato XML.
7. Falso, la classePrintStreamdefinisce il metodoprintf, la classeFormatterdefinisce il metodoformat.
8. Falso, è un gruppo di caratteri.
9. Falso, la classeDatenon ha un metodonow. La prima riga da sola avrebbe adempiuto al compito richiesto.
10. Falso, la classeSimpleNumberFormatsemplicemente non esiste. Ma esiste la classeSimpleDateFormat. -
Esercizio G.b)
Creare una classe
Traduttoreche espone un metodotraduciper tradurre un numero limitato di parole dall'inglese all'italiano e viceversa, utilizzando un resource bundle.
Soluzione
Il listato della classe
Traduttorepotrebbe 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
TestTraduttoreper 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 classeScannere rendere interattivo il programma. -
Esercizio G.d)
Creare una classe
StringUtilsche 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
TestStringUtilsper testare la correttezza del metodoricerca.
Soluzione
Il listato della classe
TestStringUtilspotrebbe 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 metodonextIntche prende in input un limite superiore (escluso), e che ha come limite inferiore0(incluso). Basta sommargli il valore1e 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
Utentedeve 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 classeProfiliUtentiin 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 oggettiUtentea partire dal file di properties.
Soluzione
Una possibile soluzione risiede nella modifica della classe
ProfiliUtentinel 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 metodogetUtenteche andiamo a richiamare dalla classeAutenticazioneopportunamente 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 metodomainlo 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 oggettoFormatterpermette 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.PrintStreamdefinisce il metodoprintfche sfrutta il metodo format della classeFormatter.
4. La classeFormatterdefinisce un overload del metodoformat.
5. La classeFormatterpermette 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 oggettoPreferencespermette 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 oggettoPreferencesnon usa il metodosetProperty.
3. La classePreferencesestendeHashtable.
4. La classePreferencessi istanzia senza poter utilizzare costruttori, ma metodi che restituiscono istanze. Infatti è addirittura una classe astratta.
5. La classePreferencesdefinisce metodi per inserire specifici tipi di dati come valore.
Soluzione
Tutte le affermazioni sono vere tranne la numero 3. Infatti la classePreferencesnon 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 classeMatcherdefinisce il metodofindche ritorna la stringa che coincide con l'espressione booleana diMatcher.
2. La classeMatchersi istanzia senza poter utilizzare costruttori, ma metodi che restituiscono istanze.
3. Un oggettoPatterndefinisce i metodistarteend.
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
findnon ritorna una stringa ma un booleano (il metodofindviene in fatti usato solitamente come condizione di un ciclo).
La numero 3 invece è falsa perché i metodistarteendnon appartengono alla classePatternma vengono dichiarati dalla classeMatcher.