In informatica, nello sviluppo software, il test-driven development, spesso abbreviato con la sigla TDD, è un modello di sviluppo del software che prevede che la stesura dei test automatici avvenga prima di quella del software e che lo sviluppo del software applicativo sia orientato esclusivamente all’obiettivo di passare i test automatici precedentemente predisposti. Il TDD prevede la ripetizione di un breve ciclo di sviluppo in tre fasi: nella prima fase viene scritto un test automatico, nella seconda fase viene sviluppata la quantità minima di codice necessaria per passare il test e nella terza fase viene eseguito un refactoring del codice per rispettare i livelli di qualità e leggibilità richiesti (fonte wiki).

Questo articolo non vuole essere una spiegazione accademica di cosa sono i TDD e come dovrebbero essere implementati, ma vuole essere solo una descrizione di una modalità di sviluppo con esempi pratici funzionanti di una delle tecniche più usate per realizzare TDD efficaci con il linguaggio Java. I test di questo tipo di TDD sono tutti atomici e devono verificare una sola operazione del requisito e, all’interno di ogni test, devono essere sviluppati tre passi:

  • Arrange: la definizione di tutti i dati di partenza
  • Act: la chiamata alla operazione da testare (di solito è una sola operazione ma può essere anche multipla)
  • Assert: la validazione del risultato confrontando la risposta del passo 2 con quanto definito nel punto 1

Nei progetti Java creati con Maven, in fase di creazione del progetto è presente la configurazione dei test con una classe vuota, lanciando il comando

$ mvn test

il sistema maven eseguirà tutte le classi di test presenti nel progetto, se il comando viene lanciato da riga di comando il risultato dei test viene espost con i messaggi:

[INFO] Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, 
Time elapsed: 0.033 s - in it.alnao.examples.AppTest

oppure un messaggio di errore in caso di test non passato con il messaggio

[INFO] Running it.alnao.examples.AppTest
[ERROR] Tests run: 4, Failures: 1, Errors: 0, Skipped: 1, Time elapsed: 0.031 s <<< FAILURE! - in it.alnao.examples.AppTest
[ERROR] dividiPerZero Time elapsed: 0.01 s <<< FAILURE!
org.opentest4j.AssertionFailedError: Expected java.lang.ArithmeticException to be thrown, but nothing was thrown.
at it.alnao.examples.AppTest.dividiPerZero(AppTest.java:40)

Mentre nei programmi IDE (come Visual studio Code e/o Eclipse) sono presenti diversi tool grafici e viste che permettono di eseguire i test e vedere il risultato in maniera grafica:

E’ possibile anche generare un report di maven e si usa il comando

$ mvn site

però è necessario censitre nel pom.xml una sezione dedicata di reporting:

<reporting>
  <plugins>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-surefire-report-plugin</artifactId>
    </plugin>
  </plugins>
</reporting>

La libreria comunemente più usata è quella JUnit alla versione 5 ma la maggior parte dei progetti creati con maven tramite archetipi è configurata con la 4 o con la 3, è consigliato sempre aggiornare il file di configurazione pom.xml di tutti i nuovi progetti impostando l’ultima versione di JUnit, gli esempi fanno riferimento alla versione 5 che ha modificato i package e le classi usate per eseguire il passo assert nelle test unit perché i metodi sono racchiusi nel pacakge:

import org.junit.jupiter.api.Assertions;

e in aggiunta bisogna ricordarsi che è necessario usare la versione 11 di Java.


Un esempio pratico: l’idea è di realizzare un “servizio” che effettui la divisione di due numeri, usando la metodologia TDD dobbiamo definire per prima i test che verificheranno il buon codice, per esempio possiamo partire dall’idea di verificare che il numero 5 diviso per il numero 2 dia come risultato due e mezzo e bisogna ricordare non è possibile eseguire divisioni se il divisore è zero, prevediamo un test specifico che verifichi la esecuzione di una ArithmeticException nel caso di divisore zero. Il codice di esempio di questi semplici test diventa:

@Test
@DisplayName("Dividi 42")
public void dividi42() throws Exception{
  //1) Arrange: la definizione di tutti i dati di partenza 
  Double dividendo=new Double(42.0);
  Double divisore=new Double(16.0);
  Double resultAtteso=new Double(2.625);
  //2) Act: la chiamata alla operazione da testare (di solito è una sola operazione ma può essere anche multipla)
  Double result=App.dividi(dividendo, divisore);
  //3) Assert: la validazione del risultato confrontando la risposta del passo 2 con quanto definito nel punto 1 
  Assertions.assertEquals(resultAtteso,result);
}
@Test
@DisplayName("Dividi per zero")
public void dividiPerZero(){
  //1) Arrange: la definizione di tutti i dati di partenza 
  final Double dividendo=new Double(5.0);
  final Double divisore=new Double(0);
  //3) Assert: la validazione del risultato confrontando la risposta del passo 2 con quanto definito nel punto 1 
  Assertions.assertThrows(ArithmeticException.class,
    ()->{ //2) Act: la chiamata alla operazione da testare (di solito è una sola operazione ma può essere anche multipla)
      App.dividi(dividendo, divisore);
    }
  );
}
@Test
public void shouldAnswerWithTrue() {
  Assertions.assertTrue( true );
}
@Test
@Disabled("Disabled test example")
  void disabledTest() {
  fail();
}

In questo semplice esempio sono stati usati i metodi:

  • assertEquals per confrontare il ritorno del metodo con il valore atteso
  • assertThrows per verificare che venga effettivamente lanciata l’exception corretta nel caso di divisore pari a zero
  • assertTrue per una verifica statica, può essere usato per per far fallire automaticamente un test in un ramo di codice che non deve essere eseguito con il parametro false.

Inoltre negli esempi si possono notare tutte diverse le annotazioni messe a disposizione dalla libreria, l’elenco completo di tutti i metodi disponibili e le varie annotazioni si può trovare alla documentazione ufficiale. L’esempio completo e funzionante di questo semplice metodo può essere trovato al solito repository:

https://github.com/alnao/JavaExamples/tree/master/GenericJava/14testJUnit5

ma è possibile consultare anche un repository ricco di esempi:

https://github.com/eugenp/tutorials/blob/master/testing-modules/junit-5-basics/pom.xml
Pubblicato il 16/03/2024 nella categoria Java & Spring Boot

Una delle evoluzioni principali dalla versione 8 di Java è stata l’introduzioni delle “Lambda Function” dette anche “Java Lambda Expression” che permettono di scrivere del codice senza dovergli dare un nome (metodo o classe), le possibili sintassi di questa tecnica prevedono 3 tipi sinonimi a seconda del numero di parametri e del numero di istruzioni nel blocco:

parameter -> expression
(parameter1, parameter2) -> expression
(parameter1, parameter2) -> { code block }

Il caso più semplice e più usato è l’uso delle lambda function per definire l’iterazione di una lista:

import java.util.ArrayList;
public class Main {
  public static void main(String[] args) {
    ArrayList<Integer> numbers = new ArrayList<Integer>();
    numbers.add(5);
    numbers.add(9);
    numbers.add(8);
    numbers.add(1);
    numbers.forEach( (n) -> { System.out.println(n); } );
  }
}

Ma allo stesso modo è possibile definire un oggetto “codice”:

import java.util.function.Consumer;
...
Consumer<Integer> method = (n) -> { System.out.println(n); };
numbers.forEach( method );

Si rimanda alla documentazione ufficiale, il quick start presentato dal sito ufficiale e alla pagina w3c per maggiori dettagli.


L’uso di questa tecnica è usato in moltissimi casi specifici, si elencano alcuni esempi dove il codice classi può essere sostituito con questa tecnica:

  • definizione del comportamento di un bottone:
    bottone.setOnAction(
      event -> System.out.println("Click done")
    );
  • filtri con ciclo for:
    public List<Persona> getMaschi(iscritti){
      List<Persona> persone = new ArrayList<Persona>();
      for (Persona p:iscritti)
        if (isMaschio(p))
          persone.add(p);
      return persone;
    }
  • filtri con metodo:
    Predicate<Persona> allMaschi = p -> p.getSesso().equals("M");
    public List<Persona> getIscrittiFiltratiPer(Predicate<Persona> pred){
      List<Persona> persone = new ArrayList<Persona>();
      for (Persona p:iscritti)
        if (pred.test(p))
          persone.add(p);
      return persone;
    }
    ms.getIscrittiFiltratiPer(allMaschi);
  • logiche annidate sulle liste con filter e map:
    lista.stream()
    .filter( p -> p.getGender() == Person.Sex.MALE) //filtrare elementi di una lista
    .map(p -> p.getEmailAddress()) //funzione map per modificare un elemento
    .forEach(email -> System.out.println(email));
  • definizione di interfacce funzionali:
    package java.awt.event;
    import java.util.EventListener;
    public interface ActionListener extends EventListener {
      public void actionPerformed(ActionEvent e);
    }
  • runnable block:
    public class RunnableTest {
      public static void main(String[] args) {
        System.out.println("=== RunnableTest ===");
        Runnable r1 = new Runnable(){// Anonymous Runnable
        
        @Override
        public void run(){
          System.out.println("Hello world old style!");
        }
      };
      // Lambda Runnable
      Runnable r2 = () -> System.out.println("Hello world with Lambda!");
        r1.run();
        r2.run(); 
      }
    }

La tecnica delle Lambda Function è stata introdotta per semplificare la vita dei programmatori, assieme alla tecnica delle classi innestate, questa tecnica è molto utile e usata tuttavia spesso rende il codice molto meno leggibile e l’utilizzo deve sempre essere pensato, si rimanda alla pagina ufficiale che consiglia quando usare e quando non usare queste tecniche.

Una nota obbligatoria è il nome di queste funzioni: “Lambda Function” e “Lambda Expression” sono nomi usati anche da altre tecnologie, come le lambda function di AWS, per questo motivo è sempre consigliato usare il nome con il linguaggio di programmazione “Java Lambda Function” per evitare fraintendimenti, poi ovviamente è possibile usare “Java Lambda Function” per definire delle “AWS Lambda Function” ma questa è una sega mentale di noi programmatori che lavorano con Java in Cloud.

Pubblicato il 24/02/2024 nella categoria Java & Spring Boot

All’interno delle applicazioni J2EE è possibile creare connessioni verso Server FTP utilizzando il protocollo omonimo o il protocollo specifico più sicuro di tipo SFTP. Non esiste una unica libreria standard per effettuare questo tipo di connessioni. La libreria principale è stata implementata da apache e mette a disposizione i package:

import org.apache.commons.net.ftp

Con la libreria importabile nei file di configurazione pom.xml:

<dependency>
  <groupId>commons-net</groupId>
  <artifactId>commons-net</artifactId>
  <version>3.6</version>
</dependency>

Con questa libreria si può creare una connessione ad un server FTP (senza lo strato di sicurezza)

public void open() throws IOException {
  ftp = new FTPClient();
  ftp.addProtocolCommandListener(new PrintCommandListener(new PrintWriter(System.out)));
  ftp.connect(server, port);
  int reply = ftp.getReplyCode();
  if (!FTPReply.isPositiveCompletion(reply)) {
    ftp.disconnect();
    throw new IOException("Exception in connecting to FTP Server");
  }
  ftp.login(user, password);
}
public void close() throws IOException {
  ftp.disconnect();
}
public List<Object> listFiles(String path) throws IOException {
  FTPFile[] files = ftp.listFiles(path);
  return Arrays.stream(files)
    .map(FTPFile::getName)
    .collect(Collectors.toList());
}

Mentre per il protocollo SFTP esistono due librerie: JSch di jcraft e Vfs2 di apache, rispettivamente importabili nei progetti con le librerie:

<dependency>
  <groupId>com.jcraft</groupId>
  <artifactId>jsch</artifactId>
  <version>0.1.55</version>
</dependency>
<dependency>
  <groupId>org.apache.commons</groupId>
  <artifactId>commons-vfs2</artifactId>
  <version>2.9.0</version>
</dependency>

Con queste librerie è possibile usare il protocollo con le chiavi di sicurezza previste del protocollo SFTP, si rimanda alla documentazione ufficiale delle due librerie per maggiori dettagli. Con la libreria Vfs2 di Apache è possibile collegarsi ad un server remoto con poche righe di codice:

public List<String> ls(String path) throws Exception {
  StandardFileSystemManager fsManager = new StandardFileSystemManager();
  fsManager.init();//Initializes the file manager
  FileSystemOptions opts = new FileSystemOptions();
  SftpFileSystemConfigBuilder.getInstance().setStrictHostKeyChecking(opts, "no");
  String sftpUri = getConnection(path).toString() ;
  File[] identities = { new File(privateKey) };
  SftpFileSystemConfigBuilder.getInstance().setIdentities(opts, identities);
  FileObject localFileObject=fsManager .resolveFile (sftpUri, opts );
  FileObject[] children = localFileObject.getChildren();
  ArrayList<String> l=new ArrayList<String>();
  for ( int i = 0; i < children.length; i++ ){
    l.add ( children[ i ].getName().getBaseName() );
  } 
  fsManager.close();
  return l;
}

Gli esempi completi e funzio0nanti possono essere trovati in un mini-progetto al solito repository:

https://github.com/alnao/JavaExamples/tree/master/GenericJava/12FTP
Pubblicato il 03/02/2024 nella categoria Java & Spring Boot

Per quanto riguarda il termine WebService esiste una definizione data dalla W3C: si tratta di un software progettato per collegare più punti di una rete in un contesto distribuito come il tipo client-server o server-server. Nelle applicazioni Java esistono diverse librerie e framework che permettono ai programmatori di costruire applicazioni è possibile creare applicazioni per l’esposizione di servizi, le principali modalità sono: applicazioni web, web service SOAP e servizi REST.

Tipicamente le applicazioni web sono costruite con l’architettura J2EE che permettono di creare applicazioni navigabili con un browser attraverso il protocollo HTTP comunemente chiamato WWW, nelle applicazioni è possibile aggiungere framework evoluti come Struts o Spring per gestire servlet e migliorare la struttura delle applicazioni. Si rimanda ai vari articoli dedicati a questi argomenti per maggior approfondimenti.


Web service esposti con il protocollo SOAP possono essere creati con le librerie Axis2, JaxWS oppure CXF, grazie a queste librerie è possibile creare servizi di tipo SOAP-XML o simili, si rimanda alla documentazione ufficiale di Apache-Axis per maggior in formazioni riguardo a questa libreria. Esistono molti tutorial e guide che descrivono i passi necessari per creare webservice con questa libreria, in particolare questi descrivono come un semplice esempio di servizio:
public int add(int num1, int num2){
    return num1+num2;
}

possa essere descritto come servizio tramite un file XML:

<service>
    <parameter name="ServiceClass" locked="false">
        com.chamiladealwis.ws.service.SimpleService
    </parameter>
    <operation name="sayHello">
        <messageReceiver class="org.apache.axis2.rpc.receivers.RPCMessageReceiver" />
    </operation>
</service>

La libreria poi permette di esportare il servizio aar (Axis2 Archive) e possono essere distribuiti tramite il pacchetto war di Axis2.

Il programma Eclipse mette a disposizione una procedura guidata molto semplice ed efficace per creare servizi con questa libreria, anche in questo caso si rimanda al sito ufficiale dove è possibile trovare un semplice tutorial. Grazie a questa procedura è possibile creare webservice esposte con un il protoccolo SOAP e un WSDL:
 

questa libreria un po’ datata e non a volte non funziona con le ultime versioni di Eclipse e del servet Tomcat.

E’ possibile anche usare la libreria JaxWS per la definizione di questo tipo di webservice senza l’uso di Eclipse e Maven, si rimanda sempre alla documentazione ufficiale.

Tramite la libreria Jersey è possibile creare semplici servizi REST, grazie a maven è possibile selezionare il tipo base “Jersey-quickstart-webapp” presente nel repository “org.glassfish.jersey”, questo archetipo costruisce una applicazione web con la servelet configurata già pronta all’uso e una piccola classe di esempio che crea e espone un semplice servizio con una risorsa e il metodo GET dello standard HTTP, il codice di esempio della classe di esemepio:

@Path("/myresource")
public class MyResource {
@GET
@Produces(MediaType.TEXT_HTML)
public String sayHtmlHello() {
  return "<html> " + "<title>" + "Hello " + "</title>"
      + "<body><h1>" + "Hello " + "</body></h1>" + "</html> ";
}
@GET
@Path("/json")
@Produces(MediaType.APPLICATION_JSON)
public Response hello() {
  Persona p=new Persona("Alberto");
  return Response.status(Response.Status.OK).entity(p).build();
}
Questo servizio può essere richiamato da un piccolo client di esempio:
public class App{
    public static void main( String[] args )    {
    System.out.println("App 11RestClient");
        ClientConfig config = new ClientConfig();
        Client client = ClientBuilder.newClient();// newClient(config);
        WebTarget target = client.target(getBaseURI());
        javax.ws.rs.core.Response response = target.path("webapi").
                            path("myresource/json").
                            request().
                            accept(MediaType.APPLICATION_JSON).
                            get(Response.class);//.toString();
        System.out.println(response.toString());
        System.out.println(response.readEntity( String.class ) );
    }
    private static URI getBaseURI() {
        return UriBuilder.fromUri("http://localhost:8081/11RestServer").build();
    }
}

L’esempio completo e funzionante può essere trovato nel solito repository:

https://github.com/alnao/JavaExamples/tree/master/GenericJava
Pubblicato il 13/01/2024 nella categoria Java & Spring Boot

La relazione tra AWS è il linguaggio di programmazione Java è sempre stato molto travagliato visto che si tratta di un linguaggio di proprietà della concorrenza ma visto che è uno dei linguaggi più usati al mondo è stato inevitabile al gestore del Cloud permettere l’uso del linguaggio nei vari servizi. In questo articolo vengono riassunti alcune modalità di sviluppo delle lambda function in linguaggio Java. Il titolo di questo articolo fa sorgente immediata un grosso problema problema: le Java Lambda Function (o lambda expression) sono una tecnica per scrivere blocchi di codice anonimi del tipo:

(parameter1, parameter2) -> { codeBlock(); }

questa tecnica non è da confondere con le AWS Lambda Function, servizio specifico per definire blocchi di codice eseguibile in modalità serverless. In questo sito si cerca sempre di distinguere le due cose anche se, avendo lo stesso nome, è impossibile non fare confusione.

Ad oggi, le versioni supportate sono le versioni stabili dalla 8 alla recente 21, in questo articolo si fa riferimento sempre alla versione 8 per retro-compatibilità, per maggior informazioni si rimanda sempre alla documentazione ufficiale dove sono indicati anche i passi per la creazione di AWS Lambda function da console web. Inoltre è disponibile una bellissima libreria SDK per permettere l’integrazione con altri servizi del Cloud, si rimanda sempre alla documentazione ufficiale per maggiori dettagli.

Come per le AWS Lammbda Function scritte negli altri linguaggi, è necessario definire un punto di ingresso detto Handler che quasi sempre viene indicato con un metodo chiamato handleRequest, questo metodo poi definisce la logica di implementazione:

package example;
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.LambdaLogger;
import com.amazonaws.services.lambda.runtime.RequestHandler;
public class HandlerIntegerJava implements RequestHandler<String, String>{
  @Override
  public String handleRequest(String event, Context context){
    LambdaLogger logger = context.getLogger();
    logger.log("EVENT TYPE: " + event.getClass().toString());
    return event.toLowerCase();
  }
}

Nella definizione da console o in maniera programmatica è necessario poi indicare il pacakge e il Handler:


Attraverso l’ambiente di sviluppo MS Visual studio è possibile creare e gestire le AWS Lambda function in maniera veloce ed efficace, è consigliato l’utilizzo dell’estensione ufficiale AWS Toolkit:

che permette di gestire, creare e invocare le AWS Lambda function in combinazione alla CLI e alla CLI-SAM indispensabili per l’utilizzo di questi sistemi.

Per creare una lambda da zero è possibile usare il comando:

> Create lambda SAM application

dalla riga di comando dell’applicazione (Ctrl+Maiusc+P), in questa piccola procedura guidata è possibile selezionare il tipo di linguaggio (java e maven), il modello (consigliato il base HelloWorld) e la cartella dove salvare il progetto. Il generato è un piccolo progetto con i componenti:

  • cartella event con un file json di esempio che permetterà di eseguire i test da locale
  • il file template.yaml con il template completo di cloudformation con la definizione della lambda e la definizione della API tramite gateway
  • il progetto java-maven in una sottocartella con la classe App già pronta per essere eseguita

Da notare che trattandosi di un piccolo template CloudFormation è possibile eseguire il rilascio in AWS con i comandi CLI-SAM:

sam build 
sam deploy --guided 
sam delete --stack-name es03j

Ma è possibile eseguire il rilascio anche con il plugin del programma con il comando:

> AWS deploy sam application

La guida completa del plugin e di tutte le funzionalità disponibili è presente nel sito ufficiale. Bisogna sempre ricordare che è possibile invocare le AWS Lambda function da CLI con il comando:

aws lambda invoke --function-name function_name --payload file://event.json out.json

da console oppure anche dal programma Visual Studio è possibile eseguirle con una semplice interfaccia. Il progetto di esempio creato dal plugin è più complesso rispetto al metodo semplice descritto sopra in quanto questo prevede in request un oggetto proveniente dal ApiGateway e in response un oggetto secondo il formato http standard per il ApiGateway, l’esempio base prevede questa forma:

public APIGatewayProxyResponseEvent handleRequest(
    final APIGatewayProxyRequestEvent input, final Context context) {
  Map<String, String> headers = new HashMap<>();
  headers.put("Content-Type", "application/json");
  headers.put("X-Custom-Header", "application/json");
  APIGatewayProxyResponseEvent response = 
    new APIGatewayProxyResponseEvent().withHeaders(headers);
  try {
    final String pageContents = "hello world";
    String output = String.format("{ \"message\": \"%s\" }", pageContents);
    return response.withStatusCode(200).withBody(output);
  } catch (IOException e) {
    return response.withBody("{}").withStatusCode(500);
  }
}

Il codice di queste semplici AWS Lambda Function scritte in linguaggio Java sono disponibili in un repository specifico:

https://github.com/alnao/AwsLambdaExamples

Negli esempi creati da Visual Studio Code, il plugin genera un template CloudFormation per la gestione delle risorse AWS, in particolare la CLI-SAM provvede anche alla compilazione della classe java e al rilascio delle classi, bisogna ricordare che il file jar deve essere rilasciato in un bucket S3, anche usando la funzionalità del plugin:

> AWS deploy sam application

bisogna indicare il nome del bucket di appoggio. Nel output del comando viene visualizzato lo stato di avanzamento del rilascio e l’esito finale del caricamento.

Con CloudFormation è possibile creare una funzione AWS-Lambda in maniera molto veloce usando maven e i vari modelli messi a disposizione dal framework serverless, in particolare la classe java deve essere definita in un progetto maven con le dipendenze già viste in precedenza. Nel template di definizione delle risorse è possibile definire le varie caratteristiche della funzione e la definizione di un metodo/stage con API-Gateway per esporre la funzione come servizio HTTP-Rest:

HelloWorldFunction:
  Type: AWS::Serverless::Function
  Properties:
    CodeUri: HelloWorldFunction
    Handler: helloworld.App::handleRequest
    Runtime: java8
    Environment:
      Variables:
         PARAM1: VALUE
    Events:
      HelloWorld:
        Type: HttpApi
        Properties:
          ApiId: !Ref ES15HttpApi
          Path: /hello
          Method: GET
ES15HttpApi:
  Type: AWS::Serverless::HttpApi
  Properties:
    StageName: !Ref StageName
    RouteSettings:
      "GET /hello":
        ThrottlingBurstLimit: 500

L’esempio completo è disponibile al solito repository:

https://github.com/alnao/AWSCloudFormationExamples/tree/master/Esempio15lambdaJava
Pubblicato il 30/12/2023 nella categoria AWS, Java & Spring Boot

Quando si pensa ai sistemi GNU Linux si pensa anche al matrimonio del secolo chiamato LAMP: l’unione perfetta tra GNU Linux, Apache, MySql & Php. Questi quattro compongono uno stack tecnologico gratuito e open source utilizzabile per creare un server web completo. Possono essere installati separatamente selezionando i vari pacchetti ma è consigliato eseguire l’installazione unendo i pacchetti con un semplice comando:

apt-get install apache2 mariadb-client mariadb-server php8.2 php8.2-mysql libapache2-mod-php8.2

oppure i pacchetti possono essere installati dai programmi di gestione dei pacchetti di Debian. In questo articolo viene usata la versione 8.2 di PHP ma si possono usare anche versioni precedenti o successive se presenti nel repository ufficiale, è sconsigliato usate versioni di pacchetti non ufficiali.

Una volta installati i pacchetti questi vengono auto-configurati tanto che il server web viene attivato automaticamente e risulta disponibile all’indirizzo:

http://localhost/

Se un programmatore vuole configurare Apache inserendo una nuova cartella web deve andare a modificare i file di configurazione del server Apache, purtroppo non esiste una applicazione in grado di farlo in maniera grafica e veloce: per attivare una nuova applicazione web è necessario modificare il file di configurazione di Apache

/etc/apache2/apache2.conf

controllando che nel file siano presenti le seguenti righe:

ServerName localhost
DirectoryIndex index.html index.cgi index.pl index.php index.xhtml
AddType application/x-httpd-php .php
AddType application/x-httpd-php-source .phps

e, se si vuole configurare la cartella per ogni utente bisogna aggiungere una sezione

UserDir public_html
<Directory /home/*/pubblic_html>
  Options Indexes SymLinksIfOwnerMatch IncludesNoExec
</Directory>

da notare che alcune righe potrebbero essere già presenti ma precedute dal simbolo # che è il simbolo per commentare una riga, in questo caso basta cancellare il carattere cancelletto. Con l’ultimo blocco è stato configurato una cartella web per ogni utente configurato nel sistema, cioè basterà andare sulla home di un utente, creare una cartella con il nome public_html e questo sarà accessibile dal web all’indirizzo:

http://localhost/~nomeutente/

Dopo aver modificato il file di configurazione, per rendere effettive le modifiche, è necessario riavviare il server con il comando:

# systemctl restart apache2

La cartella web virtuale di default è la cartella /var/www/ ma è consigliato non utilizzarla in fase di sviluppo ed è possibile configurare sottocartelle: per creare una cartella specifica esposta dal webserver, basta modificare il file di configurazione aggiungendo un blocco di codice specifoco indicando i path e il nome:

Alias "/Php/" "/mnt/Dati/Php/"
<Directory "/mnt/Dati/Php/">
  Options Indexes FollowSymLinks Includes
  AllowOverride All
  Order deny,allow
  Allow from all
  Require all granted
</Directory>

(bisogna sempre ricordarsi di prestare la massima attenzione alla differenza maiuscole/minuscole sia per i nomi delle cartelle sia per i parametri di configurazione!).


Il demone database MySql è il più utilizzato al mondo per la creazione di applicazioni, per Debian i pacchetti sono disponibili nella versione mariadb che è la versione open-source e libera e sono previsti due pacchetti principali (mariadb-client e mariadb-server), all’installazione il demone è sprovvisto di password e bisogna sempre ricordarsi di eseguire la configurazione base con il comando:

$ mysql_secure_installation

oppure impostando manualmente le password principali con i comandi dalla console mysql:

$ mysql
> use mysql;
> UPDATE user SET password=PASSWORD('password') where User='root';
> GRANT ALL ON *.* TO 'alnao'@'localhost' IDENTIFIED BY 'password';
> FLUSH PRIVILEGES;
> quit;

Da notare che con il comando mysql è possibile accedere alla console del database con il quale è possibile lanciare comandi e query, in console viene usato il carattere > per indicare che ci si trova nella console del database e non in una shell bash di GNU Linux. Dopo aver configurato la password dell’amministratore bisogna riavviare il demone con il comando:

# systemctl restart mariadb

per rendere effettive le modifiche alla password di root.
Un semplice esempio di utilizzo della console del dabatase:

$ mysql
> USE test;
> SHOW TABLES;
> CREATE TABLE prova (Nome char(120), Sito char(120));
> INSERT INTO prova (Nome,Sito) VALUES ('Alberto Nao','www.alnao.it');
> SELECT * FROM prova;

Con questi comandi è stata creata una piccola tabella nel database test, inserita una riga sulla tabella e l’ultima query visualizza la riga appena inserita, in questo modo sono state eseguite tutte le istruzioni base del demone MySql. Per quanto riguarda l’applicazione-sito PhpMyAdmin, dalla versione 10 di Debian, non è più disponibile nei Repository ufficiali e deve essere scaricato manualmente dal sito ufficiale e posizionato in una cartella per poi lanciare i comandi di configurazione (per la configurazione dei permessi e del apache.conf).
In alternativa all’ormai obsoleto PhpMyAdmin è consigliabile usare programmi più evoluti per la gestione del database come MySql-Workbench, per installarlo basta usare il repository snap e lanciare il comando:

snap install mysql-workbench-community

Il demone Mysql pevede anche alcuni comandi speciali per la gestione da riga di comando del demone, per esempio per effettuare il backup di un database non può usare il comando:

# mysqldump -u user -p password nomeDatabaseSorgente > file.sql

il backup viene eseguito in un file con estensione sql, per eseguire il restore (dallo stesso file sql) basta lanciare il comando

# mysql -u user -p password nomeDatabaseDestinazione < file.sql

Da notare che il comando mysqldump permette di collegare due server MySql per trasferire dati tra i due server, per questo e tutti gli altri comandi si rimanda alla ufficiale MySql.


Per quanto riguarda la programmazione Web con i linguaggi di scripting Php o gli altri linguaggi, ci sono moltissimi programmi grafici che permettono lo sviluppo, alcuni esempi sono: screem, BlueFish, QuantaPlus anche se è consigliato l’utilizzo di Eclipse o Visual Studio Code.

Il più semplice esempio di file php è il classico file con le informazioni base :

echo '<?php phpinfo(); ?> ' > /var/www/html/test.php

che risulta disponibile nel server apache. I moduli php sono installabili dal gestiore dei pacchetti e l’elenco dei moduli installati sono consultabili con il comando

$ php -m

Il file di configurazione base del motore php si chiama

php.ini

che nei sistemi Debian si trova nel path

/usr/lib/php/8.2

Con questo comando è possibile modificare le configurazioni del interprete/compilatore, si rimanda alla documentazione ufficiale per maggior dettagli riguardo a questo tema.


Per guardo riguarda la programmazione di applicazioni web Node.js e NPM hanno cambiato il mondo facilitando il lavoro degli sviluppatori: Node.js è lo strumento che consente agli sviluppatori di eseguire script al di fuori del browser web mentre NPM è il gestore di pacchetti per la gestione dei moduli Nodejs. L’installazione di questi due tool in una distribuzione Debian è facile e prevede l’installazione di due pacchetti

apt-get install nodejs npl curl -y

Al termine dell’instazzione è sempre necessario impostare i permessi in modo che qualunque utente possa installare moduli con Npm, questi permessi possono essere assegnati con i comandi

# mkdir /usr/local/lib/node_modules
# chmod 777 /usr/local/lib/node_modules
# chmod 777 /usr/local/bin/
# node export NODE_OPTIONS=--openssl-legacy-provider

Esistono molte guide che descrivono il processo di installazione di nuove versioni da repository esterni che spesso sono più aggiornate ma per un ambiente di sviluppo stabile è consigliato l’uso dei repository ufficiali. Per creare e provare una applicazione basata sulla libreria Angular basta lanciare i comandi:

$ npm install -g @angular/cli
$ ng new prova
$ cd prova
$ ng serve

Mentre per testare il funzionamento di un piccolo progetto basato sul framework React è possibile creare un progetto con:

$ npx create-react-app prova
$ cd prova
$ npm start

e poi andare all’indirizzo della applicazione web locale. Si rimanda alla documentazione dei tool e delle librerie per maggior in formazioni.


Il rapporto tra GNU Linux e Java è sempre stato un po’ travagliato, questo perché le società che hanno i diritti sul linguaggio e sui compilatori non rilasciavano versioni open-source andando in contrasto con le politiche open di GNU Linux e di Debian, questo portò molte distribuzioni a togliere le versioni ufficiali java e comprendere alcune JVM (Java Virtual Machine) alternative e libere (come Cacao, Javacc, Gcj). Fortunatamente, sono state create nel tempo versioni open dell’ambiente JDK, queste sono state nuovamente inserite nei repository ufficiali ed oggi sono disponibili pacchetti con il nome “openjdk”. L’ambiente Java runtime in Debian cioè il famoso JRE può essere trovato nei pacchetti openjdk-11-jre oppure le versioni precedenti a seconda della versione richiesta, la versione 11 dovrebbe essere istallato in automatico all’installazione iniziale del sistema base mentre il pacchetto per il plugin per browser era icedtea-8-plugin ma oggi è considerato obsoleto.

Se presente un ambiente LAMP, è possibile aggiungere il demone Tomcat per utilizzare le vostre applicazioni web che usano servlet e applicazioni in Java come semplici JSP. Per l’installazione i passi da seguire sono veloci: Debian mette a disposizione dei pacchetti già preconfigurati con delle impostazioni base adatte a chi vuole programmare in locale, è ovvio che Tomcat necessita di configurazioni avanzate se il server deve essere usato come server di produzione ma, se lo scopo è usarlo come server di sviluppo, è possibile sfruttare le configurazioni base che Debian vi mette a disposizione; dopo aver verificato di aver installato il LAMP correttamente, vi basta installare tutti i pacchetti che iniziano con il nome tomcat10 e l’installazione è terminata, poi dovete modificare il file:

/etc/tomcat10/tomcat-users.xml

dove vengono censiti gli utenti che possono accedere alla console di amministrazione di Tomcat, per esempio dovete aggiugnere la riga:

<role rolename="manager-gui" />
<role rolename="admin-gui" />
<user username="root" password="mypassword" roles="manager-gui,admin-gui"/>

ovviamente all’interno del root-tag <tomcat-users>, fatto questo dovete riavviare Tomcat con il comando:

# systemctl restart tomcat9

e provare ad accedere alla console all’indirizzo web

http://localhost:8080/manager/html

potete installare le vostre applicazioni web sotto forma di file WAR (Web Application aRrchives) oppure nel formato EAR (Enterprise ARchives) oppure all’indirizzo

http://localhost:8080/docs/manager-howto.html

è possibile trovare una piccola documentazione su Tomcat. L’ambiente di sviluppo Eclipse è disponibile nei repository ufficiali anche se è consigliato l’installazione tramite snap visto che il pacchetto risulta più aggiornato. Grazie a Tomcat è potete collegare il vostro ambiente di sviluppo con il server web per gestire il demone direttamente dalla vista Server di Eclipse, bisogna infatti configurare il server nelle preferenze ma, per praticità e semplicità, è consigliato creare un duplicato del server installato: in questo modo ci saranno due demoni Tomcat attivi nel sistema (quello del sistema già auto-configurato automaticamente e quello gestito da Eclipse per lo sviluppo), per configurare questa logica basta creare una cartella di link virtuali da di comando:

# mkdir /usr/share/tomcat10b
# cd /usr/share/tomcat10b
# ln -s /var/lib/tomcat10/conf conf
# ln -s /etc/tomcat10/policy.d/03catalina.policy conf/catalina.policy
# ln -s /var/log/tomcat10 log
# ln -s /var/lib/tomcat10/common common
# ln -s /var/lib/tomcat10/server server
# ln -s /var/lib/tomcat10/shared shared
# cp /usr/share/tomcat10/* /usr/share/tomcat10b/ -r
# chmod -R 777 /usr/share/tomcat10b/conf

e bisogna poi impostare nelle preferenze di Eclipse un server di tipo “Tomcat 10” nella cartella

/usr/share/tomcat10b/

così facendo nel workspace viene creato in automatico un progetto Servers con alcuni file di configurazione del demone, in questo progetto è presente un file server.xml dove è indispensabile impostare le porte HTTP diverse da quelle di default perché sono già occupate dal demone del sistema, per esempio basta cambiare 8080 in 8081 (per le applicazioni) e 8005 in 8082 (per la parte amministrativa del demone).

Per Eclipse sono consigliati i seguenti plugin scaricabili dal marketplace ufficiale:

  • Eclipse enterprise java and web dev tools 3.28
  • Eclise JST server adapters
  • Eclipse Web developer tools 3.28
  • maven 1.2.0
  • PhP development tools 7.0
  • spring tools 4 (oppure in alternativa la versione 3.9.13)

Il tool maven e glade sono disponibili nel pacchetto ufficiale e possono essere facilmente scaricati dai repository di Debian tramite i programmi di gestione dei pacchetti.

Pubblicato il 30/12/2023 nella categoria Debian, Java & Spring Boot

Con la sigla GUI (Graphical User Interface) si intendono le applicazioni con una interfaccia grafica che permette all’utente di iteragire con i dati, questo concetto comprende anche le applicazioni formate da finestre, bottoni, labels e così via. Le librerie Swing e JavaFX sono tra le due più comuni usate per creare applicazioni GUI con il linguaggio Java, in questo articolo sarà introdotta la prima libreria che è la più usata dai programmatori anche se è sempre meno usata visto che questo tipo di applicazioni è stato sostituito dalle applicazioni web. Questa si basa su’altra libreria AWT di cui è, de facto, una estensione, si rimanda ai tantissimi siti per maggiori informazioni riguardo alla libreria e le sue caratteristiche.

La guida ufficiale introduce un semplice esempio dove si costruisce una semplice classe per la visualizzazione di una finestra con un messaggio semplice messaggio:

import javax.swing.*; 
public class HelloWorldSwing {
  private static void createAndShowGUI() {
    //Create and set up the window.
    JFrame frame = new JFrame("HelloWorldSwing");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    //Add the ubiquitous "Hello World" label.
    JLabel label = new JLabel("Hello World");
    frame.getContentPane().add(label);
    //Display the window.
    frame.pack();
    frame.setVisible(true);
  }
  public static void main(String[] args) {
    //Schedule a job for the event-dispatching thread:
    //creating and showing this application's GUI.
    javax.swing.SwingUtilities.invokeLater(new Runnable() {
      public void run() {
        createAndShowGUI();
      }
    });
  }
}

Per la creazione di un progetto si può usare qualunque archetipo di tipo maven, la libreria Swing infatti si trova nel package base javax che si trova in qualunque SDK standard e non necessita librerie aggiuntive aggiunte come dipendenze nel file di configurazione.

Questa libreria si basa principalmente sull’uso della classe JFrame che definisce il comportamento della principale finestra, per esempio:

frame = new JFrame("Aws J Console");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.setLocationByPlatform(true);
frame.setSize(200,200);
frame.setContentPane(contentPane); 
frame.pack();
frame.setJMenuBar(Menu.createMenuBar(... , this ));
frame.setVisible(true);

si rimanda alla documentazione ufficiale per maggiori dettagli.


Un bellissimo progetto di applicazione costruita in Java con Swing è Jedit: un semplice editor di testo costruito per funzionare su tutti i sistemi operativi.

Per esempio per definire una lista con un metodo che descrive il compormaento al click di un elemento

public JScrollPane createListPanel(...) throws FileNotFoundException {
  String[] columnNames = {"NOME"};
  String[][] data = new String[listB.size()][1];
  for (int i=0;i<listB.size();i++) {
    data[i][0]=listB.get(i).name();
  } 
  JTable table = new JTable(data , columnNames){
    private static final long serialVersionUID = 1L;
    public boolean isCellEditable(int row, int column) { 
      return false; 
    };
  };
  table.getColumnModel().getColumn(0).setPreferredWidth(200);
  table.setPreferredSize(new Dimension(100,200));
  table.addMouseListener(new java.awt.event.MouseAdapter() {
    @Override
    public void mouseClicked(java.awt.event.MouseEvent evt) {
      JTable target = (JTable) evt.getSource();
      int row = target.rowAtPoint(evt.getPoint());
      int col = target.columnAtPoint(evt.getPoint());
      System.out.println("Click on" + row + "-" + col);
    }
  });
  JScrollPane p = new JScrollPane(table);//table
  p.setPreferredSize(new Dimension(105,205));
  return p;
}

Per ogni componente sono disponibili molto esempi e tutorial, per esempio sono disponibili molti esempi per la gestione di elenchi e tabelle:

Una semplice applicazione costruita con questa libreria per la gestione di un account AWS, scritta da Alberto Nao, è disponibile nel repository:

https://github.com/alnao/AwsAlNaoConsole/tree/master/awsJConsole
Pubblicato il 23/12/2023 nella categoria Java & Spring Boot

La libreria Hibernate è usata per creare una mappa tra le classi java e il modello di una base dati e si integra perfettamente con il concetto di persistenza dei dati verso il database visto che implementa le specifiche JPA (Java Persistence API) per la persistenza dei dati. In tutto il mondo è la libreria più usata anche da framework più evoluti come Spring e Spring Boot e. Come indicato nel sito ufficiale oltre alla libreria base per il collegamento con la base dati contiene anche altre sotto-librerie come la Hibernate Search (ricerca full-text), Hibernate Validator (gestione dei vincoli), Hibernate OGM (supporto Java Persistence per database NoSQL), Hibernate Tools (raccoglie strumenti a riga di comando e plug-in per lavorare con Hibernate).

Per importare la libreria in un progetto (dal semplice maven-archetype-quickstart al più complesso) basta importare nel pom.xml le librerie:

<dependency>
  <groupId>org.hibernate</groupId>
  <artifactId>hibernate-core</artifactId>
  <version>4.3.5.Final</version>
</dependency>
<dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
  <version>8.0.17</version>
</dependency>

come è possibile notare anche in questo caso è necessaria una libreria di connessione di tipo JDBC alla base dati, infatti la libreria necessita di un file di configurazione, che di solito si chiama hibernate.cfg.xml che i parametri di configurazione della base dati come la stringa di connessione, le credenziali e i parametri base, inoltre nel file di configurazione è necessario censire la lista delle classi

<hibernate-configuration>
  <session-factory>
    <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
    <property name="hibernate.connection.url">jdbc:mysql://X.Y.Z.U:3306/uat</property>
    <property name="hibernate.connection.username">admin</property>
    <property name="hibernate.connection.password">xxxxxxx</property>
    <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
    <property name="hibernate.current_session_context_class">thread</property>
    <property name="hibernate.show_sql">true</property>
    <property name="show_sql">true</property>
    <mapping class="it.alnao.hibernate.Model"/>
    <!-- <mapping resource="Models.xml"></mapping>-->
  </session-factory>
</hibernate-configuration>

Queste classi mapping definiscono la struttura dell’oggetto con le proprietà, con in aggiunta le informazioni della base dati con delle annotation, come il nome della tabella nella annotation table, il nome delle colonne e le proprietà nelle varie annotation previste dalla libreria

@Entity
@Table(name= "test_alberto", 
uniqueConstraints={@UniqueConstraint(columnNames={"id"} ) } ) 
public class Model{
  private long id;
  private String nome;
  private String cognome;
  
  @Id
  @Column(name = "id", unique = true, nullable = false)
  public long getId() {
    return id;
  }
  public void setId(long id) {
    this.id = id;
  }
 
  @Column(name = "nome")
  public String getNome() {
    return nome;
  }
  ....
}

Per funzionare la libreria deve essere caricata in esecuzione, prima delle operazioni dei database quindi viene spesso definita una classe di utilità per il recupero della libreria e del file di configurazione:

public class HibernateUtil {
 private static final SessionFactory sessionFactory = buildSessionFactory();
 private static SessionFactory buildSessionFactory() {
  SessionFactory sessionFactory = null;
  try {
    Configuration configuration = new Configuration();
    configuration.configure("hibernate.cfg.xml");
    System.out.println("Hibernate Configuration loaded");
    ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder()
      .applySettings(configuration.getProperties()).build();
    System.out.println("Hibernate serviceRegistry created");
    sessionFactory = configuration.buildSessionFactory(serviceRegistry);
    return sessionFactory;
  }catch (Exception e) {
    e.printStackTrace();
  }
  return sessionFactory;
 }
 public static SessionFactory getSessionFactory() {
  return sessionFactory;
 }
}

Per richiamare la libreria ed eseguire operazioni sul database è necessario richiamare prima la classe di utilità e poi si possono eseguire le operazioni sulla base dati:

public static void main( String[] args ) {
  Model m=new Model();
  m.setId(1);
  m.setNome("Alberto");
  m.setCognome("Nao");
  Session session = HibernateUtil.getSessionFactory().openSession();
  try{
    session.save(m); 
    session.flush();
    System.out.println("Saved Successfully.");
    Query q = session.createQuery("from Model");
    List<Model> resultList = q.list();
    System.out.println("num:" + resultList.size());
    for (Model next : resultList) {
      System.out.println("- " + next);
    }
    session.delete(resultList.get(0));
  }catch (Exception e) {
    e.printStackTrace(); 
  }finally {
    session.close(); 
  } 
}

Come notato in questo semplice esempio viene inserito un elemento, viene eseguita una query di selezione e successivamente viene cancellato. La query viene eseguita con il linguaggio HQL (Hibernate Query Language) che è molto simile al SQL con la differenza che si usano i nomi java e non i nomi della base dati. Si rimanda alla documentazione ufficiale per maggior informazioni riguardo a questa tecnica.

La libreria permette di censire i modelli come file xml al posto delle classi java: nel file di configurazione si deve aggiungere il riferimento ad un file xml esterno (come possibile vedere nell’esempio sopra nella riga commentata) e in questo file si devono censire la lista delle tabelle e la classe corrispondente e la lista di tutti i campi della base dati con i nomi delle proprietà java corrispondenti. Per esempio:

<hibernate-mapping package="it.alnao.hibernate">
  <class name="Model" table="test_alberto" >
    <id name="id" type="long" column = "id">
      <generator class="native"/>
    </id>
    <property name="nome" type="string" column="name" />
    <property name="cognome" type="string" column="cognome" />
  </class>
</hibernate-mapping>

Questa tecnica eviterebbe l’uso delle tante annotation nelle classi java, tuttavia nel tempo non è stata molto usata e ha sempre più preso piede l’uso delle classi con le annotation senza il mapping separato, tecnica che sarà spesso usata nei prossimi esempi visto che per Spring Boot è lo standard.

Questa libreria con queste tecnica permette di costruire applicazioni perfettamente in linea con la filosofia del MVC, dove Hibernate e il suo file di configurazione si prende il compido del Model, delegando ad altre librerie (come Struts o Spring) il compito di gestire le View e il controller.

Pubblicato il 02/12/2023 nella categoria Java & Spring Boot

In qualsiasi progetto il collegamento con la base dati è una spetto fondamentale e la maggior parte dei progetti Java prevedono uno standard chiamato JDBC, abbreviazione di Java DataBase Connection, che permette di utilizzare componenti Java per il collegamento con la base dati, questo standard prevede a basso livello i seguenti componenti:

  • un driver “registrato”: identifica il tipo di base di dati e i componenti necessari per il collegamento, come la definizione del endPoint, le credenziali di accesso e qualunque altro parametro necessario al collegamento dal programma in esecuzione alla base dati
  • la connessione: rappresenta il filo logico tra il componente in esecuzione e il server
  • lo statement: definisce tutte le istruzioni da eseguire e il risultato ritornato dalla base dati, nel caso di database relazionali, indica quali queries scritte nel linguaggio SQL eseguire

I driver di collegamento possono essere di vario tipo visto che i DBMS sono forniti da aziende diverse (più o meno amiche di Java) e possono essere di vario tipo: ODBC, Native API, Network API o pure Java; per la definizione del collegamento il tipo influisce solamente per il tipo di parametri necessari: a seconda del tipo saranno necessarie informazioni diverse, per esempio nel caso di un collegamento via network sarà necessario conoscere le impostazioni di rete mentre nel caso di pure Java sono necessarie il informazioni del socket di connessione.

I componenti di connessione e statement fanno parte di un package globale java.sql :

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

Ipotizzando di volersi collegare ad un Database di tipo Mysql con un il driver ufficiale scaricabile con maven aggiungendo nel pom.xml la dipendenza:

<dependency>
  <groupId>com.mysql</groupId>
  <artifactId>mysql-connector-j</artifactId>
  <version>8.0.29</version>
</dependency>

Il codice per collegarsi è molto semplice e prevede la definizione di driver, connessione e statement. Inoltre il resultSet ritornato dalla query può essere elaborato per il recupero dei dati:

public class MySql {
  private static final Logger logger = LogManager.getLogger(MySql.class);
  private final static String URL = "jdbc:mysql://localhost:3306/dbname";
  private final static String USERNAME = "admin";
  private final static String PASSWORD = "password";
  private final static String DRIVER = "com.mysql.jdbc.Driver";
  public static void main( String[] args ) throws Exception{
    BasicConfigurator.configure();
    String sqlCommand="select nome,eta from tab where nome is not null order by nome";
    Class.forName(DRIVER);
    Connection con = DriverManager.getConnection (URL,USERNAME,PASSWORD);
    PreparedStatement cmd = con.prepareStatement(sqlCommand);
    ResultSet res = cmd.executeQuery();
    ArrayList<String> nomi=new ArrayList<String>();
    if (res!=null){
      while(res.next()) {
        logger.debug("name:" + res.getString("nome"));
        nomi.add( res.getString("nome") );
      }
    }
    cmd.close();
    con.close();
  }
}

Le istruzioni di modifica dati possono eseguite con la stessa tecnica con l’accorgimento ulteriore che è possibile usare i parametri dello statement per creare query dinamiche:

final String K_INSERT_TABELLA="insert into tab (nome,descrizione,eta) VALUES (?,?,?)";
int numeroParametro=0;
Connection con = DriverManager.getConnection (getUrl(),getUser(),getPassword());
PreparedStatement cmd = con.prepareStatement(K_INSERT_TABELLA);
cmd.setString(numeroParametro++,"Alberto"); //imposto il primo parametro
cmd.setString(numeroParametro++,"bellissimo"); //imposto il secondo parametro
cmd.setInt(numeroParametro++,24); //imposto il terzo parametro
int result=cmd.executeUpdate();
cmd.close();
con.close();

La stessa tecnica può essere usata anche con altri tipi di DMBS come postgresql la cui configurazione del pom.xml è:

<dependency>
  <groupId>org.postgresql</groupId>
  <artifactId>postgresql</artifactId>
  <version>9.0-801.jdbc4</version>
</dependency>

Per tutti i DMBS più diffusi sono disponibili driver pubblici sui server maven, nel caso di driver proprietari è possibile importare la libreria jar nel progetto e poi usare il driver. L’uso di Connection e Statement è una tecnica talmente diffusa e usata che è diventata uno standard di fatto. Tuttavia non è necessario definire tutti questi passaggi ad ogni istruzione ma ci sono i framework che gestiscono questi componenti, nei prossimi articoli saranno introdotti tecniche meno manuali con l’ausilio di framework come Hibernate e Spring Boot.

Pubblicato il 11/11/2023 nella categoria Java & Spring Boot

La libreria standard del framework Struts mette a disposizione alcuni tag pronti all’uso molto utili che aiutano molto quando c’è la necessità di introdurre logiche in pagina e si vuole evitare di usare le scriptlet come consigliato da tutti i manuali. La documentazione ufficiale dei tag è molto ricca di esempi e questo articolo vuole esserne un riassunto , il più semplice, come già visto nel precedente articolo, è il tag per visualizzare messaggi:

<bean:message key="app.title"/>

Tuttavia la libreria bean mette a disposizione messaggi per visualizzare oggetti e proprietà all’interno degli oggetti:

<bean:write name="employee" property="username" />

Se, nelle pagine jsp, si vogliono recuperati oggetti salvati in request dalla classe action è necessario configurare struts affinché non esegua un redirect (creando una nuova request) ma impostando a false il parametro verrà mantenuta la stessa request tra classe Action pagina jsp di destinazione:

<forward name="success" path="/success.jsp" redirect="false" />

Altri tag a disposizione sono il define per dichiarae valori

<bean:define id="displayText" value="Text to Display" />
<bean:write name="displayText" />

Un altro gruppo molto usato sono i tag logici di cui si riportano alcuni esempi:

<logic:notPresent name="logonForm"> 
  <html:link forward="logon">Sign in here</html:link> 
</logic:notPresent> 
<logic:present name="logonForm"> 
  <html:link forward="logoff">Sign out</html:link> 
</logic:present> 
<logic:empty name="user">
  <forward name="login" />
</logic:empty />

<bean:define id="value2" name="bean2" property="value"/> 
<logic:equal value="<%=(String) value2 %>" name="bean1" property="value"> 
  HIT! 
</logic:equal> 
<logic:iterate id="employee" name="employees"> 
  <tr align="left"> 
    <td> <bean:write name="employee" property="username" /> </td> 
    <td> <bean:write name="employee" property="name" /> </td> 
    <td> <bean:write name="employee" property="phone" /> </td> 
  </tr> 
</logic:iterate>

In assoluto i tag più usati della libreria struts sono i tag per la gestione degli input, questi permettono di gestire i Form collegati direttamente agli ActionForm, l’esempio base già visto nel precedente articolo è la sostituzione dei classici tag HTML con i tag specifici della libreria che permetteranno a Struts di valorizzare il Bean ActionForm con i valori inseriti in pagina:

<html:form action="/login" focus="userName">
<p>Username : <html:text property="userName" /></p>
<p>Password : <html:password property="password" /></p>
<p><html:submit value="login" /></p>
</html:form>

Si rimanda alla documentazione ufficiale per approfondimenti a riguardo visto che è una tecnica indispensabile quando si lavora in progetti di grandi dimensioni con Struts 1.


Esiste un’altra libreria molto usata chiamata JSTL (Java Standard Tag Library), con lo scopo di uniformare i tag di tutti i framework, integra i tag funzionali mancanti di Struts e alcuni vengono sostituiti da sintassi molto più semplici, grazie a questi tag si può evitare “quasi” completamente l’uso delle scriptlet all’interno delle pagine jsp. Per integrare questa libreria nel progetto basta aggiungere le dipedenze:

<dependency>
  <groupId>jstl</groupId>
  <artifactId>jstl</artifactId>
  <version>1.2</version>
</dependency>
<dependency>
  <groupId>taglibs</groupId>
  <artifactId>standard</artifactId>
  <version>1.1.2</version>
</dependency>

e nelle pagina basta importare la libreria:

<%@ taglib uri="https://java.sun.com/jsp/jstl/core" prefix="c" % %>
<%@ taglib uri="https://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
<%@ taglib uri="https://java.sun.com/jsp/jstl/functions" prefix="fn" %>

I principali tag disponibili sono:

  • <c:out> permette di visualizzare un dato da un bean o da un oggetti
  • <c:import> permette di includere in una pagina altre pagine simile a jsp:include, permette l’invio dei parametri con il sottotag <c:param>
  • <c:redirect> esegue la redirect verso altre servlet
  • <c:set> esegue la set di un valore in una proprietà o in un oggetto
  • <c:remove> rimuovere una variabile o un oggetto
  • <c:catch> cattura eventuali exception generati al suo interno
  • <c:if> semplice condizione, usata anche come base per il tag catch
  • <c:choose> condizione articolata con usati in combinazione anche i <c:when> and <c:otherwise>
  • <c:forEach> semplice iterazione su una collezione di oggetti
  • <c:url> crea un link ad una servlet/action

Un semplice esempio di utilizzo di queste libreria

<c:set var = "salario" scope = "request">2200</c:set>
<c:if test = "${importo > 2000}">
  <p>L'importo di < c:out value = "${salary}"/> supera i 2000 €</p>
</c:if>

Un occhio attento avrà notato che nella condizione della c:if appena introdotta è presente una espressione con i caratteri ${variabile}, questa tecnica ufficiale è chiamata Expression Language (spesso abbreviato con EL), principale feature introdotta con la versione 2 di Jsp e permette di abbreviare ulteriomente il codice in pagine sostituendo alcuni tag (e alcune scriplet) con un linguaggio dedicato, questo prevede l’uso del simbolo dollaro seguito dalle istruzione in graffe, di fatto il codice

<% out.print(variabile); %>
<%=variabile %>
<c:out value="variabile" />

può essere sostituito con il semplice

${variabile}

questa tecnica è indispensabile quando si usano alcuni tag come le condizioni o cicli in jstl ma può essere usata in qualsiasi situazione. Deve essere attivata in pagina con il comando:

<%@ page isELIgnored="false" %>

Un esempio combinato di utilizzo di questa tecnica, per la creazione di un link con parametri:

<c:url value = "/NomeAction.do" var = "myURL">
  <c:param name = "parametro1" value = "valoreUno"/>
  <c:param name = "parametro2" value = "valoreDue"/>
</c:url>
<a href="${myURL}">Link a NomeAction</a>
<%--in alternativa <c:import url = "${myURL}"/> --%>

Oppure un altro esempio di ciclo su una collezioned i elementi:

<c:forEach items="${variabileElenco}" var="elmento" varStatus="status">
  <p>Item posizione ${status.index} con valore ${elemento} </p>
</c:forEach>
<c:if test="${empty variabileElenco }">
  <p>La lista vuota</p>
</c:if>

Un ultimo esempio di gestione delle exception misto tra JSTL e EL:

<c:catch var ="catchException">
  <% int x = 5/0;%>
</c:catch>
<c:if test = "${catchException != null}">
  <p>L'exception lanciata è : ${catchException} Con il messaggio: ${catchException.message}</p>
</c:if>

Le due librerie combinate possono semplificare molto la vita dei programmatori quando si deve eseguire formattazioni particolari, in particolare JSTP mette a disposizione la libreria:

<%@ taglib prefix = "fmt" uri = "http://java.sun.com/jsp/jstl/fmt" %>

Un elenco quasi completo di tutti i tag e le loro possibili applicazioni:

<c:set var = "balance" value = "120000.2309" />
<p>senza parametri:
<fmt:formatNumber value = "${balance}" type = "currency"/>
</p><p>con il massimo di interi :
<fmt:formatNumber type = "number" maxIntegerDigits = "3" value = "${balance}" />
</p><p>con il massimo di decimali:
<fmt:formatNumber type = "number" maxFractionDigits = "3" value = "${balance}" />
</p><p>visualizzato come percentuale:
<fmt:formatNumber type = "percent" maxIntegerDigits="3" value = "${balance}" />
</p><p>percentuale con minimo di decimali:
<fmt:formatNumber type = "percent" minFractionDigits = "10" value = "${balance}" />
<p><p>percentuale con il massimo di interi:
<fmt:formatNumber type = "percent" maxIntegerDigits = "3" value = "${balance}" />
</p><p>esadecimale:
<fmt:formatNumber type = "number" pattern = "###.###E0" value = "${balance}" />
</p><p>Con un locale particolare, per esempio in usa si usa il punto
come separtore dei decimali mentre in Italia è la virgola
<fmt:setLocale value = "en_US"/>
<fmt:formatNumber value = "${balance}" type = "currency"/>
</p>
<fmt:parseNumber var = "variabile" type = "number" value = "${balance}" />
<b>Parse senza parametri: </bp>
<fmt:parseNumber var = "variabile" integerOnly = "true" 
         type = "number" value = "${balance}" />
<p>Parse solo come integer: </p>
<c:set var = "now" value = "20-10-2010" />
<fmt:parseDate value = "${now}" var = "parsedEmpDate" pattern = "dd-MM-yyyy" />
<b>Parsed Date: </b>
<c:set var = "now" value = "<%= new java.util.Date()%>" />
<p>Visualizza l'ora: <fmt:formatDate type = "time" value = "${now}" /></p>
<p>Visualizza la data (il mese in lettere):
<fmt:formatDate type = "date" value = "${now}" /></p>
<p>Mostra data e ora (il mese in lettere):
<fmt:formatDate type = "both" value = "${now}" /></p>
<p>Mostra data e ora (il mese in numerI):
<fmt:formatDate type = "both" dateStyle = "short" timeStyle = "short" 
  value = "${now}" /></p>
<p>Mostra data e ora (il mese con il nome intero)
<fmt:formatDate type = "both" dateStyle = "long" timeStyle = "long" 
  value = "${now}" /></p>
<p>MOstra la data in formato personalizzato con un pattern specifico:
<fmt:formatDate pattern = "yyyy-MM-dd" value = "${now}" /></p>

Infine esistono delle funzionalità specifiche importabili la libreria:

<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>

Le funzioni disponibili sono

  • fn:contains testa se una stringa è contenuta in un’altra, per esempio:
    <c:set var = "theString" value = "I am a test String"/>
    <c:if test = "${fn:contains(theString, 'test')}">
    Found test string< /c:if>
  • fn:containsIgnoreCase come la precedente ma ignora maiuscole e minuscole
  • fn:endsWith testa se una stringa termina con una determinata stringa per esempio:
    <c:set var = "theString" value = "I am a test String 123"/>
    <c:if test = "${fn:endsWith(theString, '123')}">String ends with 123< /c:if>
  • fn:escapeXml codifica gli elementi XML all’interno di una stringa come testo e non come tag HTML, per esempio:
    <c:set var = "string2" value = "This is second String."/>
    <p>string: ${fn:escapeXml(string2)}< /p>
    visualizzato: This is second String.
  • fn:indexOf calcola la posizione di una stringa all’interno di un altra
  • fn:join unisce tutti gli elementi di un elenco in una stringa separati da un separatore, per esempio:
    <c:set var = "string1" value = "This is first String."/>
    <c:set var = "string2" value = "${fn:split(string1, ' ')}" />
    <c:set var = "string3" value = "${fn:join(string2, '-')}" />
    <p>${string3}< /p>
  • fn:length calcola il numero di elementi in una lista (collection) oppure la lunghezza di una stringa
  • fn:replace ritorna una stringa nella quale è stato sostituita tutte le occorrenze di una stringa con un’altra
  • fn:split divide una stringa in un array di sottostringhe
  • fn:startsWith testa se una stringa inizia con una determinata stringa
  • fn:substring estrae una stringa da una posizione ad un’altra (si comincia a contare da zero come in java), per esempio:
    <c:set var = "string1" value = "This is first String." />
    <c:set var = "string2" value = "${fn:substring(string1, 5, 15)}" />
    <p>is first S< /p>
  • fn:toLowerCase trasforma tutti i caratteri di una stringa in minuscolo
  • fn:toUpperCase trasforma tutti i caratteri di una stringa in maiuscolo
  • fn:trim rimuove tutti gli spazi bianchi all’inizio e alla fine di una stringa

Inoltre è possibile definire funzioni personalizzate creando una classe java per l’implementazione della logica da eseguire nelle EL, un esempio è descritto in un mio vecchio articolo.

Pubblicato il 07/10/2023 nella categoria Java & Spring Boot

Apache Struts è un framework open source per lo sviluppo di applicazioni studiato come estensione/evoluzione delle servlet dei progetti J2EE. Permette di creare applicazioni Web di grandi dimensioni, agevola la parallelizazione degli sviluppi e offre delle potentissime tag-lib, la validazione dei form e la gestione della localizzazione/l’internazionalizzazione. In questo sito si fa riferimento sempre alla mitica versione 1.3 del framework, la versione 2 non mi è mai piaciuta ma una guida è disponile vecchia versione del sito. La base del framework si basa su alcuni concetti:

  • classi Action gestiscono la logica di business e la logica di forward tramite metodi specifici, di fatto sono estensioni evolute delle classi Servelet
  • classi Form gestiscono i dati inseriti dall’utente nelle pagine definendo le regole di validazione dei dati
  • le pagine jsp non devono aver nessuna logica ma hanno solo il compito di visualizzare i dati

Un utente esperto avrà notato che queste tre componenti rispettano l’architettura MVC dove il Model sono le classi Form, le view sono le classi jsp e il controtroller sono le classi Action. Il framework per funzionare correttamente ha bisogno che tutte le classi Form e le classi action vengano censite in file di configurazione che storicamente viene sempre chiamato:

struts-config.xml

che deve essere censito nel web.xml. Per creare un semplice esempio di progetto con questo framework si parte da un progetto webapp standard con maven:

mvn archetype:generate -DgroupId=it.alnao -DartifactId=06Struts 
  -DarchetypeArtifactId=maven-archetype-webapp -DinteractiveMode=false

E bisogna importare le dipendenze del core e della tablib:

<dependency>
  <groupId>javax.servlet</groupId>
  <artifactId>servlet-api</artifactId>
  <version>2.5</version>
</dependency>
<dependency>
  <groupId>org.apache.struts</groupId>
  <artifactId>struts-core</artifactId>
  <version>1.3.10</version>
</dependency>
<dependency>
  <groupId>org.apache.struts</groupId>
  <artifactId>struts-taglib</artifactId>
  <version>1.3.10</version>
</dependency>

Nel web.xml si vede censire una (e una sola) servlet dove si indica la posizione del file di configurazione xml previsto dal framework:

<servlet>
  <servlet-name>action</servlet-name>
  <servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
  <init-param>
    <param-name>config</param-name>
    <param-value>/WEB-INF/struts-config.xml</param-value>
   </init-param>
   <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
  <servlet-name>action</servlet-name>
  <url-pattern>*.nao</url-pattern>
</servlet-mapping>

Nel file di configurazione si deve procedere al censimento di due blocchi: il primo riguarda i form-beans, cioè tutte le classi Form del progetto, il secondo definisce l’elenco di tutte le action con il corrispettivo form, path, classe e tutte le classi jsp sulle quali le classi Action potranno fare il forward:

<struts-config>
  <form-beans>
    <form-bean name="loginForm" type="it.alnao.mavenExamples.PrimoForm" />
  </form-beans>
  <action-mappings>
    <action name="loginForm" path="/login"
        type="it.alnao.mavenExamples.PrimaAction" scope="request"
        input="/index.jsp">
      <forward name="failure" path="/index.jsp" redirect="true" />
      <forward name="success" path="/success.jsp" redirect="true" />
    </action>
  </action-mappings>
</struts-config>

La classe Action estende un tipo previsto dall’architettura e deve definire obbligatoriamente un metodo execute che permette di definire logiche di business (come per esempio la validazione di un username-password) con conseguenti logiche di business (per esempio quale pagina visualizzare a seconda se le credenziali sono valide):

public class PrimaAction extends Action {
  @Override
  public ActionForward execute(ActionMapping mapping, ActionForm form,
      HttpServletRequest request, HttpServletResponse response)
      throws Exception {
    PrimoForm loginForm = (PrimoForm) form;
    if (loginForm.getUserName() == null 
        || loginForm.getPassword() == null
        || ! loginForm.getUserName().equalsIgnoreCase("alnao")
        || ! loginForm.getPassword().equals("bellissimo")) {
      return mapping.findForward("failure");
    } else{
      return mapping.findForward("success");  
    }
  }
}

La classe Form oltre a definire tutti i campi come un semplice Bean/model definiscono due metodi: reset e validate, il primo serve ad inizializzare i dati di un form, il secondo serve a validarli. Qualora una classe action viene eseguita e il suo form non sia valido, la servlet non viene eseguita ma viene eseguito un forward automatico nella pagina definita come input nel file xml di configurazione:

public class PrimoForm extends ActionForm {
  private String userName = null; //TODO Setter & Getter
  private String password = null;
  @Override
  public void reset(ActionMapping mapping, HttpServletRequest request) {
    this.password = null;
  }
  @Override
  public ActionErrors validate(ActionMapping mapping, HttpServletRequest request){
    // TODO Auto-generated method stub
    return super.validate(mapping, request);
  }
}

Il sistema di validazione dei dati viene attivato solo se nella pagina jsp viene essere usato un tag form previsto dalla libreria standard struts e gli input previsti dalla libreria standard del framework. Nel metodo presente nella classe ActionForm è possibile creare logiche di validazione come nell’esempio. Il form della pagina che invia dati:

<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html"%>
...
<html:form action="/login" focus="userName">
  <p>Username : <html:text property="userName" /></p>
  <p>Password : <html:password property="password" /></p>
  <p><html:submit value="login" /></p>
</html:form>

La tag-lib di struts prevede tre grandi tag: html, bean e logic che verranno descritti in un articolo dedicato.

Il tag più usato è il bean:message per la visualizzazione delle label, questo viene usato perché il framework gestisce nativamente il concento di localizzazione e multilingua: i messaggi vengono censiti in file di proprietà, per esempio il file di default di solito si chiama Application.properties e deve essere posizionato in un package dell’applicazione:

success.message=Benvenuto

E’ possibile creare un secondo file per una specifica lingua, per esempio un file dedicato ai messaggi in lingua inglese  Application_en.properties che deve essere posizionato nello stesso package del generale:

success.message=Welcome

Il bundle (cioè il gruppo di file di tipo properties) deve essere censito nel file di xml di configurazione con l’indicazione del package e del nome:

<message-resources parameter="it.alnao.Application" key="ApplicationBundle" />

Nella pagina jsp, per visualizzare i messaggi, bisogna prima importare la libreria e poi visualizzare i messaggi indicando il nome del bundle e il nome del messaggio:

<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>
...
<p><bean:message key="success.message" bundle="ApplicationBundle"/></p>

Per attivare la gestione del multi-lingua nel framework bisogna impostare il “locale” nella prima action chiamata:

request.getSession().setAttribute(Globals.LOCALE_KEY, request.getLocale());

con questa istruzione il framework riuscirà a recuperare la lingua del browser (della request) e selezionerà il messaggio corrispondente al file indicato nel file di configurazione. L’esempio completo funzionante può essere trovato al solito repository:

https://github.com/alnao/JavaExamples/tree/master/GenericJava/06Struts
Pubblicato il 16/09/2023 nella categoria Java & Spring Boot

La caratteristica principale della programmazione di Java è l’uso delle classi e degli oggetti, esattamente come nei file di una classe, all’interno di un file jsp è possibile ed usare usare classi se importate ovviamente metatag:

<%@ page import="it.alnao.manuali.java.NomeClasse"%>

dopo l’import è possibile usare la classe nelle scriptlet, per esempio un metodo statico è richiamabile con:

<% NomeClasse.NomeMetodoStatico(); %>

Per JSP Tags si intendono elementi java utilizzabili nel codice delle pagine jsp, distribuiti dentro a delle librerie particolari dette TagLib che definiscono la “firma” del tag e anche la propria implementazione, l’uso di questi permette di separare il codice “client” da quello “server” con lo scopo di evitare di scrivere codice Java puro nelle pagine jsp e di superare i limiti della programmazione XHTML. Tutti i Custom tag hanno questa sintassi:

<prefix:tag attr1="value" ... attrN="value" />

oppure:

<prefix:tag attr1="value" ... attrN="value" >Body</prefix:tag>

esattamente come un tag di HTML o come i tag standard JSP. Per usare un custom tag dentro ad una jsp bisogna importgare la TagLib e poi definirla, all’interno di ogni TagLib è presente un file TLD che rappresenta le firme dei tag e definisce quali classi java implementano quel tag, è fondamentale conscere il TLD di una taglib.


Il più semplice tag è quello messo a disposizione proprio dal J2EE, nello specifico il tag jsp permette di eseguire operazioni basiche su request e response, per esempio se si vuole usare un oggetto che è già presente in request o in sessione si può ridurre il codice scritto usando il tag usebean, per esempio:

<jsp:useBean id="NomeBean" scope="session" class="it.alnao.manuali.java.NomeClasse"/>

questo tag prende dalla sessione l’oggetto NomeVariabile, lo casta al tipo indicato e crea nella pagina una variabile di nome NomeVariabile con quel valore, cioè questo tag sostituisce l’istruzione

<% NomeClasse var=(NomeClasse) request.getSession().getAttribute("NomeBean"); %>

molto usato in quei progetti dove vengono usate le scriplet e non linguaggi più evoluti come le jstl o i tag Struts, analogamente al tag useBean esistono i tag setProperty e getProperty che servono a valorizzare e leggere proprietà di un oggetto bean, la sintassi è

<jsp:setProperty name="NomeBean" property="NomeProp" param="Valore" /> 
<jsp:getProperty name="NomeBean" property="NomeProp">

Da notare che per usare questi tag non serve importare nessuna libreria jar perchè sono tag “standard” java e vengono riconosciuti automaticamente dal WebServer, è presente anche un tag per importare altre pagine jsp in maniera dinamica:

<jsp:import

alternativa al bruttissimo:

<%@ include file="filedaincludere.jsp" %>

ma è corretto nominarlo ed evitarlo se possibile.


Per Custom tags si intendoo tag sviluppati all’interno del progetto o comunque non rilasciati ufficialmente dalla libreria J2EE, all’interno delle applicazioni JEE si hanno a disposizione più modi di creare un custom-tag, uno dei metodi più classici è quello di usare una classe java e un TLD per la descrizione del tag detti TLD acronimo di Tag Library Descriptor. Il primo passo per creare un TLD-TAG è quello di creare un file TLD nella giusta cartella del progetto WebContent/WEB-INF/tld/nome.tld, ricordandosi che ad un tld non corrisponde un solo tag ma corrisponde una libreria che può comprendere anche più tag, una documentazione completa dei file TLD può essere trovata nella documentazione ufficiale , lo scheletro base di questo tipo di file è:

<?xml version="1.0" encoding="UTF-8"?>
<taglib version="2.0" xmlns="http://java.sun.com/xml/ns/j2ee" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee 
http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd">
 <description>Alnao tag library</description>
 <tlib-version>1.0</tlib-version>
 <short-name>alnao</short-name>
 <tag>
  <name>TagCommentoSenzaParametri</name>
  <tag-class>it.alnao.prova.tags.TagCommentoSenzaParametri</tag-class>
  <body-content>JSP</body-content>
 </tag>
 <tag>
  <description>Importo</description>
  <name>Importo</name>
  <tag-class>it.alnao.prova.tags.ImportoTag</tag-class>
  <body-content>JSP</body-content>
  <attribute>
    <name>positiveStyle</name>
    <required>true</required>
    <rtexprvalue>true</rtexprvalue>
  </attribute>
  <attribute>
    <name>negativeStyle</name>
    <required>true</required>
    <rtexprvalue>true</rtexprvalue>
  </attribute>
 </tag>
</taglib>

in questo file viene definita una libreria assegnandogli anche uno short-name e un elenco di tag dove, per ogni elemento, è definito il nome, la classe e gli attributi se presenti. Poi serve implementare la classe TagCommentoSenzaParametri che deve espandere la classe TagSupport, per esempio un tag java che scrive un commento in pagina è

package it.alnao.prova.tags;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.TagSupport;
public class TagCommentoSenzaParametri extends TagSupport {
  private static final long serialVersionUID = 1L;
  public int doStartTag() throws JspException {
    String s="\n\n<!-- prova tag AlNao.it -->\n\n";
    try {
      JspWriter out = pageContext.getOut();
      out.println(s);
    } catch (Exception e) {
      e.printStackTrace();
    }
    return SKIP_BODY;
  }
  public int doEndTag() throws JspException {
    return EVAL_PAGE;
  }
}

mentre un esempio di tag per la visualizzazione di importi è:

package it.alnao.prova.tags;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.Locale;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.BodyTagSupport;
public class ImportoTag extends BodyTagSupport {
  private static final long serialVersionUID=1L;
  private String positiveStyle = null;
  private String negativeStyle = null;
  private String bodyTag = null;
  public int doStartTag() throws JspException {
    return EVAL_BODY_BUFFERED;
  }
  public int doAfterBody() throws JspException {
    bodyTag = getBodyContent().getString().trim();
    if (bodyTag != null) {
      Number decimal;
      DecimalFormat df = new DecimalFormat();
      decimal = df.parse(bodyTag);
      if (decimal != null) {
        if (decimal.doubleValue() > 0) { //positivo
          bodyTag = "<span class=\"" + getPositiveStyle() + "\">";
        } else {
          if (decimal.doubleValue() < 0) { //negativo
          bodyTag = "<span class=\"" + getNegativeStyle() + "\">";
        }
      } else { //zero
        bodyTag = "<span class=\"defaultClass\">" ;//+ bodyTag
      }
    } else { //non è un numero
      bodyTag = "<span>";
    }
    return (SKIP_BODY);
  }
  public int doEndTag() throws JspException {
    bodyTag = bodyTag + "</span>";
    try {
      this.pageContext.getOut().print(this.bodyTag);
    } catch (IOException ioe) {
      throw new JspException(ioe);
    }
    return EVAL_PAGE;
  }
  ... metodi get e set delle proprietà ...
}

All’interno delle pagine jsp, per poter usare questi tag è necessario importarli con una scriptlet:

<%@ taglib uri="/WEB-INF/tld/nome.tld" prefix="nomeTLD" %>

e poi è necessario usare il prefisso definito per chiamare i singoli tag, per esempio:

<nomeTLD:TagCommentoSenzaParametri />
<nomeTLD:Importo positiveStyle="classeCCS1" 
    negativeStyle="classeCCS2">-12.12</nomeTLD:Importo>

Un metodo più semplice per creare TLD-TAG è usare classi java pure e, grazie alla tecnica dell’ereditarietà di Java, utilizzare classi già esistenti per evitare di scrivere codice già presente in altri tag, per esempio è possibile creare un tag condizionale “if” personalizzato che abbia due parametri in input, se sono uguali il corpo del tag verrà visualizzato in pagina, altrimenti il codice verrà saltato, riportando il codice di esempio di questo tag condizionale:

import javax.servlet.jsp.JspTagException;
import javax.servlet.jsp.jstl.core.ConditionalTagSupport;
public class IfTag extends ConditionalTagSupport {
  private String value1 = null;
  private String value2 = null;
  public IfTag() {
    super();
  }
  protected boolean condition() throws JspTagException {
    if (value1==null || value2==null)
    throw new JspTagException ("Parametri nulli");
    return value1.equals(value2);
  }
}

dopo aver definito il file tld corrispettivo, in una qualsiasi pagina jsp poi basta usare il tag così:

<MieiTag:IfTag value1="uno" value2="due">
  <%
    //questo codice non viene eseguito perchè "uno" è diverso da "due"
  %>
</MieiTag:IfTag>
<MieiTag:IfTag value1="tre" value2="tre">
  <%
    //questo codice viene eseguito perchè le stringhe sono uguali
  %>
  Viene mostrato questo messaggio perchè "tre"=="tre"
</MieiTag:IfTag>

Allo stesso modo è possibile scrivere un tag “else” che estende la classe “if” in questo modo ma che nega il valore del metodo condition:

public class ElseTag extends IfTag{
  protected boolean condition() throws JspTagException {
    return ! super();
  }
}

nota: la classe ConditionalTagSupport si trova nella libreria jstl che deve essere importata nel file di configurazione di Maven.


Creare un custom-tag con una classe java è una tecnica molto elegante ma può risultare complicata se si deve scrivere un TAG che comprende molto codice client all’interno perché, per scrivere codice HTML dalla classe, si usa il metodo out che può risultato non semplicissimo da usare e il risultato rischia di diventare una classe molto lunga con molte stringhe costanti all’interno del codice. Esiste una alternativa alla tecnica dei Java-TLD: usare un file jsp dedicato al posto di una classe Java. Prima di tutto bisogna sempre definire un TLD e il riferimento al tag senza però nessun parametro anche se previsti, per esempio:

<tag-file>
  <name>tag_esempio</name>
  <path>/WEB-INF/tags/esempio.tag</path>
</tag-file>

poi è necessario creare il file che per convenzione ha estensione tag anche se in realtà è un file jsp, questo file deve sempre iniziare con il meta-tag specifico previsto da J2EE:

<%@ tag %>

e al suo interno deve essere presente l’elenco degli attributi, cioè i parametri del tag, per esempio:

<%@ attribute name="oggetto" required="true" rtexprvalue="true" description="." %>

e dopo si inserisce il codice HTML/Java del tag come se fosse un file jsp, per esempio

<DIV class="classeDiv">
  L'oggetto ha nome <%=request.getParameter("oggetto")%>
</DIV>

Per richiamare il tag da qualsiasi pagina JSP basta invocarlo richiamando il tag definito nel TLD:

<tagLib:tag_esempio oggetto="valore" />

Scegliere tra con una classe Java o con un file jsp è una questione di gusti, tipicamente si usano i file jsp quando il codice HTML è tanto e il codice Java è poco, viceversa si usano le classi quando il codice Java è molto complicato oppure se si deve definire un tag che estende un altro tag. La documentazione ufficiale è sempre il punto di riferimento per tutti gli sviluppatori.

Pubblicato il 26/08/2023 nella categoria Java & Spring Boot
MENU