Archivio tag: Programming

Scala e Pattern Matching

Ultimamente ho iniziato a giocare con Scala, passato agli onori della cronaca per essere il linguaggio con cui è scritto Apache Spark, uno dei motori di data-processing più in voga nel mondo dei Big Data (e che ha soppiantato da tempo Pig e altre tecnologie basate su Map&Reduce).

Uno degli aspetti più interessanti, riguarda la possibilità di usare dei Pattern di Matching per tipo (sia scalare che su oggetti) per evitare le righe e righe di codice di instanceof , type ,if-else, o semplicemente realizzare la tanto agognata switch basata su stringhe. Altro utilizzo molto interessante del Pattern Matching potrebbe essere quello di eseguire diverse azioni sulla base del tipo di dato incontrato, durante il traversal di strutture composite, come ad esempio un DOM Xml.

Quello che in Java si scrive con diverse if, in Scala si riduce all’essenziale.
Il seguente pezzo di codice, definisce una funziona X che torna una stringa ed accetta qualsiasi tipo di dato in input (Any in scala è l’equivalente di Object) . Un Esempio classico è quello della decodifica dei parametri passati come argomento al nostro main.

def parseMainArgument(arg:String) = arg match{
  case "--help" | "-h" => displayHelpMessage
  case "--log"  | "-l" => showLog
  case "--verbose" | "-v" => setVerbose
  case _  => showArgumentNotFound
}

Il carattere underscore “_” sta a significare il valore di default, quando nessuno dei suddetti viene invocato, quindi nella switch sopra descritta, sta a rappresentare il caso “else”..

Un esempio di utilizzo di più valori nel match:

def myDecodeParam(funcName: String, value: Any) = (funcName,value) match{
   case ("test",val) => test(val)
   case ("log",msg) => log("messaggio:"+msg)
}

Altro aspetto interessante dei match, è la possibilità di analizzare i tipi di dati, ad esempio:

def myfunc(x:Any):String = x match{
   case i:Int => ""+ i + "è un Intero"
   case d:Double => "" + d + "+ un Double"  
   case s:String => s+ " è una Stringa"
}

Un utilizzo frequente è quello tramite le Case Classes.
Le Case Classes sono classi regolari che esportano in automatico i parametri del costruttore e che è possibile poi scomporre e analizzare attraverso il Pattern Matching. Le Case Classes non richiedono l’utilizzo della new, ed il compilatore genera in automatico il metodo “equals” ed il “toString”.

Un esempio classico, riportato anche dalla documentazione, è quello della definizione di classi case per la scomposizione e risoluzione di formule matematiche, ma che è facile poi utilizzare per creare un DSL applicativo.
In questo esempio definiamo una serie di case classes che descrivono operazioni matematiche che vengono svolte in quello che definiamo un ambiente di test, un insieme di valori che assumono le variabili del calcolo. Invece di usare una collection per la definizione delle variabili, lo facciamo utilizzando le funzioni. La notazione

{case "x" => 5}

ad esempio crea una funzione che quando riceve x in input ritorna il valore “5”.
la sua definizione è :

type NomeFunzione = String => Int

dove String è l’input, e Int l’output.

In questo esempio usiamo anche i Traits,cioè i tratti, le caratteristiche da aggiungere alle classi. Sono interfacce con la possibilità di avere implementazioni parziali.

trait Espressione {
    def decodifica(caso:AmbienteTest.Ambiente) : String = {
      this match {        
        case Somma (a,b) => "(" + a.decodifica(caso) +" + " + b.decodifica(caso) + ")"
        case Sottrai (a,b) => "(" + a.decodifica(caso) +" - " + b.decodifica(caso) + ")"        
        case Moltiplica (a,b) => "(" + a.decodifica(caso) +" * " + b.decodifica(caso) + ")"
        case Dividi (a,b) => "(" + a.decodifica(caso) +" / " + b.decodifica(caso) + ")"        
        case Variabile(x) => "" + caso(x)
        case Costante(n) => "" + n
      }
    }
   
    def risolvi(caso:AmbienteTest.Ambiente) : Int = {
      this match {  
        case Somma (a,b) =>a.risolvi(caso) + b.risolvi(caso)
        case Sottrai (a,b) =>a.risolvi(caso) - b.risolvi(caso)
        case Moltiplica (a,b) =>a.risolvi(caso) * b.risolvi(caso)
        case Dividi (a,b) =>a.risolvi(caso) / b.risolvi(caso)
        case Variabile(x) => caso(x)
        case Costante(n) =>  n
      }
    }
   
}

case class Somma (a:Espressione, b:Espressione) extends Espressione
case class Sottrai (a:Espressione, b:Espressione) extends Espressione
case class Moltiplica (a:Espressione, b:Espressione) extends Espressione
case class Dividi (a:Espressione, b:Espressione) extends Espressione
case class Variabile (x:String) extends Espressione
case class Costante (n:Int) extends Espressione
package object  AmbienteTest {type Ambiente =  String => Int}

object ExpressionDecoder {
  def main(args:Array[String]){
     val myTest:AmbienteTest.Ambiente = {case "x"=>1  case "y"=>2}
     val exp:Espressione =Sottrai(Moltiplica(Somma(Somma(Costante(5),Variabile("x")),Variabile("y")),Costante(2)),Variabile("x"))
     println("Espressione: "+exp.decodifica(myTest));
     println("Risultato: "+exp.risolvi(myTest));
  }
 
}

Il risultato in console dell’esecuzione del codice sopra esposto è:

Espressione: ((((5 + 1) + 2) * 2) - 1)
Risultato: 15

Come possiamo vedere, i pattern matching con Scala, aprono tutta una serie di alternative alla programmazione per “IF” , arrivando a rendere più snello e leggibile il nostro codice.

Check Utente Loggato in JSF2 tramite PhaseListener

Verificare che vi siano determinate condizioni, affinche sia possibile mostrare pagine della nostra webapp realizzata tramite jsf, è relativamente facile.
Normalmente è bene verificare lo stato dell’autenticazione, tramite un servlet filter, in grado di processare tutte le risorse.
In questo esempio realizzeremo invece un listener JSF in ascolto sulla fase di RESTORE_VIEW.

Non essendo possibile effettuare operazioni di CDI sui listener prima della versione 2.2, al posto dell’injection delle classi necessarie, utilizziamo delle espressioni EL per ottenere ad esempio il controllerBean di sessione in cui è mantenuta la classe utente dopo l’autenticazione.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
public class UserLoggedPhaseListener implements PhaseListener{

    /**
     *
     */

    private static final long serialVersionUID = -2062847364596874153L;

    /* (non-Javadoc)
     * @see javax.faces.event.PhaseListener#afterPhase(javax.faces.event.PhaseEvent)
     */

    @Override
    public void afterPhase(PhaseEvent event) {
       
        FacesContext facesContext = event.getFacesContext();
        String currentPage = facesContext.getViewRoot().getViewId();
         
        boolean isLoginPage = currentPage.lastIndexOf("login.xhtml")>-1;
        if (isLoginPage) return;
       
        AuthController auth = facesContext.getApplication().evaluateExpressionGet(facesContext, "#{auth}", AuthController.class);
        if (auth==null || auth.getUser()==null){
            NavigationHandler nh = facesContext.getApplication().getNavigationHandler();
            nh.handleNavigation(facesContext, null, "LOGOUT");
        }
         
    }

    /* (non-Javadoc)
     * @see javax.faces.event.PhaseListener#beforePhase(javax.faces.event.PhaseEvent)
     */

    @Override
    public void beforePhase(PhaseEvent arg0) {
       
    }
   
   
    /* (non-Javadoc)
     * @see javax.faces.event.PhaseListener#getPhaseId()
     */

    @Override
    public PhaseId getPhaseId() {
         return PhaseId.RESTORE_VIEW;
    }
   
}

Come prima cosa, nell’AfterPhase, andiamo a identificare la pagina richiamata. Se stiamo mostrando la pagina di login, non eseguiamo nessuna attività.
Altrimenti, tramite EL andiamo a cercare un @ManagedBean chiamato “auth”, tramite l’espressione #{auth} .
Nel caso il bean non fosse esistente e/o il suo contenuto nullo, utilizzamo il FacesContext, per accedere al NavigationHandler e richiedere la gestione dell’outcome “LOGOUT”. In questo caso, nelle navigation-rule del faces-config, dobbiamo aver definito una regola che da qualsiasi view-id (view-id=*) permette il redirect alla pagina di login.

1
2
3
4
5
6
7
<navigation-rule>
        <from-view-id>*</from-view-id>
        <navigation-case>
            <from-outcome>LOGOUT</from-outcome>
            <to-view-id>/login.xhtml</to-view-id>
        </navigation-case>
    </navigation-rule>

Fatto questo, non dimentichiamo di registrare nel faces-config.xml il nostro listener:

1
2
3
<lifecycle>
        <phase-listener>com.metalide.test.jsf.UserLoggedPhaseListener</phase-listener>
    </lifecycle>

Buon divertimento.

Usare RouteData con mvc4 per rounting a Runtime in global.asax

Spesso capita di dover eseguire dei routing dinamici all’interno delle applicazioni sviluppate con MVC3 e MVC4 della Microsoft, e molte volte non è possibile eseguire direttamente una redirect, come nel mio caso specifico, dove dovevo eseguire un redirect all’interno del Global.asax, durante la fase di Application_EndRequest,senza scatenare un loop infinito di redirect.
Ci vengono in aiuto le api dell’MVC ed in particolare la RouteData, che permette di passare tutti i dati di chiamata ad una particolare action di uno specifico Controller, instanziato programmaticamente:

1
2
3
4
5
6
7
var rd = new RouteData();
rd.DataTokens["area"] = null;
rd.Values["controller"] = "NomeController";
rd.Values["action"] = "NomeAction";

IController c = new NomeController();
c.Execute(new RequestContext(new HttpContextWrapper(Context), rd));