Share |

mercoledì 17 novembre 2010

Java: Connessioni tramite proxy server

In questo articolo Java sarà mostrato il codice sorgente della classe Proxy che crea una connessione di rete autenticata presso un proxy server per raggiungere e leggere una risorsa remota.

La classe è stata creata per poter interrogare, tramite il metodo POST del protocollo di rete https, un web service esterno alla rete aziendale, nella quale vi sono i server WAS (WebSphere Application Server) sui quali sono deployate le applicazioni J2EE che consumano il suddetto servizio web. Ovviamente, la classe Proxy funziona correttamente anche con i protocolli di rete http ed tcp. Non è stato ancora eseguito alcun test con il protocollo ftp.

Nota

La classe è stata creata sfruttando il package java.net delle librerie J2EE 1.3 .
Non saranno spiegate, se non perchè ritenute necessarie, le specifiche di sicurezza per interfacciare un proxy server fornendo le proprie credenziali al fine di creare una connessione di rete in grado di oltrepassare lo stesso proxy server.

package net.ma.core;

  
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.util.Properties;
import java.util.Enumeration;
import java.util.Hashtable;
import sun.misc.BASE64Encoder;

/**
 * Esegue un'autenticazione presso un proxy server 
 * per ottenere risorse esterne.
 * Utilizza come realm il token "Proxy-Authorization"
 * assegnadoli il valore in base64 della stringa "username:password". 
 * 
 * @author  Mirko Agrati
 * @version 1.0
 */
public final class Proxy {
  /** Nome del Server Proxy. */
  private static String PROXY_NAME;
 
  /** Numero della porta su cui è in ascolto il Proxy Server. */
  private static String PROXY_PORT;

  /** Valore realm in base64 della coppia username:password. */
  private static String ENCODED_REALM_VALUE;
 
  /**
   * Inizializza i campi static.
   */
  static{
    PROXY_NAME = "proxy.intranet.ma.net";
    PROXY_PORT = "8080";
    ENCODED_REALM_VALUE = 
        new BASE64Encoder().encode("username:password".getBytes());
  }
 
  /** 
   * Configura le proprietà necessarie al runtime Java. 
   * Solitamente queste proprietà vengono fornite dal client(es: browser) 
   * durante la creazione di una connessione, 
   * ma girando qui lato server ...
   */
  private static void setProperties(){
    Properties props = System.getProperties();
    props.put("proxySet", "true" );
    props.put("proxyHost", PROXY_NAME);
    props.put("proxyPort", PROXY_PORT );
    
    //Setta un gestore per connessioni sicure(https, ssl, ..). 
    props.put("java.protocol.handler.pkgs", "java.net.URL" );
  }
 
  /**
   * Crea una connessione autenticata dal proxy server {@link #PROXY_NAME} .
   * 
   * @param urlpath L'indirizzo della risorsa da recuperare.
   * @return        Una connessione autenticata pronta all'uso.
   */
  public static final URLConnection getAuthURLConnection(String urlpath) 
  throws MalformedURLException, IOException{
    setProperties();
  
    URL url = new URL(urlpath);  
    URLConnection connection = url.openConnection();  
    connection.setRequestProperty( 
        "Proxy-Authorization", ENCODED_REALM_VALUE ); 
    connection.setDoInput( true );
    connection.setDoOutput( true );
    
    return connection;
  }
 
  /**
   * Legge una risorsa remota e ne restituisce il contenuto in una stringa.
   * 
   * @param url    L'indirizzo della risorsa da leggere.
   * @param params Parametri da passare in POST 
   *               durante la richiesta HTTP.
   * @return       Il contenuto della risorsa remota.
   * @throws MalformedURLException
   * @throws IOException
   */
  public static final String getRemoteResource
      (String url, Hashtable params) 
      throws MalformedURLException, IOException{
    URLConnection connection = getAuthURLConnection(url);

    //Apro un socket di rete verso l'host che espone il servizio
    DataOutputStream output = 
        new DataOutputStream(connection.getOutputStream()); 
  
    Enumeration pnames = params.keys();
    String query = "";
    
    /* 
     * Creo la querystring da inviare al server remoto
     * concatenando '&nome=valore' di ogni parametro.
     */
    while(pnames.hasMoreElements()){
      String paramname = (String) pnames.nextElement();
      String paramvalue =  (String)params.get(paramname);
   
      query += 
          paramname + "=" + URLEncoder.encode(paramvalue) + "&"; 
    }
  
    System.out.println(
        "\nRisorsa richiesta: " + url + 
        "\nparametri inviati: " + query);
   
    //Invio sul socket la querystring con le coppie nome/valore. 
    output.writeBytes( query );
   
    //Chiudo socket in uscita
    output.close();

    /*
     * Leggo il contenuto della risorsa remota e
     * lo conservo all'interno di uno StringBuffer.
     */
    StringBuffer response = new StringBuffer();
    DataInputStream input = 
        new DataInputStream(connection.getInputStream() );
    for( int c = input.read(); c != -1; c = input.read() ){
      response.append( (char)c );
    }

    //Chiudo il socket in ingresso.
    input.close();

    return response._toString();
  }
  
  /**
   * Eseguo il metodo statico getRemoteResource(... , ...)
   * e scrivo a video il contenuto della risorsa richiesta.
   * In questo caso ricevo il codice sorgente HTML della pagina
   * di ricerca di Google.it dopo aver eseguito una query
   * con il mio nome e cognome.
   */
  public static void main( String[] args ){
    try{
      String urlpath = "http://www.google.it/search";
      Hashtable params = new Hashtable();
      params.put("q", "mirko agrati");
      System.out.println(getRemoteResource(urlpath, params));
    }
    catch( Exception e ){
      System.out.println("Si è verificata un'eccezione:");
      System.out.println(e);
      e.printStackTrace();
    }
  } 
}

Ad eccezione del metodo main(String[] args), la classe Proxy espone due metodi statici grazie ai quali si può creare una connessione autenticata e leggere il contenuto di una risorsa remota.

Il cuore della classe è rappresentato dal factory method getAuthURLConnection(String urlpath) il quale contiene la logica necessaria per poter interfacciare correttamente un proxy server, fornendo le credenziali in base64; ma è altrettanto fondamentale il metodo setProperties() che setta le proprietà di sistema necessarie per il corretto funzionamento delle operazioni di autenticazione.

La classe funziona correttamente ed ha delle buone performances.

Alla prossima,
MA.

mercoledì 10 novembre 2010

Javascript: Scorrimento continuo ed effetto slide

In questo articolo Javascript sarà presentato il prototipo di un componente per la visualizzazione di elementi HTML in continuo scorrimento con un effetto slide.

Il componente, ri-adattato graficamente per esigenze di layout e con qualche effetto più accattivante (per esempio fade-in e fade-out), è stato utilizzato sulla home page di un cliente per mostrare in costante movimento le ultime news pubblicate.

Per la realizzazione del componente è stato utilizzato il framework javascript JQuery, precisamente la versione 1.4.3, ed il risultato è uno script cross-browser testato su IE7, FireFox 3.6 e 4beta, Chrome v.7 ed Opera 10.

L'intero codice sorgente dell'esempio e libreria JQuery utilizzata sono comodamente e liberamente scaricabili in formato .zip al seguente indirizzo.

Codice HTML

Il codice HTML necessario è stato ridotto al minimo per semplificare più possibile la parte javascript dell'esempio.

<body>
  <div id="container"></div>
</body>

Come premesso, all'interno del BODY è presente solo il DIV nel quale fare apparire e sparire continuamente le news.

Codice CSS

L'esempio, anche se abbastanza grezzo, è comunque minimamente presentabile dal punto di vista grafico e per ottenere questo si è agito su qualche proprietà CSS dei vari elementi della pagina.

body{text-align:center;}
  
div#container{
  overflow: hidden;height:200px;width: 100px;
  text-align: center;border: 1px solid black;margin:auto;}
  
.news{
  height:30px;width:80px;border:1px dashed black;
  vertical-align:middle;margin:auto;background-color: silver;}

.label{
  font-weight: bold; background-color: black; 
  color: white;width: 100%; margin-bottom:1px;}

Alcune classi CSS, ancora non utilizzate, saranno successivamente adoperate dinamicamente tramite javascript.

Codice Javascript

Il codice Javascript è abbastanza semplice, ben commentato e presentato chiaramente. L'unica funzionalità avanzata presente è la chiamata alla funzione setInterval(function, millisec) che, ad intervalli regolari(ogni 1.5 secondi), esegue la funzione cicleNewsForFadeOut() passatale come primo parametro.

/** Array contenente le news da visualizzare. */
var newsArr = ["Notizia 1","Notizia 2","Notizia 3"
              ,"Notizia 4","Notizia 5","Notizia 6","Notizia 7"];

/** Array che conterrà i componenti HTML da gestire. */
var elNewsArr = [];

/** Indice degli elementi HTML trattati. */
var index = -1;  

/**
 * Al caricamento della pagina creo i DIV contenenti le news
 * e li appendo al DOM. Poi attivo l'effetto.
 */
$(document).ready(function(){
  createNews();
  drawNews();
  setInterval(cicleNewsForFadeOut,1500);
});

/** Crea gli elementi HTML a partire dal contenuto delle news. */
function createNews(){
  for(var k=0; k<newsArr.Length; k++){
    elNewsArr[k] = 
        $('<div class="news">' + newsArr[k] + "</div>");
  }    
}

/** Aggiunge gli elementi HTML al contenitore. */
function drawNews(){
  $("#container")
      .html('<div class="label">Ultime news</div>');

  for(var k=0; k<elNewsArr.Length; k++){
    $("#container").append($(elNewsArr[k]));
  }
}  

/**
 * Richiamata ad intervalli regolari,
 * crea l'effetto slide delle news. 
 */
function cicleNewsForFadeOut(){
  //Attenzione: indice incrementato.
  var el = elNewsArr[++index];
  $(el).hide("fast");

  if(index >= elNewsArr.Length){
    index = -1;
    $('.news').toggle();
  }
}

Il meccanismo è governato dal valore che via via assume la variabile globale index, incrementata ad ogni richiamo di cicleNewsForFadeOut(): nel caso in cui il suo valore fosse uguale o superiore al numero di news da pubblicare, index verrà resettato e tutte le news nascoste saranno rese nuovamente visibili.

Alla prossima,
MA