.NET, java, xml, interoperabilità ma senza il web

Tempo fa mi ero ritrovato a scrivere questo tip Controllare il termine di un comando . Nella mia testa avevo tuttavia in mete che l'uso dello stream I/O sarebbe stato un buon mezzo per interoperare tra processi.

Quando si parla di interoperaizone tra java e .Net si parla sempre di messaggi xml (soap) mediato l'suo di web service, per cui trasporto HTTP. Tuttavia mi sono sempre detto a volte potrebbe essere poco pratico/macchinoso mettere in piedi un web server solo per usare una procedura java in una architettura standalone/desktop.

Proprio ieri avevo suggerito una mia idea a un collega il quale avevo mostrato interesse... visto che ha deciso diprovare a testarla ho detto lui di farmi sapere l'esito dei test... ma non ce la potevo fare ieri sera ho oluto produrre una piccola demo io stesso! :-p

Potrebbe valere la pena scriverci un articolo da pubblicare sul sito... ma ci metto sempre una vita a scrivere bene ordinato e in italiano corretto, ho infatti in canna un articolo ormai da mesi! :-O Nell'attesa di scrivere l'articolo ho deciso di immortalare l'idea postando il codice qui sul mio Blog... il tutto è da prendere "AS IS". Alcune cose sono decisamente da fare meglio, non ho usato soap ma per ora scambio di messaggi xml più semplici. Per altro è un po che non gioco più con java quindi è possibile che il codice java si potrebbe fare meglio... senza guardare che per fare i test sto un usando una versione della vm non proprio all'ultima moda :-p La cosa che voglio mostrare è l'uso dello stream I/O per scambio di messaggi xml per interoperare tra un processo .NET e uno java... che poi può essere esteso all'interoperazione generica tra processi.

Sia chiaro che questa non è mio opinione proporla come soluzione prima... ma come possibile soluzione _estrema_ nel caso si ha esigenza di usare componenti di tecnologie diverse senza necessariamente chiamare in causa strutture aggiuntive quali web server & Co.

Sia data la seguente - complessa - procedura java

package MarcoBarzaghi.Lab;
import java.io.*;
public class BusinessSystem
{
 
 public static int PerformSum(int a, int b)
 {
  return a + b; 
 }
}

Mi creo ora il seguente Programma che fungerà da connettore.

package MarcoBarzaghi.Lab.Interop;
import java.text.*;
import java.io.*;
import org.xml.sax.*;
import org.w3c.dom.*;
import javax.xml.parsers.*;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.*;
import javax.xml.transform.dom.*;
import javax.xml.transform.stream.*;
import MarcoBarzaghi.Lab.*;
public class BusinessSystemConnectorProgram
{
 /**
  * Constants 
  */
 private final static int END_OF_INPUT_STREAM = 0;
 
 /**
  * Main  
  */ 
 public static void main(String[] args) throws ParserConfigurationException, TransformerException
 {       
  try{
  
   //recupero richiesta dallo stream di input
   Document inputDocument = readRequest();  
   
   //il nome del nodo di root mi indica l'azione richiesta   
   String actionName = inputDocument.getFirstChild().getNodeName();
   if(actionName.equals("sum"))   
   {     
    //recupero i parametri di input
    String a = inputDocument.getElementsByTagName("a").item(0).getChildNodes().item(0).getNodeValue();
    String b = inputDocument.getElementsByTagName("b").item(0).getChildNodes().item(0).getNodeValue();
    
    //eseguo l'azione richiesta
    int sum = BusinessSystem.PerformSum(Integer.parseInt(a), Integer.parseInt(b));
    
    //invio il risultato sullo stream di output
    Document sumDocument = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
    Element sumElement = sumDocument.createElement("sum");
    Text sumText = sumDocument.createTextNode(Integer.toString(sum));
    sumElement.appendChild(sumText);
    sumDocument.appendChild(sumElement);
    
    writeResponse(sumDocument);
    
   }else{
    //invio risposta di errore per richiesta non valida
    writeErrorResponse("Azione non definita");
   }
   
  }catch(Exception e){
   //invio riposta di errore applicativo
   writeErrorResponse(e.toString());
  }
 }
 
 /**
  * Muove i dati da un inputstream a un outputstream fermandosi 
  * quando incontra un definito endOfStream
  */      
 private static void moveFromStreamToStream(InputStream in, OutputStream out, int endOfStream) throws IOException{
  int b;
  while((b = in.read()) != endOfStream) {out.write(b);}
 }
 
 /**
  * Legge lo stream di richiesta e carica un documento Xml 
  */
 private static Document readRequest() throws SAXException, ParserConfigurationException, IOException
 {
  ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
  moveFromStreamToStream(System.in, byteArrayOutputStream, END_OF_INPUT_STREAM);
  byte[] bytes = byteArrayOutputStream.toByteArray();
  return DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new ByteArrayInputStream(bytes));
 }
 
 /**
  * Scrive una response di errore 
  */
 private static void writeErrorResponse(String message) throws ParserConfigurationException, TransformerException
 {
  Document errorDocument = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
  Element errorElement = errorDocument.createElement("error");
  errorDocument.appendChild(errorElement);
  Text errorText = errorDocument.createTextNode(message);
  errorElement.appendChild(errorText);  
  writeResponse(errorDocument);
 }
 /**
  * Scrive la response dato un XmlDocument 
  */
 private static void writeResponse(Document document) throws ParserConfigurationException, TransformerException
 {  
  Transformer t = TransformerFactory.newInstance().newTransformer();
  t.transform(new DOMSource(document), new StreamResult(System.out));  
 }
}

Andiamo ora a defire gli oggetti .NET che vogliono usare le funzioanlità di BusinessSystem e interoperareranno con il connettore appena definito.

Interfaccia IBusinessSystemProvider

using System;
namespace MarcoBarzaghi.Lab.Interop.Java
{
 /// 
 /// Interfaccia IBusinessSystemProvider.
 /// 
 public interface IBusinessSystemProvider
 {
  int PerformSum(int a, int b);
 }
}

Il provider concreto e la classe per costruire l'xml di richiesta per interoperare con il nostro processo java, BusinessSystemProcessSumRequest e BusinessSystemProcessInteropProvider

using System;
using System.Collections.Specialized;
using System.Configuration;
using System.Xml;
using System.Xml.Serialization;
using System.Diagnostics;
namespace MarcoBarzaghi.Lab.Interop.Java
{
 /// 
 /// Classe che definisce la richiesta per la procedura di somma.
 /// 
 [XmlRoot("sum")]
 public class BusinessSystemProcessSumRequest
 {
  [XmlElement("a")]
  public int a = 0;
  
  [XmlElement("b")]
  public int b = 0;
 }
 /// 
 /// IBusinessSystemProvider che fa uso dell'interoperabilità via stream i/o dei processi
 /// 
 public class BusinessSystemProcessInteropProvider: IBusinessSystemProvider
 {
  #region consts
  private const char END_OF_INPUT_STREAM = '\x0';
  #endregion
  #region IBusinessSystemProvider Members
  public int PerformSum(int a, int b)
  {
   BusinessSystemProcessSumRequest request = new BusinessSystemProcessSumRequest();
   request.a = a;
   request.b = b;
   //-- trasporto
   NameValueCollection appSettings = ConfigurationSettings.AppSettings;
   string javaProgram = appSettings["javaProgram"];
   string businessSystemConnectorProgramArguments = appSettings["businessSystemConnectorProgramArguments"];
   string workingDirectory = appSettings["workingDirectory"];
   
   ProcessStartInfo processStartInfo = 
    new ProcessStartInfo(javaProgram, businessSystemConnectorProgramArguments);   
   processStartInfo.UseShellExecute = false;
   processStartInfo.RedirectStandardInput = true;
   processStartInfo.RedirectStandardOutput = true;
   processStartInfo.WorkingDirectory = workingDirectory;
   
   Process process = Process.Start(processStartInfo);  
   XmlSerializer xmlSerializer = new XmlSerializer(typeof(BusinessSystemProcessSumRequest));
   xmlSerializer.Serialize(process.StandardInput, request);
   process.StandardInput.Write(END_OF_INPUT_STREAM);
   
   XmlDocument response = new XmlDocument();   
   response.LoadXml(process.StandardOutput.ReadToEnd());
   
   //process.WaitForExit();    
   //--
   
   if(response.DocumentElement.Name.Equals("error"))
   {
    throw new Exception(response.FirstChild.InnerText);
   }
   else
   {
    return int.Parse(response.DocumentElement.InnerText);
   }   
  }
  #endregion
 }
}

La classe factory per gestire la creazione di una IBusinessSystemProvider concreta...

using System;
namespace MarcoBarzaghi.Lab.Interop.Java
{
 /// 
 /// Factory class per la gestione della costruzione 
 /// del IBusinessSystemProvider appropriato.
 /// 
 public abstract class BusinessSystemProviderFactory
 {
  public static IBusinessSystemProvider GetBusinessSystemProvider()
  {
   return new BusinessSystemProcessInteropProvider();
  }
 }
}

...e infine l'intergrazione finale e il config!

using System;
namespace MarcoBarzaghi.Lab.Interop.Java
{
 class Program
 {
  /// 
  /// The main entry point for the application.
  /// 
  [STAThread]
  static void Main(string[] args)
  {
   try
   {
    int a = 3;
    int b = 2;
    int sum = BusinessSystemProviderFactory.GetBusinessSystemProvider().PerformSum(a, b);
    Console.WriteLine("{0} + {1} = {2}", a, b, sum);
   }
   catch(Exception e)
   {
    Console.WriteLine(e);
   }
   Console.ReadLine();
  }
 }
}
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="workingDirectory" value="C:\Data\MyProjects\dotNet\Studio\JavaInterop\Lab\"/>
<add key="javaProgram" value="C:\Program Files\Java\j2sdk-1_4_1_01\bin\java.exe"/>
<add key="businessSystemConnectorProgramArguments" value="-classpath &quot;C:\Program Files\Java\j2sdk-1_4_1_01\lib\classes.zip;.\classes&quot; MarcoBarzaghi.Lab.Interop.BusinessSystemConnectorProgram"/>
</appSettings>
</configuration>

Attenzione, nel config qui sopra nella configurazione del valore della chiave "businessSystemConnectorProgramArguments" non ho mal codificato ". Occorre proprio scrivere così in quanto la carftella "Program Files" ha lo spazio che verrebbe interpretato come separazione di parametro!

Lanciamo applicazione e... "3 + 2 = 5" :-D Ha funzionato! :-D

posted @ mercoledì 17 novembre 2004 15:35

Print

Comments on this entry:

# re: .NET, java, xml, interoperabilità ma senza il web

Left by PIPPO at 21/11/2004 05:11
Gravatar
COMPLIMENTI
Comments have been closed on this topic.
«agosto»
domlunmarmergiovensab
31123456
78910111213
14151617181920
21222324252627
28293031123
45678910