logbitup.gif (360 bytes)

 Curso JAVA  
  Unidad 9: "Applets, Ventanas, Servidores y JavaBeans y XML"

logbitdn.gif (787 bytes)

  www.bit.es - Calendario de cursos - Solicitud de información

Curso Java
Curso Analista Programador entorno Java

Objetivos de la Unidad:


1.- Tipos de aplicaciones JAVA 

Pueden distinguirse 4 tipos:

· Como aplicación tipo consola (entrada/salida standard modo texto)
· Como aplicación tipo ventanas (Windows, X-Windows, Mac...)
· Como Applet incrustado en un navegador Web
· Como Servlet o conjunto de Servlets (ver Unidad 8)
· Como Midlet (Java Microedition - J2ME)


Ejecución como aplicación tipo consola

· Al menos una clase deberá tener un método main() que será el punto de arranque.
· La clase java.lang.System provee de dos objetos in y out que corresponden a la entrada-salida standard.
· Ejemplo mínimo:


// HolaMundo.java

class HolaMundo
{
  public static void main(String [] args)
   {
      System.out.println("Hola mundo");
   }
}

Los servlets podrían entrar en esta categoría:

import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;


public class HolaServlet extends HttpServlet
{
  public void service (HttpServletRequest req, HttpServletResponse res) throws
            ServletException, IOException
  {
    PrintWriter p = res.getWriter();
    res.setContentType("text/html");
    
    p.println("<html><body>");
    p.println("<h1>Hola mundo</h1>");
    p.println("</body></html>");
    p.close();
  }
}

Para probar el servlet hay que ir a un navegador y pedir la siguiente URL:

http://[dirección ip]:[port]/servlet/HolaServlet

Si arrancamos el servidor y el navegador en la misma máquina hay 3 alternativas más (suponiendo que arranca por el port 8080):

http://localhost:8080/servlet/HolaServlet
http://127.0.0.1:8080/servlet/HolaServlet
http://[nombre máquina windows]:8080/servlet/HolaServlet
 



Ejecución como aplicación tipo ventanas

· Hay que gestionar el evento de cerrar la aplicación


// MiVentana.java

import java.awt.*;

public class MiVentana extends Frame
{
   public static void main(String args[])
   {
      MiVentana f = new MiVentana("Ventana");
   }

   public MiVentana( String s )
   {
      // llama al constructor de la clase base
      super( s );
      Label a = new Label( "Hola Mundo." );
      add( "North", a );
      resize(300, 300);
      show();
   }

   public boolean handleEvent( Event e )
   {
       if ( e.id == Event.WINDOW_DESTROY )
       {
           hide(); // esconde la ventana
           dispose(); // libera recursos
           return true;
       }
       return super.handleEvent( e );
    }
}


Ejecución como aplicación tipo Applet  (Un ejemplo real de applet)

· La clase MiApplet deriva de Applet

// MiApplet.java

import java.applet.Applet;
import java.awt.Graphics;


public class MiApplet extends Applet
{
    public void paint( Graphics g )
    {
        g.drawString( "Hola Mundo.", 25, 25 );
    }
}


Un applet se integra dentro de una página HTML como sigue:

<html>
<head>
<title>Welcome</title>
</head>
<body>
<applet code=MiApplet.class width=200 height=200></applet>
</body>
</html>




Centrar una ventana

Podemos añadir la función centra() a nuestra ventana y llamarla antes de mostrarla:

public void centra()
{
  int anchoPantalla = getToolkit().getScreenSize().width;
  int altoPantalla = getToolkit().getScreenSize().height;
  
  int anchoVentana = getSize().width;
  int altoVentana = getSize().height;
  
  setLocation( (anchoPantalla - anchoVentana) / 2, (altoPantalla - altoVentana) / 2);
}


La versión Foundation de JBuilder no incorpora el asistente para crear Dialog. Para poder ser editado con el IDE un Dialog debe incorporar un panel según la siguiente plantilla:

import java.awt.*;
import java.awt.event.*;

public class MiDlg extends Dialog
{
  Panel dlgPanel = new Panel();

  public MiDlg(Frame parent, String tit)
  {
    super(parent, tit);
    jbInit();
    add(dlgPanel);
  }

  private void jbInit()
  {
    dlgPanel.setLayout(null);
  }
}

En
demodlg.zip se muestra cómo manipular un helper con un diálogo activado desde un Frame.


Aplicación J2ME

Java diseñado para pequeños dispositivos como teléfonos móviles ó PDAs. También disponible para PocketPC (
http://www.comp.lancs.ac.uk/computing/users/fittond/ppcjava.htm)

import javax.microedition.lcdui.*;
import javax.microedition.midlet.*;
public class HolaMidlet extends MIDlet implements CommandListener {
  private Form mainScreen;
  private Display myDisplay;
  private Command cmExit;
  public HolaMidlet() {
    myDisplay = Display.getDisplay(this);
    mainScreen = new Form("HolaMundo");
    StringItem strItem = new StringItem("Hola", "Hola Mundo");
    mainScreen.append(strItem);
    cmExit = new Command("Exit", Command.SCREEN, 1);
    mainScreen.addCommand(cmExit);
    mainScreen.setCommandListener(this);
  }
  public void startApp() throws MIDletStateChangeException {
    myDisplay.setCurrent(mainScreen);
  }
  public void pauseApp() {
  }
  public void destroyApp(boolean unconditional) {
  }
  public void commandAction(Command c, Displayable s)
  {
    if (c == cmExit)
    {
      destroyApp(false);
      notifyDestroyed();
    }
  }
}



2.- Applets de JAVA
 

Para empezar como lectura introductoria sobre applets puede empezarse por el tutorial en castellano de Agustín Froufe en http://www.fie.us.es/info/internet/JAVA/Cap2/holamapp.html

En particular la cuestión del ciclo de vida de los applets está expuesta en el mismo tutorial en http://www.fie.us.es/info/internet/JAVA/Cap2/metodv.html

Ya en inglés sobre applets  puede consultarse la versión del tutorial de Sun en http://java.sun.com/docs/books/tutorial/applet/overview/index.html
 

En http://www.galeon.com/juegosjava/menu.html se encuentra una buena colección de juegos interactivos basados en applets de Java.

En relación a la optimización de los tiempos de carga y respuesta de los applets se pueden consultar los siguientes artículos:


En cuanto a applets y seguridad (certificados):


Un applet puede abrir un fichero en el servidor de donde ha sido descargado y leer datos de él. Para ello hay que añadir las siguientes líneas de código:

    URL url = new URL(getDocumentBase(), "fichero.txt");
    InputStream in = url.openStream();
 

Para implementar una solución cliente/servidor el applet deberá comunicarse con un servidor. Una posibilidad es usar RMI. Esta opción, si bien puede ser óptima en un ámbito tipo red de área local presenta ciertas limitaciones en entornos tipo Internet en que los proxies y firewalls pueden presentar problemas de conexión. La otra posibilidad es comunicar el applet con un servlet vía HTTP. Las formas de cómo realizarlo se describen en el artículo http://www.gamelan.com/journal/techworkshop/012600_appserv.html

Un applet puede ser descargado y ejecutarse autónomamente, sin conexión con otro proceso pero realizando ciertas funcionalidades. En el ejemplo siguiente se muestra como provocar una llamada a una página web en función de un click del mouse. El ejemplo puede ampliarse carruseles dinámicos de imágenes o a mapas.

3.- Ejemplo de Marquee  
 

import java.awt.*;
import java.applet.*;
import java.net.*;
 

public class Marquee extends Applet implements Runnable
{
   String text;
   Font font = new Font("Helvetica", 1, 24);
   Color color = new Color(94, 60, 215);
   int xpos = 0;
   Thread killme = null;

   public void init()
   {
      text = getParameter("TEXT");
   }

   public void paint(Graphics g)
   {
     setBackground(new Color(196, 196, 210));
      g.setFont(font);
      g.setColor(color);
      g.drawString(text, xpos, 25);
   }

   public void start()
   {
      if(killme == null)
      {
         killme = new Thread(this);
         killme.start();
      }
   }
   
   public void setText(String s)
   {
     text = s;
   }
   
   public void setCoord(int x)

   {
     xpos = x; 
   }

     
   public void setCoord()
   {
      xpos = xpos-5;
      if(xpos < -120)
         xpos = size().width;
   }

   public void run()
   {
      while(killme != null)
      {
         try
         {
            Thread.sleep(100);
         }
         catch (InterruptedException e){}

         setCoord();
         repaint();
      }
   }

   public void stop()
   {
      killme = null;
   }

   public boolean mouseDown(Event e, int x, int y)
   {
      try
      {
        URL u = new URL("http://www.yahoo.com");
        getAppletContext().showDocument(u);
      }
      catch (Exception ex)
      {
        System.out.println(ex); // es enviado a la Java console
      }
      return false;
   }

}


 

HTML asociado ilustrando la interacción con JavaScript:

<HTML>
<BODY>
<H2>Interacci&oacute;n de JavaScript con Java</H2>
<FORM>
<APPLET
CODEBASE = "."
CODE = "Marquee.class"
NAME = "TestApplet"
WIDTH = 240
HEIGHT = 35
HSPACE = 0
VSPACE = 0
ALIGN = middle
>
<PARAM NAME="TEXT" VALUE="Hola">
</APPLET>
<P>
<INPUT TYPE="BUTTON" VALUE="Poner coordenada a 30" onClick="coord(30);"><BR>
<HR>
<INPUT TYPE="TEXT" VALUE="Adiós" NAME="TEXTO" SIZE="25">
<INPUT TYPE="BUTTON" VALUE="Poner texto" onClick="text(window.document.forms[0].elements['TEXTO'].value);"><BR>
</FORM>
</BODY>
<SCRIPT>
  function coord(x)
  {
    window.document.TestApplet.setCoord(x);
  }
  
  function text(s)
  {
    window.document.TestApplet.setText(s);
  }
</SCRIPT>
</HTML>

4.- Introspección y Java reflection

Uno de los puntales de los JavaBeans son los mecanismos de introspección:
(Nota: Para que el ejemplo funcione es preferible declarar public algunos campos de la clase Rectangulo ya utilizada en la Ud 3)

import java.lang.reflect.*;

public class PruebasReflection
{

  public static void main(String[] args) throws Exception
  {
    Rectangulo r = new Rectangulo(1, 2, 3, 4);
    Class clase = r.getClass();  // También valdría Class clase = Rectangulo.class;
    
    System.out.println("r es de la clase " + clase.getName());
    
    // introspección de los campos públicos
    // getDeclaredFieds() devuelve los declarados excluyendo los heredados
    Field[] campos = clase.getFields();
    
    for (int i = 0; i < campos.length; i++)
    {
      Class tipoCampo = campos[i].getType();
      System.out.println(campos[i].getName() + " es del tipo " + tipoCampo.getName());
    }
    
    // introspección de los métodos públicos
    // getDeclaredMethods() devuelve los declarados excluyendo los heredados
    Method[] metodos = clase.getMethods();
    
    for (int i = 0; i < metodos.length; i++)
    {
      Class tipoValorRetorno = metodos[i].getReturnType();
      Class[] tiposParametros = metodos[i].getParameterTypes();
      System.out.println("Método: " + metodos[i].getName());
      System.out.println(" Retorno: " + metodos[i].getReturnType().getName());
      
      for (int j = 0; j < tiposParametros.length; j++)
      {
        System.out.println(" Parámetro: " + tiposParametros[j].getName());
      }
    }
   
    // ejemplo del CallByName del método setX(int valor)
   
    Class[] tiposParams = new Class[1];
    tiposParams[0] = Integer.TYPE; // Interesante ver cómo referenciar a los tipos primitivos
    Method metodoSetBase = clase.getMethod("setBase", tiposParams);
   
    Object[] params = new Object[1];
    params[0] = new Integer(8);
    metodoSetBase.invoke(r, params);
   
    // comprobación:
    System.out.println("Nueva base: " + r.getBase());
   
    // CallByName de area
    tiposParams = new Class[0]; // truco para indicar que no recibe parámetros
    Method metodoArea = clase.getMethod("area", tiposParams);
    params = new Object[0];
    Double area = (Double) metodoArea.invoke(r, params);
    System.out.println("Nueva área: " + area.doubleValue());
  }
}

A continuación se dispone de una clase Rectangulo reducida en caso de no tener a mano la de la Ud3 

public class Rectangulo
{
  private int base;
  private int altura;
  public int x;
  protected int y;
  public Rectangulo(int x, int y, int base, int altura)
  {
    this.x = x;
    this.y = y;
    setBase(base);
    setAltura(altura);
  }
  public void setBase(int valor)
  {
    base = valor;
  }
  public int getBase()
  {
    return base;
  }
  public void setAltura(int valor)
  {
    altura = valor;
  }
  public int getAltura()
  {
    return altura;
  }
  public double area()
  {
    return base * altura;
  }
}



A continuación se muestra un ejemplo curioso. Se trata de un programa que crea un fichero ASCII de código fuente denominado Holax.java, lo compila generando el fichero Holax.class, a continuación carga la clase Holax con el método forName() y finalmente crea una instancia de la clase Holax con newInstance(). Debido a que el constructor de la clase Holax imprime "Holax" se comprueba que realmente dicho constructor se ejecuta.

import java.io.*;

public class Autogeneracion
{
  public static void main(String[] args) throws Exception
  {
    FileOutputStream f = new FileOutputStream("Holax.java");
    PrintWriter p = new PrintWriter(f);
    
    p.println("public class Holax");
    p.println("{");
    p.println(" public Holax()");
    p.println(" {");
    p.println(" System.out.println(\"Holax\");");
    p.println(" }");
    p.println("}");
    p.close();
    
    String params[] = new String[1];
    params[0] = "Holax.java";
    
    // Este paquete viene con el JDK pero no está documentado.
    // En realidad el comando "javac" es equivalente a "java sun.tools.javac.Main"
    new sun.tools.javac.Main(System.out, "javac").compile(params);
    
    Class clase = Class.forName("Holax");
    clase.newInstance();
  }
}

5.- JavaBeans

Los JavaBeans constituyen el sistema de software basado en componentes de la plataforma Java. Existe un mercado de componentes de software JavaBeans. Actualmente la mayoría de proveedores de componentes ofrecen tanto la versión ActiveX como la JavaBean. Un directorio de referencia global sobre este mercado está en http://www.flashline.com y para el mercado español está http://www.ramblainf.com
Un JavaBean es en terminología UML un estereotipo. No se define como una subclase de una clase o un interface sino que responde a un cierto modo de implementar una clase.
Las reglas básicas son:

Con lo dicho anteriromente la propiedad ya aparece en la ventana de propiedades y es utilizable lo que nos valdrá en la mayoría de los casos

Adicionalmente puede aumentarse el nivel de personalización de los JavaBeans que puede realizar a dos niveles:

El otro aspecto personalizable son los eventos. Se adjunta un ejemplo completo en que se crea un Bean a partir de la clase java.awt.List y a la que se añade el evento TopeEvent que se dispara al seleccionar al primero o últmo elemento. El método getDonde() indicará si estamos al principio o al fin:

 

/**************************************************************
* Ejemplo completo de JavaBean con custom event
**************************************************************/
package PruebaBeanEvent;

import java.util.*;

public class TopeEvent extends EventObject
{
  private String donde;
  
  public TopeEvent(Object origen)
  {
    super(origen);
  }
  
  public TopeEvent(Object origen, String donde)
  {
    super(origen);
    this.donde = donde;
  }
  
  public String getDonde()
  {
    return donde;
  }
}


///////////////////////////////////////////////////////////////
package PruebaBeanEvent;

import java.util.*;

public interface TopeEventListener extends EventListener
{
  public void topeAlcanzado(TopeEvent e);
}


///////////////////////////////////////////////////////////////
package PruebaBeanEvent;

import java.util.*;
import java.awt.*;
import java.awt.event.*;

public class Lista extends List
{
  private transient Vector topeEventListeners;
  
  public Lista()
  {
    super();
    try
    {
      jbInit();
    }
    catch (Exception e)
    {
      e.printStackTrace();
    }
  }
  
  private void jbInit() throws Exception
  {
    this.setFont(new Font("Courier", 0, 12));
    this.setSize(new Dimension(151, 177));
    this.addItemListener(new java.awt.event.ItemListener()
    {
      public void itemStateChanged(ItemEvent e)
      {
        this_itemStateChanged(e);
      }
    });
  }

  void this_itemStateChanged(ItemEvent e)
  {
    int item = getSelectedIndex();
    int n = getItemCount();
    
    if (item == 0)
    {
      fireTopeEvent("Inicio");
    }
   
    if (item == n - 1)
    {
      fireTopeEvent("Fin");
    }
  }
  
  public synchronized void addTopeEventListener(TopeEventListener el)
  {
    if (topeEventListeners == null)
      topeEventListeners = new Vector();
    topeEventListeners.addElement(el);
  }
  
  public synchronized void removeTopeEventListener(TopeEventListener el)
  {
    if (topeEventListeners != null)
      topeEventListeners.removeElement(el);
  }
  
  private void fireTopeEvent(String donde)
  {
    if (topeEventListeners != null)
    {
      Vector v = (Vector)topeEventListeners.clone();
      TopeEvent e = new TopeEvent(this, donde);
      
      for (int i = 0; i < v.size(); i++)
      {
        TopeEventListener el;
        el = (TopeEventListener) v.elementAt(i);
        el.topeAlcanzado(e);
      }
    }
  }
}


/**************************************************************
* Frame de pruebas
**************************************************************/
package PruebaBeanEvent;

import java.awt.*;
import java.awt.event.*;
import PruebaBeanEvent.*;

public class FramePrueba extends Frame
{
  Lista lista1 = new Lista();
  Button button1 = new Button();
  public FramePrueba()
  {
    try
    {
      jbInit();
    }
    catch (Exception e)
    {
      e.printStackTrace();
    }
  }
  
  public static void main(String[] args)
  {
    FramePrueba framePrueba1 = new FramePrueba();
    framePrueba1.setVisible(true);
  }
  
  private void jbInit() throws Exception
  {
    this.setBackground(new Color(192, 192, 213));
    this.setSize(new Dimension(372, 274));
    this.addWindowListener(new java.awt.event.WindowAdapter()
    {
      public void windowClosing(WindowEvent e)
      {
        this_windowClosing(e);
      }
    });
    lista1.setBounds(new Rectangle(30, 39, 310, 163));
    lista1.addTopeEventListener(new PruebaBeanEvent.TopeEventListener()
    {
      public void topeAlcanzado(TopeEvent e)
      {
        lista1_topeAlcanzado(e);
      }
    });
    button1.setBounds(new Rectangle(154, 226, 68, 28));
    button1.setLabel("Omplir");
    button1.addActionListener(new java.awt.event.ActionListener()
    {
      public void actionPerformed(ActionEvent e)
      {
        button1_actionPerformed(e);
      }
    });
    this.setLayout(null);
    this.add(lista1, null);
    this.add(button1, null);
    
    int anchoPantalla = getToolkit().getScreenSize().width;
    int altoPantalla = getToolkit().getScreenSize().height;
    
    int anchoVentana = getSize().width;
    int altoVentana = getSize().height;
    
    setLocation( (anchoPantalla - anchoVentana) / 2, (altoPantalla - altoVentana) / 2);
  }
  
  void button1_actionPerformed(ActionEvent e)
  {
    lista1.add("Sol |Domingo");
    lista1.add("Luna |Lunes");
    lista1.add("Mercurio |Miércoles");
    lista1.add("Venus |Viernes");
    lista1.add("Tierra |");
    lista1.add("Marte |Martes");
    lista1.add("Jupiter |Jueves");
    lista1.add("Saturno |Sábado");
    lista1.add("Urano |");
    lista1.add("Neptuno |");
    lista1.add("Plutón |");
  }
  
  void this_windowClosing(WindowEvent e)
  {
    System.exit(0);
  }
  
  void lista1_topeAlcanzado(TopeEvent e)
  {
    System.out.println("Evento al " + e.getDonde());
  }
}

Existe en Java el concepto de propiedades ligadas y funciona de forma similar al ejemplo anterior. La idea es que cuando varía una de ellas se dispara el evento PropertyChangeEvent que es enviado a todos los listeners registrados. Luego, como de cualquier EventObject (superclase de PropertyChangeEvent) se puede obtener con getSource() el objeto emisor y discriminar la acción a tomar.
El bean con propiedades ligadas deberá cumplir los siguientes requisitos:

1) import java.beans.*;

2) Referenciar un objeto del tipo PropertyChangeSupport como atributo del bean:

private PropertyChangeSupport gestorLigadas = new PropertyChangeSupport(this);

3) Incluir sendos métodos para dar de alta y de baja los PropertyChangeListeners:

public void addPropertyChangeListener (PropertyChangeListener p)
{

   gestorLigadas.addPropertyChangeListener(p);
}

public void removePropertyChangeListener (PropertyChangeListener p)
{

   gestorLigadas.removePropertyChangeListener(p);
}

4) Modificar los "setters" de las propiedades que queramos que disparen el evento PropertyChangeEvent en los listeners registrados:

public final void set<propiedad> (<Tipo> valor)
{

  ...
   gestorLigadas.firePropertyChange("<nombre_propiedad>", <valor_anterior>, valor);
  ...
}

Los IDE suelen incorporar asistentes para facilitar la personalización de los Beans. Para el caso de Visual Age for Java se puede encontrar interesante información sobre cómo crear custom events en
http://www-4.ibm.com/software/developer/education/cdtut4java/vptut2_cd_1.htm

6.- XML

El libro "Processing XML in Java" se puede consultar on-line en http://www.ibiblio.org/xml/books/xmljava/chapters y es una buena referencia bibliográfica. En castellano se puede consultar un gran tutorial en http://programacion.com/tutorial/joa_xml. Un buen punto de referencia sobre estándares sectoriales se puede encontrar en http://www.xml.org/xml/industry_industrysectors.jsp  

IBM dispone de parsers XML en Java de reconocido prestigio. IBM Japón es líder en este ámbito. Sun también provee un estándar para parsers XML denominado  JAXP y la implantación denominada Xerces. (incluido en Tomcat y  para JDK >= 1.4 está disponible en http://java.sun.com/xml/xml_jaxp.html)  

Estándares XML:

XML (y DTDs): 
http://www.w3.org/TR/REC-xml
XML Namespaces: 
http://www.w3.org/TR/REC-xml-names 
XSLT: 
http://www.w3.org/TR/xslt 
Associating Stylesheets with XML: 
http://www.w3.org/TR/xml-stylesheet 
XPath: 
http://www.w3.org/TR/xpath   (trad. http://www.sidar.org/recur/desdi/traduc/es/xml/xpath.html )
CSS (Level 1): 
http://www.w3.org/TR/REC-CSS1 
XML Schema: 
http://www.w3.org/TR/xmlschema-0 
FOP:
http://www.renderx.com/Tests/doc/html/tutorial.html 
FOP + XSLT: 
http://www.antennahouse.com/XSLsample/XSLsample.htm 


Microsoft ofrece unos módulos para que el Internet Explorer valide muestre el código resultante de las XSLT en
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/xmlsdk/htm/xslt_starter_7cf8.asp 
La compatibilidad del MS IExplorer con los estánadares se trata en http://www.albany.edu/~gilmr/xclass/c5/processors/xalanVsIE.html
Asimismo BEA nos ofrece de un editor XML en
http://dev2dev.bea.com/resourcelibrary/utilitiestools/xml.jsp?highlight=utilitiestools  
Dentro de las herramientas de edición de XSLT es interesante la Sonic Stylus Studio (se recomienda ver los videos de demo) 
http://www.sonicsoftware.com/products/additional_software/stylus_studio/index.ssp 


De XSLT hay un buen tutorial en
http://www.zvon.org/xxl/XSLTutorial/Books/Book1/ y de XPATH otro en http://www.zvon.org/xxl/XPathTutorial/Output_spa/example1.html  
Son interesantes los ejemplos en que se ve paso a paso cómo se ejecuta la XSLT
http://www.zvon.org/xxl/XSLTracer/Output/examplelist.html 
Un tutorial de XML-Schema se puede encontrar en
http://www.zvon.org/xxl/XMLSchemaTutorial/Output/series.html  


Tools de command line (JAXP):

import java.io.*;
import java.net.*;
import javax.xml.parsers.*;
import org.w3c.dom.*;

public class validaxml
{
  public static void main(String[] args) throws Exception
  {
    Document doc = null;
    DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
    factory.setIgnoringElementContentWhitespace(true);
    DocumentBuilder builder = factory.newDocumentBuilder();
    doc = builder.parse(args[0]);
    System.out.println(args[0] +  " Ok");
  }
}
Para arreglar un XML y que se vea bien podemos utilizar:
import java.io.*;
import java.net.*;
import javax.xml.parsers.*;
import javax.xml.transform.*;
import javax.xml.transform.dom.*;
import javax.xml.transform.stream.*;
import org.w3c.dom.*;
public class arreglaxml
{
  public static void main(String[] args) throws Exception
  {
    String ficheroEntrada = "entrada.xml";
    String ficheroSalida = "salida.xml";
    DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
    factory.setIgnoringElementContentWhitespace(true);
    DocumentBuilder builder = factory.newDocumentBuilder();
    Document doc = builder.parse(ficheroEntrada);
    TransformerFactory transFactory = TransformerFactory.newInstance();
    Transformer transformer = transFactory.newTransformer();
    transformer.setOutputProperty(OutputKeys.ENCODING, "ISO-8859-1");
//    transformer.setOutputProperty(OutputKeys.DOCTYPE_PUBLIC, "-//BIT//APEJ//ES");
//    transformer.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, "http://www.miservidor.com/fichero.dtd");
    // No hace falta con JDK >= 1.4
    transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");

    transformer.setOutputProperty(OutputKeys.INDENT, "yes");
    DOMSource source = new DOMSource(doc);
    File newXML = new File(ficheroSalida);
    FileOutputStream os = new FileOutputStream(newXML);
    StreamResult result = new StreamResult(os);
    transformer.transform(source, result);
    os.close();
    System.out.println("Fin");
  }
}

Para validar con DTDs por línea de comando se puede utilizar:

import java.io.*;
import java.net.*;
import javax.xml.parsers.*;
import org.xml.sax.*;
import org.w3c.dom.*;


public class validadtd
{
  public static void main(String[] args) throws Exception
  {
    Document doc = null;
    File f = new File("");
    String fichero = "file:///" + f.getAbsolutePath() + "/" + args[0];
    DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
    factory.setValidating(true);
    factory.setIgnoringElementContentWhitespace(true);
    DocumentBuilder builder = factory.newDocumentBuilder();
    InputSource entrada = new InputSource(fichero);
    System.out.println("DTD detectada = " + entrada.getSystemId());
    doc = builder.parse(args[0]);
    System.out.println(args[0] +  " Ok");
  }
}


Para validar schemas disponemos de Xerces que lo soprorta. Se puede obtener el código fuente de un validador en http://www.masda.vxu.se/~jonasl/xmljava/lab1.html 
En JAXP es:



import java.io.*;
import java.net.*;
import javax.xml.parsers.*;
import org.w3c.dom.*;

public class validaschema
{
  static final String JAXP_SCHEMA_LANGUAGE =
      "http://java.sun.com/xml/jaxp/properties/schemaLanguage";
  static final String W3C_XML_SCHEMA =
      "http://www.w3.org/2001/XMLSchema";
  static final String JAXP_SCHEMA_SOURCE =
      "http://java.sun.com/xml/jaxp/properties/schemaSource";

  public static void main(String[] args) throws Exception
  {
    Document doc = null;
    DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
    factory.setIgnoringElementContentWhitespace(true);
    factory.setNamespaceAware(true);
    factory.setValidating(true);
    factory.setAttribute(JAXP_SCHEMA_LANGUAGE, W3C_XML_SCHEMA);
    factory.setAttribute(JAXP_SCHEMA_SOURCE, new File(args[1]));

    DocumentBuilder builder = factory.newDocumentBuilder();
    doc = builder.parse(args[0]);
    System.out.println(args[0] +  " Ok");
  }
}


Hay alternativas a los XML Schema como Relax NG. Es interesante el artículo http://www.webreference.com/xml/column59/ . La especificación de Relax NG se puede consultar en http://www.oasis-open.org/committees/relax-ng/spec-20011203.html y hay herramientas que lo soportan como Jing http://www.thaiopensource.com/relaxng/jing.html 


DOM significa Document Object Model. Se trata de un conjunto de APIs XML estándar para ser implementadas en diversos lenguajes. Hay implementaciones en Java, C++, .net, etc.


import java.io.*;
import org.apache.xerces.parsers.*;
import org.apache.xerces.dom.*;
import org.apache.xml.serialize.*;
import org.w3c.dom.*;


public class PruebaXML
{
  public static void main(String[] args) throws Exception
  {
    // Interfaz DOM
   
    Document doc = new DocumentImpl();
    Text text;
    
    Element pedido = (Element) doc.createElement ("pedido");
    pedido.setAttribute ("ancho", "45");
    doc.appendChild (pedido);
    
    text = doc.createTextNode ("Etiqueta interna");
    pedido.appendChild (text);

    Element clienteId = (Element) doc.createElement ("clienteId");
    pedido.appendChild (clienteId);
    text = doc.createTextNode ("0123A");
    clienteId.appendChild (text);

    Comment com = doc.createComment("Ejemplo de XML. Bit SA");
    pedido.appendChild(com);
    
    Element items = (Element) doc.createElement ("item");
    pedido.appendChild (items);
    text = doc.createTextNode ("100");
    items.appendChild (text);
    
    items = (Element) doc.createElement ("item");
    pedido.appendChild (items);
    text = doc.createTextNode ("200");
    items.appendChild (text);
    
    System.out.println("Salida XML");

    // con el parser antiguo de Sun era así de simple:
    // doc.write (out);

    //Con Xerces es:

    OutputFormat format = new OutputFormat( doc ); //Serialize DOM
    format.setEncoding("ISO-8859-1"); // OJO: si no se pone utiliza UTF-8
    XMLSerializer serial = new XMLSerializer(System.out, format);
    serial.asDOMSerializer();
    serial.serialize(doc.getDocumentElement());
    // si en lugar de System.out utilizamos un FileOutputStream recordar cerrarlo: f.close();
 
   
    System.out.println("----------------------------");
    System.out.println("Listado del arbol:\n");
    
    NodeList nl = doc.getChildNodes();
    
    System.out.println("\nPrimer nivel:");
    for (int i = 0; i < nl.getLength(); i++)
    {
      System.out.println(nl.item(i).getNodeName());
    }
    
    nl = doc.getElementsByTagName("*");
    
    System.out.println("\nTodos los niveles:");
    for (int i = 0; i < nl.getLength(); i++)
    {
      Node nod = nl.item(i);
      System.out.println(nod.getNodeName() + ": " + nod.getFirstChild().getNodeValue());
    }
  }
}

IBM, Sun, Oracle y Microsoft (entre otras empresas) están apostando fuerte en el campo de XML. En particular Oracle ofrece soluciones Java con soporte para DTDs y XSLT de las que se habla en http://otn.oracle.com/tech/xml/info/htdocs/otnwp/about_xml.htm    

El ejemplo anterior con el parser de Oracle (disponible gratuitamente previo registro en
http://otn.oracle.com/software/content.html) quedaría como sigue:

import java.io.*;
import oracle.xml.parser.*;
import oracle.xml.parser.v2.*;
import org.w3c.dom.*;

public class PruebaXML
{
  public static void main(String[] args) throws Exception
  {
    // Interfaz DOM

    Document doc = new XMLDocument();
    Text text;

    Element pedido = (Element) doc.createElement ("pedido");
    pedido.setAttribute ("ancho", "45");
    doc.appendChild (pedido);

    text = doc.createTextNode ("Etiqueta interna");
    pedido.appendChild (text);

    Element clienteId = (Element) doc.createElement ("clienteId");
    pedido.appendChild (clienteId);
    text = doc.createTextNode ("0123A");
    clienteId.appendChild (text);

    Comment com = doc.createComment("Ejemplo de XML. Bit SA");
    pedido.appendChild(com);

    Element items = (Element) doc.createElement ("item");
    pedido.appendChild (items);
    text = doc.createTextNode ("100");
    items.appendChild (text);

    items = (Element) doc.createElement ("item");
    pedido.appendChild (items);
    text = doc.createTextNode ("200");
    items.appendChild (text);

    System.out.println("Salida XML");
    ((XMLDocument)doc).setEncoding("ISO-8859-1"); // OJO: si no se pone podría salir UTF-8

    ((XMLDocument)doc).print(System.out);
    System.out.flush();

    System.out.println("----------------------------");
    System.out.println("Listado del arbol:\n");

    NodeList nl = doc.getChildNodes();

    System.out.println("Primer nivel:");
    for (int i = 0; i < nl.getLength(); i++)
      System.out.println(nl.item(i).getNodeName());

    nl = doc.getElementsByTagName("*");

    System.out.println("\nTodos los niveles:");
    for (int i = 0; i < nl.getLength(); i++)
    {
      Node nod = nl.item(i);
      System.out.println(nod.getNodeName() + ": " + nod.getFirstChild().getNodeValue());
    }
  }
}

En el siguiente ejemplo se ve cómo descargar una página XML de Internet y cómo obtener un objeto DOM asociado y listar su contenido:

import java.io.*;
import java.net.*;
import org.apache.xerces.parsers.*;
import org.apache.xerces.dom.*;
import org.apache.xml.serialize.*;
import org.w3c.dom.*;
public class PruebaXML2
{
  public static void main(String[] args) throws Exception
  {
    // con Oracle es (acordarse de los imports - ver ej. anterior)
    //// URL url = new URL("file:///C:\\Windows\\Escritorio\\pedido.xml"); // para fichero local
    //URL url = new URL("http://www.bit-net.org/java/pedido.xml");
    //URLConnection conn = url.openConnection();
    //InputStream in = conn.getInputStream();
    //DOMParser dp = new DOMParser();
    //dp.setPreserveWhitespace(true);
    //dp.parse(in);
    //Document doc = dp.getDocument();

    // String fichero = "file:///C:/Windows/Escritorio/pedido.xml";
    String fichero = "http://www.bit-net.org/java/pedido.xml";
    DOMParser dp = new DOMParser();
    dp.setIncludeIgnorableWhitespace(true);
    dp.parse(fichero);
    Document doc = dp.getDocument();

    System.out.println("----------------------------");
    System.out.println("Listado del arbol:\n");
    NodeList nl = doc.getChildNodes();
    System.out.println("Primer nivel:");
    for (int i = 0; i < nl.getLength(); i++)
      System.out.println(nl.item(i).getNodeName());
    nl = doc.getElementsByTagName("*");
    System.out.println("\nTodos los niveles:");
    for (int i = 0; i < nl.getLength(); i++)
    {
      Node nod = nl.item(i);
      System.out.println(nod.getNodeName() + ": " + nod.getFirstChild().getNodeValue());
    }
  }
}

Con JAXP queda:

import java.io.*;
import java.net.*;
import javax.xml.parsers.*;
import org.w3c.dom.*;
public class PruebaXML3
{
  public static void main(String[] args) throws Exception
  {
    // String fichero = "file:///C:/Windows/Escritorio/pedido.xml";
    String fichero = "http://www.bit-net.org/java/pedido.xml";
    DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
//  factory.setValidating(true);
    factory.setIgnoringElementContentWhitespace(true);
    DocumentBuilder builder = factory.newDocumentBuilder();
    Document doc = builder.parse(fichero);

    System.out.println("----------------------------");
    System.out.println("Listado del arbol:\n");
    NodeList nl = doc.getChildNodes();
    System.out.println("Primer nivel:");
    for (int i = 0; i < nl.getLength(); i++)
      System.out.println(nl.item(i).getNodeName());
    nl = doc.getElementsByTagName("*");
    System.out.println("\nTodos los niveles:");
    for (int i = 0; i < nl.getLength(); i++)
    {
      Node nod = nl.item(i);
      System.out.println(nod.getNodeName() + ": " + nod.getFirstChild().getNodeValue());
    }
  }
}


Un aspecto de creciente interés es la generación de capa de presentación mediante la interacción de documentos XML y XSL. Para ilustrarlo disponemos de un ejemplo xmlpruebas.zip basado en las librerías de Oracle.

Para transformaciones XSLT puede tomarse el siguiente código como ejemplo. Está muy de actualidad transformar canales RSS de noticias. La especificación RSS está en
http://blogs.law.harvard.edu/tech/rss y publicaciones on-line RSS son numerosísimas. Para ello falta una XSLT para RSS. Una básica se puede obtener del artículo http://www-106.ibm.com/developerworks/xml/library/x-tiphdln.html  

import java.io.*;
import javax.xml.transform.*;
import javax.xml.transform.stream.*;


public class xslt
{
  public static void main(String[] args) throws Exception
  {
    genXSLT(args[0], args[1], args[2]);
  }

  public static void genXSLT(String xmlFile, String xslFile, String outFile) throws Exception
  {
    OutputStream out = new FileOutputStream(outFile);
    TransformerFactory tFactory = TransformerFactory.newInstance();
    StreamSource sc = new StreamSource(xslFile);
    Transformer transformer = tFactory.newTransformer(sc);
    FileInputStream fin = new FileInputStream(xmlFile);
    StreamSource scfin = new StreamSource(fin);
//    scfin.setSystemId("xxxxx.dtd");
//    System.out.println("System ID = " + scfin.getSystemId());
    transformer.transform(scfin, new StreamResult(out));
    out.close();
  }
}

Y para transformaciones PDF con Fop (y con pequeñas variaciones a texto o AWT) puede utilizarse el siguiente código de referencia:
import java.io.*;
import org.apache.fop.apps.*;
import org.xml.sax.XMLReader;
import org.w3c.dom.*;


public class pdf
{
  public static void main(String[] args) throws Exception
  {
    genPDF(args[0], args[1], args[2]);
  }

  public static void genPDF(File xmlFile, File xslFile, File outFile) throws Exception
  {
    Driver driver = new Driver();
    driver.setRenderer(Driver.RENDER_PDF);  // RENDER_TXT, RENDER_AWT, RENDER_SVG, RENDER_PCL etc.
    InputHandler inputHandler = new XSLTInputHandler(xmlFile, xslFile);
    XMLReader parser = inputHandler.getParser();
    FileOutputStream fout = new FileOutputStream(outFile);
    driver.setOutputStream(fout);
    driver.render(parser, inputHandler.getInputSource());
    driver.reset();
    fout.close();
  }
}

 Información de las API (SAX y DOM ya integrado en el JDK 1.4): http://java.sun.com/j2se/1.4/docs/api/index.html  

 Es habitual encapsular las APIs de XML con un controlador propio. Un ejemplo de ello puede verse en
XmlCtl.java  

XML es la base de los WebServices. Un descriptor WSDL (spec: www.w3.org/TR/wsdl) que significa Web Service Definition Language es un XML que incluye información de tipos de datos según XML-Schema (tutorial: www.zvon.org/xxl/XMLSchemaTutorial) y spec: www.w3.org/XML/Schema), así como las definiciones SOAP (spec: www.w3.org/TR/soap) de los WebServices. Puede verse en ejemplo en webservices.aeroflot.aero/flightstatus.wsdl en el que la compañía rusa Aeroflot ofrece servicios de información sobre vuelos y aeropuertos.
Idea clave: A partir del WSDL se las herramientas de desarrollo generan automáticamente código en su propio lenguaje permitiendo interoperabilidad remota entre platadormas y lenguajes con alto nivel de transparencia. Dada la complejidad de todo ello la WS-I (Web Services Interoperability Organization www.ws-i.org) es una organización que vela por la compatibilidad entre los Web Services.


Unidad anterior - Unidad siguiente