liferay: esporre web services custom
DESCRIPTION
Nel corso di quest’articolo vedremo come sia possibile sfruttare il Service Builder (Liferay Inc., 2014) per creare dei servizi da esporre all’esterno non direttamente collegati all’entità gestiste dallo specifico plugin. Quello che vogliamo ottenere è quindi un servizio personalizzato chiamato Custom Users Service disponibile pubblicamente e che sfrutti i servizi core del portale. In questo particolare scenario vogliamo fare in modo che il servizio Custom Users Service, esponga un metodo che ritorni al consumer del servizio, la lista di utenti taggati con un determinato tag (Liferay Inc., 2014).TRANSCRIPT
Antonio Musarra's Blog Document Revision:1.0 The ideal solution for a problem Blog: http://www.dontesta.it
LinkedIn: http://it.linkedin.com/in/amusarra Google+: https://plus.google.com/+AntonioMusarra Mail: [email protected]
01/09/14 1 This document is issued with license Creative Commons Attribution-‐NonCommercial-‐ShareAlike
Liferay: Esporre Web Services Custom Nel momento i cui cerchiamo di spingerci oltre a quello che strumenti di
sviluppo ci offrono di “default”, è quasi certo che incontreremo qualche difficoltà ad andare avanti e molto spesso la difficoltà è dovuta alla disponibilità di scarsa documentazione.
Nell’ambito del Service Layers, la piattaforma di sviluppo di Liferay consente in modo semplice è veloce di generare tutto lo strato dei servizi, sia locali sia remoti (Liferay Inc., 2014) per i plugin (Liferay Inc., 2014) da noi creati.
Nel corso di quest’articolo vedremo come sia possibile sfruttare il Service Builder (Liferay Inc., 2014) per creare dei servizi da esporre all’esterno non direttamente collegati all’entità gestiste dallo specifico plugin. L’articolo è rivolto a tutti, ma sicuramente a tranne maggior beneficio saranno coloro con un minimo di confidenza con l’SDK1 (Liferay Inc., 2014) di Liferay e in particolar modo con il Service Builder. In allegato all’articolo sarà disponibile l’intero progetto di modo che possiate analizzarlo con tutta la calma necessaria e fare i vostri test.
Il progetto sviluppato per quest’articolo è stato realizzato e testato su di un’installazione Liferay 6.2 Community Edition e Liferay 6.2 Enterprise Edition.
1. Introduzione In genere le portlet sviluppate sono data-‐driven e il Service Builder è il
nostro migliore “amico” perché fa al posto nostro tutto il lavoro di:
• Generare model, persistenza e Service Layers; • Generare le interfacce locali e remote; • Generare la configurazione di Hibernate2 e Spring3; • Generare i finder method per le entità.
Per un attimo ipotizziamo l’esigenza di voler esporre (dalla nostra portlet)
un servizio verso l’esterno, accessibile tramite interfacce di tipo JSON4 e SOAP5, che sia un raggruppamento di servizi esistenti. Lo schema mostrato in Figura 1 dovrebbe aiutare a capir meglio l’idea espressa.
1 Un software development kit (in acronimo SDK, traducibile in italiano come "pacchetto di sviluppo per applicazioni"), in informatica, indica genericamente un insieme di strumenti per lo sviluppo e la documentazione di software. 2 E’ una piattaforma middleware open source per lo sviluppo di applicazioni Java, attraverso l'appoggio al relativo framework, che fornisce un servizio di Object-‐relational mapping (ORM) ovvero gestisce la persistenza dei dati sul database attraverso la rappresentazione e il mantenimento su database relazionale di un sistema di oggetti Java. 3 In informatica Spring è un framework open source per lo sviluppo di applicazioni su piattaforma Java. 4 JSON, acronimo di JavaScript Object Notation, è un formato adatto per lo scambio dei dati in applicazioni client-‐server. 5 In informatica SOAP (inizialmente acronimo di Simple Object Access Protocol) è un protocollo leggero per lo scambio di messaggi tra componenti software, tipicamente nella forma di componentistica software. La parola object manifesta che l'uso del protocollo dovrebbe effettuarsi secondo il paradigma della programmazione orientata agli oggetti.
Antonio Musarra's Blog Document Revision:1.0 The ideal solution for a problem Blog: http://www.dontesta.it
LinkedIn: http://it.linkedin.com/in/amusarra Google+: https://plus.google.com/+AntonioMusarra Mail: [email protected]
01/09/14 2 This document is issued with license Creative Commons Attribution-‐NonCommercial-‐ShareAlike
Figura 1 Portlet che espone un servizio custom.
Quello che vogliamo ottenere è quindi un servizio personalizzato chiamato Custom Users Service disponibile pubblicamente e che sfrutti i servizi core del portale. In questo particolare scenario vogliamo fare in modo che il servizio Custom Users Service, esponga un metodo che ritorni al consumer del servizio, la lista di utenti taggati con un determinato tag (Liferay Inc., 2014).
Perché realizzare un servizio del genere? Alcune delle finalità principali possono essere:
• Realizzare un servizio che implementi un requisito che non è
possibile soddisfare attraverso un servizio core del portale; • Comporre più servizi core del portale per realizzare un servizio
unico che sia di più facile utilizzo per il consumer;
Nello schema di Figura 2 è rappresentato il servizio Custom Users Service messo a disposizione dall’ipotetica portlet Shirus Labs Example Services che poi andremo a realizzare.
Public Network
Liferay System
My Custom Portlet
Service Layer
Custom User Service
Liferay Core Services
Services Client
Version 1.0
Antonio Musarra's Blog Document Revision:1.0 The ideal solution for a problem Blog: http://www.dontesta.it
LinkedIn: http://it.linkedin.com/in/amusarra Google+: https://plus.google.com/+AntonioMusarra Mail: [email protected]
01/09/14 3 This document is issued with license Creative Commons Attribution-‐NonCommercial-‐ShareAlike
Figura 2 Dettaglio del servizio Custom Users Service.
2. Qual è il trucco? Il più delle volte (oserei dire sempre) il Service Builder è utilizzato per
generare il codice e le configurazioni (Spring e Hibernate) necessarie per le operazioni di tipo CRUD6 sull’entità gestite dalla portlet, ma nel nostro caso la parte Hibernate è da escludere. In Figura 3 nella sezione A è mostrato il tool del Service Builder in condizioni “normali”, al contrario, nella sezione B, manca la parte Hibernate. Il trucco è proprio nella sezione B, in altre parole, nella definizione dell’entità descritta sul file service.xml, occorre specificare esclusivamente il nome dell’entità e la disponibilità del tipo di servizio: locale, remoto o entrambe. Nel Listato 1 è riportato l’esempio della configurazione del Service Builder per la portlet del nostro esempio.
6 La tavola CRUD (Create, Read, Update, Delete) associa utenti e risorse, o loro aggregazioni, di un sistema informatico indicando i relativi privilegi di accesso.
Antonio Musarra's Blog Document Revision:1.0 The ideal solution for a problem Blog: http://www.dontesta.it
LinkedIn: http://it.linkedin.com/in/amusarra Google+: https://plus.google.com/+AntonioMusarra Mail: [email protected]
01/09/14 4 This document is issued with license Creative Commons Attribution-‐NonCommercial-‐ShareAlike
Figura 3 Il Service Builder Tool sopra Spring & Hibernate che genera codice e configurazione.
Un’entità rappresenta solitamente una business facade7 e una tabella sulla
base di dati. Se l’entità non ha nessuna colonna definita, allora l’entità rappresenta semplicemente una business facade. In queste condizioni (così come mostrato nella configurazione del Listato 1) il Service Builder genererà una business facade POJO 8 vuota. Le successive esecuzioni del Service Builder verificheranno l’esistenza della business facade, qualora esistesse e con eventuali nuovi metodi, il Service Builder si preoccuperà di aggiornare anche i wrappers SOAP.
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE service-builder PUBLIC "-//Liferay//DTD Service Builder 6.2.0//EN" "http://www.liferay.com/dtd/liferay-service-builder_6_2_0.dtd"> <service-builder package-path="it.dontesta.shirus.labs.liferay.ws.example"> <author>amusarra</author> <namespace>shirus_labs_liferay_ws_example</namespace> <entity name="CustomUsers" local-service="true" remote-service="true"> </entity> <exceptions> <exception>UsersNotFound</exception> </exceptions> </service-builder> Listato 1 Configurazione del Service Builder sul file service.xml.
Per l’entità CustomUsers definita nel file di configurazione service.xml indichiamo al Service Builder di generare le interfacce remote e le interfacce locali, inoltre chiediamo di generare un tipo di eccezione 7 Letteralmente façade significa "facciata", ed infatti nella programmazione ad oggetti indica un oggetto che permette, attraverso un'interfaccia più semplice, l'accesso a sottosistemi che espongono interfacce complesse e molto diverse tra loro, nonché a blocchi di codice complessi. 8 Plain Old Java Object (POJO) ovvero un semplice JavaBean costituito dalle proprietà e i rispettivi getters/setter (quando serve).
Portlet Code
Service Builder
Spring Hibernate
Data Base
Portlet Code
Service Builder
Spring Hibernate
Version 1.0
Sezione ASe
zion
e B
Antonio Musarra's Blog Document Revision:1.0 The ideal solution for a problem Blog: http://www.dontesta.it
LinkedIn: http://it.linkedin.com/in/amusarra Google+: https://plus.google.com/+AntonioMusarra Mail: [email protected]
01/09/14 5 This document is issued with license Creative Commons Attribution-‐NonCommercial-‐ShareAlike
UsersNotFoundException che utilizzeremo in seguito per sollevare un eccezione nel caso in cui la ricerca non produca nessun risultato.
Qualora la vostra curiosità vada oltre, consiglio di consultare il DTD9 che si riferisce al Service Builder. Dalla lettura del DTD potete scoprire un bel po’ d’informazioni che potrebbero tornare utili.
Eseguito il Service Builder, siamo pronti per l’implementazione del servizio Custom Users Service.
3. Implementazione del servizio Custom Users Service Qualcuno di voi saprà certamente che, implementare il servizio Custom
Users Services significa scrivere l’implementazione dell’interfaccia, locale (Liferay Inc., 2014) e remota (LIferay Inc., 2014), ed esattamente:
• CustomUsersLocalService: Interfaccia locale di cui implementare
il metodo getUsersCompanyByTagName; • CustomUsersService: Interfaccia remota di cui implementare il
metodo getUsersCompanyByTagName. Servizio Operation & Descrizione Parametri del servizio CustomUsersService getUsersCompanyByTagName.
Quest’operazione del servizio restituisce sulla base dei parametri in input una lista di utenti del portale che soddisfano le condizioni di ricerca.
Param Type Descrizione companyId Long CompanyId
d’interesse per la ricerca degli utenti.
tagName String Nome del tag assegnato agli utenti che si vuole ricercare (esempio: CRM, Imported, etc…).
userStatus Int Stato dell’utente (esempio: Attivo, disattivo etc…).
start Int Start index end Int End index
Tabella 1 Descrizione del servizio Custom Users Service da implementare.
Dopo l’esecuzione del Service Builder, avremo a nostra disposizione una
serie di classi e interfacce generate a supporto del nostro servizio. Il cuore del servizio è la classe CustomUsersLocalServiceImpl, all’intero della quale dovrà essere codificata la logica di business del nostro servizio. In Figura 4 è mostrato il class diagram delle classi e interfacce generate dal Service Builder, utile per
9 Il Document Type Definition (definizione del tipo di documento) è uno strumento utilizzato dai programmatori il cui scopo è quello di definire le componenti ammesse nella costruzione di un documento XML.
Antonio Musarra's Blog Document Revision:1.0 The ideal solution for a problem Blog: http://www.dontesta.it
LinkedIn: http://it.linkedin.com/in/amusarra Google+: https://plus.google.com/+AntonioMusarra Mail: [email protected]
01/09/14 6 This document is issued with license Creative Commons Attribution-‐NonCommercial-‐ShareAlike
capire le relazioni tra i vari elementi. Per questioni di spazio e chiarezza, il diagramma riporta solo la parte local del servizio. Com’è possibile notare dal diagramma di Figura 4, la classe CustomUsersLocalServiceImpl e l’interfaccia CustomUsersLocalService presentano già il metodo da implementare, questo perché il diagramma è stato generato dopo l’implementazione del metodo e la successiva esecuzione del Service Builder, inoltre è stato anche aggiunto il metodo statico alla classe CustomUsersLocalServiceUtil.
Figura 4 Class diagram interfacce e classi (parte locale) generate dal Service Builder.
Ricordiamo che dal metodo da implementare, desideriamo ottenere l’elenco degli utenti che hanno un determinato tag, apposto per esempio durante l’importazione (Musarra, 2014) di un insieme di utenti provenienti da qualche altro sistema (CRM, ERP, etc…).
L’implementazione del metodo getUsersCompanyByTagName, è abbastanza semplice, si traduce in pochissime righe di codice. Per soddisfare il requisito, ho preferito utilizzare il servizio di ricerca del SearchEngine di Liferay, in questo modo la risposta del servizio sarà anche più performante. La lista degli utenti che il metodo restituirà (sotto forma di array) sarà del tipo com.liferay.portal.model.UserSoap, questo perché vogliamo che il servizio sia disponibile tramite SOAP, quindi dobbiamo utilizzare il wrapper SOAP dell’entità User che Liferay ci mette a disposizione, e inoltre eviterà problemi di serializzazione e deserializzazione.
Il Listato 2 riporta l’implementazione del metodo getUsersCompanyByTagName per della classe CustomUsersLocalServiceImpl,
Antonio Musarra's Blog Document Revision:1.0 The ideal solution for a problem Blog: http://www.dontesta.it
LinkedIn: http://it.linkedin.com/in/amusarra Google+: https://plus.google.com/+AntonioMusarra Mail: [email protected]
01/09/14 7 This document is issued with license Creative Commons Attribution-‐NonCommercial-‐ShareAlike
mentre il Listato 3 riporta l’implementazione del metodo della classe CustomUsersServiceImpl. public com.liferay.portal.model.UserSoap[] getUsersCompanyByTagName(long companyId, String tagName, Integer userStatus, int start, int end) throws PortalException, SystemException { SearchContext searchContext = new SearchContext(); searchContext.setCompanyId(companyId); searchContext.setEntryClassNames(new String[] { User.class.getName() }); searchContext.setStart(start); searchContext.setEnd(end); searchContext.setAndSearch(true); BooleanQuery searchQuery = BooleanQueryFactoryUtil.create(searchContext); searchQuery.addRequiredTerm(Field.COMPANY_ID, companyId); searchQuery.addRequiredTerm(Field.ASSET_TAG_NAMES, tagName); searchQuery.addRequiredTerm(Field.STATUS, userStatus); Hits hits = SearchEngineUtil.search(searchContext, searchQuery); Tuple tuple = UsersAdminUtil.getUsers(hits); if (hits.getLength() == 0) { throw new UsersNotFoundException("No data available for the search parameters you set."); } List<User> results = (List<User>) tuple.getObject(0); UserSoap[] userSoap = new UserSoap[results.size()]; for (int i = 0; i < userSoap.length; i++) { userSoap[i] = UserSoap.toSoapModel(results.get(i)); } return userSoap; } Listato 2 Implementazione del metodo getUsersCompanyByTagName per l’interfaccia locale.
public UserSoap[] getUsersCompanyByTagName(long companyId, String tagName, Integer userStatus, int start, int end) throws PortalException, SystemException { return CustomUsersLocalServiceUtil.getUsersCompanyByTagName(companyId, tagName, userStatus, start, end); } Listato 3 Implementazione del metodo getUsersCompanyByTagName per l’interfaccia remota.
Antonio Musarra's Blog Document Revision:1.0 The ideal solution for a problem Blog: http://www.dontesta.it
LinkedIn: http://it.linkedin.com/in/amusarra Google+: https://plus.google.com/+AntonioMusarra Mail: [email protected]
01/09/14 8 This document is issued with license Creative Commons Attribution-‐NonCommercial-‐ShareAlike
4. Test del servizio Custom Users Service Adesso che il servizio è bello che confezionato e installato sul portale di
Liferay, è possibile accedervi da remoto tramite l’interfaccia SOAP e JSON. Gli end point del servizio esposto dalla portlet sono:
• SOAP o http://[host]:[port]/
api/axis/Plugin_shirus_labs_liferay_ws_example_CustomUsersService
• JSON o http://[host]:[port]/api/jsonws/ShirusLabsExampleServices-‐
portlet.customusers/get-‐users-‐company-‐by-‐tag-‐name
Se volessimo eseguire un test veloce del servizio SOAP, è più che sufficiente prendere nota dell’end point e utilizzare il tool soapUI. In Figura 5 è mostrato un test del servizio eseguito proprio con soapUI.
Figura 5 Test del servizio tramite il tool soapUI.
Tramite ant è possibile senza alcuno sforzo generare un client Java da poter utilizzare all’interno della vostra applicazione da dover integrare. Il task ant che assolve questo compito si chiama build-‐client. L’esecuzione del task genererà il jar (che nel nostro caso prender il nome di ShirusLabsExampleServices-‐portlet-‐
Antonio Musarra's Blog Document Revision:1.0 The ideal solution for a problem Blog: http://www.dontesta.it
LinkedIn: http://it.linkedin.com/in/amusarra Google+: https://plus.google.com/+AntonioMusarra Mail: [email protected]
01/09/14 9 This document is issued with license Creative Commons Attribution-‐NonCommercial-‐ShareAlike
client.jar) che potrete utilizzare per connettervi alla servizio dalla vostra applicazione. Il jar dopo il build sarà posizione dentro la directory docroot/WEB-‐INF/client del progetto. Nel Listato 4 le poche righe di codice mostrano come sia semplice e immediato accedere al servizio. public class TestGetUsersCompanyByTagNameService { static final String LIFERAY_USER_NAME = (System.getProperty("username") != null) ? System .getProperty("username") : "[email protected]"; static final String LIFERAY_USER_PASSWORD = (System.getProperty("password") != null) ? System .getProperty("username") : "test"; static final String USER_SERVICE = "Portal_UserService"; /** * */ public TestGetUsersCompanyByTagNameService() { } public static void main(String[] args) throws Exception, SystemException { CustomUsersServiceSoapServiceLocator locator = new CustomUsersServiceSoapServiceLocator(); CustomUsersServiceSoap customUsersService = locator .getPlugin_shirus_labs_liferay_ws_example_CustomUsersService(); ((Plugin_shirus_labs_liferay_ws_example_CustomUsersServiceSoapBindingStub) customUsersService) .setUsername(LIFERAY_USER_NAME); ((Plugin_shirus_labs_liferay_ws_example_CustomUsersServiceSoapBindingStub) customUsersService) .setPassword(LIFERAY_USER_PASSWORD); UserSoap[] listUsers = customUsersService.getUsersCompanyByTagName( 10157l, "crm", 0, 0, 10); for (UserSoap userSoap : listUsers) { System.out.println("User ScreeName : " + userSoap.getScreenName()); } } } Listato 4 Esempio di accesso al servizio SOAP tramite il client generato dall’SDK.
Per quanto riguarda il servizio JSON, è possibile eseguire un semplice test
tramite il tool curl o direttamente via browser attraverso l’opportuna URL in stile RESTful. Il Listato 5 mostra l’esempio di accesso al servizio JSON tramite curl.
Antonio Musarra's Blog Document Revision:1.0 The ideal solution for a problem Blog: http://www.dontesta.it
LinkedIn: http://it.linkedin.com/in/amusarra Google+: https://plus.google.com/+AntonioMusarra Mail: [email protected]
01/09/14 10 This document is issued with license Creative Commons Attribution-‐NonCommercial-‐ShareAlike
curl http://localhost:8080/api/jsonws/ShirusLabsExampleServices-portlet.customusers/get-users-company-by-tag-name \ -u [email protected]:test \ -d companyId=10157 \ -d tagName='crm' \ -d userStatus=0 \ -d start=0 \ -d end=3 Listato 5 Test del servizio JSON tramite curl.
Per ottenere lo stesso risultato del Listato 5 via URL, è sufficiente specificare sul browser la seguente URL: http://localhost:8080/api/jsonws/ShirusLabsExampleServices-‐portlet.customusers/get-‐users-‐company-‐by-‐tag-‐name/company-‐id/10157/tag-‐name/crm/user-‐status/0/start/0/end/3 per ottenere un risultato simile a quello mostrato nel Listato 6.
[{ "agreedToTermsOfUse": false, "comments": "", "companyId": 10157, "contactId": 12102, "createDate": 1392734987656, "defaultUser": false, "digest": "", "emailAddress": "[email protected]", "emailAddressVerified": false, "facebookId": 0, "failedLoginAttempts": 1, "firstName": "Antonio", "graceLoginCount": 0, "greeting": "Welcome Antonio Musarra!", "jobTitle": "IT Architect", "languageId": "en_US", "lastFailedLoginDate": 1393625990890, "lastLoginDate": null, "lastLoginIP": "", "lastName": "Musarra", "ldapServerId": -1, "lockout": false, "lockoutDate": null, "loginDate": null, "loginIP": "", "middleName": "", "modifiedDate": 1392735025523, "openId": "", "password": "AAAAoAAB9ABtc0QtttzLGtK931wwN957psU8maq0Jxu1Gais", "passwordEncrypted": true, "passwordModifiedDate": 1392734988948, "passwordReset": true, "portraitId": 0, "primaryKey": 12101, "reminderQueryAnswer": "", "reminderQueryQuestion": "", "screenName": "amusarra", "status": 0, "timeZoneId": "UTC", "userId": 12101, "uuid": "baf84339-315b-426c-838b-e58dc806bec8" }] Listato 6 Esempio di risposta del servizio JSON.
Antonio Musarra's Blog Document Revision:1.0 The ideal solution for a problem Blog: http://www.dontesta.it
LinkedIn: http://it.linkedin.com/in/amusarra Google+: https://plus.google.com/+AntonioMusarra Mail: [email protected]
01/09/14 11 This document is issued with license Creative Commons Attribution-‐NonCommercial-‐ShareAlike
5. Conclusioni Abbiamo fatto un breve excursus su come sia possibile e semplice
realizzare dei servizi custom da esporre verso l’esterno che utilizzano servizi del portale già preesistenti. In questo modo è fattibile realizzare in poco tempo dei servizi anche complessi come composizione di altri servizi e rendere in questo modo l’interfaccia verso il sistema client molto più semplice da utilizzare e snella.
Un piccolo favore Per quanta passione e soddisfazione possa portare a scrivere contenuti di questo tipo, ciò comporta anche un grande dispendio di tempo e risorse.
Se i contenuti gratuiti offerti in quest’articolo sono stati utili per te, potresti
restituirmi il favore condividendo l’articolo sui canali di social network, in modo da rendere utile anche per gli altri ciò che ho scritto. Clicca sulle immagini per condividere:
Vi aspetto su Antonio Musarra’s Blog -‐ The ideal solution for a problem (http://www.dontesta.it/blog/).
L’articolo è pubblicato sul blog CoseNonJaviste all’indirizzo http://goo.gl/WyiVV2
Antonio Musarra's Blog Document Revision:1.0 The ideal solution for a problem Blog: http://www.dontesta.it
LinkedIn: http://it.linkedin.com/in/amusarra Google+: https://plus.google.com/+AntonioMusarra Mail: [email protected]
01/09/14 12 This document is issued with license Creative Commons Attribution-‐NonCommercial-‐ShareAlike
Bibliography Liferay Inc. (2014). Generating Your Service Layer. Tratto da Liferay Portal 6.2 Developer's Guide: http://www.liferay.com/it/documentation/liferay-‐portal/6.2/development/-‐/ai/generating-‐your-‐service-‐layer-‐liferay-‐portal-‐6-‐2-‐dev-‐guide-‐04-‐en Liferay Inc. (2014). Plugin Management. Tratto da Using Liferay Portal 6.2: http://www.liferay.com/it/documentation/liferay-‐portal/6.2/user-‐guide/-‐/ai/plugin-‐management-‐liferay-‐portal-‐6-‐2-‐user-‐guide-‐14-‐en Liferay Inc. (2014). Tagging and Categorizing Content. Tratto da Using Liferay Portal 6.2: http://www.liferay.com/it/documentation/liferay-‐portal/6.2/user-‐guide/-‐/ai/tagging-‐and-‐categorizing-‐content-‐liferay-‐portal-‐6-‐2-‐user-‐guide-‐06-‐en Liferay Inc. (2014). What is Service Builder? Tratto da Liferay Portal 6.2 Developer's Guide: http://www.liferay.com/it/documentation/liferay-‐portal/6.2/development/-‐/ai/what-‐is-‐service-‐builder-‐liferay-‐portal-‐6-‐2-‐dev-‐guide-‐04-‐en Liferay Inc. (2014). Working with Liferay’s Developer Tools. Tratto da Liferay Portal 6.2 Developer's Guide: http://www.liferay.com/it/documentation/liferay-‐portal/6.2/development/-‐/ai/working-‐with-‐liferays-‐developer-‐tools-‐liferay-‐portal-‐6-‐2-‐dev-‐guide-‐02-‐en Liferay Inc. (2014). Writing Local Service Classes. Tratto da Liferay Portal 6.2 Developer's Guide: http://www.liferay.com/it/documentation/liferay-‐portal/6.2/development/-‐/ai/write-‐local-‐service-‐classes-‐liferay-‐portal-‐6-‐2-‐dev-‐guide-‐04-‐en LIferay Inc. (2014). Writing Remote Service Classes. Tratto da Liferay Portal 6.2 Developer's Guide: http://www.liferay.com/it/documentation/liferay-‐portal/6.2/development/-‐/ai/write-‐remote-‐service-‐classes-‐liferay-‐portal-‐6-‐2-‐dev-‐guide-‐04-‐en Musarra, A. (2014, Jan 01). Liferay Web Services -‐ Come importare utenti da un foglio Excel. Tratto da LinkedIn Corporation: http://www.slideshare.net/amusarra/liferay-‐web-‐services-‐come-‐importare-‐utenti-‐da-‐un-‐foglio-‐excel