venerdì 30 novembre 2012

Display tag decorator export e autosize colonne excel

Visualizzando una lista avevo 2 necessità:

  1. Decodificare dei valori in visualizzazione (es. a fronte di alcuni codici dare la relativa descrizione);
  2. Esportare questi valori in excel in modo da avere le colonne excel già adattate alla descrizione, senza doverlo fare a mano.
Per il punto 1 sono ricorso ad un decorator, solo che nell'export excel ancora avevo il campo codice invece della descrizione.
Infatti nella tabella bisogna specificare questo:

<display:setproperty name="decorator.media.excel" value="org.decorator.MyDecorator">



In modo da forzare ad utilizzare il decorator anche nell'export, quindi non basta definirlo soltanto a livello di tabella con l'attributo decorator.

Per quanto riguarda invece il punto 2 nel file displaytable.properties occorre dichiarare la seguente proprietà:



export.excel.class=org.displaytag.export.excel.ExcelHssfView

In questo modo tale modifica vale per tutte le tabelle dell'applicazione.
Per fare questa modifica premetto che occorre avere la versione 1.2 della libreria assieme al displaytag-export-poi-1.2.jar.
Nel mio caso poi, avendo l'applicazione la libreria POI-3.5 a Runtime avevo un errore di questo tipo:


java.lang.NoSuchMethodError: org.apache.poi.hssf.usermodel.HSSFRow.createCell(I)Lorg/apache/poi/hssf/usermodel

Evidentemente serviva una versione più recente della libreria ma essendo questa utilizzata in altri modi nell'applicazione ho deciso di ridefinirmi l'exporter nel file displaytag.properties facendolo puntare ad una mia classe.
Quindi ho decompilato la classe ExcelHssfView (che si trova dentro displaytag-export-poi-1.2.jar) e corretto manualmente gli errori trovati.
In particolare nel metodo doExport ho dovuto fare questa modifica:


                HSSFCellStyle headerStyle = wb.createCellStyle();
         headerStyle.setFillPattern((short)2);
         headerStyle.setFillBackgroundColor((short)54);
         HSSFFont bold = wb.createFont();
         bold.setBoldweight((short)700);
         bold.setColor((short)9);
         headerStyle.setFont(bold);

Inserendo il cast a short per i valori numerici.
Mentre invece nel costruttore della classe statica  ExcelGenerationException ho dovuto commentare il costruttore:


super(Messages.getString("ExcelView.errorexporting"), cause);

In quanto non definito nella libreria displaytag-1.2.jar (!?...) sostituendolo con

super(ExcelGenerationException.class,Messages.getString("ExcelView.errorexporting"));

Adesso tutto funziona come volevo.

giovedì 22 novembre 2012

Java generare random password

Metodo statico di utilità

public static String generateRandomPassword(int lunghezza){
  char[] caratteri={'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p'
 ,'q','r','s','t','u','v','w','x','y','z','è','ì','ò','ù','é','\'','&','(',')','?','!','/'};
  char[] maiuscole={'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P'
    ,'Q','R','S','T','U','V','W','X','Y','Z'};
         char[] numeri={'0','1','2','3','4','5','6','7','8','9'};
  int lunghezzaChar=caratteri.length;
  int lunghezzaMaiuscole=maiuscole.length;
  int lunghezzaNumeri=numeri.length;
  StringBuffer sb=new StringBuffer();
  List<String> composizionePwd=new LinkedList<String>();
  for(int i=0;i<lunghezza;i++){
   int x = (int)(Math.random() * 10);
   if(x<=3){
    composizionePwd.add("C");
   }
   else if(x>3 && x<=6){
    composizionePwd.add("M");
   }
   else
   {
    composizionePwd.add("N");
   }
  }
  for(int i=0;i<lunghezza;i++){
   int elementoArray=0;
   if(composizionePwd.get(i).equals("M")){
    elementoArray= (int)(Math.random()*lunghezzaMaiuscole);
    sb.append(maiuscole[elementoArray]);
   }
   else if(composizionePwd.get(i).equals("C"))
   {
    elementoArray=(int)(Math.random()*lunghezzaChar);
    sb.append(caratteri[elementoArray]);
   }
   else
   {
    elementoArray=(int)(Math.random()*lunghezzaNumeri);
    sb.append(numeri[elementoArray]);
   }
  }
  return sb.toString();
 }



mercoledì 21 novembre 2012

Javascript inibire scrittura in text area al combo change

Questa operazione apparentemente banale nasconde una piccola insidia con Internet
Explorer.
La scelta migliore rimane quella di utilizzare un framework Javascript come Jquery ma se tale scelta non fosse possibile....
Con Firefox e Chrome si può usare il costrutto:

 document.getElementById("idCampo").setAttribute("readonly","readonly");



Con Internet Explorer tale istruzione non funziona, e bisogna utilizzare

document.getElementById("idCampo").readOnly=true;

Di seguito il codice di un esempio, abbiamo una combo al cui evento onchange per un particolare codice si inibisce la scrittura nella textbox sottostante.


<html>

<head>

<script>
function disabilitaAbilita(){
if(document.getElementById("test").value=="1")
{
document.getElementById("firstName").value="Scrittura inibita!!!!";
//document.getElementById("firstName").setAttribute("readonly","readonly");
document.getElementById("firstName").readOnly=true;
}
else
{
document.getElementById("firstName").value="";
//document.getElementById("firstName").removeAttribute("readonly");
document.getElementById("firstName").readOnly=false;
}
}
</script>
</head>
<body onload="disabilitaAbilita()">

<form > Scelta

<SELECT name="example" id="test" onchange="disabilitaAbilita()">

<OPTION selected value="1">UNO </OPTION>

<OPTION  value="2">DUE</OPTION>

<OPTION value="3">TRE</OPTION>

<OPTION value="4">QUATTRO</OPTION>

</SELECT>

<br>

Testo<textarea type="text" name="firstname" id="firstName">

</form>

</body>

</html>
Ecco l'esempio renderizzato:
Scelta

Testo

Java ottenere MimeType da bytes[]

Esiste una comoda libreria che consente , dato l'array di byte, di determinare il mime type del file.
La libreria si chiama jmimemagic-0.1.0.jar ( si può scaricare qui ).
Richiede come dipendenza i 2 seguenti jar:
  • commons-logging.jar;
  • jakarta-oro.jar.
Vediamo un esempio su un progetto Java, in cui ho incluso nel classpath i 3 jar.


Magic m=new Magic();
byte[] lb=FileUtil.load(new File("file/WS.png"));
MagicMatch match = m.getMagicMatch(lb);
System.out.println(match.getMimeType());

L'array di byte dal file è stato ottenuto  con il seguente metodo statico di una classe denominata FileUtil di cui incollo il codice:

import java.io.*;
public class FileUtil {

  public final static byte[] load(String fileName)
   {
     try { 
       FileInputStream fin=new FileInputStream(fileName);
       return load(fin);
     }
     catch (Exception e) {
  
       return new byte[0];
     }
   }

   public final static byte[] load(File file)
   {
     try { 
       FileInputStream fin=new FileInputStream(file);
       return load(fin);
     }
     catch (Exception e) {
      
       return new byte[0];
     }
   }

   public final static byte[] load(FileInputStream fin)
   {
     byte readBuf[] = new byte[512*1024];
   
     try { 
       ByteArrayOutputStream bout = new ByteArrayOutputStream();
     
       int readCnt = fin.read(readBuf);
       while (0 < readCnt) {
         bout.write(readBuf, 0, readCnt);
         readCnt = fin.read(readBuf);
       }
       
       fin.close();
       
       return bout.toByteArray();
     }
     catch (Exception e) {
      
       return new byte[0];
     }
   }
}


mercoledì 14 novembre 2012

Problemi di visualizzazione in Internet Explorer 8

Mi è capitato un problema apparentemente molto strano.
Praticamente la stessa applicazione web aveva una resa grafica diversa (disallineamenti nei div nei bottoni ecc.) a seconda se veniva testata tramite localhost oppure tramite nome server.
Mettendo localhost tutto si vedeva correttamente, invece con il nome server si verificavano i disallineamenti.
Inutile dire che con tutti gli altri browser (Firefox,Chrome etc.) si vedeva sempre correttamente.
Tornando al problema era dovuto ad una impostazione di Inetrnet Explorer, la Visualizzazione Compatibilità, che serve a rendere migliore la resa grafica per siti web progettati per IE 6 e 7.
Dal menu Strumenti/Impostazione Visualizzazione Compatibilità avevo questa situazione:

Il problema si è risolto deselezionando la scelta "Visualizza siti Intranet in Visualizzazione Compatibilità".
In generale è anche possibile escludere o rimuovere dalla visualizzazione anche un solo sito.
L'applicazione infatti era realizzata con PrimeFaces 3.4 che è compatibile con IE 8 e superiori, non con 7 e 6, forzando la visualizzazione compatibilità a quanto ho capito era come forzare la visualizzazione ad Internet Explorer 7.


martedì 6 novembre 2012

Web Service Rest

In questo post vediamo come realizzare un semplice servizio Web Rest e deployarlo come una Web App sotto Tomcat 7.
Innanzitutto qui possiamo trovare le basi teoriche dei servizi Web Rest.
Tra i vantaggi dell'utilizzo di un servizio Web Rest c'è l'eliminazione del payload soap e quindi una maggiore velocità di fruizione.
Tra gli svantaggi per adesso sicuramente c'è quello di non avere un sistema built-in di security integrato (tipo WS-Security per i Web Service SOAP).
Sviluppare l'esempio di per se è stato molto semplice, l'unico problema è quello di avere le giuste dipendenze nel classpath.
Ho utilizzato le librerie di Jersey per costruire il servizio.
In particolare dentro la WEB-INF /lib ho i seguenti jar:
  • asm-3.3.1.jar;
  • jaxrs-api.jar;
  • jersey-core-1.1.5.1.jar;
  • jersey-server-1.1.5.1.jar;
  • jsr311-api-1.1.jar.
Le librerie di Jersey si trovano sul sito, la jsr311 l'ho scaricata da internet direttamente.
Nel web.xml dobbiamo configurare la servlet di Jersey per esporre il servizio.

<servlet>
    <servlet-name>RestfulContainer</servlet-name>
    <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
    <init-param>
      <param-name>com.sun.jersey.config.property.packages</param-name>
      <param-value>it.test</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>RestfulContainer</servlet-name>
    <url-pattern>/rest/*</url-pattern>
  </servlet-mapping> 

Si noti che il param-value rappresenta il nome del package in cui abbiamo deployato il servizio.

La classe che implementa il servizio Web è la seguente:

package it.test;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
// The Java class will be hosted at the URI path "/helloworld"
@Path("/helloworld")
public class RestTest {
    
  // This method is called if TEXT_PLAIN is request
   @GET
   @Produces(MediaType.TEXT_PLAIN)
   public String sayPlainTextHello() {
     return "Hello Jersey";
   }

   // This method is called if XML is request
   @GET
   @Produces(MediaType.TEXT_XML)
   public String sayXMLHello() {
     return "" + "<hello> Hello Jersey" + "</hello>";
   }

   // This method is called if HTML is request
   @GET
   @Produces(MediaType.TEXT_HTML)
   public String sayHtmlHello() {
     return "<html> " + "<title>" + "Hello Jersey" + "</title>"
         + "<body><h1>
<b>" + "Hello Gino" + "<b></b></b></h1>
</body></html></code></pre>
" + " ";
   }
   @GET
   @Path("/greetings/{test}")
    @Produces(MediaType.TEXT_PLAIN)
   public String saluta(@PathParam("test") String t){
    return "Ciao "+t;
   }
  
}  

Sono servizi in get molto semplici, con l'annotazione @Produces si specifica il tipo di output che sarà prodotto mentre con @PathParam si indica un parametro da passare in input al servizio (è il caso del metodo saluta).

Una volta fatto partire il server possiamo verificare che il servizio risponda digitando

http://localhost:8080/TestRest/rest/helloworld

Dovrebbe apparire una pagina bianca con scritto "Hello Gino".
 Poichè nei servizi rest una risorsa è indentificata dal suo URI aggiungendo al path della web app +path servlet il nome specificato nell'annotazione @Path della classe.

Scrivere un client Java al servizio

Ho creato un semplice java project inserendo le seguenti librerie nel classpath:

  • asm-3.3.1.jar;
  • jaxrs-api.jar;
  • jersey-bundle-1.9.1.jar.
Il codice del client è il seguente

package it.client;
import java.net.URI;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.UriBuilder;
import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.WebResource;
import com.sun.jersey.api.client.config.ClientConfig;
import com.sun.jersey.api.client.config.DefaultClientConfig;
public class ClientTest {
 public static void main(String[] args) {
      ClientConfig config = new DefaultClientConfig();
      Client client = Client.create(config);
      WebResource service = client.resource(getBaseURI());
      // Fluent interfaces
      System.out.println(service.path("rest").path("helloworld").accept(MediaType.TEXT_PLAIN).get(ClientResponse.class).toString());
      // Get plain text
      System.out.println(service.path("rest").path("helloworld").accept(MediaType.TEXT_PLAIN).get(String.class));
      // Get XML
      System.out.println(service.path("rest").path("helloworld").accept(MediaType.TEXT_XML).get(String.class));
      // The HTML
      System.out.println(service.path("rest").path("helloworld").accept(MediaType.TEXT_HTML).get(String.class));
     System.out.println(service.path("rest").path("helloworld/greetings/kjasdkjasd").type(MediaType.TEXT_PLAIN).get(String.class)); 

 }
  private static URI getBaseURI() {
      return UriBuilder.fromUri("http://localhost:8080/TestRest").build();
    }
}

L'output prodotto a console è il seguente:

GET http://localhost:8080/TestRest/rest/helloworld returned a response status of 200 OK
Hello Jersey
<hello> Hello Jersey</hello>
<html> <title>Hello Jersey</title><body><h1>
<b>Hello Gino<b></b></b></h1>
</body></html>
Ciao kjasdkjasd

Tomcat 7 : [org.apache.xml.resolver.tools.ResolvingXMLReader]

Sono incappato nel seguente errore allo start up di Tomcat 7 (jdk 1.7 di riferimento).


Unable to load class [org.apache.xml.resolver.tools.ResolvingXMLReader] 
to check against the @HandlesTypes annotation of one or more ServletContentInitializers. 
java.lang.ClassNotFoundException: org.apache.xml.resolver.tools.ResolvingXMLReader

L'errore si ripeteva in continuazione e di fatto il war che stavo provando (un web service) non veniva deployato.
Il web.xml era versione 3.0.
La soluzione del problema è mettere il seguente attributo dentro il nodo web-app del web.xml:

metadata-complete="true"

Questo attributo dice al server di non scansionare tutta la directory web-inf/lib alla ricerca di annotazioni compatibili con il modello di servlet 3.0.
Un vantaggio di questo attributo inoltre è che migliora di molto la velocità dell'application server allo start up.

lunedì 5 novembre 2012

Verificare versione JAX-B installata

Quando capita di incappare in errori del tipo

com.sun.xml.bind.v2.runtime.JAXBContextImpl
cannot be cast to com.sun.xml.internal.bind.api.JAXBRIContext


Occorre verificarela versione di JAX-B presente nella JDK.
Il problema può essere dovuto al class loader, ossia al caricamento nel classpath di una libreria diversa.
Questo può accadere ad esempio se nella nostra web-app nella directory WEB-INF/lib carichiamo una libreria di JAX-B con versione diversa.
Per verificare la versione della nostra jdk posizionarsi da terminale nella directory bin della jdk e digitare il comando xjc -version.


C:\Programmi\Java\jdk1.6.0_35\bin>xjc -version
xjc version "JAXB 2.1.10 in JDK 6"
JavaTM Architecture for XML Binding(JAXB) Reference Implementation, (build JAXB
2.1.10 in JDK 6)




giovedì 1 novembre 2012

Installare Tomcat 7 come servizio windows rinominandolo

Per installare Tomcat 7 come servizio occorre andare da command sulla bin del server e digitare

>> service.bat install
Per installarlo mentre per disinstallarlo


>> service.bat uninstall
Se vogliamo rinominarlo possiamo editare il file service.bat oppure farne una copia, quindi inserire nelle linee


rem Set default Service name
set SERVICE_NAME=Tomcat7
set PR_DISPLAYNAME=Apache Tomcat 7

Il SERVICE_NAME e il PR_DISPLAYNAME che si desidera.
Il  PR_DISPLAYNAME è quello che si vedrà andando sulla lista dei servizi di windows