sabato 15 novembre 2014

Primefaces combo con immagine

Primefaces (sto utilizzando la versione 3.5, ma la sintassi da utilizzare è la stessa nella  attuale 5.1)  consente agevolmente l'utilizzo di componenti web complessi, come ad esempio una combo in cui associamo una immagine per ogni elemento (esempio tipico per la scelta della lingua con la bandiera corrispondente).
Di seguito il codice utilizzato, prendendo spunto dallo showcase di PF.

POJO PER RAPPRESENTARE L'ELEMENTO DELLA COMBO


package it.test.managedBeans;

import java.io.Serializable;

public class Language implements Serializable {
 
 private static final long serialVersionUID = 442345384294917624L;
 private String code;
 private String descr;
 private String urlPath;
 
 public int hashCode() {
     return this.code.hashCode()+this.descr.hashCode()+this.urlPath.hashCode();
   }
 
 public boolean equals(Object o){
  if(o instanceof Language && o!=null) {
   Language l=(Language)o;
   
   if(l.getCode()!=null && l.getDescr()!=null 
&& l.getUrlPath()!=null && l.getCode().equals(this.code) && l.getDescr().equals(this.descr) 
&& l.getUrlPath().equals(this.urlPath)){
    return true;
   }
   else
   {
    return false;
   }
  }
  else
  {
   return false;
  }
 }
 public Language(){
  
 }
 public Language(String code,String descr,String urlPath){
  this.code=code;
  this.descr=descr;
  this.urlPath=urlPath;
 }
 public String getCode() {
  return code;
 }
 public void setCode(String code) {
  this.code = code;
 }
 public String getDescr() {
  return descr;
 }
 public void setDescr(String descr) {
  this.descr = descr;
 }
 public String getUrlPath() {
  return urlPath;
 }
 public void setUrlPath(String urlPath) {
  this.urlPath = urlPath;
 }
 
}


MANAGED BEAN

package it.test.managedBeans;
import it.dao.LanguageRetrievalDao;
import it.daoImpl.LanguageRetrievalDaoImpl;
import it.object.Language;
import it.util.Utility;
import java.util.List;
import java.util.Locale;
import javax.annotation.PostConstruct;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
import javax.faces.event.ActionEvent;
import javax.faces.event.ValueChangeEvent;
@ManagedBean(name="languageLoader")
@SessionScoped
public class LanguageLoader {
 
 private  String locale = Locale.getDefault().getDisplayLanguage();

    public void setLocale(String locale1) {
      this.locale = locale1;
    }

    public synchronized String getLocale() {
      return locale;
    }
 
  private List listaLingue;
 
  private Language linguaSelezionata=null;
  @PostConstruct
     public void init() {
   try
   {
    
    LanguageRetrievalDao ldao=LanguageRetrievalDaoImpl.getInstance();
    this.listaLingue=ldao.getListaLinguaggi();
    Language loc=ldao.getLanguageByCode("it");
    this.locale=loc.getCode();
   this.linguaSelezionata=loc;
   }
   catch(Exception ex){
    Utility.aggiungiMessaggioErrore(ex.getMessage());
   }
  }

 public List getListaLingue() {
  return listaLingue;
 }

 public void setListaLingue(List listaLingue) {
  this.listaLingue = listaLingue;
 }

 public Language getLinguaSelezionata() {
  return linguaSelezionata;
 }

 public void setLinguaSelezionata(Language linguaSelezionata) {
  this.linguaSelezionata = linguaSelezionata;
 }
  

  
 public void prova(ActionEvent ev){
   
  Utility.aggiungiMessaggioInformativo("E' stato selezionato il valore "+this.linguaSelezionata.getDescr());
  this.setLocale(this.linguaSelezionata.getCode());
 
 }
 
 public void prova3(){
   
  Utility.aggiungiMessaggioInformativo("E' stato selezionato il valore "+this.linguaSelezionata.getDescr());
  this.setLocale(this.linguaSelezionata.getCode());
  Locale.setDefault(new Locale(this.linguaSelezionata.getCode()));
 
 }
}



I due metodi prova e prova3 sono invocati il primo ad un onchange della combo, l'altro invece è un metodo invocato da un actionlistener.

ESEMPIO XHTML CON EVENTO CHE SCATTA ALL'ONCHANGE


<html xmlns="http://www.w3c.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html"  xmlns:f="http://java.sun.com/jsf/core" 
xmlns:ui="http://java.sun.com/jsf/facelets"   xmlns:p="http://primefaces.org/ui"  xmlns:fn="http://java.sun.com/jsp/jstl/functions">
<f:view contentType="text/html" locale="#{languageLoader.locale}">
<h:head >
</h:head>
<h:body> 
<h:form id="mainForm">
<br/>
<br/>
<br/>
 <p:growl id="msgs" showDetail="true"  />
<br/>
<br/>
<br/>
<br/>
<br/>
<br/>
<p:selectOneMenu value="#{languageLoader.linguaSelezionata}"  var="immagine">
<f:converter converterId="myComboLanguageConverter"></f:converter>
<f:selectItem itemLabel="Select One" itemValue="" />
<f:selectItems value="#{languageLoader.listaLingue}" var="lingua"
itemLabel="#{lingua.descr}" itemValue="#{lingua}"/>
<p:ajax event="change" update="msgs"  
                            listener="#{languageLoader.prova3}" /> 
<p:column>
<p:graphicImage value="/images/lang/#{immagine.urlPath}" width="40" height="50"/>
</p:column>
<p:column>
#{immagine.code} - #{immagine.descr}
</p:column>
</p:selectOneMenu>
</h:form>
 </h:body>
 </f:view>
</html>


ESEMPIO XHTML CON EVENTO SCATENATO DA PRESSIONE DI BOTTONE


<html xmlns="http://www.w3c.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html"  xmlns:f="http://java.sun.com/jsf/core" 
xmlns:ui="http://java.sun.com/jsf/facelets"   xmlns:p="http://primefaces.org/ui"  xmlns:fn="http://java.sun.com/jsp/jstl/functions">
<f:view contentType="text/html" locale="#{languageLoader.locale}">
<h:head >
</h:head>
<h:body> 
<h:form id="mainForm">
<br/>
<br/>
<br/>
 <p:growl id="msgs" showDetail="true"  />
<br/>
<br/>
<p:selectOneMenu value="#{languageLoader.linguaSelezionata}" id="combo1"  var="immagine">
<f:converter converterId="myComboLanguageConverter"></f:converter>
<f:selectItem itemLabel="Select One" itemValue="" />
<f:selectItems value="#{languageLoader.listaLingue}" var="lingua"
itemLabel="#{lingua.descr}" itemValue="#{lingua}"/>
<p:column>
<p:graphicImage value="/images/lang/#{immagine.urlPath}" width="40" height="50"/>
</p:column>
<p:column>
#{immagine.code} - #{immagine.descr}
</p:column>
</p:selectOneMenu>
<br/>
<p:commandButton actionListener="#{languageLoader.prova}" value="TEST"  update="msgs combo1"></p:commandButton>
<br/>
<br/>
<br/>
</h:form>
 </h:body>
 </f:view>
</html>


CONVERTER

Si noti che il converter si utilizza per trasformare l'oggetto in stringa e viceversa e non entra in gioco nella rappresentazione della combo stessa con le immagini ma negli eventi successivi al click del pulsante oppure al cambiamento della combo stessa.

package it.test.managedBeans;
import it.dao.LanguageRetrievalDao;
import it.daoImpl.LanguageRetrievalDaoImpl;
import it.object.Language;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.convert.FacesConverter;
@FacesConverter("myComboLanguageConverter")
public class LanguageConverter implements Converter {

 @Override
 public Object getAsObject(FacesContext context, UIComponent
   component, String value) {
  LanguageRetrievalDao lr=LanguageRetrievalDaoImpl.getInstance();
  return lr.getLanguageByCode(value);
 }

 @Override
 public String getAsString(FacesContext context, UIComponent component, Object value) {
  if(value instanceof Language){
   return ((Language)value).getCode();
  }
  else
  {
   return "";
  }
  
 }

}



Attenzione è importantissimo nella selectOneMenu impostare l'itemValue direttamente con il valore dell'oggetto, non con il codice della lingua, infatti poi è il componente di primefaces che sfruttando i metodi equals ed hascode dell'oggetto Language associa correttamente immagini e descrizioni al codice dato ("it" oppure "en").

mercoledì 22 ottobre 2014

Inserire shared library su WAS 8.5

Per inserire una shared library su WAS 8.5
occorre entrare nella console di WebSphere cliccare su Ambiente/Librerie Condivise

Quindi creare una nuova libreria condivisa associando i 2 jar necessari comprensivi del percorso corretto

Esempio

 Ho specificato poi nell'ambito "Tutti gli ambiti" in modo che la mia shared library fosse visibile all'applicazione web di interesse.

A questo punto andare su applicazioni/Applicazioni Enterprise WebSphere e selezionare il progetto web di interesse.

Su Applicazioni Enterprise/NOME_WEB_APP/Riferimenti libreria condivisa
selezionare il nome della applicazione quindi andare su "Librerie condivise di riferimento".


Quindi occorre far ripartire la nostra applicazione che a questo punto avrà nel classpath la shared library.

lunedì 29 settembre 2014

Carattere fine riga (CRLF LF)

Il carattere di fine riga su file di testo è diverso a seconda del sistema operativo.
Su windows abbiamo CRLF dove CR ('\r') sta per Carriage Return e LF ('\n') per Line Feed.
Invece per Linux abbiamo solo LF.
In Java se utilizziamo il metodo newLine() della classe java.io.BufferedWriter questo si comporta in maniere platform dependant, ossia inserisce CRLF su Windows e LF su Linux.
Se da una programma Java su Windows si vuole creare un file di testo con carattere di fine riga compatibile con sistemi Linux allora invece di usare il metodo newLine() occorre esplicitamente fare la write del carattere \n.
In questo modo:


BufferedWriter bw=new BufferedWriter(new FileWriter(percorsoFileCopiato));
BufferedReader br = new BufferedReader(new FileReader(percorsoFileDaCopiare));
String line;
 while ((line = br.readLine()) != null) {
        bw.write(line);
      //  bw.newLine();
        bw.write("\n");
   }
 br.close();
 bw.flush();
 bw.close();


martedì 16 settembre 2014

Verificare su che porta risponde Sql Server

Si può eseguire il seguente comando sql

xp_readerrorlog 0, 1, N'Server in ascolto sulla porta'','any', NULL, NULL, N'asc'

mercoledì 20 agosto 2014

Query per trovare tabelle che hanno una FK verso la tabella in esame

Query per trovare tutte le foreign key verso una tabella, testato con Sql Server 2008

select t.name , fk.constraint_column_id  , c.name
from sys.foreign_key_columns as fk
inner join sys.tables as t on fk.parent_object_id = t.object_id
inner join sys.columns as c on fk.parent_object_id = c.object_id and fk.parent_column_id = c.column_id
where fk.referenced_object_id = (select object_id from sys.tables where name = 'NOME_TABELLA')
order by t.name , fk.constraint_column_id

lunedì 4 agosto 2014

Apache POI generare commenti su cella

Per creare un commento su una cella con Apache POI.

public static Comment addComment(HSSFCell cell,String testo){
  
    Drawing drawing = cell.getSheet().createDrawingPatriarch();
      CreationHelper factory = cell.getSheet().getWorkbook().getCreationHelper();
      ClientAnchor anchor = factory.createClientAnchor();
      anchor.setCol1(cell.getColumnIndex());
      anchor.setCol2(cell.getColumnIndex() + 3);
      anchor.setRow1(cell.getRowIndex());
      anchor.setRow2(cell.getRowIndex() + 2);
      Comment comment = drawing.createCellComment(anchor);
      RichTextString str = factory.createRichTextString(testo);
      comment.setString(str);
      return comment;
  
 }


Occorre importare i seguenti package:

import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFCellStyle;
import org.apache.poi.hssf.usermodel.HSSFFont;
import org.apache.poi.hssf.usermodel.HSSFRichTextString;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.hssf.util.HSSFColor;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.ClientAnchor;
import org.apache.poi.ss.usermodel.Comment;
import org.apache.poi.ss.usermodel.CreationHelper;
import org.apache.poi.ss.usermodel.Drawing;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.apache.poi.ss.usermodel.RichTextString;

lunedì 14 luglio 2014

Sql per calcolare prodotto matriciale

Una matrice sparsa (cioè con molti zeri), può essere rappresentata nel formato (i,j,value)
dove i= riga j=colonna e value=il valore != 0 della matrice.
Esempio una matrice del tipo
0 0 1
1 0 0
5 5 0
sarà rappresentata dai seguenti record:
0 2 1
1 0 1
2 0 5
2 1 5

Per effettuare il prodotto matriciale si può utilizzare una join di questo tipo:


SELECT a.row_num, b.col_num, SUM(a.value*b.value) from a,b 
on a.col_num=b.row_num group by a.row_num,b.col_num;


martedì 8 luglio 2014

Java copiare un file pre java 7

Nelle versioni precedenti a Java 7 per copiare un file occorre scriversi un metodo di questo tipo:


public void copia(String in,String out) throws Exception{
 BufferedWriter bw=new BufferedWriter(new FileWriter(in));
 BufferedReader br = new BufferedReader(new FileReader(out));
 String line;
 while ((line = br.readLine()) != null) {
   bw.write(line);
   bw.newLine();
 }
 br.close();
 bw.flush();
 bw.close();
}


sabato 14 giugno 2014

Tomcat7 cifrare password per autenticazione basic web application

Per abilitare l'autenticazione BASIC (con prompt del browser per username e password) in una web application occorre agire sul web.xml e quindi sull'application server.
In Tomcat è possibile utilizzare il file tomcat-users.xml presente nella directory conf di Tomcat.
Ad esempio supponiamo di avere il seguente codice nel web.xml della nostra web app:

 <login-config>
    <auth-method>BASIC</auth-method>
    <realm-name>Richiesta autenticazione per gestione utenze</realm-name>
  </login-config>
  <security-constraint>
    <web-resource-collection>
      <web-resource-name>restricted methods</web-resource-name>
      <url-pattern>/index.jsf</url-pattern>
      <http-method>GET</http-method>
      <http-method>POST</http-method>
    </web-resource-collection>
    <auth-constraint>
      <role-name>APP_ADMIN</role-name>
    </auth-constraint>
  </security-constraint>
  <security-role>
    <role-name>APP_ADMIN</role-name>
  </security-role>


Quindi nel Tomcat devono essere definite le credenziali dell'utente APP_ADMIN.

Tipicamente nel file tomcat-users.xml si inseriranno le seguenti indicazioni:


<role rolename="LEASING_ADMIN"/>
  <user password="qawsedrf" roles="APP_ADMIN" username="master"/>


Come possiamo vedere l'inconveniente di questo approccio è che la password è memorizzata in chiaro dentro Tomcat.

E' possibile cifrare la password utilizzando i seguenti algoritmi di cifratura
  • SHA;
  • MD2;
  • MD5 
Si può utilizzare l'utility digest.bat per cifrare la password (digest.sh su sistemi linux).
Digitando quindi da prompt di dos il comando

digest.bat -a md5 qawsedrf  

premendo invio si ottiene la risposta

qawsedrf:d1c2dcfcbfd41ce1103a25475b64f7a5

Quindi dobbiamo inserire la password cifrata al posto di quella in chiaro nel tomcat-users.xml.

Ora dobbiamo comunicare a Tomcat che la modalità di controllo password deve passare per un hash md5 della password digitata dall'utente.

Nel file conf/server.xml occorre cercare il Realm UserDataBaseRealm ed inserire l'attributo digest="md5" in questo modo:

<Realm className="org.apache.catalina.realm.LockOutRealm">
    <!-- This Realm uses the UserDatabase configured in the global JNDI
         resources under the key "UserDatabase".  Any edits
         that are performed against this UserDatabase are immediately
         available for use by the Realm.  -->
    <Realm className="org.apache.catalina.realm.UserDatabaseRealm" resourceName="UserDatabase" digest="md5"/>
</Realm>



domenica 8 giugno 2014

Gestire sessione non valida con JSF 2.0

Quando in JSF la sessione scade si incappa nella tipica eccezione di tipo

javax.faces.application.ViewExpiredException

Questo errore si verifica quando l'utente richiede una pagina ma la sessione è ormai scaduta, per cui non si riesce a ricostruire l'alberatura degli oggetti propria di JSF.

Un modo molto semplice per ovviare a questa eccezione è quello di dichiararla nel web.xml in questo modo:

<error-page>
    <exception-type>javax.faces.application.ViewExpiredException</exception-type>
    <location>/viewExpired.xhtml</location>
</error-page>


Il problema di questo approccio è che non gestisce i casi di chiamate ajax, ed essendo ormai il supporto di ajax il comportamento base di tutti i componenti di JSF 2.0 la dichiarazione nel web.xml non basta.

E' necessario quindi creare una classe che estenda

javax.faces.context.ExceptionHandlerWrapper

in questo modo


package it.exceptionHandler;
import it.util.Costanti;
import it.util.Utility;
import java.io.IOException;
import java.util.Iterator;
import javax.faces.FacesException;
import javax.faces.application.ViewExpiredException;
import javax.faces.context.ExceptionHandler;
import javax.faces.context.ExceptionHandlerWrapper;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.faces.event.ExceptionQueuedEvent;
import org.apache.log4j.Logger;
public class ViexExpiredExceptionHandler extends ExceptionHandlerWrapper {
 private static final Logger log=Logger.getLogger(Costanti.LOGGER);
 private ExceptionHandler parent;
 public ViexExpiredExceptionHandler(ExceptionHandler parent){
  this.parent=parent;
 }

 @Override
 public ExceptionHandler getWrapped() {
  return this.parent;
 }
 @Override
 public void handle() throws FacesException{
   FacesContext facesContext = FacesContext.getCurrentInstance();
         for (Iterator<ExceptionQueuedEvent> iter = getUnhandledExceptionQueuedEvents().iterator(); iter.hasNext();) {
           Throwable exception = iter.next().getContext().getException();

if (exception instanceof ViewExpiredException) {


    final ExternalContext externalContext = facesContext
            .getExternalContext();

    try {


        facesContext.setViewRoot(facesContext.getApplication()
                .getViewHandler()
                .createView(facesContext, "/viewExpired.jsf"));     
        externalContext.redirect("viewExpired.jsf");             
        facesContext.getPartialViewContext().setRenderAll(true);
        facesContext.renderResponse();

    } 
    catch(IOException ioex){
     log.error(Utility.stackToString(ioex));
    }
    finally {
        iter.remove();
    }
}

}

getWrapped().handle();
 }

}


Quindi occorre creare una classe che estenda

javax.faces.context.ExceptionHandlerFactory e che faccia l'override del metodo getExceptionHandler tornando il nostro Handler personalizzato che gestisce la ViewExpiredException.


package it.exceptionHandler;
import javax.faces.context.ExceptionHandler;
import javax.faces.context.ExceptionHandlerFactory;
public class MyExceptionHandlerFactory extends ExceptionHandlerFactory {
    private ExceptionHandlerFactory parent;
    public MyExceptionHandlerFactory(ExceptionHandlerFactory parent){
     this.parent=parent;
    }
 @Override
 public ExceptionHandler getExceptionHandler() {
  ExceptionHandler result=parent.getExceptionHandler();
  result=new ViexExpiredExceptionHandler(result);
  return result;
 }

}


Quindi nel faces-config.xml dobbiamo dichiarare questa factory in questo modo:


<factory>
<exception-handler-factory>it.exceptionHandler.MyExceptionHandlerFactory</exception-handler-factory>
</factory>


giovedì 15 maggio 2014

Aggiungere repository Maven prime faces

Di seguito la struttura di un file pom utilizzato per utilizzare:
  • jsf 2.0;
  • primefaces 3.5;
  • spring 3.0;
  • javamail

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>test</groupId>
  <artifactId>webProva</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <name>test</name>
 <repositories>
  <repository>
<id>prime-repo</id>
<name>Prime Repo</name>
<url>http://repository.primefaces.org</url>
</repository>
</repositories>
  <dependencies>
  <dependency>
<groupId>org.primefaces</groupId>
<artifactId>primefaces</artifactId>
<version>3.5</version>
</dependency>
<dependency>
   <groupId>com.sun.faces</groupId>
   <artifactId>jsf-api</artifactId>
   <version>2.1.11</version>
  </dependency>
  <dependency>
   <groupId>com.sun.faces</groupId>
   <artifactId>jsf-impl</artifactId>
   <version>2.1.11</version>
  </dependency>
 

   <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-core</artifactId>
    <version>3.0.5.RELEASE</version>
   </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>3.0.5.RELEASE</version>
    </dependency>
     <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-expression</artifactId>
        <version>3.0.5.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-beans</artifactId>
        <version>3.0.5.RELEASE</version>
    </dependency>
    <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
            <version>3.0.5.RELEASE</version>
        </dependency>
        <dependency>
 <groupId>org.springframework</groupId>
 <artifactId>spring-web</artifactId>
 <version>3.0.5.RELEASE</version>
</dependency>
        <dependency> 
        <groupId>org.springframework</groupId> 
        <artifactId>spring-webmvc</artifactId> 
        <version>3.0.5.RELEASE</version>
         </dependency>
        <dependency>
 <groupId>javax.mail</groupId>
 <artifactId>mail</artifactId>
 <version>1.4</version>
</dependency>

  </dependencies>
</project>

domenica 4 maggio 2014

Caricamento Combo via Ajax tramite Json

Spesso capita di dover effettuare evolutive/manutenzioni su progetti vecchi, realizzati ad esempio Struts 1.3.
Qui metto un esempio di una classica combo a cascata (provincia comune) realizzata utilizzando Ajax e Json.
Ho utilizzato la libreria json-simple-1.1.1.jar per la creazione dell'oggetto Json lista dei comuni italiani ottenuto filtrando per provincia.
Come framework Javascript ho utilizzato Jquery versione 1.7.2.

JSP

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1"%>
    <%@taglib uri="http://struts.apache.org/tags-html" prefix="html"%>
<%@taglib uri="http://struts.apache.org/tags-bean" prefix="bean"%>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<script src="js/jquery-1.7.2.js"></script>
<script src="js/jquery-ui-1.8.22.custom.min.js"></script>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>TEST</title>
<script>
//js function per caricare via ajax i comuni
function caricaComboComuni(){
 $("#loading").show();
  $("#loaded").hide();
 $.ajax({
   url: "caricaComune.do?cdProv="+document.getElementById("cdProv").value,
   dataType: "json",
   success:
    function (data){
    $("select#idComuni option").remove();
    $("#loading").hide();
    $("#loaded").show();
         $.each(data.listaComuni, function(key, val){
          $("select#idComuni").append('<option value="' + val.codice + '">' + val.descrizione + '</option>');
         })
       ;
    },
    error:  function (data){
     alert(data);
     }
  })
}

</script>
</head>
<body>
<html:form action="/goToHello">
<html:select property="descrizione" styleId="cdProv" onchange="caricaComboComuni()" >
<html:optionsCollection  property="listaDati" label="descrizione" value="codice" />
</html:select>
<br/>
<div id="loading" style="display:none;"> 
<img src="img/ajax-loader.gif"/>
 </div>
 <div id="loaded">
<html:select property="comune" styleId="idComuni"></html:select>
</div>
<br/>
<input type="submit" value="go" />
</html:form>
</body>
</html>


ACTION

package it.action;
import it.beans.ComuneBean;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
public class SelectComune extends Action {
 public static JSONObject getJsonFromMyFormObject(List<ComuneBean> form)
   {
     JSONObject responseDetailsJson = new JSONObject();
     JSONArray jsonArray = new JSONArray();

     for (int i = 0; i < form.size(); i++)
     {
       JSONObject formDetailsJson = new JSONObject();
       formDetailsJson.put("codice", form.get(i).getCodice()+"-"+form.get(i).getDescrizione());
       formDetailsJson.put("descrizione", form.get(i).getDescrizione());
       jsonArray.add(formDetailsJson);
     }
     responseDetailsJson.put("listaComuni", jsonArray);
     return responseDetailsJson;
   }
 
 
 
 
 public ActionForward execute(ActionMapping mapping,ActionForm form,HttpServletRequest request,HttpServletResponse response)throws Exception
    {
     String codiceProv=request.getParameter("cdProv");
     List<ComuneBean> listaComuni=getComuneByProv(codiceProv);
     PrintWriter out = response.getWriter();
  out.write(getJsonFromMyFormObject(listaComuni).toJSONString());
   out.close();
  return null;
    }
 
 private List<ComuneBean> getComuneByProv(String codProv) throws Exception{
  String query="SELECT cd_catastale,descrizione FROM S_COMUNE where PROVINCIA_ID=? order by DESCRIZIONE";
  List<ComuneBean> l=new ArrayList<ComuneBean>();
  Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
  Connection conn=DriverManager.getConnection("jdbc:odbc:mydb","test","test");
  PreparedStatement pst=conn.prepareStatement(query);
  pst.setString(1, codProv);
  ResultSet rs=pst.executeQuery();
  while(rs.next()){
   ComuneBean c=new ComuneBean();
   c.setCodice(rs.getString("cd_catastale"));
   c.setDescrizione(rs.getString("descrizione").trim());
   l.add(c);
  }
  //Thread.sleep(3000);
  rs.close();
  pst.close();
  conn.close();
  return l;
 }

}


 

giovedì 24 aprile 2014

Query per trovare tabelle e righe totali in un db


Con questa query possiamo trovare tutte le tabelle con il numero di righe ordinate per tabella con più dati

select so.name as NomeTabella,max(si.rows) as righe from sysobjects as so
inner join sysindexes as si
on si.id=so.id and so.xtype='U'
group by so.name
order by 2 desc;

mercoledì 23 aprile 2014

Utility bcp Sql Server

L'utility bcp.exe, presente nelle installazioni di Sql Server di default, ci può servire per scaricarci i dati di una tabella assieme alla struttura.
Vediamo ad esempio come utilizzarla per creare un file di struttura della tabella e dopo per esportare i dati:

Con il comando

bcp domini.dbo.comuni format nul -f c:\comuni.frm -S 127.0.0.1 -U username-P password-n

Creiamo il file struttura specificando grazie al comando -n i dati nativi (quindi se ci sono campi di tipo data etc.)

Il risultato di questo comando è il seguente file:


10.0
3
1       SQLNCHAR            2       510     ""   1     codice avviamento bancario                               Latin1_General_CI_AS
2       SQLNCHAR            2       510     ""   2     denominazione                                            Latin1_General_CI_AS
3       SQLNCHAR            2       510     ""   3     sigla automobilistica                                    Latin1_General_CI_AS


Per esportare i dati possiamo fare così:

bcp domini.dbo.comuni out c:\comuni.dat -S 127.0.0.1 -U username -P password-c -v

L'opzione -c evita che ci sia il prompt per ogni campo e lo scrive di default come character usando sempre di default come separatore il tab tra un campo e l'altro.
Il risultato sarà di questo tipo (qui uno stralcio):


010009 TORINO TO
012005 AOSTA AO
014001 GENOVA GE
016006 MILANO MI
018002 TRENTO TN
020008 VENEZIA VE
022004 TRIESTE TS
024000 BOLOGNA BO
026005 ANCONA AN
028001 FIRENZE FI
030007 PERUGIA PG
032003 ROMA RM
034009 NAPOLI NA
036004 L'AQUILA AQ
038000 CAMPOBASSO CB
040006 BARI BA
042002 POTENZA PZ
044008 CATANZARO CZ
046003 PALERMO PA
......
......


Per ricaricare i dati su un'altra tabella o un altro db occorre invece eseguire il comando

bcp domini2.dbo.BKCR_DOMANDA  in comuni.dat -f comuni.fmt  -e scarico.log -S 127.0.0.1 -U gianos -P gianos -c

L'opzione -e scrive se necessario dei log di errore.

Si noti che per quanto riguarda lo scarico dei dati (creazione del file .dat) è possibile anche scaricare i dati in base ad una query con il seguente comando:

bcp "select TOP 100 * from domini.dbo.comuni" queryout output.txt -S 127.0.0.1 -U gianos -P gianos -c

mercoledì 9 aprile 2014

Log4j utilizzo di MDC (Mapped Diagnostic Context)

Utilizzando la classe org.apache.log4j.MDC è possibile inserire dei parametri in una mappa e poi riutilizzarli nei nostri appender, sfruttando il conversion pattern dell'appender.

Vediamo un esempio.

Per prima cosa dobbiamo definire un Filtro Http che inserisca nella mappa i valori, supponiamo di voler inserire l'indirizzo IP del chiamante.

Filtro


import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import org.apache.log4j.MDC;
public class MyMdcFilter implements Filter {
    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException {
     Utente utente = null;
        try {
         
         String ip = request.getRemoteAddr();
         
         MDC.put("ipHost",ip);
          
            chain.doFilter(request, response);
 
        } finally {
           
      MDC.remove("ipHost");
        }
 
    }

 public void destroy() {
 }

 public void init(FilterConfig arg0) throws ServletException {
 }
 
}


Impostazioni log4j.properties

si noti in particolare la riga in grassetto, che consente di recuperare il valore presente nell'MDC


##############################################################
################### DEFINIZIONE DEI LOGGER ###################
##############################################################



log4j.rootLogger=DEBUG
#Gestione log4j riguardante esclusivamente la comunicazione del Datagroup del log garante.
log4j.logger.MyLogger.logIp=INFO, LogIp


log4j.appender.LogIp=org.apache.log4j.DailyRollingFileAppender
## Inserire il percorso assoluto del file        "
log4j.appender.LogIp.File=
log4j.appender.LogIp.layout=org.apache.log4j.PatternLayout
log4j.appender.LogIp.layout.ConversionPattern=%d{dd-MM-yyyy HH\:mm\:ss} %X{ipHost}  %m%n
log4j.appender.LogIp.DatePattern='.'yyyy-MM-dd'.log'

Caricamento lato web del log4j.properties

Il log4j.properties si può caricare in una Servlet con parametro load on startup pari a 0, ossia una servlet il cui metodo init è richiamato allo start up della Web App


File log4jAppl = new File( PATH_FILE );
// da gestire nel caso in cui il file non sia indicato, si potrebbe loggare internamente 
PropertyConfigurator.configure(log4jProp); 

Utilizzo del logger

Come al solito si utilizzerà il logger richiamandolo così nei punti di interesse:


private static Logger myLog= Logger.getLogger("MyLogger.logIp");


Risultato nel file di log

08-04-2014 19:02:00 10.249.232.187  dati loggati.....;

sabato 5 aprile 2014

Callable vs Runnable

In Java dalla versione 1.0 è presente l'interfaccia java.lang.Runnable, che come sappiamo deve essere implementata da quelle classi che si vuole eseguire come Thread separati.
Nell'interfaccia è dichiarato un solo metodo

void run()

Dalla versione 1.5 invece è stata creata una nuova interfaccia java.util.concurrent.Callable che per certi versi può essere vista come una estensione dell'interfaccia Runnable. Lo scopo dei progettisti è sempre quello di mantenere la retrocompatibilità, e questo è il motivo per cui non hanno modificato l'interfaccia Runnable ma ne hanno creata una nuova.

L'unico metodo definito dall'interfaccia Callable è:

V call() throws Exception

Il vantaggio di utilizzare Callable è che consente di ottenere indietro il risultato dell'esecuzione del task nel thread separato, cosa che tramite Runnable non è possibile.
In generale le differenze tra le 2 interfacce sono le seguenti:

  1. Un oggetto che implementa Callable non può essere utilizzato direttamente per costruire un Thread, mentre un oggetto che implementa Runnable può farlo;
  2. Un oggetto che implementa  Callable torna un risultato mentre un oggetto che implementa Runnable no;
  3. Un  oggetto che implementa Callable può rilanciare al chiamante una eccezione di tipo checked, mentre un oggetto che implementa Runnable può lanciare soltanto unchecked exceptions.
Vediamo un esempio di utilizzo di Callable.
Per utilizzare un oggetto che implementa un Callable dobbiamo necessariamente utilizzare un ExecutorService.

CALLABLE

import java.util.concurrent.Callable;
public class Fattoriale implements Callable<Long> {
 private long n;
 public Fattoriale(long n){
 this.n=n; 
 }
 @Override
 public Long call() throws Exception {
  
  if(n<=0) throw new Exception("Attenzione inserire un valore positivo!!");
  long f=1;
  for(long i=1;i<=n;i++){
   f*=i;
  }
  return f;
 }
}

EXECUTORSERVICE

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class FattorialeTest {
 public static void main(String[] args) throws Exception {
       long N=5;
       Callable<Long> c=new Fattoriale(N);
       ExecutorService es=Executors.newSingleThreadExecutor();
       Future<Long> f=es.submit(c);
       System.out.println(String.format("Il fattoriale di %d è %d",N,f.get()));
       es.shutdown();
 }
}


Si noti che l'executor service funziona anche con oggetti di tipo Runnable ma, poichè questi non tornano un risultato, il Future associato tornerà null se il processo è terminato correttamente.

Generazione numeri casuali

L'utilizzo della classe java.util.Random in un contesto di concorrenza può portare a dei cali di performance e a problemi di contesa delle risorse.
In ambienti multithreaded è preferibile utilizzare la classe

java.util.concurrent.ThreadLocalRandom

introdotta con la java 1.7.

Ad esempio per ottenere un numero causale tra 1 e 10 si può utilizzare il metodo nextInt(int  least,int bound)

Dove la javadoc recita: "Returns a pseudorandom, uniformly distributed value between the given least value (inclusive) and bound (exclusive)", quindi


int n=ThreadLocalRandom.current().nextInt(1, 11);
System.out.println(n);



mercoledì 12 marzo 2014

Java utilizzo di Apache Derby

Per utilizzare Apache Derby occorre:
  1. Scaricare dal sito l'ultima release ( https://db.apache.org/derby/derby_downloads.html#Latest+Official+Releases );
  2. Dei vari jar scaricati prendere il derby.jar
Di default quando si creano gli archivi sono installati sotto la directory di esecuzione del programma.
In alternativa si può specificare una locazione differente settando la variabile derby.system.home .

Occorre prestare attenzione quando si fanno le insert di campi di tipo varchar a scrivere nelle query il singolo apice invece del doppio apice, per evitare di incappare in un errore di questo tipo:


 Column 'xxxx' is either not in any table in the FROM list or appears within a join specification and is outside the scope of the join specification or appears in a HAVING clause and is not in the GROUP BY list. If this is a CREATE or ALTER TABLE  statement then 'mario' is not a column in the target table.

Di seguito un esempio di codice che crea un db, una tabella, effettua una insert e recupera quindi i dati:

package it.test.derby;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
public class ProvaDerby {
 public static void main(String[] args) throws Exception {
  System.setProperty("derby.system.home", "c:\\lav\\derbyTest");
  String url="jdbc:derby:database_test;create=true;user=root;password=root;";
  Class.forName("org.apache.derby.jdbc.EmbeddedDriver").newInstance();
  int risultato=0;
  Connection conn=DriverManager.getConnection(url);
  Statement st=conn.createStatement();
  String creazione="create table persona (id integer not null,nome varchar(16),cognome varchar(30), PRIMARY KEY(id))";
  risultato=st.executeUpdate(creazione);
  System.out.println("RISULTATO: "+risultato);
  String insert= "insert into persona (id,nome,cognome) values(1,\"mario\",'rossi')";
  PreparedStatement pst=conn.prepareStatement(insert);
   risultato=pst.executeUpdate();
  System.out.println("RISULTATO INSERT: "+risultato);
  String inquiry="select * from persona";
  pst=conn.prepareStatement(inquiry);
  ResultSet rs=pst.executeQuery();
  while(rs.next()){
   System.out.println("Nome: "+rs.getString("nome"));
   System.out.println("Cognome: "+rs.getString("cognome"));
  }
  rs.close();
  pst.close();
  conn.close();
  

 }

}



Il contenuto della directory derbyTest sarà il seguente:


giovedì 6 marzo 2014

Java leggere file di properties

Utilizzando un file di properties in formato CHIAVE=VALORE è possibile leggere a runtime gli elementi presenti in questo modo


Properties p=new Properties();
p.load(new FileInputStream(new File("log.properties")));
        Set<Entry<Object, Object>> s=p.entrySet();
       Iterator<Entry<Object,Object>> it= s.iterator();
       while(it.hasNext()){
     Entry<Object,Object> e=it.next();
     String chiave=(String)e.getKey();
     String valore=(String)e.getValue();
     System.out.println("Chiave: "+chiave);
     System.out.println("Valore: "+ valore);
   }

lunedì 3 marzo 2014

JAX-B errore Two declarations cause a collision in the ObjectFactory class

Questo errore si presenta quando si prova a generare un client di un Web Service e alcuni tipi definiti nel servizio contengono elementi dal nome simile.
Una prova per cercare di risolvere il problema è specificare nel lancio del wsimport i parametri:

-B-XautoNameResolution

quindi con un comando di questo tipo

wsimport -keep -p it.test -B-XautoNameResolution [URL_WSDL]


giovedì 20 febbraio 2014

Abilitare auto complete su Eclipse Indigo per JSF

Per vedere l'autocomplete sulle pagine .xhtml in JSF 2 su Eclipse occorre andare sul progetto, tasto destro proprietà quindi andare su Project Facets e spuntare la dicitura per JSF 2.0.
La prima volta che si attua questa operazione chiede da dove si desidera prendere i jar necessari, conviene impostare direttamente i jar del progetto, scegliendo l'opzione "Disable library configuration".

Qui vediamo il menu delle facets





martedì 18 febbraio 2014

Impossibile effettuare il download di file con IE7 IE8. Impossibile aprire il sito internet. Sito non disponibile o non trovato.

Sono incappato in questo errore quando in una web application tentavo di scaricare un documento.
L'errore si verificava solo in alcune macchine di un dominio aziendale e solo con i browser IE7 ed IE8.
Con tutti gli altri browser,  Firefox, Chrome, etc.e anche con IE9 e IE10 invece funzionava correttamente.
L'errore effettivamente non è molto parlante e abbiamo perso parecchio tempo prima di capire il reale motivo.
Dal sito della microsoft e da alcuni blog si evince che in presenza di alcuni header nella cache come :

response.setHeader("Pragma", "No-cache");
response.setHeader("Cache-Control","no-cache,no-store,max-age=0");
response.setDateHeader("Expires", 1);

viene segnalato questo errore.
Il motivo francamente non si capisce bene, e sembrerebbe una sorta di bug visto che anche dal sito della microsoft consigliano di reimpostare il browser oppure di settare un parametro sul regedit di sistema (vedi qui e anche qui ).
Per risolvere noi abbiamo inserito i seguenti header:

response.setHeader("Cache-Control", "cache, must-revalidate");
response.setHeader("Pragma", "public");

martedì 4 febbraio 2014

Tomcat 7 web console

Per attivare la web console di tomcat occorre che siano presenti nel file

[TOMCAT_DIR]/conf/tomcat-users.xml

le seguenti voci:



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


Quindi in console cliccando su Manager App verranno chieste le credenziali (in questo caso tomcat tomcat) in modalità basic (con il pop up sul browser).
E si può accedere e deployare/undeployare le applicazioni.

Alcune accortezze (se il tomcat è su sistemi windows ed è su un server remoto):

1) Modificare il file [TOMCAT_DIR]/conf/context.xml aggiungendo i 2 attributi antiJARLocking e antiResourceLocking a true:


<Context antiJARLocking="true"
      antiResourceLocking="true">


2) Nel file [TOMCAT_DIR]/webapps/manager/WEB-INF/web.xml innalzare il limite del multipart config (il default è 50 MB) per consentire l'upload anche di war più grossi

Es:

 <multipart-config>

            <!-- 90MB max -->

            <max-file-size>91798008</max-file-size>

            <max-request-size>91798008</max-request-size>

            <file-size-threshold>0</file-size-threshold>

          </multipart-config>


Crivello di Eratostene

Di seguito una implementazione in Java del metodo del crivello di Eratostene per trovare dato un numero n la lista dei numeri primi fino ad n (http://it.wikipedia.org/wiki/Crivello_di_Eratostene ).

package it.test;
import java.util.*;
public class CrivelloEratostene {
 public static void main(String[] args) {
   System.out.println("Inserire il numero di interi desiderato: ");
         Scanner scan = new Scanner(System.in);
         int num = scan.nextInt();
         List<Integer> listNumbers=new ArrayList<Integer>();
         List<Integer> numbersToExclude=new ArrayList<Integer>();
         for(int i=2;i<num;i++){
          int mul=i;
          if(numbersToExclude.contains(i))
        break;
          if(i==2 || !isPrime(i)){
           for(int j=i;j<num;){
            numbersToExclude.add(j);
            j=j+mul;
           }
          }
         }
         for(int i=2;i<num;i++){
          if(!numbersToExclude.contains(i)){
           listNumbers.add(i);
          }
         }
         System.out.println(String.format("Lista numeri primi fino ad %d  : %s", num,listNumbers));
 }
 public static boolean isPrime(int n){
    for(int i=2;i<n;i++) {
          if(n%i==0)
              return false;
      }
      return true;
 }

}




.BAT per cancellare vecchi file di log


Un piccolo script .bat per cancellare tutti i file più vecchi di una certa data:

set log_path="c:\tomcat\logs"
set numDays= 7
forfiles /p %logPath% /s /d -%numDays% /c "cmd /c del @FILE"

lunedì 3 febbraio 2014

JDBC 4.0 utilizzo WebRowSet

L'interfaccia WebRowSet eredita da CachedRowSet e rappresenta una vista disconnessa dai dati del db.
Produce un output in formato xml dei risultati e della struttura delle tabelle coinvolte.
Es:

package it.rowsetTest;
import java.sql.Connection;
import java.sql.DriverManager;
import javax.sql.rowset.WebRowSet;
import com.sun.rowset.WebRowSetImpl;
public class XmlRowSet {
 public static void main(String[] args) throws Exception {
  String urlConn="";
  
  WebRowSet p= new WebRowSetImpl();
  String select="select top 5 id,descrizione from S_ATECO2007";
  p.setCommand(select);
  p.setUrl("jdbc:sqlserver://127.0.0.1:1433;databaseName=test;selectMethod=cursor");
  p.setUsername("test");
  p.setPassword("test"); 
  p.execute();
  java.io.FileOutputStream oStream =new java.io.FileOutputStream("test.xml");
  p.writeXml(oStream);
 }

}


In questo modo avremo un output di questo tipo:

<?xml version="1.0"?>
<webRowSet xmlns="http://java.sun.com/xml/ns/jdbc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/jdbc http://java.sun.com/xml/ns/jdbc/webrowset.xsd">
  <properties>
    <command>select top 5 id,descrizione from S_ATECO2007</command>
    <concurrency>1008</concurrency>
    <datasource><null/></datasource>
    <escape-processing>true</escape-processing>
    <fetch-direction>1000</fetch-direction>
    <fetch-size>0</fetch-size>
    <isolation-level>2</isolation-level>
    <key-columns>
    </key-columns>
    <map>
    </map>
    <max-field-size>0</max-field-size>
    <max-rows>0</max-rows>
    <query-timeout>0</query-timeout>
    <read-only>true</read-only>
    <rowset-type>ResultSet.TYPE_SCROLL_INSENSITIVE</rowset-type>
    <show-deleted>false</show-deleted>
    <table-name>S_ATECO2007</table-name>
    <url>jdbc:sqlserver:gianos_professionisti</url>
    <sync-provider>
      <sync-provider-name>com.sun.rowset.providers.RIOptimisticProvider</sync-provider-name>
      <sync-provider-vendor>Oracle Corporation</sync-provider-vendor>
      <sync-provider-version>1.0</sync-provider-version>
      <sync-provider-grade>2</sync-provider-grade>
      <data-source-lock>1</data-source-lock>
    </sync-provider>
  </properties>
  <metadata>
    <column-count>2</column-count>
    <column-definition>
      <column-index>1</column-index>
      <auto-increment>true</auto-increment>
      <case-sensitive>false</case-sensitive>
      <currency>false</currency>
      <nullable>0</nullable>
      <signed>true</signed>
      <searchable>true</searchable>
      <column-display-size>11</column-display-size>
      <column-label>id</column-label>
      <column-name>id</column-name>
      <schema-name></schema-name>
      <column-precision>10</column-precision>
      <column-scale>0</column-scale>
      <table-name>S_ATECO2007</table-name>
      <catalog-name></catalog-name>
      <column-type>4</column-type>
      <column-type-name>int</column-type-name>
    </column-definition>
    <column-definition>
      <column-index>2</column-index>
      <auto-increment>false</auto-increment>
      <case-sensitive>false</case-sensitive>
      <currency>false</currency>
      <nullable>1</nullable>
      <signed>false</signed>
      <searchable>true</searchable>
      <column-display-size>250</column-display-size>
      <column-label>descrizione</column-label>
      <column-name>descrizione</column-name>
      <schema-name></schema-name>
      <column-precision>250</column-precision>
      <column-scale>0</column-scale>
      <table-name>S_ATECO2007</table-name>
      <catalog-name></catalog-name>
      <column-type>12</column-type>
      <column-type-name>varchar</column-type-name>
    </column-definition>
  </metadata>
  <data>
    <currentRow>
      <columnValue>1</columnValue>
      <columnValue>AGRICOLTURA, SILVICOLTURA E PESCA                                                                                                                                                                                                                         </columnValue>
    </currentRow>
    <currentRow>
      <columnValue>2</columnValue>
      <columnValue>COLTIVAZIONI AGRICOLE E PRODUZIONE DI PRODOTTI ANIMALI, CACCIA E SERVIZI CONNESSI                                                                                                                                                                         </columnValue>
    </currentRow>
    <currentRow>
      <columnValue>3</columnValue>
      <columnValue>COLTIVAZIONE DI COLTURE AGRICOLE NON PERMANENTI                                                                                                                                                                                                           </columnValue>
    </currentRow>
    <currentRow>
      <columnValue>4</columnValue>
      <columnValue>Coltivazione di cereali (escluso il riso), legumi da granella e semi oleosi                                                                                                                                                                               </columnValue>
    </currentRow>
    <currentRow>
      <columnValue>5</columnValue>
      <columnValue>Coltivazione di cereali (escluso il riso)                                                                                                                                                                                                                 </columnValue>
    </currentRow>
  </data>
</webRowSet>


martedì 28 gennaio 2014

Errore CertificateException : No subject alternative names present in chiamata HTTPS

Ho passato un pomeriggio a dannarmi per questo errore, ottenuto in fase di chiamata ad un Web Service in HTTPS sulla mia macchina.
Lo scenario è il seguente:
  1. Ho creato un certificato HTTPS;
  2. Lo ho inserito in Tomcat per fare in modo che un mio web service rispondesse in HTTPS;
  3. Ho scritto un client JAVA per il servizio
Il client Java è stato generato con JAX-WS, con il comando wsimport prima che fosse messo in HTTPS il servizio.
L'unica modifica al client fatta è stata quella di creare un costruttore ulteriore che prendesse in input l'url del servizio come Stringa, in modo da poterlo invocare a runtime con il percorso in https.
Cercando quindi l'errore su Internet ho letto che il problema era dato dalla presenza nel CN del certificato di un nome diverso da quello del server invocato lato client; ho letto anche che tale problema si verifica soltanto in caso di certificati self signed.
Ho provato a rigenerare il certificato mettendo nel CN (il primo campo richiesto dalla procedura keytool) l'indirizzo 127.0.0.1 oppure localhost ma nulla da fare, sempre lo stesso errore.
Ho risolto così:
  1. Nel file hosts della mia macchina ho aggiunto un nome XXX mappandolo a 127.0.0.1;
  2. Ho rigenerato il certificato mettendo come nome nel CN lo stesso del punto 1;
  3. Ho effettuato la chiamata client con l'indirizzo http://XXX:8080/URL_WSDL.

Cosìfacendo ha funzionato, anche se non capisco perchè...

domenica 19 gennaio 2014

Esempio utilizzo java.util.concurrent.Future, Callable e ExecutorService

L'interfaccia java.util.concurrent.Future schematizza un oggetto che dovrà tornare in futuro il risultato di una operazione asincrona.
L'interfaccia java.util.concurrent.Executor dichiara un solo metodo, void execute(Runnable)
L'interfaccia java.util.concurrent.ExecutorService estende l'interfaccia Executor aggiungendo funzionalità che consentono il termine dei thread e la produzione di oggetti Future.
L'interfaccia java.util.concurrent.Callable dichiara invece un solo metodo V call() throws Exception

Vediamo un esempio di una classe che implementa Callable e utilizza un ExecutorService per creare un oggetto Future passando il Callable.


package it.threads;
import java.util.concurrent.Callable;
public class OperazioneAsincrona implements Callable<String> {
 @Override
 public String call() throws Exception {
  System.out.println("Simulo lavorazione.....");
  Thread.sleep(3000);
  return "Lavorazione eseguita.....";
 }
}



Vediamo ora come richiamare il callable utilizzando ExecutorService e Future.

package it.threads;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class OperazioneAsincronaTest {
 public static void main(String[] args) throws InterruptedException, ExecutionException {
       Callable<String> task=new OperazioneAsincrona();
       ExecutorService es=Executors.newSingleThreadExecutor();
       Future<String> f=es.submit(task);
       System.out.println(f.get());
       System.out.println("Esco dall'executor...");
       es.shutdown();
 }
}


Si può anche istanziare un ThradPool con l' ExecutorService, usando invece del newSingleThreadExecutor il metodo newFixedThreadPool che accetta come parametro in input un intero.

Esempio utilizzo AtomicInteger

La classe AtomicInteger si trova nel package java.util.concurrent.atomic.
Si utilizza per evitare i problemi di tipo data race nel caso di operazioni multi-thread, quindi garantisce che nel caso di accessi ad un contatore una sincronizzazione sulla variabile da incrementare.
Vediamo un esempio classico di utilizzo di variabili intere in un ambiente multi thread.


package it.threads;
public class ClassicIntTest extends Thread {
 private static Integer intero=new Integer(0);
 public void run(){
  System.out.println("CLASSICO: Incremento 
                e recupero il valore "+ ++intero);
 }
}


Se lanciamo il Thread per 10 volte in questo modo:

public static void main(String[] args) {
  for(int i=0;i<10;i++){
    new ClassicIntTest().start();
  }
 }

Lanciando il main può accadere di avere il seguente output:

CLASSICO: Incremento e recupero il valore 1
CLASSICO: Incremento e recupero il valore 2
CLASSICO: Incremento e recupero il valore 3
CLASSICO: Incremento e recupero il valore 4
CLASSICO: Incremento e recupero il valore 5
CLASSICO: Incremento e recupero il valore 6
CLASSICO: Incremento e recupero il valore 7
CLASSICO: Incremento e recupero il valore 8
CLASSICO: Incremento e recupero il valore 8
CLASSICO: Incremento e recupero il valore 9


Come possiamo vedere il risultato non è quello che ci attenderemmo, in quanto non essendoci sincronizzazione sulla variabile intera (che è statica) capita che 2 thread leggano lo stesso valore in quanto accedono nel medesimo istante al metodo.

Se utilizziamo invece l'AtomicInteger questo non accade.
Esempio:

package it.threads;
import java.util.concurrent.atomic.AtomicInteger;
public class AtomicIntTest extends Thread {
 private static AtomicInteger intero=new AtomicInteger(0);
 public void run(){
  System.out.println("ATOMIC: Incrementa e recupera il valore "+intero.incrementAndGet());
 }
}


Se nel main precedente sostituiamo la classe ClassicIntTest con la AtomicIntTest avremo invece sempre l'output atteso, ossia:

ATOMIC: Incrementa e recupera il valore 1
ATOMIC: Incrementa e recupera il valore 2
ATOMIC: Incrementa e recupera il valore 3
ATOMIC: Incrementa e recupera il valore 4
ATOMIC: Incrementa e recupera il valore 5
ATOMIC: Incrementa e recupera il valore 6
ATOMIC: Incrementa e recupera il valore 8
ATOMIC: Incrementa e recupera il valore 7
ATOMIC: Incrementa e recupera il valore 9
ATOMIC: Incrementa e recupera il valore 10

giovedì 9 gennaio 2014

Modificare il comportamento di default di una textarea sul click tasto enter

Di default una textarea associa l'evento submit del form al click sul tasto enter.
Per dare invece al tasto enter un altro significato è sufficiente agire sull'evento onkeyDown, in modo da aggiungere lo spazio al click sull'enter e prevenendo il comportamento di default.
Per aggiungere il testo a capo facciamo così: 

document.getElementById(event.target.id).innerHTML+="<br />";


Di seguito l'esempio in una  jsp:


<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
<form >
 <textarea id="prova"  rows="10" cols="50"></textarea>

</form>
</body>
<script>

document.getElementById("prova").addEventListener("keydown",test,false);

function test(e) {
 
   
     if (e.keyCode == 13  &&  e.target=="[object HTMLTextAreaElement]"  ) {
      document.getElementById(event.target.id).innerHTML+="<br />";
      e.preventDefault();
      return false;
    }
}

</script>
</html>




mercoledì 8 gennaio 2014

Java stampare tutti i get di un oggetto

Un metodo per avere in stampa tutte i getter di un oggetto:


public static String getRappresentazioneStringa(Object o) throws Exception{
   StringBuffer sb=new StringBuffer();
   Method[] listaMetodi= o.getClass().getMethods();
   for(int i=0;i<listaMetodi.length;i++){
    Method m=listaMetodi[i];
    if(m.getName().startsWith("get") && !"getClass".equals(m.getName())){
     // è un getter stampo il valore
     sb.append(m.getName().substring(3).toUpperCase());
     sb.append(" = ");
     sb.append(m.invoke(o)!=null?m.invoke(o).toString():"");
     sb.append("#");
    }
   }
   return sb.toString();
}