Esempio di creazione di un’applicazione Java che utilizza CRNP

L’esempio successivo mostra come sviluppare una Java Applicazione chiamata CRNPClient che CRNP utilizza. I registri dell’applicazione richiama gli eventi con il server CRNP del cluster, ascolta gli eventi e elabora questi eventi stampando i suoi contenuti. Prima di finalizzare, l’applicazione annulla il record della tua richiesta per richiami di eventi.

Nota i seguenti punti quando si esamina questo esempio:

  • L’esempio dell’applicazione genera e Analizza XML con JAXP (API Java per l’elaborazione XML). Questo esempio non mostra come usare JAXP. Questo strumento è descritto in modo più dettagliato in http://java.sun.com/xml/jaxp/index.html.

  • Questo esempio presenta parti di un’applicazione, che è descritta in piena nell’appendice G, application cnpclient.java. Per spiegare più efficacemente alcuni concetti, l’esempio di questo capitolo varia leggermente dall’applicazione completa presentata nell’appendice G, CRNPClient.java Application.

  • Per essere più concisa, non codice di esempio I delimitatori sono stati inclusi in questo capitolo. Per vederli, consultare l’applicazione completa nell’appendice G, CRNPClient.java Application.

  • L’applicazione che appare in questo esempio gestisce la maggior parte delle condizioni di errore semplicemente chiudendo l’applicazione. L’applicazione effettiva dovrebbe trattare gli errori più saldamente.

Come configurare l’ambiente

passaggi
  1. Scarica e installa Jaxp e la versione corretta del compilatore Java e della macchina virtuale Java.

    Puoi trovare le istruzioni su http://java.sun.com/xml/jaxp/index.html.

    Nota –

    Questo esempio richiede almeno Java 1.3.1.

  2. Nella directory in cui si trova il file sorgente, digitare quanto segue:

    % javac -classpath jaxp-root/dom.jar:jaxp-rootjaxp-api. \jar:jaxp-rootsax.jar:jaxp-rootxalan.jar:jaxp-root/xercesImpl \.jar:jaxp-root/xsltc.jar -sourcepath . source-filename.java

    Dove Jaxp-root è il percorso assoluto o relativo per la directory in cui i file Jaxp e sorgente-filename risiedono è il nome del file di origine Java.

    Se ClassPath è incluso nella riga di comando di compilazione, garantirà che il compilatore possa trovare lezioni JAXP.

  3. Eseguire l’applicazione, specificare il percorso della classe in ClassPath in modo che il L’applicazione può eseguire i file di classe JAXP corretti (tenere in considerazione A cui il primo percorso di ClassApath punta alla directory corrente):

    Ora che l’ambiente è stato configurato è possibile sviluppare l’applicazione .

Come iniziare a sviluppare l’applicazione

In questa parte dell’esempio, è necessario creare una classe base chiamata CRNPClient con un metodo principale che analizza Gli argomenti della riga di comando e costruiscono un oggetto CRNPClient. Questo oggetto passa gli argomenti della riga di comando alla classe, attendere che l’utente finisca l’applicazione, l’arresto della chiamata nella classe CRNPClient e la vendita.

Il costruttore di classe CRNPClient deve eseguire le seguenti attività:

  • Configurare gli oggetti di processo XML.

  • Creare una sottoprocesso che riceve riduzioni di eventi.

  • Contattare il server CRNP e registrati registri di eventi.

Step

    Creare il codice Java che applica la logica precedente.

    L’esempio seguente mostra il codice della struttura di base della classe CRNPClient. Le implementazioni dei metodi ausiliari indicati nei metodi di chiusura e nel costruttore sono riportati di seguito in questo capitolo, tenere presente che viene visualizzato il codice che conta tutti i pacchetti necessari.

    import javax.xml.parsers.*;import javax.xml.transform.*;import javax.xml.transform.dom.*;import javax.xml.transform.stream.*;import org.xml.sax.*;import org.xml.sax.helpers.*;import org.w3c.dom.*;import java.net.*;import java.io.*;import java.util.*;class CrnpClient{ public static void main(String args) { InetAddress regIp = null; int regPort = 0, localPort = 0; try { regIp = InetAddress.getByName(args); regPort = (new Integer(args)).intValue(); localPort = (new Integer(args)).intValue(); } catch (UnknownHostException e) { System.out.println(e); System.exit(1); } CrnpClient client = new CrnpClient(regIp, regPort, localPort, args); System.out.println("Hit return to terminate demo..."); try { System.in.read(); } catch (IOException e) { System.out.println(e.toString()); } client.shutdown(); System.exit(0); } public CrnpClient(InetAddress regIpIn, int regPortIn, int localPortIn, String clArgs) { try { regIp = regIpIn; regPort = regPortIn; localPort = localPortIn; regs = clArgs; setupXmlProcessing(); createEvtRecepThr(); registerCallbacks(); } catch (Exception e) { System.out.println(e.toString()); System.exit(1); } } public void shutdown() { try { unregister(); } catch (Exception e) { System.out.println(e); System.exit(1); } } private InetAddress regIp; private int regPort; private EventReceptionThread evtThr; private String regs; public int localPort; public DocumentBuilderFactory dbf;}

    Le variabili dei membri sono descritte più avanti in questo capitolo.

Come analizzare gli argomenti della linea di comandi

STEP

    Per analizzare gli argomenti della riga di comando, consultare il codice incluso nell’applicazione Appendice G, CRNPClient.java.

Come definire l’evento che riceve sottoprocesso

Nel codice, è necessario verificare che la ricezione degli eventi sia eseguita in un thread separato in modo che l’applicazione possa continuare a eseguire l’altro lavoro, mentre la sottoprocessa dell’evento è bloccata e rimessi degli eventi previsti.

Nota –

La configurazione XML è descritta in seguito in questo capitolo.

Passaggi
  1. Nel codice, definire La sottoclasse del filo chiamata EventRECYINGTHREAD che crea un socket, ServerCocket e si aspetta la ricezione degli eventi nella presa.

    In questa parte del codice di esempio, gli eventi non conoscono letti o vengono elaborati. La lettura e la lavorazione degli eventi sono descritte più avanti in questo capitolo.EventRECYINGTHREAD Crea una presa, ServersCocket, in un indirizzo del protocollo di Intenenction con caratteri jolly. EventRECYCYTHREAD mantiene anche un riferimento all’oggetto CRNPClient in modo che EventReCyCythread possa inviare eventi all’oggetto CRNPClient per l’elaborazione.

    class EventReceptionThread extends Thread{ public EventReceptionThread(CrnpClient clientIn) throws IOException { client = clientIn; listeningSock = new ServerSocket(client.localPort, 50, InetAddress.getLocalHost()); } public void run() { try { DocumentBuilder db = client.dbf.newDocumentBuilder(); db.setErrorHandler(new DefaultHandler()); while(true) { Socket sock = listeningSock.accept(); // Construct event from the sock stream and process it sock.close(); } // UNREACHABLE } catch (Exception e) { System.out.println(e); System.exit(1); } } /* private member variables */ private ServerSocket listeningSock; private CrnpClient client;}
  2. Costruisci un oggetto creativovtrecepthre.

    private void createEvtRecepThr() throws Exception{ evtThr = new EventReceptionThread(this); evtThr.start();}

Come registrarsi ridotti o annullare il record

Il processo di registrazione prevede le seguenti azioni:

  • Aprire una presa TCP di base per la porta e la registrazione Protocollo interred

  • Creare il messaggio di registro XML

  • Invia il messaggio di registro XML sulla presa

  • Leggi il messaggio di risposta XML dal socket

  • Chiudi la presa

passaggi
  1. Creare il codice Java che applica la logica precedente.

    Il seguente codice di esempio mostra l’implementazione del metodo di registroAlbackbacks della classe CRNPClient (chiamata dal costruttore CRNPClient). Le chiamate a Createregistrationstringstring () e ReadregistrationReply () sono descritte più dettagliate più avanti in questo capitolo.

    Regip e REGART sono oggetti membri configurati dal costruttore.

    private void registerCallbacks() throws Exception{ Socket sock = new Socket(regIp, regPort); String xmlStr = createRegistrationString(); PrintStream ps = new PrintStream(sock.getOutputStream()); ps.print(xmlStr); readRegistrationReply(sock.getInputStream(); sock.close();}
  2. Applica il metodo Unrugister.

    Il metodo di spegnimento di CRNPClClient chiama questo metodo. L’implementazione di CreateSunegistrationstring è descritta più dettagliata in seguito in questo capitolo.

    private void unregister() throws Exception{ Socket sock = new Socket(regIp, regPort); String xmlStr = createUnregistrationString(); PrintStream ps = new PrintStream(sock.getOutputStream()); ps.print(xmlStr); readRegistrationReply(sock.getInputStream()); sock.close();}

Come generare xml

Ora che la struttura dell’applicazione è stata configurata e È stato scritto tutto il codice di rete, il codice che genera e analizza XML deve essere scritto. Innanzitutto, scrivi il codice che genera il messaggio di registro XML SC_CALLACK_REG.

Un messaggio SC_CALLBACK_REG è formato da un tipo di record (add_client, remove_client, add_events o remove_events), una porta di ricomposizione e un elenco di eventi di interesse . Ogni evento consiste in una classe e una sottoclasse, seguita da un elenco di nomi di nomi e valori.

In questa parte dell’esempio, viene scritta una classe CallBackReg che consente di risparmiare il tipo di record, la porta di ricomposizione e l’elenco degli eventi di registrazione. Questa classe può anche essere serializzata per un messaggio XML SC_CALLACK_REG.

Un metodo interessante di questa classe è convertToxML, che crea una stringa di messaggi XML SC_CALLACK_REG da membri della classe. La documentazione JAXP situata in http://java.sun.com/xml/jaxp/index.html descrive più dettagliato il codice di questo metodo più dettagliato.

L’implementazione della classe evento viene visualizzata nel seguente codice di esempio. Si noti che la classe CallBackReg utilizza una classe di eventi, che consente di memorizzare un evento e convertirlo in un elemento, elemento, XML.

passaggi
  1. Crea Il codice Java che applica la logica precedente.

    class CallbackReg{ public static final int ADD_CLIENT = 0; public static final int ADD_EVENTS = 1; public static final int REMOVE_EVENTS = 2; public static final int REMOVE_CLIENT = 3; public CallbackReg() { port = null; regType = null; regEvents = new Vector(); } public void setPort(String portIn) { port = portIn; } public void setRegType(int regTypeIn) { switch (regTypeIn) { case ADD_CLIENT: regType = "ADD_CLIENT"; break; case ADD_EVENTS: regType = "ADD_EVENTS"; break; case REMOVE_CLIENT: regType = "REMOVE_CLIENT"; break; case REMOVE_EVENTS: regType = "REMOVE_EVENTS"; break; default: System.out.println("Error, invalid regType " + regTypeIn); regType = "ADD_CLIENT"; break; } } public void addRegEvent(Event regEvent) { regEvents.add(regEvent); } public String convertToXml() { Document document = null; DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); try { DocumentBuilder builder = factory.newDocumentBuilder(); document = builder.newDocument(); } catch (ParserConfigurationException pce) { // Parser with specified options can't be built pce.printStackTrace(); System.exit(1); } // Create the root element Element root = (Element) document.createElement("SC_CALLBACK_REG"); // Add the attributes root.setAttribute("VERSION", "1.0"); root.setAttribute("PORT", port); root.setAttribute("regType", regType); // Add the events for (int i = 0; i < regEvents.size(); i++) { Event tempEvent = (Event) (regEvents.elementAt(i)); root.appendChild(tempEvent.createXmlElement(document)); } document.appendChild(root); // Convert the whole thing to a string DOMSource domSource = new DOMSource(document); StringWriter strWrite = new StringWriter(); StreamResult streamResult = new StreamResult(strWrite); TransformerFactory tf = TransformerFactory.newInstance(); try { Transformer transformer = tf.newTransformer(); transformer.transform(domSource, streamResult); } catch (TransformerException e) { System.out.println(e.toString()); return (""); } return (strWrite.toString()); } private String port; private String regType; private Vector regEvents;}
  2. Implementa le classi Evento e NVPAIR.

    Nota che la classe CallBackReg utilizza l’evento, quello in Turn utilizza una classe NVPAIR.

    class Event{ public Event() { regClass = regSubclass = null; nvpairs = new Vector(); } public void setClass(String classIn) { regClass = classIn; } public void setSubclass(String subclassIn) { regSubclass = subclassIn; } public void addNvpair(NVPair nvpair) { nvpairs.add(nvpair); } public Element createXmlElement(Document doc) { Element event = (Element) doc.createElement("SC_EVENT_REG"); event.setAttribute("CLASS", regClass); if (regSubclass != null) { event.setAttribute("SUBCLASS", regSubclass); } for (int i = 0; i < nvpairs.size(); i++) { NVPair tempNv = (NVPair) (nvpairs.elementAt(i)); event.appendChild(tempNv.createXmlElement(doc)); } return (event); } private String regClass, regSubclass; private Vector nvpairs;}class NVPair{ public NVPair() { name = value = null; } public void setName(String nameIn) { name = nameIn; } public void setValue(String valueIn) { value = valueIn; } public Element createXmlElement(Document doc) { Element nvpair = (Element) doc.createElement("NVPAIR"); Element eName = doc.createElement("NAME"); Node nameData = doc.createCDATASection(name); eName.appendChild(nameData); nvpair.appendChild(eName); Element eValue = doc.createElement("VALUE"); Node valueData = doc.createCDATASection(value); eValue.appendChild(valueData); nvpair.appendChild(eValue); return (nvpair); } private String name, value;}

Come creare messaggi di registro e messaggi di cancellazione

Una volta create le lezioni dall’auxiliare Generazione dei messaggi XML, è possibile scrivere l’implementazione del metodo CreateteGistrationstring. RegisterCallbacks chiama questo metodo; Questo processo è descritto in come registrare ridotto o annullare il registro.

Criseteregistrationstring crea un oggetto callbackreg e imposta la sua porta e il tipo di registrazione. Successivamente, il crateategistrationstring costruisce vari eventi attraverso i metodi del createeallvent ausiliario, createmembembeevent, creatoevento e strisciamento. Ogni evento viene aggiunto all’oggetto CallLackreg dopo averlo creato. Infine, CreateteGistrationstring invoca il metodo ConvertToxML nell’oggetto CallBackReg per recuperare il messaggio XML nel modulo String.

Nota che il registro delle variabili dei membri memorizza gli argomenti della riga di comando forniti dall’utente per l’applicazione. Il quinto argomento e il seguente specificare gli eventi per i quali deve essere registrata l’applicazione. La quarta argomentazione specifica il tipo di record, ma viene ignorato in questo esempio. Il codice completo incluso nell’appendice G, CRNPClient.java Application mostra come utilizzare questo quarto argomento.

passaggi
  1. Creare il codice Java che applica la logica precedente che applica la logica precedente .

    private String createRegistrationString() throws Exception{ CallbackReg cbReg = new CallbackReg(); cbReg.setPort("" + localPort); cbReg.setRegType(CallbackReg.ADD_CLIENT); // add the events for (int i = 4; i < regs.length; i++) { if (regs.equals("M")) { cbReg.addRegEvent(createMembershipEvent()); } else if (regs.equals("A")) { cbReg.addRegEvent(createAllEvent()); } else if (regs.substring(0,2).equals("RG")) { cbReg.addRegEvent(createRgEvent(regs.substring(3))); } else if (regs.substring(0,1).equals("R")) { cbReg.addRegEvent(createREvent(regs.substring(2))); } } String xmlStr = cbReg.convertToXml(); return (xmlStr);}private Event createAllEvent(){ Event allEvent = new Event(); allEvent.setClass("EC_Cluster"); return (allEvent);}private Event createMembershipEvent(){ Event membershipEvent = new Event(); membershipEvent.setClass("EC_Cluster"); membershipEvent.setSubclass("ESC_cluster_membership"); return (membershipEvent);}private Event createRgEvent(String rgname){ Event rgStateEvent = new Event(); rgStateEvent.setClass("EC_Cluster"); rgStateEvent.setSubclass("ESC_cluster_rg_state"); NVPair rgNvpair = new NVPair(); rgNvpair.setName("rg_name"); rgNvpair.setValue(rgname); rgStateEvent.addNvpair(rgNvpair); return (rgStateEvent);}private Event createREvent(String rname){ Event rStateEvent = new Event(); rStateEvent.setClass("EC_Cluster"); rStateEvent.setSubclass("ESC_cluster_r_state"); NVPair rNvpair = new NVPair(); rNvpair.setName("r_name"); rNvpair.setValue(rname); rStateEvent.addNvpair(rNvpair); return (rStateEvent);}
  2. Creare la stringa di cancellazione del record.

    Creazione La stringa di cancellazione del record è un processo più semplice rispetto al processo più semplice del processo Creazione della catena di registrazione, poiché non è necessario adattare gli eventi.

    private String createUnregistrationString() throws Exception{ CallbackReg cbReg = new CallbackReg(); cbReg.setPort("" + localPort); cbReg.setRegType(CallbackReg.REMOVE_CLIENT); String xmlStr = cbReg.convertToXml(); return (xmlStr);}

Come configurare l’analizzatore XML

Una volta il codice di generazione XML e dalla rete per l’applicazione , il Builder CRNPClient chiama il metodo SetupXmlProcessing, che crea un oggetto DocumentBuilderFactory e stabilisce diverse proprietà di analisi in quell’oggetto. La documentazione JAXP descrive questo metodo più dettagliato. Vedi http://java.sun.com/xml/jaxp/index.html.

STEP

    Creare il codice Java che applica la logica precedente.

    private void setupXmlProcessing() throws Exception{ dbf = DocumentBuilderFactory.newInstance(); // We don't need to bother validating dbf.setValidating(false); dbf.setExpandEntityReferences(false); // We want to ignore comments and whitespace dbf.setIgnoringComments(true); dbf.setIgnoringElementContentWhitespace(true); // Coalesce CDATA sections into TEXT nodes. dbf.setCoalescing(true);}

Come analizzare il messaggio di risposta della registrazione

Per eseguire la scansione del messaggio XML SC_Reply che CRNP invia in risposta a A Messaggio di registrazione o cancellazione, è necessario disporre di una classe ausiliaria con una classe ausiliaria, che può essere costruita da un documento XML. Questa classe fornisce gli accessori per il codice di stato e il messaggio di stato. Per analizzare la sequenza XML del server, è necessario creare un nuovo documento XML e utilizzare il metodo di analisi. Documentazione JAXP situata in http://java.sun.com/xml/jaxp/index.html descrive questo metodo più dettagliato.

passaggi
  1. Creare il codice Java che applica il codice Java che applica logica precedente.

    Nota che il metodo ReadreGationReply utilizza la nuova classe di riprogrammazione.

    private void readRegistrationReply(InputStream stream) throws Exception{ // Create the document builder DocumentBuilder db = dbf.newDocumentBuilder(); db.setErrorHandler(new DefaultHandler()); //parse the input file Document doc = db.parse(stream); RegReply reply = new RegReply(doc); reply.print(System.out);}
  2. Implementa la classe di riprogrammazione.

    Si noti che il metodo di recupero attraverso l’albero DOM del documento XML ed estrae il codice e il messaggio di stato. La documentazione JAXP situata in http://java.sun.com/xml/jaxp/index.html contiene maggiori informazioni.

    class RegReply{ public RegReply(Document doc) { retrieveValues(doc); } public String getStatusCode() { return (statusCode); } public String getStatusMsg() { return (statusMsg); } public void print(PrintStream out) { out.println(statusCode + ": " + (statusMsg != null ? statusMsg : "")); } private void retrieveValues(Document doc) { Node n; NodeList nl; String nodeName; // Find the SC_REPLY element. nl = doc.getElementsByTagName("SC_REPLY"); if (nl.getLength() != 1) { System.out.println("Error in parsing: can't find " + "SC_REPLY node."); return; } n = nl.item(0); // Retrieve the value of the statusCode attribute statusCode = ((Element)n).getAttribute("STATUS_CODE"); // Find the SC_STATUS_MSG element nl = ((Element)n).getElementsByTagName("SC_STATUS_MSG"); if (nl.getLength() != 1) { System.out.println("Error in parsing: can't find " + "SC_STATUS_MSG node."); return; } // Get the TEXT section, if there is one. n = nl.item(0).getFirstChild(); if (n == null || n.getNodeType() != Node.TEXT_NODE) { // Not an error if there isn't one, so we just silently return. return; } // Retrieve the value statusMsg = n.getNodeValue(); } private String statusCode; private String statusMsg;}

Come analizzare gli eventi di ricomposizione

Il passo finale è analizzare ed elaborare gli eventi Reali di ricomposizione. Per aiutarti in questo compito, è necessario modificare la classe evento creata su come generare XML in modo che questa classe possa creare un evento, evento, da un documento XML e creare un elemento, elemento, XML: questa modifica richiede un costruttore aggiuntivo ( Ciò include un documento XML), un metodo recuperatore, aggiungendo due variabili membro (fornitore ed editore), metodi di accesso per tutti i campi e, infine, un metodo di stampa.

  1. Creare il codice Java che applica la logica precedente.

    Si prega di notare che questo codice è simile a quello della classe di riprogrammazione, che è descritto su come analizzare il messaggio di risposta della registrazione.

    public Event(Document doc) { nvpairs = new Vector(); retrieveValues(doc); } public void print(PrintStream out) { out.println("\tCLASS=" + regClass); out.println("\tSUBCLASS=" + regSubclass); out.println("\tVENDOR=" + vendor); out.println("\tPUBLISHER=" + publisher); for (int i = 0; i < nvpairs.size(); i++) { NVPair tempNv = (NVPair) (nvpairs.elementAt(i)); out.print("\t\t"); tempNv.print(out); } } private void retrieveValues(Document doc) { Node n; NodeList nl; String nodeName; // Find the SC_EVENT element. nl = doc.getElementsByTagName("SC_EVENT"); if (nl.getLength() != 1) { System.out.println("Error in parsing: can't find " + "SC_EVENT node."); return; } n = nl.item(0); // // Retrieve the values of the CLASS, SUBCLASS, // VENDOR and PUBLISHER attributes. // regClass = ((Element)n).getAttribute("CLASS"); regSubclass = ((Element)n).getAttribute("SUBCLASS"); publisher = ((Element)n).getAttribute("PUBLISHER"); vendor = ((Element)n).getAttribute("VENDOR"); // Retrieve all the nv pairs for (Node child = n.getFirstChild(); child != null; child = child.getNextSibling()) { nvpairs.add(new NVPair((Element)child)); } } public String getRegClass() { return (regClass); } public String getSubclass() { return (regSubclass); } public String getVendor() { return (vendor); } public String getPublisher() { return (publisher); } public Vector getNvpairs() { return (nvpairs); } private String vendor, publisher;
  2. Attuare manutenzione e metodi aggiuntivi della classe NVPAIR compatibili con l’analisi XML.

    Le modifiche apportate nell’evento mostrate nel passaggio 1 devono essere eseguite anche nella classe NVPAIR.

    public NVPair(Element elem) { retrieveValues(elem); } public void print(PrintStream out) { out.println("NAME=" + name + " VALUE=" + value); } private void retrieveValues(Element elem) { Node n; NodeList nl; String nodeName; // Find the NAME element nl = elem.getElementsByTagName("NAME"); if (nl.getLength() != 1) { System.out.println("Error in parsing: can't find " + "NAME node."); return; } // Get the TEXT section n = nl.item(0).getFirstChild(); if (n == null || n.getNodeType() != Node.TEXT_NODE) { System.out.println("Error in parsing: can't find " + "TEXT section."); return; } // Retrieve the value name = n.getNodeValue(); // Now get the value element nl = elem.getElementsByTagName("VALUE"); if (nl.getLength() != 1) { System.out.println("Error in parsing: can't find " + "VALUE node."); return; } // Get the TEXT section n = nl.item(0).getFirstChild(); if (n == null || n.getNodeType() != Node.TEXT_NODE) { System.out.println("Error in parsing: can't find " + "TEXT section."); return; } // Retrieve the value value = n.getNodeValue(); } public String getName() { return (name); } public String getValue() { return (value); }}
  3. Implementa il ciclo mentre loop in eventrecythread, che attende i richiami degli eventi.

    EventRecyThreadSthread è descritto su come definire la sottoprocessa della ricevuta di eventi.

    while(true) { Socket sock = listeningSock.accept(); Document doc = db.parse(sock.getInputStream()); Event event = new Event(doc); client.processEvent(event); sock.close(); }

Come eseguire l’applicazione

passaggi
  1. Diventa un superuso o assumere una funzione simile.

  2. Esegui l’applicazione.

    # java CrnpClient crnpHost crnpPort localPort ...

    Il codice completo dell’applicazione CRNPCLENT è visualizzata nell’appendice G, Application CRNPClient.java.

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *