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;
 }
}