venerdì 27 dicembre 2013

Localizzazione 2/2

L'utilizzo del locale in Java prevede la possibilità di sfruttare 2 classi che derivano dalla classe abstract ResourceBundle e che sono:
  • La classe concreta PropertyResourceBundle che prevede il supporto dei locale tramite file di properties;
  • La classe astratta ListResourceBundle che può essere estesa per aggiungere il supporto ad uno specifico locale effettuando l'override del metodo getContents() che torna un Object[][] con la lista delle chiavi e valori necessari.
ESEMPIO UTILIZZO PROPERTYRESOURCEBUNDLE

Creiamo un Java Project con una classe main di questo tipo:


package it.bundles;
import java.util.Locale;
import java.util.ResourceBundle;
public class CiaoLocalizzato {
	public static void main(String[] args){
Locale.setDefault(Locale.FRANCE);
Locale current=Locale.getDefault();
System.out.println(String.format("Locale %s %s %s", current.getDisplayName(),
current.getLanguage(),current.getCountry()));
ResourceBundle bundle=ResourceBundle.getBundle("it.localizzazione.ResourceBundle",current);
System.out.println(bundle.getString("greetings"));
	}
}


Quindi definiamo un package it.localizzazione al cui interno mettiamo i nostri file di properties.
La regola base è che il file deve essere presente nel classpath per poter consentire il caricamento a runtime delle proprietà.
Nel package avremo ad esempio i file :
  •  ResourceBundle.properties (il default nel caso non si trovi il locale giusto);
  • ResourceBundle_it.properties (per l'estensione italiana);
  • ResourceBundle_fr.properties (per l'estensione francese utilizzata in questo caso).
I file saranno semplici file di properties con la chiave greetings mappata ai valori "Hello" "Ciao" oppure "Bonjour!" a seconda della tipologia di locale.
Mandando in esecuzione il programma di volta in volta sarà stampato il valore a seconda del locale (sarà stampato Hello se il locale settato non ha un file di properties di riferimento).



ESEMPIO UTILIZZO LISTRESOURCEBUNDLE 

In questo caso dobbiamo definire 2 classi che estendono ListResourceBundle

package it.bundles;
import java.util.ListResourceBundle;
public class ResBundle extends ListResourceBundle {
	static final Object[][] contents={{"nome","ARSENAL"},{"anno","1886"},{"scudetti","13"}};
	@Override
	protected Object[][] getContents() {
		return this.contents;
	}

}



package it.bundles;
import java.util.ListResourceBundle;
public class ResBundle_it_IT extends ListResourceBundle {
	static final Object[][] contents={{"nome","INTER"},{"anno","1908"},{"scudetti","18"}};
	@Override
	protected Object[][] getContents() {
		return this.contents;
	}

}


Quindi nel nostro main come al solito:


package it.bundles;
import java.util.Locale;
import java.util.ResourceBundle;
public class TestResBundle {
	public static void main(String[] args) {
		Locale.setDefault(Locale.CANADA);
		Locale l=Locale.getDefault();
		ResourceBundle rb=ResourceBundle.getBundle("it.bundles.ResBundle",l);
		System.out.println(rb.getString("nome"));
		System.out.println(rb.getString("anno"));
		System.out.println(rb.getString("scudetti"));
	}
}


Giocando con il settaggio del default locale possiamo leggere i dati dalla classe localizzata di nostro interesse.

Localizzazione 1/2

Un oggetto di tipo Locale rappresenta un insieme di caratteristiche peculiari di un paese, ossia linguaggio, cultura, formattazione di numeri e di date.
La classe java.util.Locale possiede alcune importanti proprietà riferite a 3 entità fondamentali, il nome del paese, il nome della lingua e il nome del cosiddetto VARIANT, che indica la variazione del linguaggio all'interno del paese (praticamente il dialetto):
  • getCountry() che torna il codice del paese;
  • getDisplayCountry() che torna il nome del paese;
  • getLanguage() che torna il codice del linguaggio del paese;
  • getDisplayLanguage() che torna la descrizione del linguaggio del paese;
  • getVariant() che torna il variant code;
  • getDisplayVariant() che torna la descrizione del variant.
 Con questo semplice codice possiamo avere informazioni sui Locale presenti nella nostra JVM.
Oltre alle 3 informazioni fondamentali su paese-linguaggio-dialetto abbiamo anche informazioni aggiuntive in alcuni linguaggi (script ed extensions) che vedremo poi negli esempi.


public static void main(String[] args) {
  Locale[] lista=Locale.getAvailableLocales();
  System.out.println(Locale.getDefault());
  System.out.printf("Numero locale presenti %s",lista.length);
  System.out.println();
  for(Locale l:lista){
   //if(l.getLanguage().equals("it"))
   System.out.println(l+" - "+l.getDisplayName());
   
  }
  

 }


L'output può essere filtrato se si inserisce il commento, stampando soltanto quello che corrisponde al country code desiderato.
Se ad esempio mettiamo "en" nel filtro avremo il seguente output:

it_IT
Numero locale presenti 156
en_MT - inglese (Malta)
en_GB - inglese (Regno Unito)
en_CA - inglese (Canada)
en_US - inglese (Stati Uniti)
en_ZA - inglese (Sudafrica)
en - inglese
en_SG - inglese (Singapore)
en_IE - inglese (Irlanda)
en_IN - inglese (India)
en_AU - inglese (Australia)
en_NZ - inglese (Nuova Zelanda)
en_PH - inglese (Filippine)

I primi 2 caratteri rappresentano il codice del linguaggio, quindi abbiamo il codice del Paese (es en_GB ).
Ci possono poi essere anche il codice variant. Ad esempio abbiamo (se filtriamo per language "no") tra gli output questa stringa:
no_NO_NY ossia norvegese (Norvegia,Nynorsk)
Il Nyorsk è infatti un dialetto norvegese utilizzato da circa il 10-15% della popolazione norvegese.

In alcuni casi sono presenti anche gli script e le extension, ossia altre informazioni aggiuntive oltre al dialetto (variant)  . Ad esempio se scegliamo come codice "th" otteniamo tra gli altri risultati:
th_TH_TH_#u-nu-thai - thai (Thailandia,TH)


qui non abbiamo lo script ma abbiamo le extension, in questo caso u-nu-thai.
Per vedere un esempio di script possiamo provare il codice linguaggio "sr".
Vedremo in output ad esempio:
sr_ME_#Latn - serbo (Latino,Montenegro)

Latn è un esempio di script, deve essere  fatto da 4 lettere di cui la prima maiuscola (e. Latn,Cyrl).

domenica 22 dicembre 2013

Java implementazione pattern observer

Il pattern OBSERVER rientra nella categoria dei pattern "comportamentali".
Esistono 3 categorie principali di pattern:
  • CREAZIONALI ( es. SINGLETON, FACTORY, ABSTRACT FACTORY)
  • STRUTTURALI  (es. DECORATOR, PROXY, FACADE)
  • COMPORTAMENTALI (es. OBSERVER, STATE,STRATEGY)
Per realizzare questo pattern la classe che deve "segnalare" le modifiche agli osservatori dovrà estendere la classe java.util.Observable, mentre le classi che ricevono la segnalazione dovranno implementare l'interfaccia java.util.Observer che definisce il metodo  update.

Vediamo un semplice esempio.

CLASSE OBSERVABLE


package it.pattern;
import java.util.ArrayList;
import java.util.List;
import java.util.Observable;
public class Lista extends Observable {
 private List<String> lista=new ArrayList<>();
 public List<String> getLista() {
  return lista;
 }
 public void addString(String elemento){
  setChanged();
  notifyObservers(elemento);
  lista.add(elemento);
 }
}


CLASSE OBSERVER

package it.pattern;
import java.util.Observable;
import java.util.Observer;
public class ListaObserver implements Observer {
 @Override
 public void update(Observable o, Object arg) {
  System.out.println("Aggiunto elemento "+arg);
 }
}


Vediamo di seguito un esempio di utilizzo:

package it.pattern;

public class Test {
 public static void main(String[] args) {
  Lista l=new Lista();
  l.addObserver(new ListaObserver());
  l.addString("dsjksdkjd");
  l.addString("dsddsds");

 }

}


Il risultato dell'esecuzione del codice sarà il seguente:

Aggiunto elemento dsjksdkjd
Aggiunto elemento dsddsds


domenica 8 dicembre 2013

Esempio zip file con Java 7

Di seguito un esempio su come zippare un file in Java utilizzando le features della Java7 in particolare:
  1. Il try with resources, che ci risparmia la fatica di dover chiudere gli stream aperti, basta dichiarare tutte le aperture nel blocco di parentesi tonde del try;
  2. Le suppressed exceptions, ossia la possibilità di farsi stampare nel catch anche tutte le altre eccezioni eventualmente riscontrate nel blocco delle parentesi tonde del try. 
Esempio


package it.varie;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
public class ZipFile {
public static final int CHUNK=1024;
public static final String DIR="C:\\lav\\workspace\\wsTest\\Java7Cert\\src\\";
public static void main(String[] args) {
  if(args.length==0){
   System.err.println("Attenzione passare come parametro il nome file");
   System.exit(-1);
  }
  String fileName=args[0];
  String zipFileName=fileName+".zip";
  byte[] buffer=new byte[CHUNK];
  try( ZipOutputStream zipFile=new ZipOutputStream(new FileOutputStream(zipFileName));
    FileInputStream fis=new FileInputStream(DIR+fileName);
    )
    {
            zipFile.putNextEntry(new ZipEntry(zipFileName));
            int lenRead=0;
            while((lenRead=fis.read(buffer))>0){
             zipFile.write(buffer, 0, lenRead);
            }
    }
  catch(Exception e){
   System.out.println("Eccezione: "+e);
   System.out.println("Eventuali eccezioni soppresse");
   for(Throwable t: e.getSuppressed()){
    System.out.println(t);
   }
  }
 }
}


giovedì 28 novembre 2013

Tomcat 7 personalizzare parametri allo start up

Per modificare dei parametri specifici per un singolo Tomcat (es per cambiare il path oppure per definire una variabile di utilità) occorre modificare lo script startup.bat inserendo all'inizio istruzioni di questo tipo. Qui cambiamo la CATALINA_HOME, inseriamo un percorso diverso dallo standard nel path e definiamo una variabile di ambiente valida solo per questo tomcat senza definirla sul S.O.:


set CATALINA_HOME=d:\prove\apache-tomcat-7.0.39
set path=d:\miaDirectory;%path%
set @TESTVARIABILE=c:\pippo 

sabato 23 novembre 2013

Tomcat 7 settare connessione HTTPS

In questo post vediamo come:
  1. Creare un certificato per l'https;
  2. Visionare le caratteristiche del certificato;
  3. Configurare Tomcat 7 in modo da poter rispondere in https.
CREARE UN CERTIFICATO PER L'HTTPS

 Per effettuare questa operazione si utilizza la JDK 1.6, in particolare il programma keytool.
L'opzione -keystore consente di specificare dove creare il file. Attenzione è obbligatorio specificare un nome di file; va bene anche se non esistente, l'importante è che non sia  una directory la procedura va in errore dicendo "Errore keytool: java.lang.Exception: Il file keystore esiste ma Þ vuoto: c:\lav\certificati"

Da cmd:

C:\lav\ApplicationServers\apache-tomcat-7.0.23\bin>keytool -genkey -alias carlo
-keyalg RSA -keystore c:\lav\certificati\certificato.txt
Immettere la password del keystore:
Immettere nuovamente la nuova password:
Specificare nome e cognome
 [Unknown]:  MARIO BREGA
Specificare il nome dell'unitÓ aziendale
 [Unknown]:  AZIENDA ABC
Specificare il nome dell'azienda
 [Unknown]:  AZIENDA TEST
Specificare la localitÓ
 [Unknown]:  ROMA
Specificare la provincia
 [Unknown]:  RM
Specificare il codice a due lettere del paese in cui si trova l'unitÓ
 [Unknown]:  IT
Il dato CN=MARIO BREGA, OU=AZIENDA ABC, O=AZIENDA TEST, L=ROMA, ST=RM, C=IT Þ co
rretto?
 [no]:  s

Immettere la password della chiave per <carlo>
        (INVIO se corrisponde alla password del keystore):


VISIONARE LE CARATTERISTICHE DEL CERTIFICATO

Sempre utilizzando la keytool con l'opzione -list e specificando il keystore è possibile visionare le caratteristiche del certificato appena creato:


C:\lav\ApplicationServers\apache-tomcat-7.0.23\bin>keytool -list -keystore c:\la
v\certificati\certificato.txt
Immettere la password del keystore:

Tipo keystore: JKS
Provider keystore: SUN

Il keystore contiene 1 entry

carlo, 23-nov-2013, PrivateKeyEntry,
Impronta digitale certificato (MD5): F5:F9:15:AB:98:31:2D:19:C3:65:5C:EF:EA:7E:4
8:B1


CONFIGURARE TOMCAT7 IN HTTPS

Per configurare Tomcat7 in modo da essere raggiungibile in HTTPS con il certificato appena creato è sufficiente agire sul file server.xml presente nella directory conf dell'application server.
Bisogna scommentare il nodo Connector dove troviamo il commento:
Define a SSL HTTP/1.1 Connector on port 8443 e modificarlo nel nostro caso così:

 <Connector port="8443" protocol="HTTP/1.1" SSLEnabled="true" keystorePass="xxxx" keystoreFile="c:\lav\certificati\certificato.txt "
               maxThreads="150" scheme="https" secure="true"
               clientAuth="false" sslProtocol="TLS" />


aggiungendo le opzioni keystoreFile e keystorePass.

Per testare il tutto è sufficiente andare via browser all'indirizzo https://localhost:8443. Il certificato ovviamente non è trusted e quindi dovremmo aggiungere una eccezione di sicurezza al browser per accettarlo.

mercoledì 20 novembre 2013

Passare più di 9 parametri ad un file .bat

Se passiamo più di 9 parametri ad un .bat ci sono dei problemi.
Infatti scrivendo %10 in realtà prende il parametro 1 (%1) e poi vede uno zero.
Per gestire più parametri occorre usare l'operatore shift che trasla la lista di parametri.
Nell'esempio riportato sotto vogliamo passare 11 parametri e quindi, dopo aver letto il parametro 9,con un doppio shift "perdiamo" i valori 1 e 2 e quindi i valori %8 e %9 letti dopo saranno il decimo e undicesimo parametro inviato.
L'esempio seguente scrive questi parametri su di un file txt.

set arg1=%1
set arg2=%2
set arg3=%3
set arg4=%4
set arg5=%5
set arg6=%6
set arg7=%7
set arg8=%8
set arg9=%9
shift
shift
set arg10=%8
set arg11=%9
set
echo Utente %arg1% ci invia i parametri %arg2% %arg3% %arg4% %arg5% %arg6% %arg7% %arg8% %arg9% %arg10% %arg11% > risultato.txt


lunedì 18 novembre 2013

Java invocare bat file passando parametri

Supponiamo di voler invocare un semplic bat di questo tipo:

echo %1 %2
echo Ciao %1 %2 > pippo.txt

Quindi un bat che prende in input dei parametri e li scrive su un file (creandolo ex novo), in un path RELATIVO rispetto alla posizione del path.

Via java è possibile utilizzare la seguente istruzione:

Runtime.getRuntime().exec("cmd /c start  C:\\excel\\test.bat ciao sam");


E in effetti funziona ma il file non viene creato nella directory relativa c:excel bensì in quella dove stiamo eseguendo il nostro pgm Java.

Non sono riuscito a capire come fare (sicuramente ci deve essere un modo) per fare in modo che non scrivesse il file nel percorso relativo al pgm Java ma inutilmente.
Ho provato anche a fare una cosa del tipo:

System.setProperty("user.dir","c:\\excel");

ma niente da fare.

Per risolvere ho utilizzato la libreria commons-exec-1.1.jar.

DefaultExecutor df=new DefaultExecutor();
df.setWorkingDirectory(new File("c:\\excel"));
CommandLine cl=new CommandLine("c:\\excel\\test.bat");
cl.addArgument("uno");
cl.addArgument("due");
df.execute(cl)

Si noti che se si commenta il rigo 2 (il setWorkingDirectory) torniamo alla situazione precedente, mentre se non si specifica nel CommandLine il percorso assoluto e si mette quello relativo comunque non funziona (nonostante abbia settata la working directory) e da proprio errore lamentando la mancanza del file bat.

sabato 16 novembre 2013

Java7 watch service

Una feature molto utile è quella del WatchService, che consente di registrare una directory e notificare ogni evento che viene compiuto su di essa (es. inserimento nuovi file, cancellazioni file,modifiche eccetera).
Si ottiene questo risultato seguendo la seguente serie di passaggi logici:
  1. Ottenere un'istanza del WatchService dalla classe FileSystem;
  2. Registrare la directory di interesse, specificando nei varargs tutti gli eventi per cui si vuole registrarla;
  3. Effettuare un loop infinito dove si attendono gli eventi. Si utilizza il metodo take() per ottenere l'evento appena disponibile. La chiamata a take() è bloccante, se si vuole invece la versione non bloccante bisogna invocare il poll();
  4. Una volta ottenuto l'evento di tipo WatchKey si cicla tra tutti gli eventi disponibili per ottenere quello di interesse;
  5. Una volta processato l'evento si resetta la chiave (key.reset() nel codice), senza questo passaggio soltanto il primo evento nella directory sarà registrato.
Vediamo il codice:

package it.test;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardWatchEventKinds;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
public class ObserveDirChanges {

 public static void main(String[] args) {
  Path p=Paths.get("c:\\excel");
  WatchService ws=null;
  try
  {
ws=p.getFileSystem().newWatchService();
p.register(ws, StandardWatchEventKinds.ENTRY_CREATE,StandardWatchEventKinds.ENTRY_DELETE,StandardWatchEventKinds.ENTRY_MODIFY,StandardWatchEventKinds.OVERFLOW);
for(;;){
WatchKey key=null;
try
{
key=ws.take();
}
catch(InterruptedException ex){
ex.printStackTrace();
}
for(WatchEvent<?> event:key.pollEvents()){
switch(event.kind().name()){
 case "OVERFLOW":
 System.out.println("Attenzione è andato perduto un evento");
 break;
 case "ENTRY_MODIFY":
 System.out.println("File "+event.context()+" modificato");
 break;
 case "ENTRY_CREATE":
 System.out.println("File "+event.context()+" creato");
 break;
 case "ENTRY_DELETE":
 System.out.println("File "+event.context()+" cancellato");
 break;
 }
 key.reset();
    }
      }
  }
  catch(IOException e){
   e.printStackTrace();
  }

 }

}


JAVA 7 ottenere informazioni su data creazione file

Con le nuove API è possibile ottenere semplicemente molte informazioni sulla natura dei file.
Nel seguente esempio vediamo come ottenere queste informazioni utilizzando l'interfaccia java.nio.file.attribute.BasicFileAttributes.


package it.test;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.BasicFileAttributes;
public class CheckAttributiFile {
 public static void main(String[] args) {
  Path p=Paths.get("c:\\excel\\2_BAHIA.xls");
  try
  {
BasicFileAttributes fileAttributes=Files.readAttributes(p, BasicFileAttributes.class);
System.out.println("Size: "+fileAttributes.size());
System.out.println("Directory: "+fileAttributes.isDirectory());
System.out.println("File normale: "+fileAttributes.isRegularFile());;
System.out.println("Link simbolico: "+fileAttributes.isSymbolicLink());
System.out.println("Ultima modifica: "+fileAttributes.lastModifiedTime());
System.out.println("Ultimo accesso:"+fileAttributes.lastAccessTime());
System.out.println("Data creazione file: "+fileAttributes.creationTime());
  }
  catch (IOException e) {
   e.printStackTrace();
  }
 }
}


 Il risultato è il seguente:

Size: 5120
Directory: false
File normale: true
Link simbolico: false
Ultima modifica: 2013-08-08T09:04:30.744618Z
Ultimo accesso:2013-04-04T06:56:47.668827Z
Data creazione file: 2013-04-04T06:56:47.668827Z

Java 7 classe java.nio.file.Files esempio creazione link simbolico

Con  la classe Files si può fare praticamente di tutto a livello di file system con semplici ed intuitivi metodi.
Vediamo come creare e testare la creazione di un link simbolico:

package it.test;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
public class FilesExample {
 public static void main(String[] args) throws IOException{
  Path p3=Paths.get("C:\\lav\\testFile\\simbolicLink.txt");
  Path p4=Paths.get("C:\\lav\\testFile\\fileReale.txt");
  System.out.println(Files.createFile(p4));
  Files.createSymbolicLink(p3, p4);
  System.out.println(Files.isSymbolicLink(p3));
  
 }
 
}


Andando sulla directory del filesystem via dos vedremo quindi:

JAVA NIO package interfaccia Path

Con java 7 abbiamo un package java.nio.file con interessanti feature per lo scandagliamento del file System.
Una di queste è rappresentato dall'interfaccia java.nio.Path che consente di ottenere molte informazioni con metodi semplici ed intuitivi.
Si crea una implementazione di tale interfaccia chiamando il metodo statico get sulla classe Paths.
Esempio:

package it.test;
import java.nio.file.Path;
import java.nio.file.Paths;
public class PathExample {
public static void main(String[] args) {
  Path p=Paths.get("c:\\lav\\trash\\prof");
  System.out.println(p.getFileName());
        System.out.println(p.getRoot());
        System.out.println(p.getParent());
        for(Path element:p){
         System.out.println(element.toString());
        }
 }

}



Ci fornisce l'output seguente:

prof
c:\
c:\lav\trash
lav
trash
prof

In questo altro esempio ho preso il path relativo al progetto Java di test, utilizzando anche ulteriori metodi dell'interfaccia come il toAbsolutePath() o il toUri().

package it.test;
import java.io.IOException;
import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.nio.file.Paths;
public class PathExample {
public static void main(String[] args) throws IOException {
  Path p=Paths.get("src");
  System.out.println("File name: "+p.getFileName());
        System.out.println("Root: "+p.getRoot());
        System.out.println("Parent: "+p.getParent());
        for(Path element:p){
         System.out.println(element.toString());
        }
        System.out.println("Absolute path: "+p.toAbsolutePath());
        System.out.println("Uri: "+p.toUri());
        System.out.println("Path normalized:"+p.normalize().toString());
        System.out.println("Real path: "+p.toRealPath(LinkOption.NOFOLLOW_LINKS));
        
 }

}


Risultato:

File name: src
Root: null
Parent: null
src
Absolute path: C:\lav\workspace\wsTest\Java7Cert\src
Uri: file:///C:/lav/workspace/wsTest/Java7Cert/src/
Path normalized:src
Real path: C:\lav\workspace\wsTest\Java7Cert\src

In particolare:
  • toUri() ritorna un path che può essere aperto da un browser ;
  • toAbsolutePath() ritorna il path assoluto dato un path relativo;
  • normalize() rimuove simboli non necessari (es. "." e "..");
  • toRealPath() ritona il path assoluto e normalizzato e può risolvere anche i symbolic link (nel caso in esame usando l'opzione NOFOLLOWLINKS è stato scelto di non considerare i link simbolici. A differenza degli altri metodi esposti però se il file non esiste fisicamente lancia una eccezione tipo IOException.
E' possibile anche effettuare delle comparazioni tra path, ed esistono 2 metodi:
  • compareTo che effettua un confronto puramente lessicografico e torna 0 se i path sono uguali;
  • equals() che effettua un confronto reale tra i path (ma bisogna accertarsi che i percorsi di entrambi i path siano percorsi assoluti).
Esempio:

 // comparing paths
        Path p2=Paths.get("C:\\lav\\workspace\\wsTest\\Java7Cert\\src");
        System.out.println(p.compareTo(p2)==0?"OK":"KO");
        System.out.println(p.equals(p2));
        System.out.println(p.toAbsolutePath().equals(p2));

In questo caso ci torna prima KO, poi false e poi true (quando si confrontano i path entrambi assoluti).

mercoledì 13 novembre 2013

Primefaces p:dialog problemi nel submit ajax

Utilizzando primefaces 3.5 ho avuto il seguente problema.
Avevo un form principale che richiamava un p:dialog.
Il p:dialog si vedeva sempre con i campi in grigetto e non riuscivo quindi ad effettuare l'update.
Ho usato quindi la proprietà appendToBody="true" che effettivamente consentiva la visualizzazione corretta del form ma i valori dei campi non passavano in request (il bean aveva scope di tipo view).
A questo punto per risolvere il tutto ho messo tutto il p:dialog all'interno di un form separato e fuori dal form principale (occhio che definire form innestati provoca problemi in alcuni browser) e a questo punto tutto ho potuto rimettere il valore di default appendToBody e tutto ha funzionato correttmaente.
Incollo sotto l'xhtml del p:dialog:

<ui:composition xmlns="http://www.w3.org/1999/xhtml"  
    xmlns:h="http://java.sun.com/jsf/html"  
    xmlns:f="http://java.sun.com/jsf/core"   
    xmlns:p="http://primefaces.org/ui"
    xmlns:ui="http://java.sun.com/jsf/facelets">  
 <h:form id="ghersi">
    <p:dialog id="parametrizzazioneGi" widgetVar="par020WidgetVar" appendToBody="false"
       closable="true" resizable="true" draggable="true" modal="true" 
                width="600" height="250" header = "#{msg['paramGi']}" dynamic="true">
              <table>
              <tr><td>#{msg['codInt']}</td>
              <td> 
              <p:inputText id="codiceInterno" value="#{par020.codiceInterno}" maxlength="5" size="10"></p:inputText>
              </td>
              </tr>
              <tr><td>#{msg['codOgg']}</td>
              <td> 
              <p:inputText id="codiceOggetto" value="#{par020.codiceOggetto}" maxlength="30" size="40"></p:inputText>
              </td>
              </tr>
               <tr><td>#{msg['dtUltElabor']}</td>
              <td> 
              <p:inputText id="dtUltElab" value="#{par020.dataUltimaElaborazione}" maxlength="9" size="18"></p:inputText>
              </td>
              </tr>
               <tr><td>#{msg['schedPar']}</td>
              <td> 
              <p:inputText id="schedaParam" value="#{par020.schedaParametro}" maxlength="80" size="45"></p:inputText>
              </td>
              </tr>
              <tr>
              <td colspan="2">
              <p:commandButton id="insertParam" value="Inserimento" icon="ui-icon-arrowreturnthick-1-w"  
          actionListener="#{par020.insert}"   
             style="margin-top:15px;margin-right:5px;color:white;background:#868686;"
             update=":mainForm:parametri :mainForm:messages " oncomplete="par020WidgetVar.hide()" /> 
              </td>
              </tr>
              </table>
              
  </p:dialog>
    </h:form>
    </ui:composition>


lunedì 11 novembre 2013

Internet Explorer evitare messaggio sul window.close()

Con questo javascript si riesce ad evitare che richiamando il window.close() sia chiesta conferma all'utente della volontà di chiudere la finestra.
Il messaggio di conferma viene richiesto perchè c'è solo una finestra di tipo parent, per cui si avvisa in quanto chiuderla significa praticamente chiudere il browser.
Per evitare il problema ho trovato qui un trucco utile.
La funzione window.open ha la seguente firma:
window.open(URL,name,specs,replace)
I valori di name possono essere:
    _blank - Il caricamento dell'URL avviene in una nuova finestra
    _parent - Il caricamento dell'URL avviene nel frame parent
    _self - Il caricamento dell'url rimpiazza l'url corrente
    _top - Il caricamento dell'url rimpiazza qualsiasi frameset caricato
    name - il nome della finestra
Si imbroglia IE aprendo una pagina vuota superiore nella stessa pagina e quindi richiudendola.
Il valore true nel parametro replace indica che sostituisce il valore nella history.
Il parametro specs, qui lasciato vuoto, serve a specificare eventuali caratteristiche grafiche della finestra.
Così:

var win=window.open("","_top","","true");
win.opener=true;
win.close(); 


giovedì 7 novembre 2013

Determinare data creazione di un file

Dalla versione JDK 7 ci sono le API apposite di java.nio.
Con la jdk 1.6 si può procedere così (funziona in sistemi windows non l'ho testato su linux):


File f=new File("c:\\lav\\popso.txt");
SimpleDateFormat sdf=new SimpleDateFormat("dd/MM/yyyy HH:mm:ss");
Date d=new Date(f.lastModified());
System.out.println(sdf.format(d));


domenica 27 ottobre 2013

Trovare tutti i numeri lunghi x-cifre in un campo di testo

Questo metodino cerca in una stringa tutti i valori numerici di 5 cifre in un campo di testo.


String testo2=" cerchiamo i cap 00175 oppure anche 03029, so si 234 09909 carlo";
  Pattern pt=Pattern.compile("\\b\\d{5}\\b");
  Matcher mc=pt.matcher(testo2);
  while(mc.find()){
   System.out.println(mc.group());
  }



Il separatore \b indica il termine parola , quindi becca e riconosce anche le virgole immediatamente dopo, se avessimo usato una espressione regolare del tipo \D\d{5}\D (ossia un qualsiasi carattere non numerico + 5 numeri + qualsiasi carattere non numerico) avremmo preso ad esempio 03029, con la virgola eccetera.

Differenze tra uso dei generics e il tipo object

C'è una sottile differenza tra la notazione con i generics e quella con il tipo generico java.lang.Object
Infatti se scriviamo questo metodo:

public static void stampa(List<?> lista){
  for(Object o:lista){
   System.out.println("["+o+"]");
  }
 }


Possiamo passare ad esso qualsiasi struttura dati ed avere l'output in stampa.
Es:

List<String> ls=new ArrayList<>();
  ls.add("uno");
  ls.add("due");
  ls.add("tre");
  stampa(ls);
  List<Integer> lista=new ArrayList<>();
  lista.add(5);
  lista.add(7);
  lista.add(9);
  stampa(lista);




Se invece proviamo a cambiare la firma del metodo da List<?> a List<Object> il
codice non compila più, il messaggio è "The method stampa(List<Object>) in the type xxxxx is not applicable for the arguments (List<String>) "

dove xxx è il nome della classe da cui invochiamo il metodo.
La differenza quindi tra i due approcci è che usando List<Object>  si incappa nel problema del subtyping, ossia il tipo List<String> poichè String è sottoclasse di object non va bene per invocare il metodo, bisognerà esplicitamente castare il parametro.
La notazione ? ci evita di effettuare cast e il nostro metodo funzionerà con Liste di qualsiasi tipo.

mercoledì 23 ottobre 2013

Java, invocare tutti i set di un metodo con input di tipo Stringa

Un metodo utile per istanziare al volo un oggetto e settare tutte le sue proprietà con input una stringa casuale

public Object getOggettoIstanziato(String nomeClasse) throws Throwable {
Class dynamicClass = null;
dynamicClass = Class.forName(nomeClasse);
Object iClass = dynamicClass.newInstance();
Method[] listaMetodi = dynamicClass.getMethods();
for (Method m : listaMetodi)
{
if (m.getName().startsWith("set")
&& m.getGenericParameterTypes()[0].toString().equals("class java.lang.String"))
{
 int numeroCaratteri = RandomUtils.nextInt(20) + 1;
 m.invoke(iClass,new Object[] { getRandomString(numeroCaratteri) });
   }
  }
  return iClass;
 }

lunedì 21 ottobre 2013

Jquery errore con IE8 SCRIPT65535: Chiamata inattesa al metodo o alla proprietà di accesso

Ho perso tantissimo tempo per risolvere questo problema.
Utilizzavo prime faces versione 3.5 e avevo un p:commandButton che all'oncomplete faceva apparire un p:dialog e aveva poi nell'update settato l'id del form da aggiornare.
In sostanza data una lista di eventi si cliccava su uno di essi e appariva una finestra modale (il p:dialog appunto).
Il codice funzionava senza problemi su IE9 IE10 Firefox e Chrome.
Purtroppo su IE8 invece la finestra modale non si apriva.
Dopo ore di autentica disperazione  ho capito il motivo dell'errore.
Il problema è che in HTML sarebbe illegale (ma è ormai praticamente tollerato da tutti i browser) avere form innnestati l'uno nell'altro.
Le versioni di IE fino alla 8 segnalavano come errore bloccante questa disattenzione.
Quindi , per farla breve, per consentire il funzionamento corretto ho dovuto spostare il tag del form del p:dialog al di fuori del form padre.
Il problema quindi non è di Jquery e neppure di prime faces....
Solo colpa mia

giovedì 10 ottobre 2013

Configurare sicurezza con autenticazione BASIC in IIS 7.5

Essendo nuovo del mondo Microsoft mi sono trovato un pò in difficoltà nel configurare l'autenticazione BASIC su IIS.
La documentazione sul sito Microsoft è esauriente ma sono incappato in alcuni problemi risolti grazie ai forum e alla pazienza.
Per prima cosa occorre installare IIS sulla propria macchina con le principali componenti aggiuntive (filtri ISAPI, ASP net, Autenticazione BASIC compresa che di default non la mette).
Per farlo da Windows 7 procedere in Pannello di controllo/Programmi/Attivazione Disattivazione componenti di Windows e quindi selezionare IIS.

A questo punto, dopo aver creato un semplice Web Service con Visual Studio 2010, task banale, copiare il contenuto del progetto in una directory e da dentro IIS , sul pannello di sinistra, creare un nuovo sito in questo modo:

Quindi specificare il percorso fisico della directory, e fin qui tutto risulta molto intuitivo.

Per prima cosa adesso bisogna configurare la sicurezza sul sito, quindi una volta cliccato sul sito nella parte centrale dello schermo clicchiamo su "Autenticazione"

Qui occorre DISABILITARE l'autenticazione anonima ed abilitare l'autenticazione di base semplicemente selezionando le scelte multiple:


DIFFERENZE CON JAVA

A questo punto una nota importante.
In una web app Java per configurare l'autenticazione basic occorre impostare la security sul web.xml dell'applicazione, utilizzando il nodo < login-config> e quindi il <security-constraint> per vincolare la sicurezza solo ad alcuni metodi o url pattern oppure ruoli.
Sul server poi si attiva il realm inserendo gli utenti abilitati su file o su db.
Con IIS invece bisogna configurare soltanto il server, senza toccare la web app e in più (cosa che mi sembra strana e sono sicuro si possa cambiare ma io non so farlo) gli utenti abilitati possono essere solo utenti abilitati sul pc con pieni diritti sulla cartella.

IMPOSTAZIONE REALM

Si può impostare il realm facendo tasto destro su Autenticazione di base e settando il parametro Area Autenticazione:


In questo caso cercando di entrare nel sito da URL comparirà il seguente prompt del browser
:


PRINCIPALI PROBLEMI RISCONTRATI

Innanzitutto il primo problema è stato attivare la componente ASP net e l'autenticazione BASIC, che come scritto precedentemente non sono installati di default con IIS.

L'errore che si verificava in questo caso diceva chiaramente che non era possibile interpetare gli script, quindi IIS non avendo il modulo asp.net non riusciva ad interpetare le pagine.

Poi ho avuto un problema dovuto al fatto che l'applicazione utilizzava il .NET Framework 4.0 mentre il default di IIS 7.5 è il .NET Framework 2.0.
L'errore in questo caso si riferiva all'attributo targetFramework presente sul web.config.
Per sistemarlo ho creato un nuovo "pool di applicazioni" sul menu di sinistra sopra la voce Siti


ovviamente modificando la versione del .NET Framework utilizzato (scegliere Integrata come modalità pipeline).

Quindi cliccando sul tasto destro del nostro sito accedere alla voce Gestisci sito Web/Impostazioni avanzate

e quindi impostare il pool di applicazioni corretto.

L'ultimo errore, questo davvero rognoso, lo ho avuto una volta risolti tutti i precedenti.
Praticamente dopo essermi autenticato avevo il messaggio:

Nell'elenco moduli del gestore "WebServiceHandlerFactory-Integrated" è presente un modulo "ManagedPipelineHandler" non valido


Spulciando alcuni forum ho visto che si tratta di un problema relativo all'installazione di asp.net.

Ho risolto lanciando da dos il seguente comando:

%windir%\Microsoft.NET\Framework\v4.0.30319\aspnet_regiis.exe -i

e riavviando IIS











Java creare Url da Stringa



 private static URL getUrlFromWsdl(String wsdl) {
     URL baseUrl=null;
        try {
            baseUrl = new URL(wsdl);
        } catch (MalformedURLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return baseUrl;
 }

Web Service Java con autenticazione BASIC

Utilizzando JAX-WS, per effettuare una chiamata ad un servizio web protetto con autenticazione BASIC occorre impostare il codice in questo modo:


Authenticator.setDefault(new Authenticator() {
    @Override
    protected PasswordAuthentication getPasswordAuthentication() {
      return new PasswordAuthentication(
        username,
       password.toCharArray());
    }
   });
   // WsTestService s=new WsTestService(urlWsdl);
        Service1 s=new Service1();
        
        Service1Soap ws=s.getService1Soap();
        BindingProvider prov=(BindingProvider)ws;
        prov.getRequestContext().put(BindingProvider.USERNAME_PROPERTY, username);
        prov.getRequestContext().put(BindingProvider.PASSWORD_PROPERTY, password);


Da notare che occorre anteporre l'authenticator perchè il client costruito con JAX-WS effettua una connessione tramite oggetto url prima di invocare le operazioni, quindi bisogna fornire prima i dati di autenticazione e poi dopo in fase di chiamata inserendole nel request context.

Mi è capitato di avere problemi con questo sistema su una installazione avvenuta in ambiente Windows Server 2008.
Ricevevo infatti errore di tipo 401 (Autenticazione non valida) e mettendo un TCP Monitor ho visto che nella richiesta effettuata la modalità di autorizzazione era NTLM invece di BASIC.
Quindi ho aggiunto prima dell'authenticator la seguente riga di codice:


System.setProperty("http.auth.preference", "basic");



mercoledì 9 ottobre 2013

Differenze conteggio caratteri tra javascript e java lato server

Con un formato testo libero (es. textarea) mi è capitato il seguente problema.
Il codice javascript lato client effettua un controllo sulla numerosità dei caratteri e lato server allo stesso modo si verifica di non eccedere la quota per evitare problemi sul db.
Il massimo impostabile è 1000 caratteri.
Solo che i due valori non collimano sempre.
In presenza di testi copiati e incollati da fonti esterne (siti di giornali etc.) succede che il frontend passa 1000 caratteri ma il back end se ne trova di più.
Indagando un pò il problema ho visto che è dovuto ai caratteri di  a capo, che nella request http sono identificati dallo /r.
Quindi ogni carattere di a capo mi viene a contare per 2.
Ho risolto inserendo una replace lato java, in questo modo:

x.replaceAll("\r", "");

martedì 24 settembre 2013

Java esempio completo creazione xml con JAX-B

In questa classe vediamo un esempio completo di generazione completa dell'xml.
In particolare abbiamo:
  • generazione di attributi a livello di nodo padre e di nodi figli;
  • liste WRAPPED oppure no sia di semplici valori che di oggetti (se si aggiunge l'annotation @XmlElementWrapper si racchiude una collection di valori dentro un nodo "padre")
L'unico punto che mi rimane da capire meglio riguarda proprio la generazione degli attributi su un singolo nodo di un xml.
Bisogna per fare ciò creare una classe che rappresenta il nostro nodo con attributi, e gli attributi saranno proprietà di questa classe, solo che verranno annotati con @XMLAttribute.
Il problema è che se definisco questi attributi come campi pubblici nessun problema, se invece decido di esporli come proprietà con getter e setter ho il seguente errore nella creazione dell'xml :

com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException: 2 counts of IllegalAnnotationExceptions

dove le 2 illegal annotation sono proprio quelle messe nei set delle property del bean usato per mappare l'attributo.

Di seguito riporto l'xml che si vuole ottenere:


<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<persona nota="test">
    <nome>carlo</nome>
    <cogn>resi</cogn>
    <info cf="dsdfsdfsd" age="35"/>
    <lista>abc</lista>
    <lista>def</lista>
    <lista>ghi</lista>
    <contenitore>
        <elemento>abc</elemento>
        <elemento>def</elemento>
        <elemento>ghi</elemento>
    </contenitore>
    <elemento>
        <codice>001</codice>
        <descrizione>A01</descrizione>
    </elemento>
    <elemento>
        <codice>002</codice>
        <descrizione>A02</descrizione>
    </elemento>
    <elemento>
        <codice>003</codice>
        <descrizione>A03</descrizione>
    </elemento>
    <contenitoreCodici>
        <elemento>
            <codice>001</codice>
            <descrizione>A01</descrizione>
        </elemento>
        <elemento>
            <codice>002</codice>
            <descrizione>A02</descrizione>
        </elemento>
        <elemento>
            <codice>003</codice>
            <descrizione>A03</descrizione>
        </elemento>
    </contenitoreCodici>
</persona>


Classe Persona.java


package it.prova;
import java.util.Collection;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
@XmlRootElement(name="persona")
@XmlType(name="persona",propOrder={"nome","cognome","info","campiVari","campiVari2","listaNotWrapped","listaWrapped"})
public class Persona {
 private String nome;
 private String cognome;
 private String nota;
 private Info info;
 private Collection campiVari;
 private Collection campiVari2;
 private Collection listaNotWrapped;
 private Collection listaWrapped;
 @XmlElement(name="elemento")
 public Collection getListaNotWrapped() {
  return listaNotWrapped;
 }
 public void setListaNotWrapped(Collection listaNotWrapped) {
  this.listaNotWrapped = listaNotWrapped;
 }
 
 @XmlElementWrapper(name="contenitoreCodici")
 @XmlElement(name="elemento")
 public Collection getListaWrapped() {
  return listaWrapped;
 }
 public void setListaWrapped(Collection listaWrapped) {
  this.listaWrapped = listaWrapped;
 }
 @XmlElementWrapper(name="contenitore")
 @XmlElement(name="elemento")
 public Collection getCampiVari2() {
  return campiVari2;
 }
 public void setCampiVari2(Collection campiVari2) {
  this.campiVari2 = campiVari2;
 }
 @XmlElement(name="lista")
 public Collection getCampiVari() {
  return campiVari;
 }
 public void setCampiVari(Collection campiVari) {
  this.campiVari = campiVari;
 }
 @XmlElement(name="info")
 public Info getInfo() {
  return info;
 }
 public void setInfo(Info info) {
  this.info = info;
 }
 @XmlAttribute(name="nota")
 public String getNota() {
  return nota;
 }
 public void setNota(String nota) {
  this.nota = nota;
 }
 @XmlElement(name="nome")
 public String getNome() {
  return nome;
 }
 public void setNome(String nome) {
  this.nome = nome;
 }
 @XmlElement(name="cogn")
 public String getCognome() {
  return cognome;
 }
 public void setCognome(String cognome) {
  this.cognome = cognome;
 }
}


Classe Info.java (per gli attributi)

package it.prova;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement
public class Info {
 @XmlAttribute(name="age")
 public String eta;
 @XmlAttribute(name="cf")
 public String cf;
// cosi' non funziona!! 
// private String eta;
// private String cf;
// @XmlAttribute(name="age")
// public String getEta() {
//  return eta;
// }
// public void setEta(String eta) {
//  this.eta = eta;
// }
// @XmlAttribute(name="cf")
// public String getCf() {
//  return cf;
// }
// public void setCf(String cf) {
//  this.cf = cf;
// }
// 
}


Classe Campi.java ( qui abbiamo i campi della lista composta da oggetti )


package it.prova;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement
public class Campi {
 private String codice;
 private String descrizione;
 public Campi(){
  
 }
 public Campi(String codice,String descrizione){
  this.codice=codice;
  this.descrizione=descrizione;
 }
 public String getCodice() {
  return codice;
 }
 public void setCodice(String codice) {
  this.codice = codice;
 }
 public String getDescrizione() {
  return descrizione;
 }
 public void setDescrizione(String descrizione) {
  this.descrizione = descrizione;
 }
}



Classe GenerateXml.java per la generazione dell'xml


package it.prova;
import java.io.File;
import java.io.FileWriter;
import java.util.*;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
public class GenerateXml {
 public static void main(String[] args) throws Exception{
  File f=new File("out/test.xml");
  Persona p=new Persona();
  p.setCognome("resi");
  p.setNome("carlo");
  p.setNota("test");
  Info i=new Info();
  i.cf="dsdfsdfsd";
  i.eta="35";
//  i.setCf("sdfsdsdf");
//  i.setEta("33");
  p.setInfo(i);
  List l=new ArrayList();
  l.add("abc");
  l.add("def");
  l.add("ghi");
  p.setCampiVari(l);
  p.setCampiVari2(l);
  List listaCampi=new ArrayList();
  listaCampi.add(new Campi("001", "A01"));
  listaCampi.add(new Campi("002", "A02"));
  listaCampi.add(new Campi("003", "A03"));
  p.setListaWrapped(listaCampi);
  p.setListaNotWrapped(listaCampi);
  JAXBContext jc=JAXBContext.newInstance(Persona.class);
  Marshaller m=jc.createMarshaller();
   m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
         FileWriter fw=new FileWriter(f);
         m.marshal(p, fw);
         fw.flush();
 }
}

Java validare xml con xsd usando JAX-B

Per validare un file xml con un xsd occorre procedere in questo modo.
  1. Per prima cosa dall'xsd si genera la classe java corrisponente (tramite il comando xjc.exe );
  2. Quindi i file di input xml in ingresso vengono validati rispetto all'xsd fornito tentando di parsare il file nell'oggetto ottenuto al punto 1
Questo è il codice usato in un esempio (INPUT è un oggetto Java complesso annotato via jax-b ed ottenuto dall'operazione compiuta al punto 1).
Il metodo prende in input il file con l'xsd e l'xml con i dati.

import java.io.*;
import javax.xml.XMLConstants;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Unmarshaller;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
public static boolean valida(File xsd,File fileOrigine) {
try
    {
 SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
 Schema schema = schemaFactory.newSchema(dtd); 
 JAXBContext jc=JAXBContext.newInstance(INPUT.class);
 Unmarshaller um = jc.createUnmarshaller();
 um.setSchema(schema);
 INPUT i=(INPUT) um.unmarshal(fileOrigine);
        return true;
    }
catch(Exception ex){
          ex.printStackTrace();
   return false;
  }
 }


venerdì 20 settembre 2013

Export runnable jar in modalità package oppure extract

Nel caso in cui da Eclipse si voglia esportare in un unico jar sia il codice della nostra applicazione sia quello delle librerie utilizzate da essa, si può procedere in 3 modi:
  • Extract required libraries into generated jar;
  • Package required libraries into generated jar;
  • Copy required libraries into a sub-folder next to the generated JAR.
Il terzo modo è intuitivo, tutti i jar sono inseriti in una cartella a fianco del jar principale.

Nel primo caso (EXTRACT) il contenuto dei jar viene esploso dentro il jar (Eclipse correttamente avvisa che trattandosi di un repackaging di jar esterni potrebbero esserci problemi di licenza).
In questo caso nel manifest del jar abbiamo la seguente situazione:

Main-Class: classe con il nostro main

Nel secondo caso (package) i jar sono inseriti all'interno del jar esportato, e il manifest risulta modificato in questo modo:

Manifest-Version: 1.0
Rsrc-Class-Path: ./ logback-core-1.0.9.jar commons-lang-2.6.jar logbac
 k-classic-1.0.9.jar slf4j-api-1.7.2.jar log4j-1.2.17.jar commons-code
 c-1.6.jar....[lista di tutti i jar utilizzati]
Class-Path: .
Rsrc-Main-Class:classe con il nostro main
Main-Class: org.eclipse.jdt.internal.jarinjarloader.JarRsrcLoader


Le differenze principali tra i 2 approcci sono:
  1. Usando la modalità package crescono le dimensioni del jar ;
  2. Usando la modalità extract si potrebbe incappare in problemi di licenza sw;
  3. Usando la modalità extract se si aggiorna un jar esterno bisogna rieffettuare il pacchetto, nel caso invece della modalità package basta sostituire il jar interno. 






mercoledì 28 agosto 2013

Java 7 try with resources

Con Java 7 è stato introdotto il concetto di try with resources.
Essendo comune il problema di chiudere le risorse utilizzate , siano esse connessioni al db piuttosto che BufferedReader etc. etc., Java 7 introduce questa feature proprio per risolvere  queste mancanze.
Vediamo un esempio:

public static void main(String[] args) {
 String url="jdbc:mysql://localhost:3306/";
 String db="addressBook";
 String username="root";
 String password="admin";
 try(Connection connection=DriverManager.getConnection(url+db,username,password)){
     System.out.println("Connessione ok!!!");
 }
 catch(Exception ex){
     ex.printStackTrace();
 }
}

Come possiamo vedere dopo il try, ma prima dell'inizio del blocco try con le parentesi graffe, è creata la connessione.
Automaticamente sarà chiusa alla fine del programma da Java, senza necessità di farlo esplicitamente.
Si possono usare anche con più risorse, scrivendole di seguito alla prima. L'unica regola è che le varie risorse devono essere separate da ; tranne l'ultima.

Esempio:

try (FileWriter fw = new FileWriter(file);
            BufferedWriter bw = new BufferedWriter(fw)) {
        bw.write(text);
    } catch (IOException ex) {
        // handle ex
    }


Notiamo nell'esempio di recupero connessione che abbiamo utilizzato JDBC 4.0 ; con questa libreria non è più necessario  cercare il driver per la connessione al db tramite l'istruzione Class.forName utilizzata nelle precedenti versioni.
L'importante è che il driver sia nel classpath, se c'è la JVM lo trova da solo e ci risparmia un ulteriore passaggio nel codice , dove bisognava per altro indicare il nome esatto del driver utilizzato (nel caso di Mysql  com.mysql.jdbc.Driver).

martedì 27 agosto 2013

h:panelGrid dare uno stile alle colonne

L' h:panelGrid di JSF si renderizza come una tabella HTML.
Non è possibile utilizzare tuttavia attributi come il colspan, se vogliamo farlo dobbiamo comunque utilizzare una table html.
Ci viene però incontro l'attributo columnClasses che consente di associare ad ogni colonna uno stile css diverso.
All'interno del columnClasses definiamo, separati dal carattere ',' , tanti stili quante sono le colonne del panelgrid.
Esempio:



<h:panelGrid columns="4"  columnClasses="cella1,cella2,cella3,cella4">
<h:outputLabel value="Tipo Persona" style="font-weight:bold" />
.....altre colonnne 
</h:panelGrid>
<style type="text/css">
 .cella1{
 width:15%;
 white-space: nowrap;
   
 }
 .cella2{
 width:35%;
   
 }
.cella3{
 width:15%;
 }
.cella4{
 width:35%;
}
</style> 

giovedì 22 agosto 2013

JSF 2.0 debug online component tree e scoped library

 E' possibile in maniera molto semplice effettuare un debug della struttura della pagina jsf renderizzata e dei componenti presenti nei vari scope (view,request,session).
Per effettuare tale operazione per prima cosa dobbiamo aggiungere nel web.xml il seguente parametro di contesto:


<context-param>
    <param-name>javax.faces.FACELETS_DEVELOPMENT</param-name>
    <param-value>true</param-value>
</context-param>


Quindi nelle pagine di nostro interesse dobbiamo importare la libreria delle facelets (il namespace è xmlns:ui="http://java.sun.com/jsf/facelets" da aggiungere all'interno del nodo html).

E definire il seguente tag:


 <ui:debug hotkey="x" />

Il valore da assegnare all'hotkey è a nostra scelta. Quindi premendo la combinazione di tasti CTRL+SHIFT+X vedremo apparire una finestra di debug con le informazioni ricercate, molto utile per avere a runtime un'evidenza dei dati presenti sulla pagina e negli scope.
Se non si specifica nessun valore per l'hotkey la finestra di debug apparirà una volta premuta la combinazione di tasti CTRL+SHIFT+D.

mercoledì 21 agosto 2013

Jquery accedere ad una cella di una tabella

Per accedere al contenuto di una cella di una tabella html e ad esempio effettuare un test sul valore presente in una certa posizione si può fare così:


var a=$("#datiB").find("tr:eq(1)").find("td:eq(2)").html().substring(2,3);



In questo caso stiamo assegnando alla variabile a la stringa di testo presa dalla tabella con id datiB, nella seconda riga e nella terza colonna. Del valore presente nella stringa interna prendiamo il 3 carattere.

lunedì 12 agosto 2013

JSF leggere proprietà da resource bundle in un backing bean

In questo post avevamo visto come associare un resource bundle alle pagine xhtml, in modo da referenziare con la chiave presente nel file i componenti web senza cablare all'interno le descrizioni.
Se vogliamo effettuare la stessa operazione in un backing bean è sufficiente utilizzare il codice Java solitamente utilizzato per l'internazionalizzazione, ossia la classe java.util.ResourceBundle.
In questo caso non ci serve registrare il file nel faces-config come visto nell'altro post.
Supponiamo di avere il file messages_growl.properties dentro il package resources.
Il codice per accedere alle nostre proprietà sarà il seguente:

ResourceBundle.getBundle("resources.messages_growl").getString("chiavee");



venerdì 9 agosto 2013

Inserire tooltip su h:selectOneMenu

Sfortunatamente il componente JSF h:selectOneMenu, che rappresenta a runtime la classica select HTML, non possiede nella implementazione nativa la proprietà title che serve a fornire un tooltip al passaggio del mouse sul valore selezionato.
Per ovviare a questa lacuna si può o utilizzare la p:selectOneMenu di primefaces (che di default consente di legare il tooltip all'itemDescription).
Per ovviare al problema senza inserire una nuova libreria ho trovato una soluzione un pò rozza  via javascript con questo metodo:


function aggiungiTooltip(idComponente){
  var options = document.getElementById(idComponente).options;
 for(var i = 0; i < options.length; i++) {
                options[i].title = options[i].value;
         }

    }



Questo metodo mi serviva in alcuni casi in cui nel value della combo avevo il testo esteso mentre nel text l'abbreviazione.

Se si vuole aggiungere il testo a video della combo come tooltip basta sostituire options[i].value con options[i].text. In Firefox funziona anche con la proprietà options[i].innerHTML , ma questa proprietà non funziona con IE 10.

L'ideale sarebbe richiamare il metodo all'onload della pagina, altrimenti si può collegare anche al metodo onmouseover del componente stesso.

lunedì 29 luglio 2013

Resettare valore autoincrement di una chiave primaria

Nel caso di chiavi primarie del tipo autoincrement è possibile resettarle con la seguente istruzione

DBCC CHECKIDENT ([NOME_TABELLA], RESEED, 0)



giovedì 25 luglio 2013

Commenti server side su JSF

Con JSF 2.0 non è più possibile utilizzare il tag di commento server side delle JSP, ossia

<%-- comments --%>



Si possono ovviamente utilizzare i classici commenti html:

<!-- comments  -->



Ma il solito inconveniente è che questi commenti sono poi riportati nella pagina web generata, appesantiscono il sorgente htmle se dobbiamo fare del debug con bugzilla e altri software sono solo un fastidio.
E' possibile però in maniera elegante far si che tutti i commenti HTML utilizzati non siano riportati nelle nostre pagine web.
E' sufficiente per fare ciò configurare sul web.xml il seguente context param:


<context-param>
    <param-name>javax.faces.FACELETS_SKIP_COMMENTS</param-name>
    <param-value>true</param-value>
</context-param>


In alternativa si può utilizzare sulla singola pagina il tag
<ui:remove> 
sui singoli commenti da escludere sulla pagina xhtml.

lunedì 22 luglio 2013

CamelCase di una stringa

Metodo per ottenere una stringa in formato camel case,

public static String toCamelCase(String s){
  StringBuffer sb=new StringBuffer();
  if(!isNullOrEmpty(s)){
   StringTokenizer st=new StringTokenizer(s," ");
   while(st.hasMoreElements()){
    String testo=st.nextToken();
    sb.append(testo.substring(0, 1).toUpperCase());
    sb.append(testo.substring(1).toLowerCase());
    sb.append(" ");
   }
  }
  else
  {
   sb.append("");
  }
  
  return sb.toString();
 }



venerdì 19 luglio 2013

Exception in thread "main" java.lang.NoSuchMethodError: javax.xml.ws.WebFault.messageName()Ljava/lang/String

Questo errore si è verificato nell'interrogazione di un web service scritto utilizzando JAX-WS 2.2.8
su un Tomcat 7 con la jdk 1.6.
Il problema è che di default la jdk 1.6 utilizza JAX.WS 2.1 e  l'attributo messageName sulla annotation javax.xml.ws.WebFault è stato introdotto solo con la versione JAX WS 2.2.
Per risolverlo ho utilizzato il sistema delle librerie endorsed di Tomcat.
I passi da seguire sono abbastanza semplici.
Bisogna creare una directory denominata endorsed dentro la root di Tomcat,tipicamente allo stesso livello della cartella webapps.
Qui dentro bisogna inserire i 2 jar (versione 2.2 ovviamente):

  • jaxb-api.jar;
  • jaxws-api.jar

Occorre poi verificare che la variabile java.endorsed.dirs punti correttamente alla nostra directory.
Ad esempio nei parametri di start up del servizio (nel tab java se lanciamo l'utiliti tomcat7w.exe //ES//nomeServizio) assicuriamoci che ci sia il seguente settaggio:

 -Djava.endorsed.dirs=D:\lavoro\apache-tomcat-7.0.3\endorsed

Trim in javascript

Per effettuare il trim di una stringa Javascript non ha funzioni built in.
Si può utilizzare la seguente funzione


function trim(s)
{ 
   return s.replace(/^\s+|\s+$/g, ''); 
}



Per testarla scrivi un testo e premi il pulsante, il testo trimmato è racchiuso tra i caratteri "*"


lunedì 15 luglio 2013

Rilanciamento eccezioni con "type-checking" inclusivo

Questa feature introdotta con la Java 7 e ben spiegata nel sito oracle (http://docs.oracle.com/javase/7/docs/technotes/guides/language/catch-multiple.html#rethrow) consente di rilanciare eccezioni specifiche senza dover utilizzare la generica Exception.
Il compilatore infatti a Runtime è in grado di capire di quale eccezione si tratta.
Vediamo un esempio:


public class Exceptions {
   static class Eccezione1 extends Exception { }
   static class Eccezione2 extends Exception { }
   public static  void rethrowException(String exceptionName) throws Eccezione1,Eccezione2 {
     try {
      switch(exceptionName){
      case "uno":
       throw new Eccezione1();
      case "due":
      throw new Eccezione2();
      default:
       System.out.println("Nessuna eccezione");
      
      }
      
     } catch (Exception e) {
       throw e;
     }
   }
}


Nelle versioni precedenti di Java saremmo stati costretti a mettere nel throws Exception, adesso invece possiamo rilanciare le nostre eccezioni specifiche.


mercoledì 10 luglio 2013

Content is not allowed in prolog

Questo errore generico si verifica durante la lettura di un xml, il motivo è che c'è uno spazio (o comunque un qualche carattere, prima dell'inizio del tag  <?xml

sabato 22 giugno 2013

Validazione nel marshalling di un xml

Utilizzando JAXB tipicamente quando effettuiamo l'operazione di lettura dall'xml per popolare i nostri oggetti di business (UNMARSHALLING), si può abilitare la validazione in questo modo:


JAXBContext context=JAXBContext.newInstance("it.esempio");
Unmarshaller um= context.createUnmarshaller();
um.setValidating(true);


Nel caso inverso però (il cosiddetto marshalling) non è disponibile questa feature.
Quindi bisogna utilizzare il seguente costrutto:

Item item=....
JAXBContext context=JAXBContext.newInstance("it.esempio");
Marshaller m= context.createMarshaller();
Validator v=context.createValidator();
v.validate(item);
m.marshal(item,new FileOutputStream("newItem.xml");

domenica 16 giugno 2013

XML Signature

Per la firma digitale di un xml sono disponibili 3 modalità:

ENVELOPING SIGNATURE

L'oggetto da firmare è racchiuso all'interno del contenitore della firma.
Un esempio (preso dal sito Microsoft):

<?xml version="1.0" encoding="UTF-8"?>
<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
  <ds:SignedInfo>
    <ds:CanonicalizationMethod 
         Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
    <ds:SignatureMethod 
         Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
    <ds:Reference URI="#obj">
      <ds:DigestMethod 
          Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
      <ds:DigestValue/>
    </ds:Reference>
  </ds:SignedInfo>
  <ds:SignatureValue/>
  <ds:Object Id="obj">Hello, World!</ds:Object>
</ds:Signature>


ENVELOPED SIGNATURE

Al contrario dell'enveloping in questo caso è l'oggetto che al suo interno ha un nodo firma. Se ci si pensa è una soluzione più vicina al modello che abbiamo in mente noi quando parliamo di firmare un documento, la firma diventa un attributo del documento.
Sempre dal sito Microsoft un esempio molto calzante, che fa riferimento proprio ad un oggetto di tipo lettera:

<Letter>
   <Return-address>address</Return-address>
   <To>You</To>
   <Message>msg body</Message>
   <From>
      <ds:Signature xmlns:ds="&ds;">
         <ds:SignedInfo>
            <ds:CanonicalizationMethod Algorithm=
                "http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
            <ds:SignatureMethod Algorithm=
                "http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
            <ds:Reference URI="">
               <ds:Transforms>
                  <ds:Transform Algorithm="&enveloped;">
                  </ds:Transform>
               </ds:Transforms>
               <ds:DigestMethod Algorithm="&digest;"/>
               <ds:DigestValue></ds:DigestValue>
           </ds:Reference>
         </ds:SignedInfo>
         <ds:SignatureValue/>
      </ds:Signature>
   </From>
   <Attach>attachement</Attach>
</Letter>
DETACHED SIGNATURE In questo caso la firma è esterna al documento, può essere in un file separato oppure possono comparire entrambe nello stesso file ma come elementi "fratelli" separati:


<internally-detached>
  <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
    <ds:SignedInfo>
      <ds:CanonicalizationMethod 
         Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
      <ds:SignatureMethod 
         Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
      <ds:Reference URI="#data">
        <ds:DigestMethod 
          Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
        <ds:DigestValue/>
      </ds:Reference>
    </ds:SignedInfo>
    <ds:SignatureValue/>
  </ds:Signature>

  <document Id="data">
     <title>title</title>
     <author>writer</author>
     <date>today</date>
     <content>
        <para>First paragraph</para>
        <para>Second paragraph</para>
     </content>
  </document>
</internally-detached>


sabato 15 giugno 2013

Note sugli Handlers

Gli Handlers possono essere di 2 tipi:

  • SOAPHandler , che implementano l'interfaccia java.xml.ws.handler.SOAPHandler e che hanno accesso sia al payload del messaggio che alla sezione header e a quella degli attachments;
  • LogicalHandler, che implementano l'interfaccia javax.xml.ws.handler.LogicalHandler e che hanno accesso solo al payload del messaggio.
Gli handler sono dichiarati in un file xml, di solito denominato handler-chain.xml (ma possiamo chiamarlo come vogliamo) .
L'ordine di esecuzione cambia a seconda se il messaggio è in uscita o in entrata.
In un messaggio in entrata (incoming message) vengono eseguiti prima i SOAPHandler e poi i logical handler (nell'oridine inverso stabilito nel file xml!!).
In un messaggio in uscita (outgoing message) invece sono prima coinvolti i LogicalHandler e poi i SOAPHandler(nell'ordine stabilito nel file handler-chain.xml).
E' possibile anche filtrare l'esecuzione degli handler secondo determinate condizioni, in particolare nel file handler-chain.xml, prima della lista degli handler, si possono filtrare:

  1. SERVIZI con alcuni name pattern usando l'elemento   javaee:service-name-patter;
  2. PORTE utilizzando javaee:port-name-pattern;
  3. PROTOCOLLI DI BINDING specifici inserendo l'elemento javaee:protocol-bindings

venerdì 14 giugno 2013

Stampare variabili di ambiente

Per avere una lista completa delle variabili d'ambiente del pc:


Map map= System.getenv();
   Set chiavi= map.keySet();
   for(String s: chiavi){
    System.out.println("Chiave: "+s+" Valore: "+map.get(s));
   }


lunedì 10 giugno 2013

Sql Server ottenere le constraint di una tabella

Di seguito una query per ottenere le informazioni su tutti i constraint di una tabella


SELECT 
     t.Name as NOME_TABELLA,
    c.Name as NOME_COLONNA,
    dc.Name as NOME_CONSTRAINT,
    dc.definition as VALORE
FROM sys.tables t
INNER JOIN sys.default_constraints dc ON t.object_id = dc.parent_object_id
INNER JOIN sys.columns c ON dc.parent_object_id = c.object_id 
AND c.column_id = dc.parent_column_id 
where t.NAME='?';


venerdì 7 giugno 2013

Java lanciare un file .bat da una web app

Per lanciare un file .bat da una web application , oltre al comando nativo

java.lang.Runtime.getRuntime().exec("cmd /c ....");


è possibile utilizzare la libreria commons.exec (qui un tutorial sull'utilizzo).
Io ho utilizzato la versione 1.1..
Nel mio caso era necessario solo invocare il batch senza intercettare il risultato, ed ho fatto in questo modo (la jsp passava in un parametrochiamato batchVal il nome del .bat da invocare):

private void gestisciRichiesta(HttpServletRequest request,
   HttpServletResponse response) throws ServletException, IOException {
  try
  {
     String batchDaInvocare=request.getParameter("batchVal");
     log.info("Parametro inviato: "+batchDaInvocare);
     CommandLine commandLine = new CommandLine(batchDaInvocare );
     DefaultExecutor executor = new DefaultExecutor();
     executor.setExitValue( 0 );
     executor.execute( commandLine );
  request.setAttribute("richiestaBatchInviata", "La richiesta di avvio batch alert è stata trasmessa al server, attendere la mail di risposta prima di verificare i risultati");
  }
  catch(Throwable t)
  {
   request.setAttribute("error", "Attenzione si è verificato un errore nell'invocazione del batch, messaggio "+t.getMessage()+" errore di tipo "+t.getClass().getName());
  }
  finally{
   RequestDispatcher rd=request.getRequestDispatcher("index.jsp");
   rd.forward(request, response);
  }
 }

Comunque con la libreria di apache è agevole anche il passaggio dei parametri il parsing dell'output.
Ad esempio si possono passare dei parametri invocando il mentodo addArgument sull'oggetto Command per aggiungere dei parametri al batch etc.

martedì 4 giugno 2013

Internazionalizzazione JSF 2.0

Utilizzando JSF 2.0 (io sto usando come implementazione Mojarra) per l'internazionalizzazione bastano 2 passaggi:
  1. Creare i propri file di properties e posizionarli dove si vuole nel proprio workspace;
  2. Referenziare tale file di properties all'interno del faces-config, associando all'identificazione un nome;
  3. Referenziare all'interno dell'xhtml il nome definito al punto 2 in corrispondenza del punto in cui si vuole inserire il messaggio.
Esempio: supponiamo di aver inserito un semplice file di properties denominato message.properties all'interno del package resources del progetto.

FACES-CONFIG

 <application>
<locale-config>
<default-locale>it</default-locale>
<supported-locale>en</supported-locale>
</locale-config>
<resource-bundle>
<base-name>resources.messages</base-name>
<var>msg</var>
</resource-bundle>
</application>


Si noti che bisogna indicare nel base name il percorso del file comprensivo del package e il nome file generico.
Se all'interno del package resources inserissi anche un file denominato message_it.properties poichè it è il default locale avrei precedenza rispetto al generico message.properties.

XHTML

All'interno dell'xhtml la referenziazione alla variabile msg consente in maniera trasparente l'accesso alla proprietà, in questo modo (qui lo usiamo all'interno del p:commandButton di prime faces:


 <p:commandButton update="nomeIdColl cognomeIdColl cfIdColl" 
action="#{soggetto.valorizzaDatiIdentificazione}" icon="ui-icon-check" 
value="#{msg['miachiave']}" ></p:commandButton>


lunedì 3 giugno 2013

JAVA connettersi ad un DSN

Per connettersi ad un DSN (parliamo quindi di ambiente Windows):


import java.sql.DriverManager;
public static void main(String[] args) {
Connection con = null;
     try {
       Class.forName("sun.jdbc.odbc.JdbcOdbcDriver") ;
       con = DriverManager.getConnection("jdbc:odbc:NOME_DSN","uname","pwd");
       System.out.println("DSN Connection ok.");
       con.close();

     } catch (Exception e) {
       System.err.println("Errore: "+e.getMessage());
     }

 }


sabato 1 giugno 2013

Web Service autenticazione token profile con parsing dell'header tramite SAAJ

Nel seguente esempio vediamo come abilitare la sicurezza in modalità token profile su un Web Service sfruttando per il parsing dell'header le api della libreria SAAJ.
Vediamo il servizio (volutamente banale)

import javax.jws.HandlerChain;
import javax.jws.WebService;
@WebService
@HandlerChain(file="handlers.xml")
public class HelloWorld {
public String saluta(String nome){
 return "Ciao "+nome;
}
}


Possiamo vedere come nell'annotation HandlerChain si specifica il file dove sarà definito l'handler di sicurezza; tale handler provvede a leggere username e password dall'header soap.
L'handler è di tipo SOAPHandler, ossia è in grado di leggere e gestire la sezione di header, deputata all'inserimento dei dati di autenticazione.
Vediamo il codice dell'handler

import java.io.IOException;
import java.util.Iterator;
import java.util.Set;
import java.util.logging.Logger;
import javax.xml.namespace.QName;
import javax.xml.soap.Node;
import javax.xml.soap.SOAPBody;
import javax.xml.soap.SOAPConstants;
import javax.xml.soap.SOAPEnvelope;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPFault;
import javax.xml.soap.SOAPHeader;
import javax.xml.soap.SOAPMessage;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.handler.soap.SOAPHandler;
import javax.xml.ws.handler.soap.SOAPMessageContext;
import javax.xml.ws.soap.SOAPFaultException;
/**
 * Controlla user name e password
 * @author 
 *
 */
public class ServerHandler implements SOAPHandler {

 private static final String LoggerName = "ServerSideLogger";
 private static boolean trace=true;
 private Logger logger;
 private final boolean log_p = true; 
 public ServerHandler(){
  logger = Logger.getLogger(LoggerName);
 }
 @Override
 public void close(MessageContext context) {
  
 }

 @Override
 public boolean handleFault(SOAPMessageContext context) {
  return true;
 }

 @Override
 public boolean handleMessage(SOAPMessageContext ctx) {
  Boolean response_p = (Boolean)
    ctx.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
    // Handle the SOAP only if it's incoming.
    if (!response_p) {
    try {
    SOAPMessage msg = ctx.getMessage();
    SOAPEnvelope env = msg.getSOAPPart().getEnvelope();
    SOAPHeader hdr = env.getHeader();
    String user="";
    String password="";
    // Ensure that the SOAP message has a header.
    if (hdr == null)
    generateSOAPFault(msg, "Attenzione manca la parte header del messaggio soap");
    
    Iterator it =hdr.extractHeaderElements(SOAPConstants.URI_SOAP_ACTOR_NEXT);
    if (it == null || !it.hasNext())
     generateSOAPFault(msg, "Attenzione header non corretto");
     Node next = (Node) it.next();
     if(!"user".equals(next.getNodeName())){
      generateSOAPFault(msg, "attenzione header non corretto");
     }
     else
     {
      user=next.getValue();
      logger.info("Username: "+user);
     }
     if (it == null || !it.hasNext())
      generateSOAPFault(msg, "Attenzione header non corretto"); 
     Node pnode=(Node)it.next();
     if(!"pwd".equals(pnode.getNodeName())){
      generateSOAPFault(msg, "attenzione header non corretto");
     }
     else
     {
      password=pnode.getValue();
     }
     if(!("kermit".equals(user) && "thefrog".equals(password))){
      logger.info("Attenzione errore nelle credenziali di accesso");
      generateSOAPFault(msg, "Attenzione errore nelle credenziali di accesso");
     }
     
     if (trace) msg.writeTo(System.out);
    }
    catch(SOAPException e) { System.err.println(e); }
    catch(IOException e) { System.err.println(e); }
     
    }
    return true;
 }

 @Override
 public Set getHeaders() {
  // TODO Auto-generated method stub
  return null;
 }

 private void generateSOAPFault(SOAPMessage msg, String reason) {
  try {
  SOAPBody body = msg.getSOAPPart().getEnvelope().getBody();
  SOAPFault fault = body.addFault();
  fault.setFaultString(reason);
  throw new SOAPFaultException(fault);
  }
  catch(SOAPException e) { }
  }
}



Nel metodo handle message si recuperano username e password dall'header confrontandole con dei valori cablati nel codice (nella realtà ovviamente ci sarà un invocazione ad un db o un servizio LDAP).
Per pubblicare velocemente il servizio possiamo utilizzare le api della jdk 6, in particolare la classica EndPoint.publish.


Endpoint.publish("http://127.0.0.1:8089/Verifica", new HelloWorld());

Possiamo già testare il servizio con un client tipo SOAP UI. Se lo invochiamo senza inserire nulla nella sezione header avremo il seguente messaggio di errore:

<S:Fault xmlns:ns3="http://www.w3.org/2003/05/soap-envelope">
         <faultcode>S:Server</faultcode>
         <faultstring>Attenzione header non corretto</faultstring>
      </S:Fault>



Se invece effettuiamo una chiamata correttamente, ossia così:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ser="http://servizio.it/">
   <soapenv:Header>
<user xmlns="http://ch03.fib" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" SOAP-ENV:actor="http://schemas.xmlsoap.org/soap/actor/next">kermit</user>
<pwd xmlns="http://ch03.fib" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" SOAP-ENV:actor="http://schemas.xmlsoap.org/soap/actor/next">thefrog</pwd>
</soapenv:Header>
   <soapenv:Body>
      <ser:saluta>
         <!--Optional:-->
         <arg0>carlo</arg0>
      </ser:saluta>
   </soapenv:Body>
</soapenv:Envelope>


La risposta in soap ui è la seguente:

<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
   <S:Body>
      <ns2:salutaResponse xmlns:ns2="http://servizio.it/">
         <return>Ciao carlo</return>
      </ns2:salutaResponse>
   </S:Body>
</S:Envelope>


Vediamo come scrivere ora un client java per il servizio. Dovremo dichiarare un handler che gestisca il messaggio in invio, scrivendo la sezione di autenticazione nell'header.

import java.io.IOException;
import java.util.Set;
import java.util.logging.Logger;
import javax.xml.namespace.QName;
import javax.xml.soap.SOAPConstants;
import javax.xml.soap.SOAPEnvelope;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPHeader;
import javax.xml.soap.SOAPHeaderElement;
import javax.xml.soap.SOAPMessage;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.handler.soap.SOAPHandler;
import javax.xml.ws.handler.soap.SOAPMessageContext;
/**
 * Controlla user name e password
 * @author 
 *
 */
public class ClientHandler implements SOAPHandler {

 private static final String LoggerName = "ClientSideLogger";
 private static final String USER_NAME="kermit";
 private static final String PASSWORD="thefrog";
 private Logger logger;
 private final boolean log_p = true; 
 public ClientHandler(){
  logger = Logger.getLogger(LoggerName);
 }
 @Override
 public void close(MessageContext arg0) {
  if (log_p) logger.info("close");  
 }

 @Override
 public boolean handleFault(SOAPMessageContext ctx) {
  if (log_p) logger.info("handleFault");
  try {
  ctx.getMessage().writeTo(System.out);
  }
  catch(SOAPException e) { System.err.println(e); }
  catch(IOException e) { System.err.println(e); }
  return true;
 }

 @Override
 public boolean handleMessage(SOAPMessageContext ctx) {
  if (log_p) logger.info("handleMessage");
  Boolean request_p = (Boolean)
  ctx.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
  if (request_p) {
  try {
  SOAPMessage msg = ctx.getMessage();
  SOAPEnvelope env = msg.getSOAPPart().getEnvelope();
  SOAPHeader hdr = env.getHeader();
  if (hdr == null) hdr = env.addHeader();
  QName qname = new QName("http://ch03.fib", "user");
  SOAPHeaderElement helem = hdr.addHeaderElement(qname);
  helem.setActor(SOAPConstants.URI_SOAP_ACTOR_NEXT); // default
  helem.addTextNode(USER_NAME);
  QName pqname=new QName("http://ch03.fib", "pwd");
  SOAPHeaderElement pelem=hdr.addHeaderElement(pqname);
  pelem.setActor(SOAPConstants.URI_SOAP_ACTOR_NEXT);
  pelem.addTextNode(PASSWORD);
  msg.saveChanges();
  msg.writeTo(System.out);
  }
  catch(SOAPException e) { System.err.println(e); }
  catch(IOException e) { System.err.println(e); }
  }
  return true; // continue down the chain
 }

 @Override
 public Set getHeaders() {
  if (log_p) logger.info("getHeaders");
  return null;
 }
}

A questo punto, dopo aver generato il client Java al servizio tramite il comando wsimport, prima di effettuare la chiamata è necessario settare da codice l'handler, sfruttando la proprietà setHandlerResolver della classe HelloWorldService generata dal wsimport, in questo modo:

import it.handlers.ClientHandler;
import java.util.ArrayList;
import java.util.List;
import javax.xml.ws.handler.Handler;
import javax.xml.ws.handler.HandlerResolver;
import javax.xml.ws.handler.PortInfo;
public class CallService {

 public static void  main(String[] args){
  HelloWorldService servizio=new HelloWorldService();
  servizio.setHandlerResolver(new ClientHandlerResolver());
  System.out.println(servizio.getHelloWorldPort().saluta("muttillo"));
 }
}
class ClientHandlerResolver implements HandlerResolver {
 public List getHandlerChain(PortInfo port_info) {
  List hchain = new ArrayList();
  hchain.add(new ClientHandler());
  //hchain.add(new TestHandler()); // for illustration only
  return hchain;
 }
}



giovedì 30 maggio 2013

Capire dal .class la java version

I compilati Java, con estensione .class, hanno una struttura iniziale che ci consente di capire con quale java version sono stati compilati.
Tutti i .class iniziano con la sequenza 0xCAFEBABE, detto magic number, il numero esadecimale che consente di capire il formato del file.
Sulla storia di questo magic number sono raccontate in rete storie divertenti, secondo Wikipedia inglese pare che tutto venga da un caffè frequentato dai Grateful Dead (http://en.wikipedia.org/wiki/Java_class_file).
Dopo il magic number c'è l'identificativo di versione major e minor.
I valori del major identificano la versione e sono i seguenti:
46 -> java 1.2
47-> java 1.3
48-> java 1.4
49-> java 1.5
50 -> java 1.6
51-> java 1.7

Di seguito incollo il testo di una classe Java che dato un file .class restituisce la versione Java.


package it.test;
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.*;
public class CheckCompiledVersion {
 static Map myMap=null;
 private static CheckCompiledVersion istanza;
 private CheckCompiledVersion(){
  
 }
 public static CheckCompiledVersion getInstance(){
  if(istanza==null){
   istanza=new CheckCompiledVersion();
  }
  return istanza;
 }
 static
 {

      myMap=new HashMap();
      myMap.put(46, "Java 1.2");
      myMap.put(47, "Java 1.3");
      myMap.put(48, "Java 1.4");
      myMap.put(49, "Java 1.5");
      myMap.put(50, "Java 1.6");
      myMap.put(51, "Java 1.7");
 }
 public static void main(String[] args) throws IOException {
  System.out.println(CheckCompiledVersion.getInstance().checkClassVersion("out/Myfile.class"));
 }

 public String checkClassVersion(String filename)
         throws IOException
     {
         DataInputStream in = new DataInputStream(new FileInputStream(filename));
         int magic = in.readInt();
         if(magic != 0xcafebabe) {
           return filename + " non è un .class";
         }
         int minor = in.readUnsignedShort();
         int major = in.readUnsignedShort();
         System.out.println(filename + ": " + major + " . " + minor);
         in.close();
         return myMap.get(major);
     }
}


sabato 18 maggio 2013

UDDI

L'UDDI (acronimo di Universal Description Discovery and Integration) è un registry (ovvero una base dati ordinata e indicizzata), basato su XML e indipendente dalla piattaforma hardware, che permette alle aziende la pubblicazione dei propri dati e dei servizi offerti su internet. (cit. wikipedia)

  UDDI include un  XML Schema che descrive 4 tipi di informazioni principali:
  • businessEntity (include informazioni sul business name,la descrizione etc);
  • businessService (include informazioni sui web service)
  • bindingTemplate (include informazioni su come e dove accedere il web service)
  • tModel (Technical model, include descrizioni o puntatori a specifiche tecniche esterne oppure a tassonomie).
UDDI può avere come identificativo di un servizio un numero detto Dun & Bradstreet D-U-N-S® Number, un numero di nove cifre codificato secondo un certo standard (vedi qui ).

 Vediamo degli esempi:

businessEntity



<businessEntity
businessKey="0076b468-eb27-42e5-ac09-9955cff462a3"
operator="Microsoft Corporation" authorizedName="Martin Kohlleppel">
<name>Microsoft Corporation</name>
<description xml:lang="en">Empowering people through great software
- any time, any place and on any device is Microsoft's vision. As the
worldwide leader in software for personal and business computing, we
strive to produce innovative products and services that meet our
customer's...
</description>
<contacts>
<contact useType="Corporate Addresses and telephone">
<description xml:lang="en">Corporate Mailing Addresses</
description>
<personName />
<phone useType="Corporate Headquarters">(425) 882-8080</phone>
<address sortCode="~" useType="Corporate Headquarters">
<addressLine>Microsoft Corporation</addressLine>
<addressLine>One Microsoft Way</addressLine>
<addressLine>Redmond, WA 98052-6399</addressLine>
<addressLine>USA</addressLine>
</address>
</contact>
<contact useType="Technical Contact - Corporate UD">
<description xml:lang="en">World Wide Operations</description>
<personName>Martin Kohlleppel</personName>
<email>martink@microsoft.com</email>
</contact>
</contacts>
<identifierBag>
<keyedReference
tModelKey="uuid:8609c81e-ee1f-4d5a-b202-3eb13ad01823"
keyName="D-U-N-S" keyValue="08-146-6849" />
</identifierBag>
<categoryBag>
<keyedReference
tModelKey="uuid:c0b9fe13-179f-413d-8a5b-5004db8e5bb2"
keyName="NAICS: Software Publisher" keyValue="51121" />
</categoryBag>
</businessEntity>


business service / binding Template


<businessService
serviceKey="d5921160-3e16-11d5-98bf-002035229c64"
businessKey="ba744ed0-3aaf-11d5-80dc-002035229c64">
<name>XMethods Delayed Stock Quotes</name>
<description xml:lang="en">20-minute delayed stock quotes</description>
<bindingTemplates>
<bindingTemplate
serviceKey="d5921160-3e16-11d5-98bf-002035229c64"
bindingKey="d594a970-3e16-11d5-98bf-002035229c64">
<description xml:lang="en">
SOAP binding for delayed stock quotes service
</description>
<accessPoint URLType="http">
http://services.xmethods.net:80/soap
</accessPoint>
<tModelInstanceDetails>
<tModelInstanceInfo
tModelKey="uuid:0e727db0-3e14-11d5-98bf-002035229c64" />
</tModelInstanceDetails>
</bindingTemplate>
</bindingTemplates>
</businessService>


tModel 


<tModel
tModelKey="uuid:0e727db0-3e14-11d5-98bf-002035229c64"
operator="www.ibm.com/services/uddi" authorizedName="0100001QS1">
<name>XMethods Simple Stock Quote</name>
<description xml:lang="en">Simple stock quote interface</description>
<overviewDoc>
<description xml:lang="en">wsdl link</description>
<overviewURL>
www.it-ebooks.info
Web Services Essentials
144
http://www.xmethods.net/tmodels/SimpleStockQuote.wsdl
</overviewURL>
</overviewDoc>
<categoryBag>
<keyedReference
tModelKey="uuid:c1acf26d-9672-4404-9d70-39b756e62ab4"
keyName="uddi-org:types" keyValue="wsdlSpec" />
</categoryBag>
</tModel>


Quindi esiste una API per interfacciare UDDI che si divide in 2 macro aree:
  • Inquiry API;
  • Publisher API.
La prima per ricercare servizi UDDI e la seconda invece per pubblicarli o aggiornarli o rimuoverli.
Questa API è basata su SOAP e comprende una serie di funzioni, essitono quindi librerie declinate nel linguaggio di specifico interesse che consentono di effettuare le chiamate al registro UDDI.

Inquiry API

find_binding cerca bindings associati ad uno specifico servizio
find_business cerca un tipo di business che rispetti il criterio specificato
find_service cerca i servizi associati a quello specifico business
find_tModel cerca i technical model che rispettano il criterio specificato
get_bindingDetail per ottenere un completo binding detail
get_businessDetail per ottenere un completo record di una businessEntity
get_serviceDetail per ottenere un completo record di un servizio
get_tModelDetail per ottenere un completo record di un tmodel

Publishing API

get_authToken : richiede un token di autenticazione. Il token verrà richiesto anche per tutte le successive invocazioni
discard_authToken: invalida il token
save_binding Inserisce o aggiorna un bindingTemplate record.
save_business Inserisce o aggiorna un businessEntity record.
save_service Inserisce o aggiorna un businessService record.
save_tModel Inserisce o aggiorna un tModel record.
delete_binding Cancella il  bindingTemplate record specificato dalla bindingKey.
delete_business Cancella il businessEntity record specificato dallabusinessKey.
delete_service Cancella il  businessService record specificagto dalla serviceKey.
delete_tModel nasconde il tModel record specificato dalla tModelKey (non si possono cancellare tModel)