[PrimeFaces] Due approcci per customizzare i tag di PrimeFaces

Logo Prime FacesIn un recente post, ho “lodato” PrimeFaces come uno dei migliori framework Java Server Faces (JSF) attualmente in circolazione. In effetti, le componenti grafiche, rispetto ad altre librerie del genere (vedi Rich Faces), sono sicuramente più stabili, complete e graficamente più carine. Tuttavia, può capitare di dover fare delle customizzazioni, specie per renderne alcune davvero accessibili (nonostante da documentazione si dichiari l’aderenza di PrimeFaces alle specifiche Accessibile Rich Internet Applications – WAI-ARIA).

Illustro qui due metodi per customizzare i tag di Prime. Nell’esempio, verrà modificato la componente “PanelGrid” (p:panelGrid), ma la procedura può essere applicata a tutti i tag della libreria “core”.

Entrambi metodi che vi illustrerò, prendono come riferimento la documentazione dei tag di PrimeFaces. Ecco il link alla documentazione dell’attuale versione (4.0): Documentazione PrimeFaces 4.0

Ad esempio, per il tag PanelGrid ecco le informazioni che ci servono:

tagPanelGrid PrimeFaces

 

1° Metodo: Customizzazione della Renderer Class (RenderKit)

Se vi occorre customizzare soltanto il rendering della componente grafica, basta estendere la classe “Renderer” ad essa associata. Come già anticipato sopra, per capire qual è tale classe, occorre leggere la documentazione relativa al tag da customizzare. Nel caso del panelGrid, la classe da estendere è org.primefaces.component.panelgrid.PanelGridRenderer

NOTA. Gli attributi rendererType e rendererClass, che si leggono da documentazione, non coincidono. Quindi, state attenti ad annotarvi queste informazioni, perché occorre configurarle allo stesso modo per la classe “custom” che scriverete, come illustrato di seguito.

Ecco la classe CustomPanelGridRenderer che estende quella “core” PanelGridRenderer:

package it.francescoficetola.prime.web.component;

import java.io.IOException;

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;

import org.primefaces.component.column.Column;
import org.primefaces.component.panelgrid.PanelGrid;
import org.primefaces.component.panelgrid.PanelGridRenderer;
import org.primefaces.component.row.Row;

public class CustomPanelGridRenderer extends PanelGridRenderer{

	 public CustomPanelGridRenderer() {
                super();
	        System.out.println("costruttore classe custom renderer");
	 }

         @Override
	 public void encodeRow(FacesContext context, Row row, String columnRole, String rowClass, String columnClass) throws IOException {
	        ResponseWriter writer = context.getResponseWriter();

	        writer.startElement("tr", null);
	        if(shouldWriteId(row)) {
	            writer.writeAttribute("id", row.getClientId(context), null);
	        }

	        writer.writeAttribute("class", rowClass, null);
	        writer.writeAttribute("role", "row", null);

	        for(UIComponent child : row.getChildren()) {
	            if(child instanceof Column && child.isRendered()) {
	                Column column = (Column) child;
	                String styleClass = null;
	                String userStyleClass = column.getStyleClass();

	                if(userStyleClass != null && columnClass != null) styleClass = columnClass + " " + userStyleClass;
	                else if(userStyleClass != null && columnClass == null) styleClass = userStyleClass;
	                else if(userStyleClass == null && columnClass != null) styleClass = columnClass;

	                writer.startElement("td", null);
	                if(shouldWriteId(column)) { 
	                    writer.writeAttribute("id", column.getClientId(context), null);
	                }
	                writer.writeAttribute("role", columnRole, null);

	                if(column.getStyle() != null) writer.writeAttribute("style", column.getStyle(), null);
	                if(styleClass != null) writer.writeAttribute("class", styleClass, null);
	                if(column.getColspan() > 1) writer.writeAttribute("colspan", column.getColspan(), null);
	                if(column.getRowspan() > 1) writer.writeAttribute("rowspan", column.getRowspan(), null);

	                column.encodeAll(context);

	                writer.endElement("td");
	            }
	        }

	        writer.endElement("tr");
	    }
}

Nell’esempio precedente, è stato effettuato l’override del metodo encodeRow. In base al comportamento che vorrete customizzare, occorre individuare nella classe “Renderer” della vostra componente, il metodo associato a tale comportamento ed effettuarne l’override, modificandone la logica.

Per poter registrare la vostra classe “custom” di rendering, occorre configurare il faces.config.xml:

<?xml version='1.0' encoding='UTF-8'?>
<faces-config xmlns="http://java.sun.com/xml/ns/javaee"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd"
        version="2.0">

	<render-kit>
            <renderer>
                <component-family>org.primefaces.component</component-family>
                <renderer-type>org.primefaces.component.PanelGridRenderer</renderer-type>
                <renderer-class>it.francescoficetola.prime.web.component.CustomPanelGridRenderer</renderer-class>
            </renderer>
        </render-kit>

</faces-config>

I valori da associare agli attributi component-familyrenderer-type li prendete da documentazione del tag, come suddetto. In questo modo, ogni volta che utilizzate il “core tag” sulle pagine JSF della vostra applicazione (nell’esempio, p:panelGrid), in automatico verrà richiamata la vostra classe “renderer”.

NOTA. Se notate che nulla accade, provate ad inserire nel file web.xml della vostra applicazione le seguenti righe di codice (inserendo anche il file faces-config.xml nella cartella WEB-INF):

    <context-param>
        <param-name>javax.faces.CONFIG_FILES</param-name>
        <param-value>
            /WEB-INF/faces-config.xml, /faces-config.xml
        </param-value>
    </context-param>

 

2° Metodo: Customizzazione del tag (CustomTag)

Il secondo metodo che vi illustro è sicuramente il più completo, perché vi permette di modificare tutto il comportamento del tag associato alla componente grafica, e non solo il suo rendering.

In questo caso, vanno estesi sia il Component Class che il Renderer Class. Nel caso del p:panelGrid di questo esempio:

Renderer class: org.primefaces.component.panelgrid.PanelGridRenderer

Component class: org.primefaces.component.panelgrid.PanelGrid

package it.francescoficetola.prime.web.component;

import javax.faces.component.FacesComponent;
import org.primefaces.component.panelgrid.PanelGrid;

@FacesComponent("it.francescoficetola.prime.component.CustomPanelGrid")
public class CustomPanelGrid extends PanelGrid{

	public CustomPanelGrid() {
		super();
		System.out.println("Costruttore del component class");
	}

	@Override
	public void setColumns(int _columns) {
	    getStateHelper().put(PropertyKeys.columns, _columns);
	}
}

Nell’esempio del PanelGrid, è stato effettuato l’override del metodo setColumns, ma anche qui vale quanto detto nel 1° metodo di customizzazione, ossia che in base al comportamento che vorrete customizzare, occorre individuare nel Component Class il metodo associato per modificandone la logica, attraverso l’override.

Di seguito, vi ripeto la classe di “renderer” custom, che anche per questo metodo va prevista, ma che in più presenta anche delle annotazioni di classe:

package it.francescoficetola.prime.web.component;

import java.io.IOException;

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.faces.render.FacesRenderer;

import org.primefaces.component.column.Column;
import org.primefaces.component.panelgrid.PanelGrid;
import org.primefaces.component.panelgrid.PanelGridRenderer;
import org.primefaces.component.row.Row;

@FacesRenderer(
	    componentFamily=PanelGrid.COMPONENT_FAMILY,
	    rendererType="it.francescoficetola.prime.web.component.CustomPanelGridRenderer"
	)
public class CustomPanelGridRenderer extends PanelGridRenderer{

	public CustomPanelGridRenderer() {
	    System.out.println("costruttore della classe renderer");
	    }

         @Override
	 public void encodeRow(FacesContext context, Row row, String columnRole, String rowClass, String columnClass) throws IOException {
	        ResponseWriter writer = context.getResponseWriter();

	        writer.startElement("tr", null);
	        if(shouldWriteId(row)) {
	            writer.writeAttribute("id", row.getClientId(context), null);
	        }

	        writer.writeAttribute("class", rowClass, null);
	        writer.writeAttribute("role", "row", null);

	        for(UIComponent child : row.getChildren()) {
	            if(child instanceof Column && child.isRendered()) {
	                Column column = (Column) child;
	                String styleClass = null;
	                String userStyleClass = column.getStyleClass();

	                if(userStyleClass != null && columnClass != null) styleClass = columnClass + " " + userStyleClass;
	                else if(userStyleClass != null && columnClass == null) styleClass = userStyleClass;
	                else if(userStyleClass == null && columnClass != null) styleClass = columnClass;

	                writer.startElement("td", null);
	                if(shouldWriteId(column)) { 

	                    writer.writeAttribute("id", column.getClientId(context), null);
	                }
	                writer.writeAttribute("role", columnRole, null);

	                if(column.getStyle() != null) writer.writeAttribute("style", column.getStyle(), null);
	                if(styleClass != null) writer.writeAttribute("class", styleClass, null);
	                if(column.getColspan() > 1) writer.writeAttribute("colspan", column.getColspan(), null);
	                if(column.getRowspan() > 1) writer.writeAttribute("rowspan", column.getRowspan(), null);

	                column.encodeAll(context);

	                writer.endElement("td");
	            }
	        }

	        writer.endElement("tr");
	    }

}

A questo punto, occorre creare la taglib con i propri “custom tag”. Nel mio caso, ho creato un file customtag.taglib.xml, con il seguente contenuto:

<?xml version="1.0" encoding="UTF-8"?>
<facelet-taglib
    xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facelettaglibrary_2_0.xsd"
    version="2.0"
>
    <namespace>http://francescoficetola.it/ui</namespace>

    <tag>
        <tag-name>custompanelgrid</tag-name>
        <component>
            <component-type>it.francescoficetola.prime.web.component.CustomPanelGrid</component-type>
            <renderer-type>it.francescoficetola.prime.CustomPanelGridRenderer</renderer-type>
        </component>
    </tag>
</facelet-taglib>

Nel file della taglib, occorre registrare tutti i “custom tag” che prevedete (nel mio caso, ho per ora solo il custompanelgrid, che vi ricordo estende il tag “core” p:panelgrid di PrimeFaces). Salvate questo file nella directory WEB-INF del vostro progetto.

Infine, occorre registrare la taglib nel file web.xml, come segue:

	<!-- CONTEXT PARAM  -->
	<context-param>
	    <param-name>javax.faces.FACELETS_LIBRARIES</param-name>
	    <param-value>/WEB-INF/customtag.taglib.xml</param-value>
	</context-param>

Ora siamo pronti ad utilizzare il nostro custom tag. Ricordatevi di definire il namespace nella pagina JSF in cui utilizzare la vostra componente custom:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 
<ui:composition 
	xmlns="http://www.w3.org/1999/xhtml"
	xmlns:c="http://java.sun.com/jsp/jstl/core"
	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:customtag="http://francescoficetola.it/ui"
>

<h:html>

   <h:head>
   </h:head>

   <h:body>

	<h:form id="myForm">

            <p:messages id="messages" /> 

	     <h:panelGroup id="myPanelGroup">

		 <customtag:custompanelgrid>

		        <p:row>
				<p:column>Colonna 1</p:column>
                                <p:column>Colonna 1</p:column>
			</p:row>

	         </customtag:custompanelgrid>

	     </h:panelGroup>

	</h:form>

   </h:body>

</h:html>

</ui:composition>

 

Riferimenti utili:

[iOs] Librerie per la scansione di codici a barre ed il riconoscimento OCR

Come acquisire un codice a barre in un’app iOs? Ce lo dice Marco Giraudo, giovane sviluppatore iOs e PHP, che mi ha mandato questo ottimo articolo, in cui si è trovato a valutare delle librerie per la scansione di codici a barre (BarCode Reader), ma anche per il riconoscimento OCR.

iPhone_barcode_scanner

Eccovi l’esperienza di Marco, a cui va il mio ringraziamento:

“Mi sono fatto questa domanda nelle ultime settimane per motivi di lavoro e vi voglio riportare 2 esempi che sono riuscito a trovare e testare. In particolare, mi serviva riconoscere un codice a barre e cosi ho utilizzato due librerie: ZBarSDK e Tesseract .

1° Esempio: ZBarSDK

ZBarSDKhttp://zbar.sourceforge.net/iphone/sdkdoc/

C’è stato subito un problema da superare, dopo l’installazione sul mio progetto xCode:

Warning: Only the iPhone 3GS and iPhone 4 are supported, as they have a camera with auto-focus. The ZBar library does not support the iPhone 3G and is unlikely to ever support it.

Su iPhone 5 con iOs 6 e successivi mi dava problemi, ma ho trovato la soluzione su http://stackoverflow.com:

http://stackoverflow.com/questions/12506671/zbar-sdk-is-not-working-in-ios6

Oltre a settare come “Valid Architecture” il processore armv7s nelle impostazioni del vostro progetto xCode, potete provare a scaricare ed installare la versione beta compatibile con iOs 6 (ZBarSDK-1.3.1.dmg):

http://sourceforge.net/projects/zbar/files/iPhoneSDK/beta/

Una  volta installato il .DMG, l’unica cosa da fare è leggere il file README riportato e seguire le istruzioni per inserire la libreria nel proprio progetto:

    • trascinare la cartella ZBarSDK nel proprio progetto
    • inserire nel proprio progetto i Frameworks: AVFoundation (weak), CoreMedia (weak), CoreVideo (weak), QuartzCore, libiconv.dylib

Per usare ZBar, basta importare nel vostro controller la libreria:

#import “ZBarSDK.h”

Nello stesso .dmg troverete una cartella con molti esempi da cui prendere spunto. L’ho anche provata con iPhone5 con su installato ios7 beta4 e funziona benissimo.

2° Esempio: Tesseract-OCR (Google)

Secondo esempio che vi riporto è tesseract-ocr, un OCR Engine che vi permette di riconoscere i testi riportati in un’immagine. Ecco il sito ufficiale su Google Code:

http://code.google.com/p/tesseract-ocr/

Per far funzionare l’OCR Engine in iOs, esiste un Wrapper: Tesseract for iOs (https://github.com/ldiqual/tesseract-ios)

Eccovi le istruzioni di installazione:

http://lois.di-qual.net/blog/install-and-use-tesseract-on-ios-with-tesseract-ios/

Spero che questi due esempi possano esservi utili. Alla prossima!”

[iOs] Come importare la libreria Google Data Library in un progetto xCode – Esempio con YouTube Service

Il presente articolo costituisce un “upgrade” di quello già scritto su questo blog qualche tempo fa: “[iOS] Un video player nelle nostre app con il MediaPlayer framework e Google Data Library“. A quella data, la Google Data Library (o semplicemente “GData“) in Objective-C era disponibile ed importabile nei nostri progetti xCode come libreria compressa in estensione “.a”.

La GData compressa non dava nessun problema sui progetti compatibili con architettura armv6armv7, ma su armv7s non è più possibile compilarla (restituisce spesso errori del tipo “Symbols not found” su alcune sue classi).

E’ necessario, dunque, installare la libreria GData importando direttamente i suoi sorgenti e compilandoli per tutte le architetture supportate (da armv6 ad armv7s).

Ecco gli step da eseguire:

1. Download della libreria GData con SVN: basta clonare il codice sorgente della libreria dalla repo SVN di Google (http://code.google.com/p/gdata-objectivec-client/):

svn checkout http://gdata-objectivec-client.googlecode.com/svn/trunk/ gdata-objectivec-client-read-only

dove gdata-objectivec-client-read-only è la cartella in cui volete clonare la lib.

2. Trascinare il progetto GData.xcodeproj nel progetto xCode: occorre trascinare il progetto GData.xcodeproj, presente nella cartella Sources della libreria GData scaricata al punto 1, direttamente nel vostro progetto (senza checkare l’opzione “Copy items into destination group’s folder (if needed)“):

GData Installazione in xCode - Step 2

3. Importazione delle librerie e delle dipendenze nel progetto xCode: spostatevi nella scheda Build Phases del target di progetto ed importate le seguenti librerie:

  • in Target Dependencies, aggiungete la libreria GDataTouchStaticLib
  • in Link Binary With Libraries, aggiungete la libreria libGDataTouchStaticLib.a
  • sempre in Link Binary With Libraries, aggiungete i framework: Security.frameworkSystemConfiguration.framework

 GData Installazione in xCode - Step 3 GData Installazione in xCode - Step 3GData Installazione in xCode - Step 3

4. Configurazione dei parametri e dell’header path: spostatevi nella scheda Build Settings del target di progetto e configurare i seguenti parametri e path:

  • sotto la voce “Other Linker Flags“, inserire i seguenti parametri: 
    • -ObjC
    • -lxml2
    • -all_load

GData Installazione xCode - Step 4

  •  Sotto la voce Header Search Paths, inserire il valore /usr/include/libxml2

GData Installazione xCode - Step 4

5. Rimozione delle API di GData non necessarie: se nel vostro progetto vi interessa utilizzare soltanto determinate funzionalità offerte dalla libreria GData (per esempio, CalendarYoutube), occorre effettuare anche la seguente configurazione:

  • cliccate sulla root del progetto GData importato in xCode e selezionate il target GDataTouchStaticLib. In “Build Setting“, sotto la voce “Other C Flags” (sia per Release che per Debug), inserite i seguenti parametri:
    • -DGDATA_REQUIRE_SERVICE_INCLUDES=1
    • -DGDATA_INCLUDE_CALENDAR_SERVICE=1 (se vi interessa utilizzare Google Calendar)
    • -DGDATA_INCLUDE_YOUTUBE_SERVICE=1 (se volete interfacciarvi con Youtube)
    • -DGDATA_INCLUDE_CONTACTS_SERVICE=1 (per utilizzare le Contact API)

GData Installazione xCode - Step 5

 

6. Compilazione e importazione degli header di GData nel progetto: dopo aver eseguito i precedenti step, compilate il vostro progetto e, se non vi sono errori, andate nell’Organizer di xCode, nella scheda Projects. Qui vi trovate un link per accedere alla cartella Derived Data con il codice compilato del vostro progetto. Nella directory Build/Products/Debug-iphonesimulator/Product troverete la cartella Header in cui sono presenti le interfacce di GData. Dovete trascinare tutta la cartella Header nel vostro progetto.

GData Installazione xCode - Step 6 GData Installazione xCode - Step 6

 

NOTA. Se all’atto del rilascio (distribuzione su Apple Store o “ad hoc”), vi ritrovate un file .xarchive, invece, di un .app/.ipa, eseguite i seguenti step:

  • Eliminate tutti i target di GData (click destro sul progetto GData.xcodeproj)
  • Settate la voce SKYP INSTALL a YES in “Build Settings” del target di GData.

 

7. Utilizzo della libreria GData: se i passi precedenti vengono eseguiti correttamente e senza errori, potete utilizzare le API di Google Data Library.

Come prova, importate la seguente riga in una vostra classe. Se non vi sono errori di compilazione, la configurazione di GData è stata eseguita correttamente.

#import "GData.h"

Vi allego un progettino di esempio in cui mi interfaccio con Youtube per scaricare la lista di video da un canale. La libreria GData qui è stata inserita in una cartella Submodules e potete importarla trascinandola da lì. NOTA. Occorre ripetere la procedura riportata sopra per far riconoscere al progetto la GData lib, altrimenti vi ritrovete un errore in compilazione: “ld: library not found for -lGDataTouchStaticLib“.

https://github.com/fficetola/GDataLibTest

——————

Riferimenti utili:

 

[iOs] Accesso alla fotocamera, alla Libreria e modifica di una foto con Aviary

Condivido qui un progettino xCode che esemplifica come accedere ad una foto salvata  nella Libreria Foto oppure scattata direttamente con la fotocamera del vostro device. Una volta prelevata la foto, è possibile modificarla, applicando su di essa vari effetti e filtri, grazie alla libreria Aviary (disponibile non solo per iOs, ma anche per Windows Phone, Android e HTML5).

Aviary-iOS-SDK_4

Ecco il link al mio progetto su GitHubhttps://github.com/fficetola/AviaryExample/

Quindi, ricapitolando, ecco le funzionalità che troverete nell’esempio:

  • visualizzazione di una foto, prelevata dalla Libreria Foto oppure dalla fotocamera del device, in una UIImageView
  • possibilità di modificarla grazie all’editor di Aviary
  • visualizzazione della foto in modalità fullscreen con gesture per lo zoom (pinch-on/pinch-off/double tap)
  • salvataggio dell’immagine modificata nella Libreria Foto

Procediamo per step, descrivendo le funzionalità su elencate (vi invito a visionare i file ViewController.m e BannerViewController.m  in cui vi è tutta la logica di questo tutorial).

 

1. Selezione di una immagine dalla Libreria Foto o dalla fotocamera

La selezione di una foto già salvata in Libreria o scattata dalla fotocamera avviene grazie al controller UIImagePickerController, il quale appare come una modale che a seconda del “source type” richiesto (camera o gallery), vi permetterà di importare appunto la foto da libreria o fotocamera.

- (void) selectPhoto:(UIImagePickerControllerSourceType) pickerType {

    picker = [[UIImagePickerController alloc] init];
    picker.delegate = self;

    picker.sourceType = pickerType;
    //picker.sourceType = UIImagePickerControllerSourceTypeCamera;
    //picker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;

    [self presentModalViewController:picker animated:YES];

}

Il parametro in input al metodo selectPhoto è di tipo UIImagePickerControllerSourceType e può essere valorizzato con i seguenti valori:

  • UIImagePickerControllerSourceTypeCamera
  • UIImagePickerControllerSourceTypePhotoLibrary

I metodi di callback, che permettono di intercettare se l’utente ha selezionato o meno una foto, sono quelli dell’UIImagePickerControllerDelegate (metodi implementati nel ViewController):

#pragma mark - UIImagePicker Delegate

-(void)imagePickerControllerDidCancel:(UIImagePickerController *) photopicker {

    [photopicker dismissModalViewControllerAnimated:YES];

    [photopicker release];

}

- (void)imagePickerController:(UIImagePickerController *) photopicker didFinishPickingMediaWithInfo:(NSDictionary *)info {

    imageView.image = [info objectForKey:UIImagePickerControllerOriginalImage];
    [photopicker dismissModalViewControllerAnimated:YES];
    [photopicker release];

}

A cosa servono questi metodi del delegate?

  • imagePickerControllerDidCancel: permette di intercettare il click sul tasto Annulla del picker (o della libreria o della fotocamera), quindi senza che sia avvenuta la selezione della foto. In questo metodo viene chiuso il picker (con il dismissModalViewControllerAnimated)
  • didFinishPickingMediaWithInfo: permette di intercettare il click sul tasto Done con conseguente scelta della foto. Il recupero dell’immagine selezionata viene fatto accedendo alla proprietà UIImagePickerControllerOriginalImage del dizionario. Nell’esempio, tale foto viene inserita in una UIImageView per la visualizzazione.

 

2. Modifica dell’immagine con Aviary 

ios_aviary

Per poter modificare la foto, applicandovi  filtri ed effetti vari (cropping, resizing, rotating, ecc.), ho utilizzato la libreria Aviaryla cui versione “free” vi permette di eseguire tutte le features disponibili, ma non di trattare foto ad altissima risoluzione (vedi i dettagli nella sezione Pricing del sito www.aviary.com). Per maggiori dettagli sulle funzionalità previste, ecco la pagina della documentazione per iOshttp://www.aviary.com/ios/documentation

L’installazione della libreria Aviary nel vostro progetto è molto semplice:

  1. Innanzitutto scaricate il progetto di esempio direttamente dal sito ufficiale di Aviary: lo troverete nella sezione Documentazione, cliccando sul bottone GET CODE. Potete eseguirlo per vedere quali sono le features messe a disposizione dalla libreria o come esempio per implementare funzionalità più avanzate (visto che in questo tutorial vi spiego solo l’uso di base)
  2. Registratevi al sito (gratuitamente) per ottenere un’API key, che dovrete inserire all’interno del vostro progetto. Basta cliccare sul bottone GET API KEY della sezione Documentazione, registrarvi e poi aggiungere una nuova app. Vi verranno generate una API Key e una Secret Key ( a voi servirà solo la prima).
  3. Dal codice di esempio scaricato dal sito ufficiale, dovrete copiare la cartella AviarySDK e importarla nel vostro progetto. Questa contiene la libreria libAviarySDK.a, i file di Header e di Resources. Copiatela integralmente nel vostro progetto.
  4. inserite nel vostro progetto i seguenti framework, da cui Aviary dipende:
    1. Accelerate.framework
    2. AdSupport.framework (selezionare `Optional` invece di `Required`)
    3. CoreData.framework
    4. CoreText.framework
    5. libsqlite3.0.dylib
    6. libz.1.2.5.dylib
    7. Foundation.framework
    8. MessageUI.framework
    9. OpenGLES.framework
    10. QuartzCore.framework
    11. StoreKit.framework
    12. SystemConfiguration.framework
    13. UIKit.framework
  5. Infine, inserite nel vostro file MyProject-Info.plist la chiave Aviary-API-Key valorizzandola con l’API Key generata al punto 2.

Dopo la fase di configurazione della libreria, si passa all’utilizzo vero e proprio dell’editor. Innanzitutto il ViewController deve avere come delegate la classe AFPhotoEditorControllerDelegate, che ci permetterà di interagire con l’editor di Aviary, ossia con la classe AFPhotoEditorController.

Ecco come far apparire l’editor di Aviary in una modale del ViewController:

- (void)displayEditorForImage:(UIImage *)imageToEdit
{
    AFPhotoEditorController *editorController = [[AFPhotoEditorController alloc] initWithImage:imageToEdit];
    [editorController setDelegate:self];
    [self presentViewController:editorController animated:YES completion:nil];
}

Basta passare all’AFPhotoEditorController l’immagine da modificare (imageToEdit), in modo da farla apparire nell’editor. Qui Aviary vi permetterà di applicare gli effetti e i filtri disponibili. Per poter intercettare le modifiche o l’annullamento delle modifiche (chiusura con tasto Annulla dell’editor), basta implementare i seguenti metodi dell’ AFPhotoEditorControllerDelegate:

#pragma mark - Aviary Delegate

- (void)photoEditor:(AFPhotoEditorController *)editor finishedWithImage:(UIImage *)image
{

    //read modified image
    [imageView setImage:image];

    [self dismissViewControllerAnimated:YES completion:NULL];

}

- (void)photoEditorCanceled:(AFPhotoEditorController *)editor
{
    [self dismissViewControllerAnimated:YES completion:NULL];
}

Penso che le righe di codice precedenti siano abbastanza esplicative: photoEditorCanceled intercetta la chiusura di Aviary (tap su tasto Annulla) e permette di chiudere l’editor (dismissViewControllerAnimated), invece finishedWithImage permette di recuperare l’immagine modificata per riproporla nella UIImageView.

 

3. Zoom in/out dell’immagine (pinch-off/pinch-on o doppio tap per ingrandire/ridurre)

Per poter ingrandire/ridurre la foto visualizzata nell‘UIImageView, nel mio progettino di esempio trovate la classe BannerViewController.m. In essa vi è la logica per poter eseguire lo zoom-in e lo zoom-out della foto eseguendo le gesture pinch-on/pinch-off o il double tap sulla view. Inoltre, nel file BannerViewController.xib noterete una ScrollView contenente l’UIImageView, in cui visualizzare e zoomare l’immagine.

Ecco il codice:

-(void)handleDoubleTap:(UIGestureRecognizer *)gestureRecognizer {

    if(self.scrollView.zoomScale > scrollView.minimumZoomScale)
        [self.scrollView setZoomScale:scrollView.minimumZoomScale animated:YES];
    else
        [self.scrollView setZoomScale:scrollView.maximumZoomScale animated:YES];
}

- (void)viewDidLoad
{
    [super viewDidLoad];

    self.title = NSLocalizedString(@"BANNER_TITLE", nil);

    self.navigationItem.rightBarButtonItem = [[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(closeModal:)]autorelease];

    UITapGestureRecognizer *tapTwice = [[[UITapGestureRecognizer alloc] initWithTarget:self  action:@selector(handleDoubleTap:)]autorelease];

    tapTwice.numberOfTapsRequired = 2;

    [self.view addGestureRecognizer:tapTwice];

    UIImage *imageFullScreen = image;
    imageView.contentMode = UIViewContentModeScaleAspectFit;
    [self.imageView setImage:imageFullScreen];

    //self.scrollView.contentSize = image.size;
    self.scrollView.delegate = self;
    //self.scrollView.minimumZoomScale = 10.0;
    self.scrollView.maximumZoomScale = 50.0;   

}

Nelle precedenti righe notiamo l’allocazione di un oggetto di tipo UITapGestureRecognizer per la registrazione del doppio “tap” sulla vista: la action associata a tale gesture è handleDoubleTap, che permette di zoomare alla scala massima o minima impostata per la ScrollView. Infatti, il minimumZoomScale e il maximumZoomScale si impostano nel viewDidLoad.

Invece, per gestire le gesture di pinch-on e pinch-off sull’immagine, sono utili le seguenti righe di codice:

- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];
    CGPoint centerPoint = CGPointMake(CGRectGetMidX(self.scrollView.bounds),
                                      CGRectGetMidY(self.scrollView.bounds));
    [self view:self.imageView setCenter:centerPoint];
}

- (void)view:(UIView*)view setCenter:(CGPoint)centerPoint
{
    CGRect vf = view.frame;
    CGPoint co = self.scrollView.contentOffset;

    CGFloat x = centerPoint.x - vf.size.width / 2.0;
    CGFloat y = centerPoint.y - vf.size.height / 2.0;

    if(x < 0)
    {
        co.x = -x;
        vf.origin.x = 0.0;
    }
    else 
    {
        vf.origin.x = x;
    }
    if(y < 0)
    {
        co.y = -y;
        vf.origin.y = 0.0;
    }
    else 
    {
        vf.origin.y = y;
    }

    view.frame = vf;
    self.scrollView.contentOffset = co;
}

// MARK: - UIScrollViewDelegate
- (UIView*)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
    return  self.imageView;
}

- (void)scrollViewDidZoom:(UIScrollView *)sv
{
    UIView* zoomView = [sv.delegate viewForZoomingInScrollView:sv];
    CGRect zvf = zoomView.frame;
    if(zvf.size.width < sv.bounds.size.width)
    {
        zvf.origin.x = (sv.bounds.size.width - zvf.size.width) / 2.0;
    }
    else 
    {
        zvf.origin.x = 0.0;
    }
    if(zvf.size.height < sv.bounds.size.height)
    {
        zvf.origin.y = (sv.bounds.size.height - zvf.size.height) / 2.0;
    }
    else 
    {
        zvf.origin.y = 0.0;
    }
    zoomView.frame = zvf;
}

 

4. Salvataggio dell’immagine nella Libreria Foto

Infine, per salvare la foto nella Libreria Foto del device, basta utilizzare la classe nativa UIImageWriteToSavedPhotosAlbum:

//Per salvare la foto modificata nella Gallery

-(void)image:(UIImage *)image finishedSavingWithError:(NSError *)error contextInfo:(void *)contextInfo {

    if (error) {

        UIAlertView *message = [[UIAlertView alloc] initWithTitle:@"Salvataggio Fallito"
                                                          message:@" *ATTENZIONE* non è stato possibile salvare l'immagine"
                                                         delegate:nil
                                                cancelButtonTitle:@"OK"
                                                otherButtonTitles:nil];
        [message show];
    }
    else{
        UIAlertView *message = [[UIAlertView alloc] initWithTitle:@"Salvataggio effettuato con successo"
                                                          message:@"La tua foto è stata salvata nella Gallery"
                                                         delegate:nil
                                                cancelButtonTitle:@"OK"
                                                otherButtonTitles:nil];
        [message show];
    }
}

-(IBAction)savePhotoInGallery:(id)sender{

    //salvo la foto nella Gallery
    UIImageWriteToSavedPhotosAlbum(imageView.image, self, @selector(image:finishedSavingWithError:contextInfo:), nil);
}

[iOs] Far votare o recensire un’app su Apple Store con Appirater

Volete invitare l’utente a lasciare un voto o una recensione sulla vostra app su Apple Store? Sono diverse le librerie che permettono di fare il rating, ma tutte di terze parti.

La libreria che vi consiglio, che è anche quella più utilizzata per tale scopo, è Appirater, di cui trovate una presentazione sul sito ufficiale del creatore:

http://arashpayan.com/blog/2009/09/07/presenting-appirater/

Il codice è scaricabile direttamente da GitHubhttp://github.com/arashpayan/appirater/

AppiraterScreenshot

Cosa vi permette di fare Appirater:

  • chiedere all’utente se vuole recensire e votare la vostra app su Apple Store, visualizzando una popup con opzioni
  • permettere di impostare quando visualizzare la popup, se dopo N giorni dall’installazione dell’app (o sua nuova release) sul device oppure dopo N utilizzi (uses) dell’app stessa
  • permettere di posticipare la recensione da parte dell’utente, ricordandogli di farlo dopo N giorni
  • oppure, se l’utente non vuole recensire l’app, “ricordare” tale opzione e non visualizzare più il messaggio di popup
  • i messaggi della popup sono localizzabili in varie lingue (compreso italiano)

Le opzioni sono configurabili nel file Appirater.h e le vedremo in dettaglio dopo aver definito come installare la libreria.

Installazione

  1. Scaricare da GitHub la libreria ed importare tutti i suoi file (comprese le cartelle delle lingue .lproj che vi interessano) nel vostro progetto
  2. Se utilizzate l’ARC (Automatic Reference Counting), poiché la libreria è un pò vecchiotta e non utilizza l’ARC, dovete marcare il file Appirater.m con il flag -fobjc-arc (basta andare sulla root del progetto, selezionare il target e nella scheda Build Phases » Compile Sources, in corrispondenza della classe Appirater.m, inserire il flag su citato cliccando due volte su tale classe)
  3. Aggiungete al vostro progetto i seguenti framework (sempre nella scheda Build Phases, sezione Link Binary with Libraries): CFNetworkSystemConfiguration e StoreKit. Assicuratevi di cambiare da Required ad Optional il flag del framework StoreKit, sempre nella sezione Build Phases » Link Binary with Libraries del target.
  4. Infine, potete utilizzare Appirater direttamente nel vostro AppDelegate.m, in corrispondenza del metodo application:didFinishLaunchingWithOptions:

Utilizzo. Un esempio di utilizzo di Appirater è il seguente: 

    //call the Appirater class
    [Appirater setAppId:YOUR_APPID];
    [Appirater setDaysUntilPrompt:1];
    [Appirater setUsesUntilPrompt:10];
    [Appirater setSignificantEventsUntilPrompt:-1];
    [Appirater setTimeBeforeReminding:2];
    //[Appirater setDebug:YES];
    [Appirater appLaunched:YES];

Tale codice è stato inserito nel metodo dell’AppDelegate application:didFinishLaunchingWithOptions:

Potete settare una serie di opzioni o come fatto nell’esempio precedente o modificando i valori di default dell’interfaccia Appirater.h.

Importante è inserire l’AppID della vostra app, così come visualizzata nelle informazioni relative all’app stessa sull’account di iTunesConnect. Per farvi rilasciare un AppID dovete aver pubblicata una vostra app sullo store o almeno registrato tutte le sue informazioni lì prima dell’invio ad Apple per l’approvazione.

Tra le opzioni che possiamo settare abbiamo:

  • + (void) setAppId:(NSString*)appId; permette di settare l’AppID dell’app (da prelevare su iTunesConnect)
  • + (void) setDaysUntilPrompt:(double)value;  setta il numero di giorni da quando è installata l’app (o una sua nuova release) dopo i quali visualizzare la popup di rating
  • + (void) setUsesUntilPrompt:(NSInteger)value; setta il numero degli “usi” (uses) dopo i quali si chiede all’utente se vuole votare. Un uso potrebbe essere il fatto che l’app si apra in primo piano nel device.
  • + (void) setSignificantEventsUntilPrompt:(NSInteger)value; setta il numero di eventi significativi prima di chiedere all’utente se vuol fare il rating
  • + (void) setTimeBeforeReminding:(double)value; setta il numero di giorni prima di rivisualizzare la popup di rating (una sorta di promemoria)
  • + (void) setDebug:(BOOL)debug; questa opzione è comoda per gli sviluppatori, perché consente di visualizzare immediatamente la popup per poter fare test o sviluppo. Tale opzione è da disabilitare quando si rilascia l’app sullo store.

Eccovi un altro riferimento utile (stavolta in italiano): http://www.htmedia.it/2012/03/tip-ios-31-chiediamo-una-recensione-con-appirater/

Buon rating!

[Javascript] Un nuovo modo di scrivere il front-end con Backbone.js + CoffeeScript

Ultimamente sto usando il framework Backbone.js per scrivere il front-end di una web app, con un linguaggio molto minimale, e non proprio immediato, che è CoffeeScript. Non condivido appieno le implementazioni “massicce” lato front-end in Javascript, forse perché sono un convinto “accessibilista”, e forse perché non ho ancora compreso appieno quanto sia accessibile lo stesso Javascript. Comunque, sono contrario ad avere troppa logica spostata sul client e la struttura delle stesse pagine mi piace averla ben organizzata e semplice.

Tuttavia, questo Backbone, che implementa una “logica MVC” lato front-end, ho visto che è parecchio utilizzato in parecchie web app importanti.

Vi allego un ottimo tutorial, oltre al link della documentazione ufficiale e un ebook (a pagamento):

Da quanto si legge su HTML.it, per la sua architettura, Backbone.js rientra nella categoria delle librerie MV*, in quanto implementa Model e View, ma non ha un componente Controller tradizionale, delegandone i compiti alle View e ad un componente di routing. Questo approccio è abbastanza diffuso in ambito JavaScript, dove la diversa e più complessa gestione dell’interazione utente e dello stato dell’applicazione non si adattano bene ai compiti di un controller.

 

I componenti base di Backbone.js sono:

  • Backbone.Model: modelli
  • Backbone.Collection: liste di modelli
  • Backbone.View: view
  • Backbone.Router: routing e gestione centralizzata dello stato dell’applicazione

CoffeeScript, invece, non è altro che una riscrittura sintattica di Javascript, che si rifà a Ruby e Python. E’ simbioticamente legato, ma non necessario, a Backbone.js, visto che l’ha creato lo stesso Jeremy Ashkenas  (insieme ad Underscore.js ed Handlebars.js, librerie Javascript di utility, che è buono e consigliato conoscere per usare Backbone).

Eccovi un rapido tour nella sintassi CoffeeScript: CoffeeScript – perchè amarlo?

L’accoppiata Backbone+CoffeeScript ha un bel pò di vantaggi che ho potuto apprezzare:

  • semplicità e pulizia nella scrittura del codice, grazie ad una sintassi molto minimale (si richiede una conoscenza di base della sintassi Python)
  • il paradigma MVC crea una struttura di progetto ben organizzata, dove ogni cosa è al suo posto
  • propagazione di eventi e dell’aggiornamento “automatico” dello stato dei modelli e delle viste collegate
  • modalità di testing con fixture molto semplice e veloce
  • librerie di utility Javascript che implementano logiche anche complesse
  • realizzazione di Single-Page Application e di web app per il mobile (Using backbone.js with jQuery Mobile)
  • Backbone+REST=GRAVY: integrazione “nativa” di Backbone con le API REST (Vedi l’articolo sulla filosofia REST su questo blog)
  • possibilità di utilizzare tutto ciò che è Javascript (anche librerie come jQuery)
Gli svantaggi, a mio parere, sono:
  • troppa logica Javascript lato front-end
  • troppi modelli e viste possono rendere difficile la lettura e complicare la “navigazione” del progetto
  • se non si definisce un “modus operandi” e una struttura di progetto con relative “regole” e “convenzioni” prima dell’inizio della fase di sviluppo, risulta difficile lavorare in gruppo e scrivere software “manutenibile”
  • tecnologia molto evoluta e complessa, con una curva di apprendimento bassa
  • scarsa compatibilità con browser “datati”

Scritto ciò, se siete smanettoni Javascript, Backbone+CoffeeScript dovete assolutamente conoscerli ed usarli.

Vi allego un file di testo con delle note mie estratte dalla documentazione ufficiale di Backbone.

Backbonejs Notes
Titolo: Backbonejs Notes (0 click)
Etichetta:
Filename: backbones.txt
Dimensione: 25 kB

[iOS] Inserire le mappe e la localizzazione con MapKit e CoreLocation Framework

Sicuramente sapete che Apple è arrivata ai ferri corti, da un bel pezzo, con Google e ha comunicato di non includere neanche YouTube come app preinstallata sul melafonino:

Addirittura Apple ha lanciato la sfida a Google Maps con un’app che dà anche informazioni sul traffico e fornisce un sistema di navigazione in 3D, che dovrebbe uscire su iOS 6.

Tuttavia, per chi volesse ad inserire una mappa o indicazioni di geolocalizzazione nelle proprie app, in attesa delle API ufficiali del nuovo sistema di navigazione Apple succitato, vi riporto un esempio su come utilizzare MapKit e CoreLocation Framework.

Nel progetto di esempio (che vi allego alla fine di questo articolo), trovate una view con una mappa centrata su un punto GPS (latitudine, longitudine) definito (e segnalato da un marker). Vi è possibilità di definire diverse modalità di visualizzazioni della mappa (normale, satellitare, ibrida) e la possibilità di segnalare il percorso (a piedi o in macchina) dal punto GPS in cui si trova il vostro device e il luogo di arrivo preimpostato. Inoltre, viene calcolato anche il tempo stimato e i metri da percorrere (a piedi o in macchina) dall’origine alla destinazione.

Ecco la “lista della spesa” delle librerie/framework che ci servono:

  • RegexKitLite: è una libreria di supporto che permette di utilizzare le espressioni regolari (regex) – Lightweight Objective-C Regular Expressions for Mac OS X using the ICU Library
  • MapKit Framework: fornisce le API per il download delle mappe, lo zoom e l’inserimento delle annotazioni (marker)
  • CoreLocation Framework: permette di determinare la posizione GPS corrente e la direzione spaziale del device

[iOS] Un video player nelle nostre app con il MediaPlayer framework e Google Data Library

Vi spiego come realizzare un video player da inserire nelle nostre app che vi permetta di visualizzare i video in vari formati (mp4 e avi, i formati che ho provato io) oppure di prelevarli da un canale youtube e listarli in una UITableView. Quindi, divido questo tutorial in due parti:

Alla fine di questo tutorial, trovate in allegato l’archivio con il progetto di esempio che verrà qui spiegato, con una tab bar application con due viste in cui vengono realizzati i due video player di esempio.

Continua la lettura

[iOS] La libreria Three20: navigazione “persistente” e galleria fotografica nelle vostre app

Three20 è una libreria open-source scritta in Objective-C e molto utilizzata e diffusa tra le maggiori app in Apple Store, come Facebook, Posterous, Pulse, Meetup.com e SCVNGR (vedi il seguente link con l’elenco delle app più famose che lo hanno utilizzato: http://three20.info/gallery).

La libreria mette a disposizione un ricco set di view controller come il Launcher, il popolare Photo Browser per la creazione di una gallery, e varie tables concepite per i contenuti Internet.

Altra funzionalità potentissima è quella che permette di accedere alle view delle vostra app tramite delle URL, come se la navigazione avvenisse su un sito web. Tale possibilità è davvero eccezionale, perché senza tale framework siamo costretti a programmare la navigazione con una gestione a stack delle view (grazie al navigation controller), dove il “salto” tra una vista e l’altra può avvenire solo tramite operazioni di push e pop delle stesse. Three20 introduce, quindi, il concetto di “persistenza“: permette di ricordare lo stato dell’app, ovvero ogni pagina (vista) viene “raggiunta” grazie ad una specifica URL. Quando un utente naviga attraverso le viste, Three20 memorizza una history di navigazione e la scrive su disco. Quando l’app viene ricaricata di nuovo, queste URLs vengono rieseguite istantaneamente così che la navigazione inizia dall’ultima vista visualizzata.

La libreria è modulare, ossia è possibile scegliere quali elementi includere nelle nostre app. Ciò fa di Three20 l’unico framework in Objective-C che incoraggia l’utilizzo di quelle che la comunità chiama “extensions“. Il Launcher è un’altra implementazione open-source disponibile in Three20 che emula l’app launcher standard dei dispositivi iOs (ossia il “desktop” con tutte le app installate, detto springboard). Il Launcher permette, dunque, di riordinare e cancellare gli elementi, personalizzare il numero di colonne e righe e di gestire pagine multiple.

Ecco il link al sito ufficiale con la demo delle funzionalità disponibili in Three20http://three20.info

Per una introduzione vi consiglio di leggere il post sul blog di Ray Wenderlich:

http://www.raywenderlich.com/656/introduction-to-three20

Per l’installazione del framework nelle vostre app vi invito a vedere il seguente video:

http://www.youtube.com/watch?v=-0-E-Z0fihg

Il progetto si trova su GitHub e prendi il nome di Facebook-Thre20, perché Facebook lo utilizzò per implementare la sua prima app mobile e lo ha fixato/customizzato nella versione attualmente (1.0.6) più stabile: https://github.com/facebook/three20/

L’installazione di Three20 nelle vostre app è molto semplice. Prima occorre scaricare la libreria a questo link: http://three20.info/roadmap/1.0.6.2 (o da riga di comando con GIT o decomprimendo il .tar). Vi consiglio di mettere la cartella decompressa di Three20 (rinominandola in three20) nella parent directory che contiene il progetto della vostra app. Basta poi digitare da terminale il seguente comando python per procedere all’installazione e configurazione automatica della libreria nella vostra app:

python three20/src/scripts/ttmodule.py -p path/to/myProject.xcodeproj Three20

Vi ritrovate comunque tutti i passi, anche quelli della procedura manuale di installazione, al seguente link: http://three20.info/article/2010-10-06-Adding-Three20-To-Your-Project Dopo l’installazione, nel progetto della vostra app, vi ritroverete (nella cartella Frameworks o Dependencies) i seguenti sottoprogetti (o moduli):

  • Three20Core
  • Three20Network
  • Three20Style
  • Three20UICommon
  • Three20UINavigator
  • Three20UI
  • Three20

Quando compilerete il vost ro progetto, XCode compilerà prima i precedenti sottoprogetti, rendendo disponibili i compilati alla vostra app.

Per realizzare una galleria di foto in una app iPhone/iPad, io ho seguito l’articolo sul blog di Ray Wenderlichhttp://www.raywenderlich.com/1430/how-to-use-the-three20-photo-viewer

Grazie al precedente articolo, ho potuto realizzare le gallery di foto delle mie app iVagabro e LuBannApp.

 

[Spring] Spring3: dai Controller ai Repository [Parte2]

In questa Parte 2 del tutorial di Spring3, vengono mostrate alcune classi di esempio con relative annotazioni per ciascuno strato della nostra web application:

  • strato controller (Spring MVC): annotazione @Controller
  • strato di business logic: annotazione @Service
  • strato di persistenza e modellazione dei dati (Spring ORM): annotazioni @Entity e @Repository

Model @Entity

Riporto qui di seguito un esempio di model o entity, che mappa una tabella sul database con relative associazioni ad altre entità. Il model viene annotato con @Entity e deve essere enumerato nelle annotatedClass del file spring-orm.xml, visto nella Parte1 di questo tutorial. Per maggiori informazioni, leggere anche Hibernate Annotations.

Nell’esempio sono riportate anche delle annotazioni per la validazione dei campi del model (javax.validation e org.hibernate.validator.constraints), che spostano la validazione lato server verso il lato persistenza e che Spring intercetta e gestisce con opportuni messaggi di stato.

package com.yourDomain.model;

import com.yourDomain.interceptors.IAuditLog;

import java.io.Serializable;
import java.util.Date;
import java.util.List;
import java.util.Set;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.Transient;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;

import org.hibernate.validator.constraints.Email;
import org.hibernate.validator.constraints.Length;
import org.hibernate.validator.constraints.NotEmpty;
import org.springframework.format.annotation.DateTimeFormat;

@Entity
@Table(name="utenti")
@NamedQueries({
	@NamedQuery(name = "Utente.findAll", query = "from Utente where id!=-1 order by cognome"),
	@NamedQuery(name = "Utente.findById", query = "from Utente r where r.id = :idUtente"),
	@NamedQuery(name = "Utente.findByCodiceFiscaleAndEmail", query = "from Utente r where lower(r.codiceFiscale) = lower(:codiceFiscale) and lower(r.email) = lower(:email) and dataCancellazione is null and abilitato = true"),
	@NamedQuery(name = "Utente.findByCodiceFiscaleOrEmail", query = "from Utente r where (lower(r.codiceFiscale) = lower(:codiceFiscale) or lower(r.email) = lower(:email) ) and dataCancellazione is null")
})
public class Utente implements Serializable,IAuditLog {

	private static final long serialVersionUID = 8797826874630455491L;

	public static final Integer ID_ADMINISTRATOR = -1;

	@Id
	@GeneratedValue
	@Column(name="id")
	private Integer id;

	@Column(name="matricola",nullable=true,length=50)
	private String matricola;

	@Column(name="cognome",nullable=false,length=255)
	@NotEmpty
	@NotNull
	private String cognome;

	@Column(name="nome",nullable=false,length=255)
	@NotEmpty
	@NotNull
	private String nome;

	@ManyToOne(fetch=FetchType.EAGER, targetEntity=StatoCivile.class)
	@JoinColumn(name = "fk_stato_civile", nullable = true, referencedColumnName = "id")
	private StatoCivile statoCivile;

	@Column(name="codice_fiscale",nullable=true,length=16)
	@NotEmpty
	@NotNull
	@Pattern(regexp="^[a-zA-Z]{6}[0-9]{2}[abcdehlmprstABCDEHLMPRST]{1}[0-9]{2}([a-zA-Z]{1}[0-9]{3})[a-zA-Z]{1}$")
	private String codiceFiscale;

	@Column(name="data_nascita",nullable=true)
	@Temporal(TemporalType.DATE)
	@DateTimeFormat(pattern="dd/MM/yyyy")
	private Date dataNascita;

	@ManyToOne(fetch=FetchType.EAGER, targetEntity=Comune.class)
	@JoinColumn(name = "fk_comune_nascita", nullable = true, referencedColumnName = "id")
	private Comune comuneNascita;

	@ManyToOne(fetch=FetchType.EAGER, targetEntity=Stato.class)
	@JoinColumn(name = "fk_stato_nascita", nullable = true, referencedColumnName = "id")
	private Stato statoNascita;

	@Column(name="indirizzo_residenza",length=255,nullable=true)
	private String indirizzoResidenza;

	@Column(name="cap_residenza",length=5,nullable=true)
	@Length(min=0,max=5)
	@Pattern(regexp="^[0-9]{0,5}")
	private String capResidenza;

	@ManyToOne(fetch=FetchType.EAGER, targetEntity=Comune.class)
	@JoinColumn(name = "fk_comune_residenza", nullable = true, referencedColumnName = "id")
	private Comune comuneResidenza;

	@Column(name="email",length=255,nullable=false)
	@NotEmpty
	@NotNull
	@Email
	private String email;

	@Column(name="partita_stipendio",length=50,nullable=true)
	private String partitaStipendio;

	@Column(name="sesso",length=1,nullable=true)
	@NotEmpty
	@NotNull
	private String sesso;

	@Column(name="telefono",length=100,nullable=true)
	//@Pattern(regexp="^(1\\s*[-\\/\\.]?)?(\\((\\d{3})\\)|(\\d{3}))\\s*[-\\/\\.]?\\s*(\\d{3})\\s*[-\\/\\.]?\\s*(\\\\d{4})\\s*(([xX]|[eE][xX][tT])\\.?\\s*(\\d+))*$")
	private String telefono;

	@Column(name="fax",length=100,nullable=true)
	//@Pattern(regexp="^(1\\s*[-\\/\\.]?)?(\\((\\d{3})\\)|(\\d{3}))\\s*[-\\/\\.]?\\s*(\\d{3})\\s*[-\\/\\.]?\\s*(\\\\d{4})\\s*(([xX]|[eE][xX][tT])\\.?\\s*(\\d+))*$")
	private String fax;

	@Column(name="cellulare",length=100,nullable=true)
	//@Pattern(regexp="^(1\\s*[-\\/\\.]?)?(\\((\\d{3})\\)|(\\d{3}))\\s*[-\\/\\.]?\\s*(\\d{3})\\s*[-\\/\\.]?\\s*(\\\\d{4})\\s*(([xX]|[eE][xX][tT])\\.?\\s*(\\d+))*$")
	private String cellulare;

	@Column(name="codice_abi",length=100,nullable=true)
	private String codiceABI;

	@Column(name="codice_cab",length=100,nullable=true)
	private String codiceCAB;

	@Column(name="numero_figli",nullable=true)
	private Integer numeroFigli;

	@Column(name="perc_invalidita",length=100,nullable=true)
	//@Pattern(regexp="^\\d+((\\.|,)\\d+)?$")
	private String percentualeInvalidita;

	@ManyToOne(fetch=FetchType.EAGER, targetEntity=AlboProfessionale.class)
	@JoinColumn(name = "fk_albo_professionale", nullable = true, referencedColumnName = "id")
	private AlboProfessionale alboProfessionale;

	@ManyToOne(fetch=FetchType.EAGER, targetEntity=SpecializzazioneProfessionale.class)
	@JoinColumn(name = "fk_spec_professionale", nullable = true, referencedColumnName = "id")
	private SpecializzazioneProfessionale specializzazioneProfessionale;

	@Column(name="data_modifica",nullable=true)
	@Temporal(TemporalType.DATE)
	@DateTimeFormat(pattern="dd/MM/yyyy")
	private Date dataModifica;

	@Column(name="data_creazione",nullable=true)
	@Temporal(TemporalType.DATE)
	@DateTimeFormat(pattern="dd/MM/yyyy")
	private Date dataCreazione;

	@Column(name="data_cancellazione",nullable=true)
	@Temporal(TemporalType.DATE)
	@DateTimeFormat(pattern="dd/MM/yyyy")
	private Date dataCancellazione;

	@Column(name="data_ultimo_accesso",nullable=true)
	@Temporal(TemporalType.DATE)
	@DateTimeFormat(pattern="dd/MM/yyyy")
	private Date dataUltimoAccesso;

	@ManyToOne(fetch=FetchType.LAZY, targetEntity=Utente.class)
	@JoinColumn(name = "fk_utente_modifica", nullable = true, referencedColumnName = "id")
	private Utente utenteModifica;

	@Column(name="abilitato",nullable=false)
	private boolean abilitato = true;

	@ManyToMany(fetch = FetchType.EAGER,cascade = CascadeType.ALL,targetEntity=GruppoOperativo.class)
	@JoinTable(name = "associativa_utenti_gruppi",
			joinColumns = @JoinColumn(name = "fk_Utente", referencedColumnName = "id"),
			inverseJoinColumns = @JoinColumn(name = "fk_gruppo", referencedColumnName = "id")
	)
	private List<GruppoOperativo> gruppiOperativi;

	@ManyToMany(fetch = FetchType.EAGER,cascade = CascadeType.ALL,targetEntity=TitoloStudio.class)
	@JoinTable(name = "associativa_utenti_titolistudio",
			joinColumns = @JoinColumn(name = "fk_Utente", referencedColumnName = "id"),
			inverseJoinColumns = @JoinColumn(name = "fk_titolo_studio", referencedColumnName = "id")
	)
	private Set<TitoloStudio> titoliStudio;

	@OneToMany(mappedBy = "Utente", cascade = CascadeType.ALL, fetch = FetchType.LAZY, targetEntity=Rapporto.class)
	private List<Rapporto> rapporti;

	//@Column(name="nato_estero",nullable=true)
	@Transient
	private boolean natoInStatoEstero;

	@Transient
	private boolean isLogged;

	@Transient
	private String maxVisibility;

	public Utente(){

	}

/**
    QUI TUTTI I METODI DI GET E SET
**/

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((id == null) ? 0 : id.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Utente other = (Utente) obj;
		if (id == null) {
			if (other.id != null)
				return false;
		} else if (!id.equals(other.id))
			return false;
		return true;
	}

}

 

@Controller

Il controller è una interfaccia che riceve una richiesta HTTP – HttpServletRequest e HttpServletResponse – simile alla Action di Struts. Dunque, definisce lo strato di interfaccia tra presentation e business layer. L’annotazione da utilizzare è @Controller e per permettere a Spring MVC di effettuare l’autowiring in fase di startup della web app occorre inserire nel file di configurazione (spring-application.xml) il tag mvc:annotation-driven (come visto nella Parte1). Nel seguente esempio, vengono annotati anche gli editors, che sono utili per popolare dinamicamente select o checkbox lato template. L’annotazione @InitBinder serve per configurare il controller allo startup della web application (in questo caso, a registrare la lista degli Editors). Con @Autowired viene iniettato il servizio che verrà utilizzato dal controller.

Non è necessaria una interfaccia da implementare, ma occorre solo annotare la classe con @Controller, in quanto il controller stesso non viene “iniettato”.

Per maggiori dettagli sui controller e su Spring MVC, leggere questo link: Web MVC Framework.

package com.yourDomain.controller;

import com.yourDomain.model.Profilo;
import com.yourDomain.model.Rapporto;
import com.yourDomain.model.Utente;

import java.beans.PropertyEditorSupport;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.validation.Valid;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.propertyeditors.CustomDateEditor;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.ModelAndView;

@Controller
@RequestMapping("/anagraficautente")
@SessionAttributes({"utenteLoggato","utenteSelezionato"})
public class UtenteController {

	protected static Logger log = Logger.getLogger(UtenteController.class);

	@Autowired
	UtenteService UtenteService;

	@InitBinder
	public void initBinder(WebDataBinder b) {
		DateFormat dateFormat = new SimpleDateFormat(SiapConfigProperties.getValue("dateFormat.small"));
		b.registerCustomEditor(Profilo.class, new ProfiloEditor());
	}

	//EDITOR
	private class DateEditor extends CustomDateEditor {

		public DateEditor(DateFormat dateFormat, boolean allowEmpty) {

			super(dateFormat, allowEmpty);
			// TODO Auto-generated constructor stub
			}
			@Override
			public void setAsText(String text) throws IllegalArgumentException {
				try {
					if(!Utils.isEmpty(text)){
						DateFormat df = new SimpleDateFormat(SiapConfigProperties.getValue("dateFormat.small"));
						setValue(df.parse(text));
					}
				} catch (Exception e) {
					setValue(null);
				}
		    }
		    @Override
		    public String getAsText() {
		    	if(((Date) getValue()) == null)
		    		return "";

		    	DateFormat formatter = new SimpleDateFormat("dd/MM/yyyy");
		        return ((String) formatter.format(getValue()));
		    }
	}

	//EDITOR
	private class ProfiloEditor extends PropertyEditorSupport {
			@Override
			public void setAsText(String text) throws IllegalArgumentException {
				try {
					if(!Utils.isEmpty(text)){
						Profilo profilo = profiloService.getProfilo(Integer.valueOf(text));
						if(profilo.getId()==null)setValue(null);
						else setValue(profilo);
					}
				} catch (Exception e) {
					setValue(null);
				}
		    }
		    @Override
		    public String getAsText() {
		    	if((Profilo) getValue() == null ||((Profilo) getValue()).getId()==null)
		    		return "";
		    	String id = ((Profilo) getValue()).getId().toString();
		        return id;
		    }
	}

	@RequestMapping(value = "/utente", method = {RequestMethod.GET,RequestMethod.POST})
	public ModelAndView redirectToAnagraficaUtente(@RequestParam(value = "id", required = false) String id,
			HttpSession session,
			ModelAndView mav) {

		try{
			Utente Utente = null;

			if(id == null && session.getAttribute("UtenteSelezionata") != null){
				Utente = (Utente)session.getAttribute("UtenteSelezionata");
			}else if(id != null){
				if(id != ""){
					Utente = UtenteService.getUtente(Integer.parseInt(id));
				}else if(id == ""){
					Utente = (Utente) session.getAttribute("utenteLoggato");
				}
				mav.addObject("UtenteSelezionata", Utente);
			}

			if(Utente == null){
				mav.addObject("result", ResultBean.writeMessageByKey(ResultBean.ERROR, "Utente.storico.error"));
				mav.setViewName(MappingMavController.STORICO_CARRIERA_JSP);

			}

			mav.setViewName("/anagraficaUtente");

		} catch (Exception e) {
			mav.addObject("result", ResultBean.writeMessageByKey(ResultBean.ERROR, "error.generic"));
			log.error("UtenteController - metodo redirectToAnagraficaUtente: ",e);
		}	

		return mav;
	}

}

 

@Service

Con l’annotazione @Service viene definita la classe di servizio con la logica di business, iniettata nel controller o in altri strati della nostra web application. Tale classe implementa una interfaccia. In tale strato è possibile definire la gestione delle transazioni, sia mappando le regole sul file spring-orm.xml, come visto nella Parte1, che inserendo sulla classe di servizio (gestione globale della transazione) e sui singoli metodi (gestione locale della transazione) l’annotazione @Transactional (definendo il tipo di propagazione, come avviene negli EJB). In tale classe vengono “iniettati” anche i DAO per la comunicazione con il database o altre classi di servizio (annotazione @Autowired).

package com.yourDomain.service;

import com.yourDomain.model.Utente;
import java.util.Map;

public interface UtenteService {
	Utente getUtenteByCodiceFiscaleAndEmail (String codiceFiscale, String email) throws Exception;
	boolean updateUtente (Utente Utente) throws Exception;
	public Map<String,String> getContestiAndOperazioniUtente (Utente Utente) throws Exception;
}


package it.beniculturali.siapweb.service.impl;

import it.beniculturali.siapweb.dao.ContestoDao;
import it.beniculturali.siapweb.dao.UtenteDao;
import it.beniculturali.siapweb.model.Contesto;
import it.beniculturali.siapweb.model.Utente;
import it.beniculturali.siapweb.service.UtenteService;
import it.beniculturali.siapweb.utilities.Utils;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@Service("utenteService")
@Transactional(propagation=Propagation.REQUIRED, readOnly=true)
public class UtenteServiceImpl implements UtenteService {

	protected static Logger log = Logger.getLogger(UtenteServiceImpl.class);

	public UtenteServiceImpl(){

	}

	@Autowired
	private ContestoDao contestoDao;

	@Autowired
	private UtenteDao utenteDao;

	@Override
	/**
	 * metodo che ritorna la utente con un dato codice fiscale e una email
	 *
	 * @param codiceFiscale, email
	 * @return Utente
	 *
	 */
	public Utente getUtenteByCodiceFiscaleAndEmail (String codiceFiscale, String email) throws Exception{
		log.debug("UtenteServiceImpl - getUtenteByCodiceFiscaleAndEmail: inizio metodo");
		Utente utente = utenteDao.getUtenteByCodiceFiscaleAndEmail(codiceFiscale, email);
		log.debug("UtenteServiceImpl - getUtenteByCodiceFiscaleAndEmail: fine metodo");
		return utente;
	}

	/**
	 * metodo che aggiorna i dati di una determinata utente
	 *
	 * @param codiceFiscale, email
	 * @return Utente
	 *
	 */
	@Override
	@Transactional(propagation=Propagation.REQUIRED, readOnly=false)
	public boolean updateUtente(Utente utente) throws Exception {
		log.debug("UtenteServiceImpl - updateUtente: inizio metodo");

			boolean result = false;
			log.debug("Aggiornamento dei dati della utente con id."+utente.getId());
			result = utenteDao.updateUtente(utente);

		log.debug("UtenteServiceImpl - updateUtente: fine metodo");
		return result;
	}

	/**
	 * metodo che ritorna la mappa dei contesti di un utente con relative operazioni permesse
	 *
	 * @param utente
	 * @return
	 * @throws Exception
	 */
	@Override
	public Map<String,String> getContestiAndOperazioniUtente(Utente utente) throws Exception {
		Map<String,String> contestiUtenteMap = new HashMap<String, String>();
		log.debug("UtenteServiceImpl - getContestiAndOperazioniUtente: inizio metodo");
		try{

			List<Contesto> contestiUtente = contestoDao.getContestiAndOperazioniUtente(utente);

			//costruisco la lista delle operazioni per ogni contesto

			if(contestiUtente!=null){

				for(Contesto contestoUtente : contestiUtente){

					//costruisco la mappa con le operazioni permesse dell'utente loggato
					contestiUtenteMap.put(contestoUtente.getId().toString(), Utils.convertListStringInSingleString(contestoUtente.getTipiOperazioniUtente(),","));
				}
			}

		}
		catch(Exception e){
			log.error("ContestoDaoImpl - getContestiAndOperazioniUtente: ",e);
		}
		log.debug("UtenteServiceImpl - getContestiAndOperazioniUtente: fine metodo");
		return contestiUtenteMap;
	}

}

 

@Repository

L’annotazione @Repository definisce le classi dello strato di persistenza, ossia i cosiddetti DAO (Data Access Object). Anche qui le classi implementano una interfaccia, visto che verranno “iniettate” nello strato di servizio (con annotazione @Autowired). Qui viene iniettato anche il SessionFactory (o l’HibernateTemplate), il manager che ci permetterà di eseguire le operazioni CRUD sulla basedati definita e configurata nella Parte1 di questo tutorial.

package com.yourDomain.dao;

import com.yourDomain.model.GruppoOperativo;
import com.yourDomain.model.Utente;

import java.util.List;

public interface UtenteDao {

	List<Utente> getUtenti() throws Exception;
	Utente getUtenteByCodiceFiscaleAndEmail (String codiceFiscale, String email) throws Exception;
	boolean isUtenteRegistrato (String codiceFiscale, String email, Integer idUtente) throws Exception;
	Utente getUtente (Integer idUtente) throws Exception;
	public int countUtentiForGruppoOperativo (GruppoOperativo gruppoOperativo) throws Exception;
	boolean saveUtente(Utente nuovoUtente) throws Exception;
	boolean updateUtente(Utente risorsa) throws Exception;

}

 

package com.yourDomain.dao.impl;

import com.yourDomain.dao.UtenteDao;
import com.yourDomain.model.GruppoOperativo;
import com.yourDomain.model.Utente;

import java.util.Date;
import java.util.List;

import org.apache.log4j.Logger;
import org.hibernate.Criteria;
import org.hibernate.FetchMode;
import org.hibernate.Hibernate;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.CriteriaSpecification;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@Repository("utenteDao")
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
public class UtenteDaoImpl implements UtenteDao {

	@Autowired
	SessionFactory sessionFactory;

	protected static Logger log = Logger.getLogger(UtenteDaoImpl.class);

	public UtenteDaoImpl() {

	}

	/**
	 * metodo che ritorna la lista degli utenti
	 *
	 * @return List<Utente>
	 */
	@SuppressWarnings("unchecked")
	@Override
	public List<Utente> getUtenti() {
		Session session = sessionFactory.getCurrentSession();
		Query query = session.getNamedQuery("Utente.findAll");
		return query.list();
	}

	/**
	 * metodo che ritorna l'utente con un dato codice fiscale e una email
	 *
	 * @param codiceFiscale
	 *            , email
	 * @return Utente
	 *
	 */
	@Override
	public Utente getUtenteByCodiceFiscaleAndEmail (String codiceFiscale, String email) throws Exception{
		Session session = sessionFactory.getCurrentSession();
		Query query = session.getNamedQuery("Utente.findByCodiceFiscaleAndEmail")
			.setString("codiceFiscale", codiceFiscale)
			.setString("email", email);
		return (Utente)query.uniqueResult();
	}

	/**
	 * metodo che ci dice se c'è un utente registrato con un dato codice fiscale o email
	 *
	 * @param codiceFiscale
	 *            , email
	 * @return Utente
	 *
	 */
	@SuppressWarnings("unchecked")
	@Override
	public boolean isUtenteRegistrato (String codiceFiscale, String email, Integer idUtente) throws Exception{
		Session session = sessionFactory.getCurrentSession();
		Criteria criteria = session.createCriteria(Utente.class);
		criteria.add(Restrictions.isNull("dataCancellazione"));

		Criterion criterion1 = Restrictions.eq("codiceFiscale", codiceFiscale).ignoreCase();
		Criterion criterion2 = Restrictions.eq("email", email).ignoreCase();

		Criterion ORcriterion	= Restrictions.or(criterion1, criterion2);
		criteria.add(ORcriterion);

		if(idUtente!=null){

			criteria.add(Restrictions.ne("id",idUtente));
		}

		List<Utente> utentiRegistrati = criteria.list();

		return utentiRegistrati!=null&&utentiRegistrati.size()!=0?true:false;
	}

	/**
	 * metodo che ritorna un utente corrispondente ad un determinato id
	 *
	 * @param idUtente
	 *
	 */
	@Override
	public Utente getUtente(Integer idUtente) throws Exception {
		Session session = sessionFactory.getCurrentSession();
		Query query = session.getNamedQuery("Utente.findById")
			.setInteger("idUtente", idUtente);
		return (Utente)query.uniqueResult();
	}

	/**
	 *  metodo che conta quanti utenti sono associati a quel gruppo operativo
	 *
	 * @param gruppoOperativo
	 * @return
	 * @throws Exception
	 */
	@Override
	public int countUtentiForGruppoOperativo (GruppoOperativo gruppoOperativo) throws Exception{
		Session session = sessionFactory.getCurrentSession();
		Criteria criteria = session.createCriteria(Utente.class)
		.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY)
	    .setFetchMode("gruppiOperativi", FetchMode.JOIN);  

		criteria.createAlias("gruppiOperativi", "g",CriteriaSpecification.INNER_JOIN);
		criteria.add(Restrictions.eq("g.id", gruppoOperativo.getId()));
		criteria.add(Restrictions.isNull("dataCancellazione"));

		Number count = (Number) criteria.setProjection(Projections.rowCount()).uniqueResult();

		return count!=null?count.intValue():0;

	}

	/**
	 * metodo che aggiorna i dati di una determinata utente
	 *
	 * @param idUtente
	 *
	 */
	@Override
	@Transactional(propagation=Propagation.REQUIRED, readOnly=false)
	public boolean updateUtente(Utente utente) throws Exception {

		try{

			Session session = sessionFactory.getCurrentSession();

			utente.setDataModifica(new Date());
		    session.clear();
			session.update(utente);
			session.flush();
			//session.evict(utente);
		}
		catch(Exception e){
			log.error("UtenteDaoImpl - updateUtente: ",e);
			return false;
		}
		return true;
	}

	/**
	 * metodo per il salvataggio di una nuova utente
	 *
	 * @param utente
	 * @return
	 * @throws Exception
	 */
	@Override
	@Transactional(propagation=Propagation.REQUIRED, readOnly=false)
	public boolean saveUtente(Utente nuovoUtente) throws Exception {

		try{
			Session session = sessionFactory.getCurrentSession();
			session.clear();
			session.save(nuovoUtente);
			session.flush();
			//session.evict(nuovoUtente);
		}
		catch(Exception e){
			log.error("UtenteDaoImpl - saveUtente: ",e);
			return false;
		}
		return true;
	}
}
Spring3_SampleAnnotations
Titolo: Spring3_SampleAnnotations (0 click)
Etichetta:
Filename: spring3_sampleannotation.zip
Dimensione: 8 kB