Un ejemplo inicial de conexión applet-servlet

Ramiro Lago (Noviembre 2005)

Nuestro inicio es un applet que lanza una petición POST a un servlet. El servlet devuelve su habitual respuesta en modo texto al applet. Su aspecto es sencillo, arriba aparecen dos campos de edición donde el usuario puede poner el nombre y el valor del parámetro de la petición POST. El texto que genera el servlet aparecerá en el editor.

Archivos de propiedades

Una de los atributos de nuestro applet es un objeto de la clase Utilidades. Se puede ver en el código fuente del applet que lo usamos para varias cosas, la primera y principal es conseguir el host en el que reside el servlet:


	 Utilidades util = new Utilidades();
	 ...
	 String pathServlet = util.getHostHTTP()+"servlet/inicialservlet";

Esto tiene una sencilla explicación, que todo aquel que haya estado en equipos de desarrollo de software puede entender. Habitualmente el desarrollo implica el uso de tres entornos: entorno de desarrollo, que tiene una versión ligera del servidor de aplicaciones, en segundo lugar un entorno de preproducción, que tiene una versión más completa del contexto real de aplicación y, por último, el entorno de producción que es en el que se despliegan estas páginas y aplicaciones. Para evitar tener que recompilar las clases cada vez que cambio de entorno, guardo en un archivo .properties la referencia al host que en cada caso estoy utilizando. Por ejemplo, en el entorno de producción la propiedad 'http.host' señala esta web que está viendo:


http.host=http://www.mi_web_final.com/

Lo que hace la clase Utilidades es cargar las propiedades de dicho archivo:


public class Utilidades {
   private String archivoParametros = "parametros.properties";
   private Properties prop = new Properties();
   private String pathPropiedades;
   public String mensajeError;

   /***************************************************************************
    * Carga en atributo 'prop' el archivo de propiedades
    * El argumento es el path donde se encuentra el archivo de propiedades.
    ***************************************************************************/
   public boolean cargarPropiedades( String path ) {
      try {
	 pathPropiedades = path;
	 URL url = new URL( pathPropiedades + archivoParametros ); // Abro URL
	 prop.load( url.openStream() );     // Cargo propiedades desde InputStream de URL
	 return true;
      }
      catch (MalformedURLException e) {
	 mensajeError = new String( e.toString() );
	 return false;
      }
      catch (IOException e) {
	 mensajeError = new String( e.toString() );
	 return false;
      }
   }
   ....

De esta forma, cambiar de entorno supone cambiar un sencillo archivo de texto, ya que el método getHostHTTP() me devuelve siempre la URL del entorno actual:


   public String getParametro(String clave, String defecto) {
      String retorno = defecto;
      try {
	 retorno = prop.getProperty(clave, defecto);
      }
      catch (Exception e) {
	 retorno = defecto;
      }
      finally {
	 return retorno;
      }
   }
   /****************************************************************************
    * Sobrecargado. Si no encuentra clave, devuelve ""
    ****************************************************************************/
   public String getParametro(String clave) {
      return getParametro(clave, "");
   }

   public String getHostHTTP() {
      return getParametro( "http.host" );
   }

Más adelante podremos ver algún otro servicio que nos da esta clase.

Conectarse al servlet y enviar petición POST

La conexión al servlet sigue un orden conocido:

  1. Obtengo la URL del servlet:
  2. 
    	 String pathServlet = util.getHostHTTP()+"servlet/inicialservlet";
    	 URL urlServlet = new URL( pathServlet );
    	
  3. Con la URL abro la conexión:
  4. 
    	 URLConnection conServlet = urlServlet.openConnection(); // Abro conexión con url
    	

Petición POST

Ya sabemos que en una petición GET los parámetros se mandan en la invocación al servlet y de manera explícita, inmediatamente después del nombre de éste, por ejemplo:


	http://mi_host/servlet/mi_servlet?param1=valor1¶m2=valor2 ...
	

En una petición POST no es así, los parámetros se mandan en la petición, pero de forma "implicita".

Antes de lanzar la petición tengo que conseguir y codificar el parámetro. Para obtener su nombre y valor no hay más que dirigirse al campo de texto correspondiente, mediante getText:


	 String params[] = { txtParam.getText() };
	 String valores[] = { txtValor.getText() };
	

Los guardo en arrays por razones evidentes, aunque en este sencillo ejemplo sólo se manda un parámetro, el código está preparado para el uso de varios.

Debemos codificar los parámetros, puesto que el juego de caracteres que utilizan es especial. Por ejemplo, su codificación no permite espacios en blanco (que se sustituyen por el símbolo '+'). Para ello nos ayudamos de la clase Utilidades, concretamente de un método static:


	 //// Codifico parámetros y valores. Aunque está muy extendida, si uso codificación UTF-8
	 //// no pueden aparecer tildes ni eñes
	 String paramsCodificados = Utilidades.getParamsCodificados( params, valores, "iso-8859-1");
	

Este método codifica uno por uno, todos los nombres y valores de los parámetros. Para ello utilizamos el método static URLEncoder.encode():


   static public String getParamsCodificados( String parametros[], String valores[], String codificacion ) {
      try {

	 StringBuffer paramsCodificados = new StringBuffer();

	 //// Codifica cada param y cada valor, los alamcena en StringBuffer
	 for ( int i = 0; i<parametros.length; i++)
	    paramsCodificados.append( (i==0?"":"&") + URLEncoder.encode(parametros[i], codificacion) + "=" +
				      URLEncoder.encode(valores[i], codificacion) );

	 return paramsCodificados.toString();  // Devuelve cadena con params codificados

      } catch( Exception e) {
	 return null;
      }
   }
	

Una vez hecho esto ya estamos preparados para el envío de los parámetros, que se realiza con la llamada enviarParams( conServlet, paramsCodificados ). Este método es muy sencillo:

  1. Prepara la conexión, indicando que la habilite para la salida, setDoOutput(true), y que no use el caché del navegador:
  2. 
       public void enviarParams( URLConnection con, String params ) throws IOException {
          con.setDoOutput( true);                         // Conexión para salida
          con.setUseCaches( false );                      // El navegador no usa caché
    	
  3. A continuación se abre un stream a partir de la conexión (con.getOutputStream()):
  4. 
          PrintWriter salida = new PrintWriter( con.getOutputStream() );
    	
  5. Por último, se mandan al stream los parámetros y se cierra el flujo:
  6. 
          salida.print( params );
          salida.close();
    	

Así de sencillo.

¿Qué hace el servlet?

Es algo bastante fácil de comprender. Este servlet tiene la salida habitual, pero en vez de ser texto formateado al estilo HTML, es texto puro. Su respuesta es el conjunto de parámetros y de cabeceras de la petición:


   public void doGet(HttpServletRequest request, HttpServletResponse response)  throws ServletException, IOException {
      response.setContentType("text/html; charset=iso-8859-1");  // Definir tipo de salida
      PrintWriter out = response.getWriter();                    // Obtener flujo salida

      out.println("PARAMETROS DE LA PETICION:" );
      for ( Enumeration e= request.getParameterNames(); e.hasMoreElements(); ) {
	 String nom_par = (String) e.nextElement();
	 out.println("  Parámetro: " + nom_par + ". Valor: " + request.getParameter( nom_par ));
      }

      out.println("CABECERAS (HEADERS) DE LA PETICION:" );
      for ( Enumeration e= request.getHeaderNames(); e.hasMoreElements(); ) {
	 String nom_h = (String) e.nextElement();
	 out.println("  Header: " + nom_h + ". Valor:" + request.getHeader( nom_h ));
      }
   }
	

¿Cómo recibe el applet la salida (respuesta) del servlet?

Como todo lo anterior, de forma sencilla. El trabajo lo hace el método recibirRespuesta():

  1. Para mandar los parámetros obteníamos un stream de salida a partir de la conexión. Ahora es a la inversa, conseguimos un stream de entrada (también a partir de la conexión). En este caso, puesto que recibimos texto, podemos usar un método sencillo: crear un stream del tipo InputStreamReader. Cuando recibamos objetos utilizaremos ObjectInputStream, pero eso queda para otro ejemplo. Por ahora:
  2. 
          InputStreamReader streamEntrada = new InputStreamReader (con.getInputStream());
    	
  3. A continuación obtenemos un lector de buffer (que resulta algo más cómodo que trabajar byte a byte o caracter a caracter):
  4. 
          BufferedReader bfEntrada = new BufferedReader( streamEntrada );
    	
  5. Lo siguiente es el manejo habitual de un stream de entrada. En nuestro caso, cada línea se la enviamos al editor, por medio de llamadas nuestro método println() (sí, el nuestro; no es System.out.println() ):
  6. 
          String linea;
          while ((linea = bfEntrada.readLine()) != null )
    	 println( linea );
    
          bfEntrada.close();
    	



Volver al índice