lunedì 22 agosto 2011

Confrontare date in Java

Nell’effettuare una comparazione tra due date bisogna stare attenti all’utilizzo del metodo getTime().
Spesso infatti quello che ci serve è soltanto paragonare le date in base a giorno mese e anno mentre il metodo getTime() tiene conto anche delle ore minuti secondi e millisecondi.

Può essere fuoriviante il seguente codice:

Date d1=new Date();
Date d2=new Date();
System.out.println(d1.getTime()==d2.getTime());

Se si prova ad eseguirlo il risultato sarà quasi sempre true, poiché d1 e d2 vengono create praticamente nello stesso istante, millisecondo compreso.
Se proviamo ad inserire però un Thread.sleep(1000) tra le due dichiarazioni di date vediamo che il risultato torna false, in quanto d2 ha 1 secondo in più di d1.

Anche se ci costruiamo una data usando l’oggetto Calendar in questo modo (come visto in un precedente post )


public static Date creaData(int giorno,int mese,int anno){
              Calendar c=Calendar.getInstance();
              c.set(Calendar.YEAR, anno);
              c.set(Calendar.MONTH, mese-1);
              c.set(Calendar.DAY_OF_MONTH, giorno);
              return c.getTime();
          }


Si verifica lo stesso problema se tra d1 e d2 passa anche solo un millisecondo di differenza.

Le soluzioni possono essere due:
  1. Utilizzare  Joda , una api appositamente creata per velocizzare le operazioni sulle Date, da sempre un tallone d’Achille del linguaggio Java (su cui conto di fare un post a breve);
  2. Modificare il metodo creaData inserendo anche ore minuti secondi e millisecondi

Se usiamo il metodo 2 e riscriviamo così il metodo creaData:


public static Date creaData(int giorno,int mese,int anno){
            Calendar c=Calendar.getInstance();
            c.set(Calendar.YEAR, anno);
            c.set(Calendar.MONTH, mese-1);
            c.set(Calendar.DAY_OF_MONTH, giorno);
            c.set(Calendar.HOUR_OF_DAY, 0);
            c.set(Calendar.MINUTE, 0);
            c.set(Calendar.SECOND, 0);
            c.set(Calendar.MILLISECOND, 0);
            return c.getTime();
      }



Il problema è risolto, e le date saranno finalmente paragonate senza tener conto dei loro diversi istanti di creazione.


venerdì 5 agosto 2011

Zippare File in Java


Una semplice classe di utilità per zippare dei file.
Ha due costruttori, in uno passiamo la lista dei file (il percorso completo) da zippare e il nome (percorso incluso) dello zip risultante, l’altro invece accetta come parametri la Stringa contenente il singolo file da zippare
e il nome dell’output.




      
package it.test;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.LinkedList;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
public class ZipFile {
    private List<String> listaFiles;
    private String nomeZip;
       public ZipFile(List<String> listaFile,String nomeZip){
             this.nomeZip=nomeZip;
             this.listaFiles=listaFile;
       }
       public ZipFile(String nomeFile,String nomeZip){
             listaFiles=new LinkedList<String>();
             listaFiles.add(nomeFile);
             this.nomeZip=nomeZip;
       }
       public static void main(String[] args) {
             try
             {
                    ZipFile zf=new ZipFile("out/AMF050_test_20100601_20110228.txt", "out/test.zip");
                    zf.zippa();
             }
             catch(Exception ex){
                    ex.printStackTrace();
             }           
       }
       /**
        * Per evitare che dentro lo zip sia ricreata <br>
        * la eventuale struttura delle directory e sottodirectory del file di origine
        * @param nome
        * @return
        */
       private String nomeFile(String nome){
             StringBuffer sb=new StringBuffer();
             if(nome.indexOf("/")>0){
                    sb.append(nome.substring(nome.lastIndexOf("/")+1));
             }
             else if(nome.indexOf(File.separator)>0){
                    sb.append(nome.substring(nome.lastIndexOf(File.separator)+1));
             }
             else
             {
                    sb.append(nome);
             }
             return sb.toString();
       }
       public void zippa() throws Exception
       {
             byte[] buf = new byte[1024];
              ZipOutputStream out = new ZipOutputStream(new FileOutputStream(nomeZip));
              for(String file:listaFiles){
                     FileInputStream in = new FileInputStream(file);
                     out.putNextEntry(new ZipEntry(nomeFile(file)));
                     int len;
                     while ((len = in.read(buf)) > 0) {
                         out.write(buf, 0, len);
                     }
                     out.closeEntry();
                     in.close();

              }
              out.close();
       }
}



 


Accedere in maniera rapida ai Servizi di Windows

Molte volte mi capita di cercare e ricercare prima di accedere ai servizi di Windows.
La via più rapida è andare su start quindi esegui e poi digitare services.msc!!!!!

giovedì 4 agosto 2011

Display Tag, abilitare export PDF


La libreria display tag dispone di una serie impressionante di agevolazioni per visualizzare liste di dati ordinabili ed eventualmente esportare i dati nei più svariati formati.

Di default effettua export in xml,csv ed excel ma non in pdf o in rtf.

Per personalizzarla occorre procedere così:

  1. Scompattare il jar di display tag che abbiamo nella directory WEB-INF/lib (displaytag-1.x.jar);
  2. Aprire la directory org.displaytag.properties, qui si trova il file displaytag.properties;
  3. Copiare il file e inserirlo nella directory WEB-INF/classes del nostro WAR
  4. Aprire il file e modificare la proprietà export.PDF sostituendo true al posto di false.

E’ possibile anche personalizzare i messaggi, che di default sono in inglese.

Quello che accade è molto semplice, le regole di caricamento del classpath prevedono che sia prima caricato tutto ciò che sta dentro WEB-INF/classes e poi quello che si trova dentro WEB-INF/lib, quindi le proprietà di display tag caricate per prime saranno quelle da noi modificate e potremo disporre dell’export PDF.

Si noti che è necessario avere i jar di itext nella cartella lib, altrimenti l’export non può funzionare.

La lista dei jar necessari a display tag si trova qui.

martedì 2 agosto 2011

MySql replace con RegExp


MySql purtroppo non possiede una funzione di tipo replace abbinata ad una regular expression, come altri db.
Mi è capitato di dover effettuare una bonifica su una tabella che aveva un campo di tipo text con una serie di indirizzi mail separati dal carattere ;.

La modifica consisteva nel rimpiazzare tutti gli indirizzi di un certo dominio (poniamo ad esempio @xyz ) con una mail precisa, lasciando ovviamente inalterati gli altri indirizzi

Pensavo quindi di utilizzare una funzione del tipo replace(VALORE_CAMPO,’.*@xyz’,’mioindirizzo@xyz’), utilizzando quindi una regular expression per fare il match.
Purtroppo invece la replace di Mysql non suppporta regular expression.

Ho quindi dovuto realizzare il seguente work around con 3 stored procedures:

  1. La stored sprocedure split che effettua lo split dei valori in base al ;, inserendo i record risultanti su tabella (MySql non dispone di una funzione split);
  2. La stored procedure sp_lavoraMessaggio che dato in input l’id Primary Key della tabella dei messaggi da modificare chiama la procedura split sul campo degli indirizzi mail, quindi sulla tabella creata da split effettua le replace del caso e poi recupera tutti i dati dalla tabella di appoggio per effettuare l’update nel campo text della tabella da bonificare;
  3. La stored procedure cleanMail che legge dalla tabella da bonificare tutte le occorrenze da modificare e quindi per ogni occorrenza richiama la stored procedure del punto 2

Di seguito posto il codice delle 3 stored procedures:


Split


DELIMITER $$

DROP PROCEDURE IF EXISTS `split` $$
CREATE DEFINER=`root`@`localhost` PROCEDURE `split`(input text, delim VARCHAR(10))
begin
/** SCRIVE I DATI SPLITTATI SU UNA TABELLA DI APPOGGIO, CHE OGNI VOLTA DROPPA E RICREA **/
declare foundPos tinyint unsigned;
declare tmpTxt text;
declare delimLen tinyint unsigned;
declare element text;

drop  table if exists tmpValues;
create  table tmpValues
(
`valori` text not null
) engine = MyIsam;

set delimLen = length(delim);
set tmpTxt = input;

set foundPos = instr(tmpTxt,delim);
while foundPos <> 0 do
set element = substring(tmpTxt, 1, foundPos-1);
set tmpTxt = replace(tmpTxt, concat(element,delim), '');


insert into tmpValues (`valori`) values ( element);

set foundPos = instr(tmpTxt,delim);
end while;

if tmpTxt <> '' then
insert into tmpValues (`valori`) values (tmpTxt);
end if;


END $$

DELIMITER ;


sp_lavoraMessaggio





DELIMITER $$

DROP PROCEDURE IF EXISTS `db`.`sp_lavoraMessaggio` $$
CREATE PROCEDURE `db`.`sp_lavoraMessaggio` (in idMessaggio int)
BEGIN
declare listaDestinatariAppo text;
declare listaDestinatariTable text;
declare listaDestinatari text;
DECLARE doneCursorValori INT DEFAULT 0;
declare cursorValori cursor for select concat(valori,';') from tmpvalues;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET doneCursorValori = 1;
set listaDestinatari="";
select destinatari into listaDestinatariTable from messaggio_email where id_messaggio=idMessaggio;
call split(listaDestinatariTable,';');
update tmpValues set valori=’mioindirizzo@xyz.it'
where valori like '%@xyz%';
open cursorValori;
/** QUI SCORRE I VALORI DALLA TABELLA SPLITTATA E LI ACCODA ALLA NUOVA LISTA DESTINATARI **/
repeat
fetch cursorValori into listaDestinatariAppo;
if not doneCursorValori then
set listaDestinatari=concat(listaDestinatari,listaDestinatariAppo);
end if;
until doneCursorValori end repeat;
close cursorValori;
/** A QUESTO PUNTO EFFETTUA UPDATE, TOGLIENDO L’ULTIMO ; ALLA LISTA PRECEDENTEMENTE OTTENUTA **/
update messaggio_email
set destinatari=substring(listaDestinatari,1,char_length(listaDestinatari)-1)
where id_messaggio=idMessaggio;

END $$

DELIMITER ;


cleanMail


DELIMITER $$

DROP PROCEDURE IF EXISTS `sp_cleanMail` $$
CREATE DEFINER=`root`@`localhost` PROCEDURE `sp_cleaniMail`()
BEGIN
declare idMessaggio int;
declare cont int;
DECLARE doneIdMessaggi INT DEFAULT 0;
/**        RECUPERO MESSAGGI DA BONIFICARE **/
declare  cursoreMessaggi  cursor for select id_messaggio from messaggio_email where data_invio is null;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET doneIdMessaggi = 1;
set cont=0;
open cursoreMessaggi;
repeat

fetch cursoreMessaggi into idMessaggio;
if not doneIdMessaggi then

call sp_lavoraMessaggio(idMessaggio);
set cont=cont+1;
end if;
UNTIL doneIdMessaggi END REPEAT;
close cursoreMessaggi;
select concat('Procedura terminata con successo, messaggi lavorati: ',cont);
END $$

DELIMITER ;



Come ottenere l'input utente da console in Java


Spesso può essere utile richiedere un input utente per programmi client java classici, senza interfaccia swing.
Per recuperare l’input utente non esiste una funzione statica ad hoc (penso al C# con la sua Console.ReadLine() ).

Per recuperare l’input utente, prima della java 1.5 bisognava utilizzare le classi del package java.io.

Ecco il codice di una routine che torna l’input digitato dall’utente:


private String getInput() throws IOException{
      BufferedReader bf=new BufferedReader((new InputStreamReader(System.in)));
      String lettura=bf.readLine();
      return lettura;
    }

 


Dalla versione 1.5 di Java in poi è possibile utilizzare invece la classe java.util.Scanner, in questo modo:


private String getInputWithScanner(){
      Scanner sc=new Scanner(System.in);
      return sc.nextLine();
    }
 



lunedì 1 agosto 2011

Filtrare file in una directory


Per filtrare file tramite la loro estensione è molto comodo utilizzare l’interfaccia java.io.FilenameFilter.
Tale interfaccia, presente fin dalla versione 1.4 di Java, ha un solo metodo con firma


public boolean accept(File dir, String name)
 



Dove si implementa la logica di filtro che vogliamo.
Quindi per filtrare i file secondo la logica implementata basta chiamare il metodo della classe java.io.File


public String[] list(FilenameFilter filter)
 





Vediamo un esempio in cui abbiamo una classe generica di filtro in base alla estensione file, che accetta come costruttore l’estensione in base alla quale si vuole filtrare e che implementa FilenameFilter.



package it.test;
import java.io.File;
import java.io.FilenameFilter;
class GenericFilter implements FilenameFilter
{
       public static final String PDF=".pdf";
       public static final String EXCEL=".xls";
       String estensione;
        GenericFilter(String estensione){
             this.estensione=estensione;
       }

       @Override
       public boolean accept(File dir, String name) {
             return name.toLowerCase().endsWith(estensione);
       }
      
}
public class FileTest {
       public static void main(String[] args){
            
             try
             {
                    String directory="c:\\temp";
                    File f=new File(directory);
                   
                    FilenameFilter filtro=new GenericFilter(GenericFilter.EXCEL);
                    String[] listaExcel=f.list(filtro);
                    if(listaExcel!=null){
                    for(String s:listaExcel){
                           System.out.println(s);
                    }
                    }
                    else
                    {
                           System.out.println(String.format("Lista vuota dentro directory %s", directory));
                    }
                   
             }
             catch(Exception ex){
                    ex.printStackTrace();
             }
       }

}