lunedì 14 novembre 2011

Apache Poi esempio lettura cella e riga generica da un xls

Un semplice metodo per prendere da un excel il valore di una certa cella.
La libreria utilizzata è Apache POI 3.7, il metodo accetta come parametri l'oggetto HSSFWorkBook, il nome del foglio Excel su cui ricercare e la riga e colonna esatta dove farlo.
Per istanziare l'oggetto basta la seguente new

HSSFWorkbook wb = new HSSFWorkbook(new FileOutputStream(new File("test.xls")));

private String getContenutoCella(HSSFWorkbook wb,String nomeFoglio,int riga,int colonna){
        String retVal="";
        HSSFSheet sheet=wb.getSheet(nomeFoglio);
        Iterator rows = sheet.rowIterator();
        int rigCont=0;
        while(rows.hasNext()){
            HSSFRow row = (HSSFRow) rows.next();
            rigCont++;
            if(rigCont==riga){
                Iterator cells=row.cellIterator();
                int cellCont=0;
                while(cells.hasNext()){
                    HSSFCell cella=(HSSFCell)cells.next();
                    cellCont++;
                    if(cellCont==colonna){
                        retVal=cella.getRichStringCellValue().getString();
                    }
                   
                }
            }
        }
        return retVal;
    }

venerdì 11 novembre 2011

Maven, installare un jar nel repository locale

Vediamo in questo post come installare un jar creato da noi nel repository locale di maven, in modo da poterlo poi allegare ad altri progetti semplicemente aggiungendo la dipendenza sul file pom (senza quindi aggiungerlo noi ogni volta tra le lib di progetto).

Per prima cosa dobbiamo assegnare un nome a:
  • groupId: di solito qui si specifica il package del nostro jar, comunque la scelta è libera;
  • artifactId: indica il nome univoco da assegnare all'artefatto, il nome deve essere univoco all'interno del groupId;
  • version;
  • packaging (es JAR,WAR, specifica l'output prodotto da Maven);
 Il jar sarà installato nel percorso M2_REPO/goupId/artifactId/version
Quindi specificare dove è presente il nostro jar file con l'opzione -Dfile=e, in alcuni casi (esempio con Maven 2.2) specificare anche l'opzione

-DgeneratePom=true

In modo da assicurarci che il POM sia generato.

Ipotizzando quindi di avere un progetto Java con una sola classe Test così creata, in un package it.prova:

package it.prova;
public class Test {

      public static void main(String[] args) {
        String v=args[0];
       
        int val=Integer.valueOf(v);
        System.out.println("Il fattoriale è "+getFattoriale((long)val)+"");
    }
    public static long getFattoriale(long n){
        if(n==1 || n==0){
            return 1;
        }
        else
        {
            return n*getFattoriale(n-1);
        }       
    }
}

Da eclipse creiamo il jar test.jar

Quindi da console digitiamo:

C:\>mvn install:install-file -Dfile=c:\test.jar -DgroupId=it.prova -DartifactId=myTest -Dversion=1.0 -Dpackaging=jar  -DgeneratePom=true

La console di esecuzione ci darà un messaggio di questo tipo:

[INFO] Scanning for projects...
[INFO] Searching repository for plugin with prefix: 'install'.
[INFO] ------------------------------------------------------------------------
[INFO] Building Maven Default Project
[INFO]    task-segment: [install:install-file] (aggregator-style)
[INFO] ------------------------------------------------------------------------
[INFO] [install:install-file {execution: default-cli}]
[INFO] Installing c:\test.jar to C:\Documents and Settings\user\.m2\repos
itory\it\prova\myTest\1.0\myTest-1.0.jar

[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ------------------------------------------------------------------------
[INFO] Total time: < 1 second
[INFO] Finished at: Fri Nov 11 13:06:55 CET 2011
[INFO] Final Memory: 3M/15M
[INFO] ------------------------------------------------------------------------

Quindi è possibile verificare che a livello di repository Maven locale è presente la directory evidenziata sopra in grassetto.


Il contenuto della directory è il seguente:


Come possiamo vedere è stato generato anche il pom che è così composto:

<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <modelVersion>4.0.0</modelVersion>
  <groupId>it.prova</groupId>
  <artifactId>myTest</artifactId>
  <version>1.0</version>
  <description>POM was created from install:install-file</description>
</project>


lunedì 7 novembre 2011

NullPointerException su com.sun.xml.internal.bind.v2.runtime.reflect.Lister$CollectionLister.addToPack durante parsing Xml con JAX-B

Stavo effettuando il parsing di un xml utilizzando JAXB.

L'xml aveva una struttura di questo tipo

<root>
<lista>
<classpathentry kind="var" path="M2_REPO/javax/transaction/jta/1.0.1B/jta-1.0.1B.jar"/>
.......
</lista>
</root>

La classe JAXB era definita così:

package it.eng.classpathreader;
import java.util.Collection;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement(name="root")
public class MyXml {
    private Collection<ClassPathElement> lista;
    @XmlElementWrapper(name="lista",nillable=true,required=false)
    @XmlElement(name="classpathentry",nillable=true,required=false)
    public Collection<ClassPathElement> getListaElementi() {
          return lista;
    }
    public void setListaUtenti(Collection<ClassPathElement> listaEl) {
          this.lista = listaEl;
    }
    public String toString(){
        StringBuilder sb=new StringBuilder();
        for(ClassPathElement i : lista){
            sb.append(i);
            sb.append("\n");
        }
        return sb.toString();
    }
}

Andando a parsare l'Xm l ho avuto questo errore:



java.lang.NullPointerException
    at com.sun.xml.internal.bind.v2.runtime.reflect.Lister$CollectionLister.addToPack(Unknown Source)
    at com.sun.xml.internal.bind.v2.runtime.reflect.Lister$CollectionLister.addToPack(Unknown Source)
    at com.sun.xml.internal.bind.v2.runtime.unmarshaller.Scope.add(Unknown Source)
    at com.sun.xml.internal.bind.v2.runtime.property.ArrayERProperty$ReceiverImpl.receive(Unknown Source)
    at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext.endElement(Unknown Source)
    at com.sun.xml.internal.bind.v2.runtime.unmarshaller.SAXConnector.endElement(Unknown Source)
    at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.endElement(Unknown Source)
    at com.sun.org.apache.xerces.internal.parsers.AbstractXMLDocumentParser.emptyElement(Unknown Source)
    at com.sun.org.apache.xerces.internal.impl.XMLNSDocumentScannerImpl.scanStartElement(Unknown Source)
    at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl$FragmentContentDriver.next(Unknown Source)
    at com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl.next(Unknown Source)
    at com.sun.org.apache.xerces.internal.impl.XMLNSDocumentScannerImpl.next(Unknown Source)
    at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanDocument(Unknown Source)
    at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(Unknown Source)
    at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(Unknown Source)
    at com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(Unknown Source)
    at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.parse(Unknown Source)
    at com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl$JAXPSAXParser.parse(Unknown Source)
    at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal0(Unknown Source)
    at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal(Unknown Source)
    at javax.xml.bind.helpers.AbstractUnmarshallerImpl.unmarshal(Unknown Source)
    at javax.xml.bind.helpers.AbstractUnmarshallerImpl.unmarshal(Unknown Source)
    at javax.xml.bind.helpers.AbstractUnmarshallerImpl.unmarshal(Unknown Source)
    at javax.xml.bind.helpers.AbstractUnmarshallerImpl.unmarshal(Unknown Source)
    at it.eng.classpathreader.Reader.main(Reader.java:21)



Dopo qualche "googlata" ho trovato il problema: la Collection rappresentante la lista di nodi di tipo classpathentry va inizializzata.
Quindi invece di
private Collection<ClassPathElement> lista;
ho scritto
private Collection<ClassPathElement> lista=new ArrayList<ClassPathElement>();
e tutto ha funzionato a dovere.

domenica 6 novembre 2011

Maven per Eclipse

Utilizzando Maven è possibile dato il file pom.xml generare il workspace eclipse direttamente da linea di comando posizionandosi sulla directory contenente il file pom.xml e quindi digitando:

mvn eclipse:eclipse

Viceversa per cancellare il tutto è sufficiente digitare

mvn eclipse:clear

sabato 5 novembre 2011

Verificare se un numero è intero

Un metodo per verificare se il numero dato in input è un intero:

public static boolean checkIfInt(double d){
        boolean retVal=false;
        int v=(int)d;
        if((double)v==d){
            retVal=true;
        }
        return retVal;
    }


martedì 1 novembre 2011

Java Web Services su Tomcat

Di seguito un esempio di WS deployato su tomcat ed invocato da client Java.
Per effettuare il deploy su Tomcat per prima cosa ho dovuto scaricare da qui l'ultima versione di JAX/WS.
Una volta scompattato lo zip ho messo tutti i jar dentro la directory lib di Tomcat 7.
Ho quindi creato un progetto Web da Eclipse.
Ho definito quindi la seguente interfaccia:

package it.ws;
import java.math.BigInteger;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
import javax.jws.soap.SOAPBinding.Style;
import javax.jws.soap.SOAPBinding.Use;
@WebService
@SOAPBinding(style=Style.DOCUMENT,use=Use.LITERAL)
public interface FattorialeInt {
public BigInteger getFattoriale(long n);
}

Implementata dalla seguente classe:

package it.ws;
import java.math.BigInteger;
import javax.jws.WebService;
@WebService(endpointInterface="it.ws.FattorialeInt")
public class FattorialeImpl implements FattorialeInt {

    @Override
    public BigInteger getFattoriale(long n) {
        BigInteger fatt=new BigInteger("1");
        for(long i=1;i<=n;i++){
            fatt=fatt.multiply(new BigInteger(String.valueOf(i)));
        }
        return fatt;
    }
   

}

A questo punto, poichè lo style di binding è DOCUMENT e non RPC ho dovuto lanciare dalla console il comando wsgen per generarmi gli artefatti software necessari.
Quindi dalla directory dove sono i .class (quindi in build/class/it/ws, proprio a livello dei file .class)  ho digitato il seguente comando:

wsgen -keep -cp . it.ws.FattorialeImpl

In questo modo sono state generate le seguenti classi
1) GetFattoriale.java
2) GetFattorialeResponse.java

Codice 1

package it.ws.jaxws;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;

@XmlRootElement(name = "getFattoriale", namespace = "http://ws.it/")
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "getFattoriale", namespace = "http://ws.it/")
public class GetFattoriale {

    @XmlElement(name = "arg0", namespace = "")
    private long arg0;

    /**
     *
     * @return
     *     returns long
     */
    public long getArg0() {
        return this.arg0;
    }

    /**
     *
     * @param arg0
     *     the value for the arg0 property
     */
    public void setArg0(long arg0) {
        this.arg0 = arg0;
    }

}
Codice 2
package it.ws.jaxws;
import java.math.BigInteger;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
@XmlRootElement(name = "getFattorialeResponse", namespace = "http://ws.it/")
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "getFattorialeResponse", namespace = "http://ws.it/")
public class GetFattorialeResponse {

    @XmlElement(name = "return", namespace = "")
    private BigInteger _return;

    /**
     *
     * @return
     *     returns BigInteger
     */
    public BigInteger getReturn() {
        return this._return;
    }

    /**
     *
     * @param _return
     *     the value for the _return property
     */
    public void setReturn(BigInteger _return) {
        this._return = _return;
    }

}

A questo punto bisogna effettuare delle configurazioni sul file Web Xml, in particolare bisogna dichiarare

1) la servlet com.sun.xml.ws.transport.http.servlet.WSServlet ;
2) il Listener com.sun.xml.ws.transport.http.servlet.WSServletContextListener

NOTA: per questo serve copiare quei jar dentro la lib di Tomcat
Quindi nel web.xml avremo:

 <servlet>
    <description></description>
    <display-name>WsTest</display-name>
    <servlet-name>FattConvert</servlet-name>
    <servlet-class>com.sun.xml.ws.transport.http.servlet.WSServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>FattConvert</servlet-name>
    <url-pattern>/FattConvert</url-pattern>
  </servlet-mapping>
  <listener>
  <listener-class>
  com.sun.xml.ws.transport.http.servlet.WSServletContextListener
  </listener-class>
  </listener>

Il listener dichiarato ha bisogno del seguente file xml sun-jaxws.xml dove è dichiarato l'endpoint:

<?xml version="1.0" encoding="UTF-8"?>
<endpoints xmlns="http://java.sun.com/xml/ns/jax-ws/ri/runtime" version="2.0">
<endpoint name="FattConvert" implementation="it.ws.FattorialeImpl" url-pattern="/FattConvert"/>
</endpoints>

Notare che bisogna fare in modo che nell'endpoint il name sia uguale al servlet name, l'implementation sia la classe del web service e l'url-pattern sia l'url-pattern della servlet.

Una volta effettuato il deploy il WS è richiamabile al seguente url:

http://localhost:8080/TestWs/FattConvert?wsdl

Per richiamare il Web Service è possibile come sempre generare un client tramite l'utility wsimport (già analizzato in un precedente POST).








venerdì 28 ottobre 2011

Jboss 4.0.5 problemi nel deploy EJB

Mi è capitato di incappare in un curioso problema, praticamente lanciando da Eclipse una workspace preesistente cou un Ear project da deployare su Jboss, avevo un errore di questo tipo:


org.jboss.deployment.DeploymentInfo@d6781bc5 {
url=file:/C:xxx.ear}
deployer: null
status: null
state: INIT_WAITING_DEPLOYER
watch: file:/C:xxx.ear
lastDeployed: 1103151946718
lastModified: 1103151948000
mbeans:

Dopo un pò di panico (era una vecchia applicazione che risiedeva su una macchina virtuale spenta da tempo).
ho trovato il problema: nella cartella default/deploy per motivi a me ignoti era stato cancellato il file ear-deployer.xml, un file di 1KB del quale ignoravo addirittura l'esistenza (ignoranza mia).
Praticamente me ne sono accorto soltanto una volta scaricata la nuova versione di Jboss e facendo una diff tra i file presenti (una domanda, perchè se c'è una cartella conf non si mettono lì tutti i file di configurazione????) ho notato la mancanza di questo file, che si presenta così:

<?xml version="1.0" encoding="UTF-8"?>

<!-- The JBoss service configuration file for the EAR deployer service.
$Id: ear-deployer.xml 23463 2004-09-05 15:50:37Z starksm $
-->
<server>
   <!-- EAR deployer, remove if you are not using ear deployments -->
   <mbean code="org.jboss.deployment.EARDeployer"
      name="jboss.j2ee:service=EARDeployer">
      <!-- A flag indicating if ear deployments should have their own scoped
      class loader to isolate their classes from other deployments.
      -->
      <attribute name="Isolated">false</attribute>
      <!-- A flag indicating if the ear components should have in VM call
      optimization disabled.
      -->
      <attribute name="CallByValue">false</attribute>
   </mbean>
</server>


Note sugli attributi:
ISOLATED se true fa in modo che tutti gli ear presenti abbiano un diverso classloader.
CALLBYVALUE se true disabilita l'impostazione del call by reference degli Ejb, praticamente mettendolo a true ogni chiamata ad un Ejb viene serializzata.

Questo è quello che ho trovato in merito sul sito di una community JBOSS( qui ):

The use of call by value and marshalling is very inefficient. It typically means
method invocations take 10 times the cpu. Why? Compare Call By Value with Call By Reference

Call By Reference

  1. caller does ejb.someMethod()
  2. invocation is passed through ejb container
  3. container does bean.someMethod()
  4. result is returned to the caller

Call By Value

  1. caller does ejb.someMethod()
  2. invocation is marshalled - the parameters are turned into ObjectStream (a byte[])
  3. container with a different classloader unmarshalls - byte[] -> java Objects - including classloading
  4. invocation is passed through ejb container
  5. container does bean.someMethod()
  6. result is marshalled - the return value is turned into ObjectStream
  7. result is unmarshalled using the caller's classloader - byte[] -> java Object
  8. result is return to the caller

The marshalling and unmarshalling uses a lot of cpu.

Con JBOSS 5 l'ear-deployer ha cambiato nome, si trova nella cartella defalut/deployers e si chiama ear-deployer-jboss-beans.xml,.