PROTOCOLLO DI TRASFERIMENTO IN PRATICA – INVIO
Per questo punto abbiamo visto che è necessario serializzare il documento XML in uno stream secondario che useremo per calcolare la dimensione dei dati inviati e che poi riverseremo direttamente nello stream di output del socket.
Per fare questo estendiamo ByteArrayOutputStream, in questo modo sfrutteremo tutti i metodi di questa classe, fra i quali, quello che ci fornisce la dimensione dei dati contenuti. Tale classe dovrà avere inoltre:
- Un attributo che memorizza lo stream di output reale. Nel nostro caso memorizzerà lo stream di output del socket.
- Un metodo send che fa quello che vogliamo. Prende i dati memorizzati e li invia nel canale di output preceduti da un intero che indica la quantità di dati inviati.
Per il canale di output ho scelto DataOutputStream poiché fornisce in modo nativo molte funzioni a cui siamo interessati, fra cui l’invio diretto di un Int.
La classe risultante è:
public class XMLOutputStream extends ByteArrayOutputStream { private DataOutputStream outchannel; public XMLOutputStream(OutputStream outchannel) { super(); this.outchannel = new DataOutputStream(outchannel); } public void send() throws IOException { byte[] data = toByteArray(); outchannel.writeInt(data.length); outchannel.write(data); reset(); } }
Ora però vogliamo che tale classe sia gestibile in modo più semplice. Vogliamo infatti un metodo in cui noi inseriamo un documento XML e lo stream di uscita del socket e lui faccia tutto il lavoro di serializzazione e invio.
public class XMLSender { public static void send(Document tosend, OutputStream channel) throws TransformerConfigurationException, IOException { XMLOutputStream out = new XMLOutputStream(channel); StreamResult sr = new StreamResult(out); DOMSource ds = new DOMSource(tosend); Transformer tf = TransformerFactory.newInstance().newTransformer(); try { tf.transform(ds, sr); } catch (TransformerException ex) { Logger.getLogger(XMLSender.class.getName()).log(Level.SEVERE, null, ex); } out.send(); } }
Questa classe fa proprio ciò che vogliamo. Contiene un unico metodo statico che prende come parametri documento XML e canale di uscita e si occupa di serializzare e inviare il tutto in modo corretto sfruttando il nostro XMLOutputStream.
PROTOCOLLO DI TRASFERIMENTO IN PRATICA – INVIO
Per la ricezione sfruttiamo un meccanismo simile anche se, per mantenere una certa linearità di funzionamento sono stato costretto ad usare in modo piuttosto sporco la classe ByteArrayInputStream ma al momento era l’unica soluzione coerente che mi veniva in mente. Funziona a patto di tollerare il mio intervento brutale sugli attributi della classe.
public class XMLInputStream extends ByteArrayInputStream {
private DataInputStream inchannel;
public class XMLInputStream extends ByteArrayInputStream { private DataInputStream inchannel; public XMLInputStream(InputStream inchannel) { super(new byte[2]); //MOLTO BRUTTO. this.inchannel = new DataInputStream(inchannel); } public void recive() throws IOException { int i = inchannel.readInt(); //Legge l'int con il numero di byte. byte[] data = new byte[i]; inchannel.read(data, 0, i); //Legge esattamente quel numero di byte. this.buf = data; //BRUTTO ANCHE QUESTO. this.count = i; this.mark = 0; this.pos = 0; } }
Come potete vedere rispetta esattamente il nostro protocollo. Anche in questo caso vogliamo una classe involucro che ci permetta, dato lo stream di input del socket di restituire il documento corrispondente.
public class XMLReceiver { public static Document receive(InputStream channel) throws ParserConfigurationException, TransformerConfigurationException, IOException, SAXException { //Inizializza il DOM per la ricezione del pacchetto. DocumentBuilderFactory docBuilderFact = DocumentBuilderFactory.newInstance(); DocumentBuilder docBuilder = docBuilderFact.newDocumentBuilder(); Document request = null; //Inizializza il canale. XMLInputStream xmlin = new XMLInputStream(channel); xmlin.recive(); request = docBuilder.parse(xmlin); return request; } }
Anche in questo caso eccovi accontentati. Lascio qui di seguito un archivio contenente tutte le classi di cui abbiamo parlato.
LICENZA:
Tutte le classi sono estratte dal progetto Enea. Come tali ereditano la sua licenza GPLv3. Nonostante ciò non è che per 10 righe di codice mi straccerò i vestiti se la licenza non è applicata. E’ solo premura citare me e questo. A questo ci terrei. 🙂
Grazie ad andbin del forum di HTML.it per il supporto.
Ciao Davide, ho trovato questi tuoi articoli che mi hanno molto aiutato nella realizzazione di una piccola applicazione server che sto scrivendo, alla fine è venuto fuori un piccolo framework.. un pò come hai fatto tu con ENEA.
Pensavo di approfondire il discorso dello scambio di messaggi xml sicuri.. sarebbe una bella feature da aggiungere.. se ti va ne parlamo assieme!