Non sapete come realizzare un client SOAP in OSGi Style su Liferay 7?La risposta è il framework Apache CXF installato a bundle e poi OSGi Service Pattern.
Liferay 7: Come realizzare un client SOAP con Apache CXF in OSGi Style
1. Antonio Musarra’s Blog Document Revision: 1.0
The ideal solution for a problem Blog: http://www.dontesta.it/blog
LinkedIn: https://it.linkedin.com/in/amusarra
Twitter: @antonio_musarra
Liferay 7: Come realizzare un client
SOAP con Apache CXF in OSGi Style
L’argomento integrazione è il mio forte e per questo
primo mio vero articolo su Liferay 7 ho deciso di
trattare l’argomento Web Services dal punto di vista
del consumer. Senza dubbio scrivere su questa
tematica significa aprire tanti altri punti da
approfondire e magari con i prossimi articoli.
Qualcuno di voi chiederà a se stesso: ma esistono ancora e sono utilizzati i Web Services
SOAP? La risposta è affermativa, continuate quindi la lettura.
1. Qual è lo scenario e obiettivo
Immaginate il caso che dal vostro portale Liferay abbiate la necessità di dover
integrare i dati dei vostri utenti con informazioni provenienti dal vostro sistema di CRM
(Customer Relationship Management). Il sistema di CRM espone l’accesso ai propri dati
attraverso un interfaccia SOAP, occorre quindi realizzare un client che risiederà sul Liferay
7 e che consenta l’interazione con il CRM.
Figura 1 - Scenario d’integrazione Liferay 7 e CRM via SOAP
Immaginato lo scenario (mostrato in Figura 1), l’obiettivo di questo articolo è quello di
mostrare step-by-step quali sono i componenti che andranno installati sul Liferay 7 e
come ad alto livello è composta l’applicazione che realizza il colloquio via SOAP con il
sistema di CRM all’interno del contesto OSGi di Liferay.
24/07/16 1
Questo documento è rilsciato con licenza Creative Commons
Attribuzione - Non commerciale - Condividi allo stesso modo 3.0 Italia (CC BY-NC-SA 3.0 IT)
2. Antonio Musarra’s Blog Document Revision: 1.0
The ideal solution for a problem Blog: http://www.dontesta.it/blog
LinkedIn: https://it.linkedin.com/in/amusarra
Twitter: @antonio_musarra
Figura 2 - Dettaglio dello scenario d’integrazione tra Liferay 7 e il sistema di CRM
Il diagramma di Figura 2 mostra il dettaglio dello scenario e in particolare la
composizione dell’applicazione CRM Application che consta di tre moduli e il noto
framework Apache CXF che sarà il nostro punto di riferimento per quel che riguarda il
supporto ai Web Service SOAP.
L’applicazione CRM Application è stata già realizzata e disponibile sul Repository OSGi
e Liferay 7 creato in occasione del primo Liferay User Group Italiano a Bologna #LRBO16.
Sul repository git l’applicazione CRM Application corrisponde al modulo webservices-
client.
24/07/16 2
Questo documento è rilsciato con licenza Creative Commons
Attribuzione - Non commerciale - Condividi allo stesso modo 3.0 Italia (CC BY-NC-SA 3.0 IT)
3. Antonio Musarra’s Blog Document Revision: 1.0
The ideal solution for a problem Blog: http://www.dontesta.it/blog
LinkedIn: https://it.linkedin.com/in/amusarra
Twitter: @antonio_musarra
2. Requisiti
Per seguire in modo efficace il contenuto di questo articolo è necessario avere: una
conoscenza base di Liferay 7 e OSGi e la disponibilità dentro la vostra “borsa degli attrezzi”
questi pezzi:
1. Liferay 7.0.1 Community Edition GA2 (installata e in esecuzione) consigliato il bundle
basato su tomcat
2. Git Tools
3. Gradle (versione >= 2.12)
4. Telnet (come installare su Windows, sui sistemi *nix o Unix-like non serve
installazione)
5. Liferay IDE 3.0.1 GA2 (opzionale)
6. Text Editor (opzionale)
7. Apache CXF 3.1.6 (opzionale)
Il primo elemento e l’ultimo sono gli unici requisiti necessari per l’esecuzione
dell’applicazione CRM Application.
Per i novizi di Liferay 7 consiglio la lettura della presentazione OSGi e Liferay 7 che io
(@antonio_musarra) e Jader Francia (@JedJds) abbiamo discusso al primo Liferay User
Group Italiano a Bologna #LRBO16.
3. Installazione del framework Apache CXF
Apache CXF è in genere il punto di riferimento come framework per quel che
riguarda i Web Services. L’applicazione CRM Application adotta Apache CXF come
framework a supporto dello sviluppo del client SOAP, è quindi indispensabile installare
sull’istanza Liferay 7 alcuni dei componenti del framework affinché l’applicazione sia in
grado di funzionare.
Liferay 7 supporta gli standard JAX-WS e JAX-RS tramite l’adozione del framework
Apache CXF (versione 3.0.3) che esporta in parte tramite il bundle che si chiama Liferay
Portal Remote CXF Common, e per vostra curiosità potreste vedere il file bnd dove sono
evidenti gli Export-Package di Apache CXF. Nulla vieta quindi l’utilizzo del bundle di Liferay
che esporta Apache CXF, preferisco però essere indipendente da Liferay per due motivi:
24/07/16 3
Questo documento è rilsciato con licenza Creative Commons
Attribuzione - Non commerciale - Condividi allo stesso modo 3.0 Italia (CC BY-NC-SA 3.0 IT)
4. Antonio Musarra’s Blog Document Revision: 1.0
The ideal solution for a problem Blog: http://www.dontesta.it/blog
LinkedIn: https://it.linkedin.com/in/amusarra
Twitter: @antonio_musarra
utilizzare una versione diversa del framework e possibilmente l’ultima, in questo
caso la versione 3.1.6, Liferay uilizza la vecchia versione 3.0.3. OSGi consente tutto
ciò in modo semplice e agevole.
Liferay 7 non esporta e non utilizza tutto lo stack offerto da Apache CXF, come per
esempio la parte di WS-Security, WS-Policy, etc..
Per maggiorni approfondimenti consiglio la lettura del documento Liferay SOAP e
REST Extender pubblicato sulla LDN.
Ricordate che siamo in ambiente OSGi? Bene, installare Apache CXF significa
eseguire l’installazione dei bundle all’interno di Apache Felix che ricordo essere il container
OSGi scelto dal Liferay.
Per il tipo di client SOAP da realizzare, i bundle di Apache CXF (versione 3.1.6) da
installare sono:
Apache XmlSchema Core (v. 2.2.1)
Apache CXF Core
Apache CXF Runtime JAXB DataBinding
Apache CXF Runtime XML Binding
Apache CXF Runtime SOAP Binding
Apache CXF Runtime Core for WSDL
Apache CXF Runtime Simple Frontend
Apache CXF Runtime JAX-WS Frontend
Apache CXF Runtime HTTP Transport
L’installazione dei nove elementi del framework di Apache CXF può essere eseguita
connettendosi via telnet alla Apache Felix GoGo Shell. Accertato che il portale Liferay 7 sia
in esecuzione, l’installazione del framework Apache CXF può avvenire eseguendo i
comandi mostrati a seguire (in Console 1).
24/07/16 4
Questo documento è rilsciato con licenza Creative Commons
Attribuzione - Non commerciale - Condividi allo stesso modo 3.0 Italia (CC BY-NC-SA 3.0 IT)
5. Antonio Musarra’s Blog Document Revision: 1.0
The ideal solution for a problem Blog: http://www.dontesta.it/blog
LinkedIn: https://it.linkedin.com/in/amusarra
Twitter: @antonio_musarra
$ telnet localhost 11311
g! install
https://repository.apache.org/content/repositories/releases/org/apache/ws/xmlschema/xmlschema-
core/2.2.1/xmlschema-core-2.2.1.jar
g! install https://repository.apache.org/content/repositories/releases/org/apache/cxf/cxf-
core/3.1.6/cxf-core-3.1.6.jar
g! install https://repository.apache.org/content/repositories/releases/org/apache/cxf/cxf-rt-
databinding-jaxb/3.1.6/cxf-rt-databinding-jaxb-3.1.6.jar
g! install https://repository.apache.org/content/repositories/releases/org/apache/cxf/cxf-rt-
bindings-xml/3.1.6/cxf-rt-bindings-xml-3.1.6.jar
g! install https://repository.apache.org/content/repositories/releases/org/apache/cxf/cxf-rt-
bindings-soap/3.1.6/cxf-rt-bindings-soap-3.1.6.jar
g! install https://repository.apache.org/content/repositories/releases/org/apache/cxf/cxf-rt-
wsdl/3.1.6/cxf-rt-wsdl-3.1.6.jar
g! install https://repository.apache.org/content/repositories/releases/org/apache/cxf/cxf-rt-
frontend-simple/3.1.6/cxf-rt-frontend-simple-3.1.6.jar
g! install https://repository.apache.org/content/repositories/releases/org/apache/cxf/cxf-rt-
frontend-jaxws/3.1.6/cxf-rt-frontend-jaxws-3.1.6.jar
g! install https://repository.apache.org/content/repositories/releases/org/apache/cxf/cxf-rt-
transports-http/3.1.6/cxf-rt-transports-http-3.1.6.jar
Console 1 - Installazione del bundle framework Apache CXF 3.1.6
Il comando install esegue l’installazione dei bundle su Apache Felix. Il comando
install accetta come parametro una URI e personalmente preferisco installare i bundle
prelevando gli stessi dal repository maven di Apache, ecco perché ho specificato la URL
per ogni bundle da installare. E’ possibile scaricare prima i bundle e poi eseguire
l’installazione specificando come URI file:///$ABSOLUTE_PATH_BUNDLE_FILE
I bundle che sono stati installati sono tutti nello stato Installed e il comando sulla
gogo shell mostrato a seguire fa vedere lo stato dei bundle appena installati.
g! lb|grep -e "Xml|Apache CXF"
479|Installed | 1|XmlSchema Core (2.2.1)
480|Installed | 1|Apache CXF Core (3.1.6)
481|Installed | 1|Apache CXF Runtime JAXB DataBinding (3.1.6)
482|Installed | 1|Apache CXF Runtime XML Binding (3.1.6)
483|Installed | 1|Apache CXF Runtime SOAP Binding (3.1.6)
484|Installed | 1|Apache CXF Runtime Core for WSDL (3.1.6)
485|Installed | 1|Apache CXF Runtime Simple Frontend (3.1.6)
486|Installed | 1|Apache CXF Runtime JAX-WS Frontend (3.1.6)
487|Installed | 1|Apache CXF Runtime HTTP Transport (3.1.6)
Console 2 - Stato dei bundle Apache CXF installati
Con ben in mente il ciclo di vita delle applicazioni in ambiente OSGi mostrato nella
presentazione OSGi e Liferay 7 (e che riporto in Figura 3), l’obiettivo è quello di avere tutti i
bundle nello stato Active.
24/07/16 5
Questo documento è rilsciato con licenza Creative Commons
Attribuzione - Non commerciale - Condividi allo stesso modo 3.0 Italia (CC BY-NC-SA 3.0 IT)
6. Antonio Musarra’s Blog Document Revision: 1.0
The ideal solution for a problem Blog: http://www.dontesta.it/blog
LinkedIn: https://it.linkedin.com/in/amusarra
Twitter: @antonio_musarra
Figura 3 - Life cycle dei bundle in OSGi
Il bundle XmlSchema Core è quello da cui dipende il bundle Apache CXF Core, il primo
step è quindi quello di avviare lo starting del bundle XmlSchema Core utilizzando il
comando start $idBundle$, così come mostrato a seguire.
g! start 479
g! lb|grep -e "XmlSchema|Apache CXF"
479|Active | 1|XmlSchema Core (2.2.1)
480|Starting | 1|Apache CXF Core (3.1.6)
481|Starting | 1|Apache CXF Runtime JAXB DataBinding (3.1.6)
482|Starting | 1|Apache CXF Runtime XML Binding (3.1.6)
483|Starting | 1|Apache CXF Runtime SOAP Binding (3.1.6)
484|Starting | 1|Apache CXF Runtime Core for WSDL (3.1.6)
485|Starting | 1|Apache CXF Runtime Simple Frontend (3.1.6)
486|Starting | 1|Apache CXF Runtime JAX-WS Frontend (3.1.6)
487|Starting | 1|Apache CXF Runtime HTTP Transport (3.1.6)
Console 3 - Start del bundle XmlSchema Core
Dopo l’esecuzione del comando start per il bundle XmlSchema Core, tutti i bundle
Apache CXF sono passati nello stato Starting. Questo stato è dovuto al fatto che i bundle
di Apache CXF hanno dichiarato nel proprio Manifest File (MANIFEST.MF) una policy di
attivazione del bundle impostata a lazy, questo vuol dire che il passaggio allo stato Active
avverrà nel momento in cui qualche altro bundle richiederà i “servigi” del framework
Apache CXF.
24/07/16 6
Questo documento è rilsciato con licenza Creative Commons
Attribuzione - Non commerciale - Condividi allo stesso modo 3.0 Italia (CC BY-NC-SA 3.0 IT)
7. Antonio Musarra’s Blog Document Revision: 1.0
The ideal solution for a problem Blog: http://www.dontesta.it/blog
LinkedIn: https://it.linkedin.com/in/amusarra
Twitter: @antonio_musarra
4. Struttura dell’applicazione CRM Application
I l modulo client SOAP di esempio è stato realizzato rispettando l’OSGi Service
Pattern mostrato nella presentazione OSGi e Liferay 7 e con la struttura seguente:
crmservices-api – Definizione delle API dell’interfaccia per l’accesso al servizio
SOAP target
crmservices-service – Implementazione delle API definite in crmservices-api. Il
servizio SOAP del CRM utilizzato è disponibile pubblicamente all’indirizzo
http://www.predic8.com:8080/crm/CustomerService. L’implementazione fa uso
del framework Apache CXF
crmservices-commands – Client che mostra come consumare le API
In Figura 4 è mostrato il class diagram dell’intera applicazione e in particolare:
L’interfaccia CRMService definisce la nostra API pubblica d’interazione con i servizi
del CRM e si trova nel modulo crmservices-api
La classe CRMSOAPService definita come component OSGi implementa
l’interfaccia CRMService. Questo è il componete che implementata il colloquio via
SOAP con il sistema di CRM e si trova nel modulo crmservices-service
L’interfaccia CRMSOAPServiceConfiguration definisce i medatadi OSGi per la
configurazione del componente CRMSOAPService e in particolare l’end point del
servizio SOAP. L’interfaccia si trova nel modulo crmservices-service
L a c l a s s e CRMCustomerServiceCommand definita come component OSGi
implementa una serie di comandi OSGi che consentono di eseguire le operazioni
definite dall’interfaccia CRMService, insomma, agisce da consumer dei servizi CRM
24/07/16 7
Questo documento è rilsciato con licenza Creative Commons
Attribuzione - Non commerciale - Condividi allo stesso modo 3.0 Italia (CC BY-NC-SA 3.0 IT)
8. Antonio Musarra’s Blog Document Revision: 1.0
The ideal solution for a problem Blog: http://www.dontesta.it/blog
LinkedIn: https://it.linkedin.com/in/amusarra
Twitter: @antonio_musarra
Figura 4 - Class Diagram dell’applicazione CRM Application
Ognuno dei moduli rappresenta fisicamente un bundle OSGi, esattamente i bundle
sono:
crmservices-api.jar
crmservices-service.jar
crmservices-commands.jar
5. Deploy dei bundle dell’applicazione
Il processo di deploy dei bundle del modulo webservices-client sulla vostra istanza
Liferay 7 può avvenire in questo modo:
Clone del repository liferay-italia-bo-usergroup
Modifica del file gradle.properties specificando la propria directory d’installazione
di Liferay 7, che nel mio caso è /opt/liferay-ce-portal-7.0-ga2-blog. La property da
modificare è liferay.workspace.home.dir
24/07/16 8
Questo documento è rilsciato con licenza Creative Commons
Attribuzione - Non commerciale - Condividi allo stesso modo 3.0 Italia (CC BY-NC-SA 3.0 IT)
9. Antonio Musarra’s Blog Document Revision: 1.0
The ideal solution for a problem Blog: http://www.dontesta.it/blog
LinkedIn: https://it.linkedin.com/in/amusarra
Twitter: @antonio_musarra
A seguire i comandi per il deploy dei bundle del modulo webservices-client, così
come indicato in Console 4.
$ git clone https://github.com/amusarra/liferay-italia-bo-usergroup
$ cd liferay-italia-bo-usergroup
$ cd modules/webservices-client
$ gradle deploy
Console 4 - Deploy bundle modulo web services-client su Liferay 7
L’effetto del comando del comando gradle deploy è visibile dai log di Liferay di cui
riporto un esempio (vedi Console 5).
2 0 : 3 4 : 3 9 , 5 6 3 I N F O [fileinstall-/opt/liferay-ce-portal-7.0-ga2-blog/osgi/modules]
[BundleStartStopLogger:35] STARTED crmservices-api_1.0.0 [489]
2 0 : 3 4 : 3 9 , 5 6 8 I N F O [fileinstall-/opt/liferay-ce-portal-7.0-ga2-blog/osgi/modules]
[BundleStartStopLogger:35] STARTED crmservices.commands_1.0.0 [488]
2 0 : 3 4 : 4 9 , 7 3 6 I N F O [fileinstall-/opt/liferay-ce-portal-7.0-ga2-blog/osgi/modules]
[BundleStartStopLogger:35] STARTED crmservices-service_1.0.0 [490]
2 0 : 3 4 : 4 9 , 7 4 6 I N F O [fileinstall-/opt/liferay-ce-portal-7.0-ga2-blog/osgi/modules]
[CRMSOAPService:149] Configured SOAP EndPoint : { http://www.predic8.com:8080/crm/CustomerService
}
2 0 : 3 4 : 5 0 , 1 2 5 I N F O [fileinstall-/opt/liferay-ce-portal-7.0-ga2-blog/osgi/modules]
[BundleStartStopLogger:35] STARTED org.apache.cxf.cxf-core_3.1.6 [480]
2 0 : 3 4 : 5 0 , 7 7 8 I N F O [fileinstall-/opt/liferay-ce-portal-7.0-ga2-blog/osgi/modules]
[BundleStartStopLogger:35] STARTED org.apache.cxf.cxf-rt-frontend-simple_3.1.6 [485]
2 0 : 3 4 : 5 0 , 7 8 8 I N F O [fileinstall-/opt/liferay-ce-portal-7.0-ga2-blog/osgi/modules]
[BundleStartStopLogger:35] STARTED org.apache.cxf.cxf-rt-frontend-jaxws_3.1.6 [486]
2 0 : 3 4 : 5 0 , 8 6 3 I N F O [fileinstall-/opt/liferay-ce-portal-7.0-ga2-blog/osgi/modules]
[BundleStartStopLogger:35] STARTED org.apache.cxf.cxf-rt-bindings-soap_3.1.6 [483]
2 0 : 3 4 : 5 0 , 8 8 5 I N F O [fileinstall-/opt/liferay-ce-portal-7.0-ga2-blog/osgi/modules]
[BundleStartStopLogger:35] STARTED org.apache.cxf.cxf-rt-wsdl_3.1.6 [484]
2 0 : 3 4 : 5 1 , 2 2 5 I N F O [fileinstall-/opt/liferay-ce-portal-7.0-ga2-blog/osgi/modules]
[BundleStartStopLogger:35] STARTED org.apache.cxf.cxf-rt-transports-http_3.1.6 [487]
2 0 : 3 4 : 5 1 , 2 3 7 I N F O [fileinstall-/opt/liferay-ce-portal-7.0-ga2-blog/osgi/modules]
[BundleStartStopLogger:35] STARTED org.apache.cxf.cxf-rt-bindings-xml_3.1.6 [482]
2 0 : 3 4 : 5 1 , 6 6 7 I N F O [fileinstall-/opt/liferay-ce-portal-7.0-ga2-blog/osgi/modules]
[BundleStartStopLogger:35] STARTED org.apache.cxf.cxf-rt-databinding-jaxb_3.1.6 [481]
Console 5 - Estratto dal file di log Liferay durante il deploy dei bundle del modulo webservices-client
Cosa notate dall’estratto del file di log di Liferay 7? Tutto normale, nulla di
strano. Al momento del deploy dei nostri bundle anche i bundle di Apache CXF sono stati
avviati e passati nello stato Active. Per evidenziare ancora meglio la nuova situazione
eseguire il comando lb dalla gogo shell così come indicato in Console 6
24/07/16 9
Questo documento è rilsciato con licenza Creative Commons
Attribuzione - Non commerciale - Condividi allo stesso modo 3.0 Italia (CC BY-NC-SA 3.0 IT)
10. Antonio Musarra’s Blog Document Revision: 1.0
The ideal solution for a problem Blog: http://www.dontesta.it/blog
LinkedIn: https://it.linkedin.com/in/amusarra
Twitter: @antonio_musarra
g! lb | grep -e "XmlSchema|Apache CXF|crmservices"
479|Active | 1|XmlSchema Core (2.2.1)
480|Active | 1|Apache CXF Core (3.1.6)
481|Active | 1|Apache CXF Runtime JAXB DataBinding (3.1.6)
482|Active | 1|Apache CXF Runtime XML Binding (3.1.6)
483|Active | 1|Apache CXF Runtime SOAP Binding (3.1.6)
484|Active | 1|Apache CXF Runtime Core for WSDL (3.1.6)
485|Active | 1|Apache CXF Runtime Simple Frontend (3.1.6)
486|Active | 1|Apache CXF Runtime JAX-WS Frontend (3.1.6)
487|Active | 1|Apache CXF Runtime HTTP Transport (3.1.6)
488|Active | 10|crmservices.commands (1.0.0)
489|Active | 10|crmservices-api (1.0.0)
490|Active | 10|crmservices-service (1.0.0)
Console 6 - Visualizzazione stato bundle
6. Test del client SOAP
Per il test del client possiamo utilizzare i comandi OSGi definiti in crmservices-
commands:
createDefaultCustomer – Comando per la creazione di un customer
getCustomer – Comando per il retrieve di un customer specificando l’id del
customer
getCustomers – Comando per il retrieve di tutti i customer
A seguire l’esecuzione in ordine dei comandi OSGi sopra descritti e il relativo output.
g! createDefaultCustomer
Customer with id: 4ab5a868-e41e-4c26-867e-2a88af56866e created
g! getCustomer 4ab5a868-e41e-4c26-867e-2a88af56866e
Customer [person=PersonType [id=4ab5a868-e41e-4c26-867e-2a88af56866e, firstName=Antonio,
lastName=Musarra, age=35, address=AddressType [street=Via Guseppe Verdi 90, city=Bronte,
zipCode=95034, country=IT]], companyAddressType=CompanyAddressType [companyName=Antonio Musarra's
Blog], id=4ab5a868-e41e-4c26-867e-2a88af56866e]
g! getCustomers
it.dontesta.labs.liferay.lrbo16.webservice.crm.exception.CRMServiceException: The be implement!
Console 7 - Esempio di esecuzione dei comandi OSGi per l'interazione con i servizi SOAP
24/07/16 10
Questo documento è rilsciato con licenza Creative Commons
Attribuzione - Non commerciale - Condividi allo stesso modo 3.0 Italia (CC BY-NC-SA 3.0 IT)
11. Antonio Musarra’s Blog Document Revision: 1.0
The ideal solution for a problem Blog: http://www.dontesta.it/blog
LinkedIn: https://it.linkedin.com/in/amusarra
Twitter: @antonio_musarra
7. Risorse
Per approfondimenti su Liferay 7 e OSGi vi lascio queste serie di utili risorse da
leggere:
Liferay Developer Network (LDN)
EclipseCon Europe 2015 Liferay Modularity Patterns – Liferay portal modern
architecting and development
Liferay 7 OSGi and Modularity
OSGi Modularity – Tutorial
OSGi in Action
OSGi in Depth
8. Conclusioni
In questo articolo abbiamo visto come impostare il vostro ambiente Liferay 7 al fine
di realizzare applicazioni che hanno necessità di colloquiare con altri sistemi attraverso il
protocollo SOAP. Sicuramente è più interessante vedere com’è strutturata un’applicazione
seguendo l’OSGi Service Pattern.
Esistono altri modi per utilizzare il framework Apache CXF all’interno delle vostre
applicazioni, per esempio, includendo i jar direttamente all’interno dell’applicazione,
metodo che personalmente sconsiglio. In genere preferisco l’approccio a bundle quando
non ci sono restrizioni particolari, in questo caso ogni applicazione potrà sfruttare il
framework Apache CXF dichiarandone solamente l’utilizzo e i bundle delle applicazioni
risulteranno più contenuti in termini di dimensioni.
24/07/16 11
Questo documento è rilsciato con licenza Creative Commons
Attribuzione - Non commerciale - Condividi allo stesso modo 3.0 Italia (CC BY-NC-SA 3.0 IT)
12. Antonio Musarra’s Blog Document Revision: 1.0
The ideal solution for a problem Blog: http://www.dontesta.it/blog
LinkedIn: https://it.linkedin.com/in/amusarra
Twitter: @antonio_musarra
9. 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 offerti in quest’articolo sono stati utili per
te, potresti restituirmi il favore condividendo l’articolo
sui canali di social network o altri, magari ciò che ho
scritto potrebbe essere utile anche a altre persone.
Basta un click per condividere!
24/07/16 12
Questo documento è rilsciato con licenza Creative Commons
Attribuzione - Non commerciale - Condividi allo stesso modo 3.0 Italia (CC BY-NC-SA 3.0 IT)