## Servlets

Un servelt es un programa del lado del servidor escrito en lenguaje Java que interactúa con clientes y que normalmente está unido a unservidor de "HyperText Transfer Protocol" (HTTP). Uno uso común para un servlet es ampliar un servidor web proporcionando contenidos web dinámicos.

Los servelts tienen la ventaja sobre otras tecnologías que de están compilados, tienen capacidad de threads interna, y proporcionan un entorno de programación seguro. Incluso las sites web que antes no proporcionaban soporte para servlets, pueden hacerlo ahora usando programas como JRun o el módulo Java para el servidor Web Apache.

La aplicación subastas basada en web usa un servelt para aceptar y procesar entradas del comprador y vendedor a través del navegador y devuelve dinámicamente información sobre el ítem de la subasta hacia el navegador. El programa AuctionServlet se creo extendiendo la clase HttpServlet. Esta clase proporciona un marco de trabajo para manejar peticiones y respuestas HTTP.

Esta sección examina el AuctionServlet e incluye información sobre cómo usar objetos Cookie y Session en un servlet.

- HttpServlet
- El método init
- El método destroy
- El método service
- Peticiones HTTP
- Usar Cookies en Servlets
  - Configurar una Cookie
  - Recuperar una Cookie
  - Generar Sesiones
  - Evitar el Caché Redireccionamiento
- Códigos de Error HTTP
- Leer valores GET y POST
- Threads
- HTTPS

### HttpServlet

La clase AuctionServlet extiende la clase HttpServlet, que es una clase abastracta.

>       public class AuctionServlet extends HttpServlet {

Un servlet puede cargarse cuando se arranca el servidor web o cuando se solicita una petición HTTP a una URL que especifica el servlet. El servlet normalmente es cargado mediante un cargador de clases separado en el sevidor web porque esto permite que el servlet sea recargado descargando el cargador de clases que cargo la clase servlet. Sin embargo, si el servlet depende de otras clases y una de estas clases cambia, necesitaremos actualiza el sello de la fecha del servlet para recargarlo.

Después de cargar un servlet, el primer estado en su ciclo de vida es la llamada a su método init por parte del servidor web. Una vez cargado e inicializado, el siguiente estado en el ciclo de vida del servlet es para servir peticiones. El servlet sirve peticiones a través de las implementaciones de su métodos service, doGet, o doPost.

El servlet puede opcionalmente implementar un método destroy para realizar operaciones de limpieza antes de que el servidor web descarge el servlet.

### El método init

El método init sólo se llama una vez por el servidor web cuando se arranca el servlet por primera vez. A este método se le pasa un objeto ServletConfig que contiene la información de inicialización perteniente al servidor web donde se está ejecutando la aplicación.

El objeto ServletConfig es usado para acceder a la información mantenida por el servidor web incluyendo valores del parámetro initArgs en el fichero de propiedades del servlet. El código del método init usa el objeto ServletConfig para recuperar los valores de initArgs llamando al método config.getInitParameter("parameter").

El método AuctionServlet.init también contacta con el servidor de JavaBeans Enterprise para crear un objeto contexto (ctx). Este objeto e susado en el método service para establecer una conexión con el servidor de JavaBeans Enterprise.

>     Context ctx=null;
>     private String detailsTemplate;

>     public void init(ServletConfig config)
>     		throws ServletException{
>       super.init(config);
>       try {
>         ctx = getInitialContext();
>       }catch (Exception e){
>         System.err.println(
>                     "failed to contact EJB server"+e);
>       }
>       try {
>         detailsTemplate=readFile(
>     	    config.getInitParameter("detailstemplate"));
>       } catch(IOException e) {
>         System.err.println(
>     		"Error in AuctionServlet <init>"+e);
>       }
>     }

### El método destroy

El método destroy es un método de ciclo de vida implementado por servlets que necesitan grabar su estado entre cargas y descargas del servlet. Por ejemplo, el método destroy podría gabar el estado actual del servlet, y la siguiente vez que el servlet sea cargado, el estado grabado podría ser recuperado por el método init. Deberíamos tener cuidado con que no se podría haber llamado al método destroy si la máquina servidor se bloquea.

>     public void destroy() {
>       saveServletState();
>     }

### El método service

El AuctionServlet es un servlet HTTP que maneja peticiones de clientes y genera respuestas a través de su método service. Acepta como parámetros los objetos de petición y respuesta HttpServletRequest y HttpServletResponse.

- HttpServletRequest conteine las cabeceras y los streams de entrada desde el cliente hacia el servidor.

- HttpServletResponse es el stream de salida que se utiliza para enviar información de vuelta desde el servidor hacia el cliente.

El método service maneja peticiones HTTP estándars del cliente recibidas mediante su parámetro HttpServletRequest y delengando la petición a uno de los siguientes métodos designados para manejar peticiones. Los diferentes tipos de peticiones se describen en la sección Peticiones HTTP.

- doGet para GET, GET condicional, y peticiones HEAD.
- doPost para peticiones POST.
- doPut para peticiones PUT.
- doDelete para peticiones DELETE.
- doOptions para peticiones OPTIONS.
- doTrace para peticiones TRACE.

el programa AuctionServlet proporciona su propia implementación del método service que llama a uno de los siguiente métodos basándose en el valor devuelto por la llamada a cmd=request.getParameter("action"). Estas implementaciones de métodos corresponden a las implementacione por defecto proporcionadas por los métodos doGet y doPost llamadas por el método service, pero añade algunas funcionalidades específicas de la aplicación subasta para buscar Beans Enterprise.

- listAllItems(out)
- listAllNewItems(out)
- listClosingItems(out)
- insertItem(out, request)
- itemDetails(out, request)
- itemBid(out, request)
- registerUser(out, request)

>     public void service(HttpServletRequest request,
>                     HttpServletResponse response)
>     		throws IOException {

>       String cmd;
>       response.setContentType("text/html");
>       ServletOutputStream out = response.getOutputStream();
>       if (ctx == null ) {
>         try {
>           ctx = getInitialContext();
>         }catch (Exception e){
>           System.err.println(
>                       "failed to contact EJB server"+e);
>         }
>       }

>       cmd=request.getParameter("action");
>       if(cmd !=null)  {
>         if(cmd.equals("list")) {
>           listAllItems(out);
>         }else
>         if(cmd.equals("newlist")) {
>           listAllNewItems(out);
>         }else if(cmd.equals("search")) {
>           searchItems(out, request);
>         }else if(cmd.equals("close")) {
>           listClosingItems(out);
>         }else if(cmd.equals("insert")) {
>           insertItem(out, request);
>         }else if (cmd.equals("details")) {
>           itemDetails(out, request );
>         }else if (cmd.equals("bid")) {
>           itemBid(out, request) ;
>         }else if (cmd.equals("register")) {
>           registerUser(out, request);
>         }
>       }else{
>         // no command set
>         setTitle(out, "error");
>       }
>       setFooter(out);
>       out.flush();
>     }

### Peticiones HTTP

Una petición es un mensaje enviado desde un programa cliente como un navegador a un programa servidor. La primera línea del mensaje de petición contiene un método que indica la acción a realizar sobre la URL que viene después. Los dos mecanismos más comunes para enviar información al servidor son POST y GET.

- Las peticiones GET podrían pasar parámetros a una URL añadiéndolas a la URL. Estas peticiones pueden ser guardadas en el bookmark o enviadas por correro e incluyen la información de la URL de respuesta.

- Las peticiones POST podrían pasar datos adicionales a la URL enviándolas directamente al servidor de forma separada a la URL. Estas peticiones no pueden ser almacenadas en el bookmark ni enviadas por email y no cambiar la URL de la respuesta.

Las peticiones PUT son la inversa de la peticiones GET. En lugar de leer la página, las peticiones PUT escriben (o almacenan) la página.

Las peticiones DELETE son para eliminar páginas Web.

Las peticiones OPTIONS son para obtener información sobre las opciones de comunicación disponibles en la cadena petición/respuesta.

Las peticiones TRACE son para realizar pruebas de diagnóstico porque permite que el cliente vea lo que se está recibiendo al orto final de la cadena de petición.

### Usar Cookies en servlets

LAs cookies HTTP son exencialmente cabeceras HTTP personalizadas que son pasadas entre el cliente y el servidor. Aunque las cookies no son muy populares, permiten que el estado sea compartido entre dos máquinas. Por ejemplo, cuando un usuario hace login en una site, una cookie puede mantener una referencia verificando que el usuario ha pasado el chequeo de password y puede usar esta referencia para identificar al mismo usuario en futuras visitas.

Las cookies normalmente están asociadas con un servidor. Si configuramos el dominio a .java.sun.com, entonces la cookies está asociada con ese dominio. Si no se configura nignún dominio, la cookie sólo está asociada con el servidor que creó la cookie.

#### Configurar una Cookie

El API Servlet de Java incluye una clase Cookie que podemos usar para configurar o recuperar la cookie desde la cabecera HTTP. Las cookies HTTP incluyen un nombre y una pareja de valores.

El método startSession mostrado aquí está en el programa LoginServlet. En este método, el nombre en la pareja nombre valor usado para crea el Cookie es JDCAUCTION, y un identificador único generado por el servidor es el valor.

>       protected Session startSession(String theuser,
>     		String password,
>             	HttpServletResponse response) {
>         Session session = null;
>         if ( verifyPassword(theuser, password) ) {
>         // Create a session
>           session = new Session (theuser);
>           session.setExpires (sessionTimeout + i
>     	System.currentTimeMillis());
>           sessionCache.put (session);

>         // Create a client cookie
>           Cookie c = new Cookie("JDCAUCTION",
>            	String.valueOf(session.getId()));
>           c.setPath ("/");
>           c.setMaxAge (-1);
>           c.setDomain (domain);
>           response.addCookie (c);
>         }
>         return session;
>       }

Versiones posteriores del API Servlet incluye un API Session, para crear una sesión usando el API Servelt en el ejemplo anterior podemos usar el método getSession.

>        HttpSession session = new Session (true);

El método startSession es llamado mediante peticiones de acción login desde un POST al LoginServlet de esta forma:

>     <FORM ACTION="/LoginServlet" METHOD="POST">
>     <TABLE>
>     <INPUT TYPE="HIDDEN" NAME="action" VALUE="login">
>     <TR>
>     <TD>Enter your user id:</TD>
>     <TD><INPUT TYPE="TEXT" SIZE=20
>       NAME="theuser"></TD>
>     </TR>
>     <TR>
>     <TD>Enter your password:<TD>
>     <TD><INPUT TYPE="PASSWORD" SIZE=20
>       NAME="password"></TD>
>     </TR>
>     </TABLE>
>     <INPUT TYPE="SUBMIT" VALUE="Login" NAME="Enter">
>     </FORM>

La cookie es creada con un edad máxima de -1, lo que significa que el cookie es almacenado pero permanece vivo miesntras el navegador se esté ejecutando. El valor se selecciona en segunod, aunque cuando s eusan valores menores que unos pocos segundos necesitamos tener cuidado con que los tiempos de las máquinas pudieran estar ligeramente desincronizados.

El valor de path puede ser usado para especificar que el cookie sólo se aplica a directorios y ficheros bajo el path seleccionado en esa máquina. En este ejemplo, el path raíz / significa que el cookie es aplicable a todos los directorios.

El valor del dominio en este ejemplo es leído desde los parámetros de inicialización del servlet. Si el dominio es null, el cookie es sólo aplicado a esa máquina de domino.

#### Recuperar un Cookie

El cookie es recuperado desde las cabeceras HTTP con una llamada al método getCookies para solicitarlo:

>       Cookie c[] = request.getCookies();

Posteriormente podemos recuperar la pareja de selecciones nombre y valor llamando al método Cookie.getName para recuperar el nombre y al método Cookie.getValue para recuperar el valor.

LoginServlet tiene un método validateSession que chequea las cookies del usuario para encontrar un cookie JDCAUCTION que fué enviada en este dominio:

>       private Session validateSession
>                     (HttpServletRequest request,
>       		HttpServletResponse response) {
>         Cookie c[] = request.getCookies();
>         Session session = null;
>         if( c != null ) {
>           Hashtable sessionTable = new Hashtable();
>           for (int i=0; i < c.length &&
>                  session == null; i++ ) {
>             if(c[i].getName().equals("JDCAUCTION")) {
>               String key = String.valueOf (c[i].getValue());
>               session=sessionCache.get(key);
>             }
>           }
>         }
>         return session;
>       }

Si usamos el API Servlet podemos usar el siguiente método, observamos que el parámetro es false para especificar que el valor de sesión es devuelto y que no se cree una nueva sesión:

>         HttpSession session = request.getSession(false);

#### Generar Sesiones

El método LoginServlet.validateSession devuelve un objeto Session representado por la clase Session. Esta clase usa un generado desde una secuencia numérica. Esta identificador de sesión numerada es la parte del valor de la pareja de nombe y valor almacenadas en el cookie.

La única forma de referenciar el nombre del usuario en el servidor es con este identificador de sesión, que está almacenado en un sencillo caché de memoria con los otros identificadores de sesión. Cuando un usuario termina una sesión, se llama a la acción LoginServlet de esta forma:

>       http://localhost:7001/LoginServlet?action=logout

El caché de sesión implementado en el programa SessionCache.java incluye un thread para eliminar sesiones más viejas que el tiempo preseleccionado. Este tiempo podría medise en horas o días, dependiendo del tráfico de la web site.

#### Evitar el Caché de Páginas

El método LoginServlet.setNoCache selecciona los valores Cache-Control o Pragma (dependiendo de la versión del protocolo HTTP que estemos usando) en la cabecera de respuesta a no-cache. La cabecera de expiración Expires también se selecciona a 0, alternativamente podemos seleccionar la hora para que se la hora actual del sistema. Incluso si el cliente no cachea la página, frecuentemente hay servidores proxys en una red corporativa que si lo harían. Sólo las páginas que usan Secure Socket Layer (SSL) no son cacheadas por defecto.

>     private void setNoCache (HttpServletRequest request,
>                    HttpServletResponse response) {
>       if(request.getProtocol().compareTo ("HTTP/1.0") == 0) {
>         response.setHeader ("Pragma", "no-cache");
>       } else if (request.getProtocol().compareTo
>                    ("HTTP/1.1") == 0) {
>         response.setHeader ("Cache-Control", "no-cache");
>       }
>       response.setDateHeader ("Expires", 0);
>     }

#### Restringir Accesos y Redireccionamientos

Si instalamos el LoginServlet como el servlet por defecto o el servler a ejecutar cuando se sirva cualquier página bajo el documento raiz, odemos usar cookies para restringir los usuarios a ciertas secciones de la site. Por ejemplo, podemos permitir que los usuarios que tengan cookies con el estado de que han introducido su passweord acceder a secciones de la site que requieren un login y mantener a los otros fuera.

El programa LoginServlet chequea un directorio restringido en este método init. El método init mostrado abajo configura la variable protectedDir a true si la variable config pasada a él especifica un directorio protegido. El fichero de configuración del servidor Web proporciona las configuraciones pasadas a un servlet en la variable config.

>     public void init(ServletConfig config)
>     	          throws ServletException  {
>       super.init(config);
>       domain = config.getInitParameter("domain");
>       restricted = config.getInitParameter("restricted");
>       if(restricted != null) {
>         protectedDir=true;
>       }

Más tarde en los métodos validateSession y service, la variable protectedDir es comprobada y se llama al método HttpResponse.sendRedirect para viar al usuario a la página correcta basándose en sus estados de login y sesión

>       if(protectedDir) {
>         response.sendRedirect (restricted+"/index.html");
>       }else{
>         response.sendRedirect (defaultPage);
>       }

El método init también recupera el contexto del servlet para el servlet FileServlet para que los métodos puedan ser llamados sobre FileServlet en el método validateSession. La ventaja de llamar a los métodos sobre el servlet FileServlet para servir los ficheros desde dentro del servlet LoginServlet, es que obtenemos todas las ventajas de la funcionalidades añadidas dentro del servlet FileServlet como el mepeo de memoria o el chaché de ficheros. La parte negativa es que el código podría no ser portable a otros servidores que no tengan un servlet FileServlet. Este código recupera el contexto FileServlet:

>       FileServlet fileServlet=(FileServlet)
>             config.getServletContext().getServlet("file");

El método validateSession evita que los usuarios sin login de sesión accedan a los directorios restringidos.

### Códigos de Error HTTP

Podemos devolver un código de error HTTP usando el método sendError. Por ejemplo, el código de error HTTP 500 indica un error interno en el seridor, y el código de error 404 indica página no encontrada. Este segmento de código devuelve el código de error HTTP 500:

>       protected void service (HttpServletRequest request,
>             HttpServletResponse response)
>     	throws ServletException {
>          response.sendError (500);
>       }

### Leer Valores GET y POST

El API Servlet tiene un método getParameter en la clase HttpServletRequest que devuelve el valor GET o POST para el nombre suministrado.

- La petición HTTP GET maneja parejas de nombre/valor como parte de la URL. El método getParameter analiza la URL pasada, recupera las parejas name=value determinadas por el caracter (&), y devuelve el valor.

- La petición HTTP POST lle el nombre de las parejas nombre/valor desde el stream de entrada desde el cliente. El método getParameter analiza en el stream de entrada las parejas de nombre/valor.

El método getParameter funciona vien para servlet sencillos, pero si necesitamos recuperar los parámetros POST en el orden enque fueron situados en la página wev o manejar posts multi-parte, podemos escribir nuestro propio para analizar el stram de entrada.

El siguiente ejemplo devuelve los parámetros POST en el orden en que fueron recibidos desde la página Web. Normalmento, los parámetros son almacenados en un Hashtable que no mantiene el orden de secuencia de los elementos almacenados. El ejemplo mantiene una referencia a cada pareja nombre/valoren un vector que puede ser ser analizado para devolver valores en el orden en que fueron recibidos por el servidor.

>     package auction;

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

>     public class PostServlet extends HttpServlet {
>       private Vector paramOrder;
>       private Hashtable parameters;

>       public void init(ServletConfig config)
>     	throws ServletException  {
>         super.init(config);
>       }

>       public void service(HttpServletRequest request,
>     	HttpServletResponse response)
>     	throws IOException {

>         response.setContentType("text/html");
>         PrintWriter out = response.getWriter();
>         if(request.getMethod().equals("POST")
>     	&& request.getContentType().equals(
>     	"application/x-www-form-urlencoded")) {

>           parameters=parsePostData(
>                        request.getContentLength(),
>                        request.getInputStream());
>         }

>         for(int i=0;i<paramOrder.size();i++) {
>           String name=(String)paramOrder.elementAt(i);
>           String value=getParameter((
>                          String)paramOrder.elementAt(i));
>           out.println("name="+name+" value="+value);
>         }
>         out.println("</body></html>");
>         out.close();
>       }

>       private Hashtable parsePostData(int length,
>     	ServletInputStream instream) {
>         String valArray[] = null;
>         int inputLen, offset;
>         byte[] postedBytes = null;
>         boolean dataRemaining=true;
>         String postedBody;
>         Hashtable ht = new Hashtable();
>         paramOrder= new Vector(10);
>         StringBuffer sb = new StringBuffer();

>         if (length <=0) {
>           return null;
>         }
>         postedBytes = new byte[length];
>         try {
>            offset = 0;
>            while(dataRemaining) {
>              inputLen = instream.read (postedBytes,
>     			offset,
>     			length - offset);
>              if (inputLen <= 0) {
>                throw new IOException ("read error");
>              }
>              offset += inputLen;
>              if((length-offset) ==0) {
>                dataRemaining=false;
>              }
>            }
>         } catch (IOException e) {
>           System.out.println("Exception ="+e);
>           return null;
>         }

>         postedBody = new String (postedBytes);
>         StringTokenizer st =
>                 new StringTokenizer(postedBody, "&");

>         String key=null;
>         String val=null;

>         while (st.hasMoreTokens()) {
>           String pair = (String)st.nextToken();
>           int pos = pair.indexOf('=');
>           if (pos == -1) {
>             throw new IllegalArgumentException();
>           }
>           try {
>              key = java.net.URLDecoder.decode(
>                             pair.substring(0, pos));
>              val = java.net.URLDecoder.decode(
>                             pair.substring(pos+1,
>     			pair.length()));
>           } catch (Exception e) {
>              throw new IllegalArgumentException();
>           }
>           if (ht.containsKey(key)) {
>             String oldVals[] = (String []) ht.get(key);
>             valArray = new String[oldVals.length + 1];
>             for (int i = 0; i < oldVals.length; i++) {
>                valArray[i] = oldVals[i];
>             }
>             valArray[oldVals.length] = val;
>           } else {
>             valArray = new String[1];
>             valArray[0] = val;
>           }
>           ht.put(key, valArray);
>           paramOrder.addElement(key);
>         }
>         return ht;
>       }

>       public String getParameter(String name) {
>         String vals[] = (String []) parameters.get(name);
>         if (vals == null) {
>           return null;
>         }
>         String vallist = vals[0];
>         for (int i = 1; i < vals.length; i++) {
>           vallist = vallist + "," + vals[i];
>         }
>         return vallist;
>       }
>     }

Para saber si una petición es POST o GET, llamados al método getMethod de la clase HttpServletRequest. Para determinar el formato de los datos que están siendo posteados, llamamos al método getContentType de la clase HttpServletRequest. Para sencillas páginas HTML, el tipo devuelto por está llamada será application/x-www-form-urlencoded.

Si necesitamos crear un post con más de una parte como el creado por el siguiente formulario HTML, el servler necesitará ller el stream de entrada desde el post para alcanzar las secciones individuales. Cada sección se dstingue por un límite definido en la cabecera post.

>     <FORM ACTION="/PostMultiServlet"
>        METHOD="POST" ENCTYPE="multipart/form-data">
>     <INPUT TYPE="TEXT" NAME="desc" value="">
>     <INPUT TYPE="FILE" NAME="filecontents" value="">
>     <INPUT TYPE="SUBMIT" VALUE="Submit" NAME="Submit">
>     </FORM>

El siguiente ejemplo extrae una descripción y un fichero desde los navegadores del cliente. Lee el stream de entrada buscando una línea que corresponda con un string de límite, lee el contenido de la línea y lueo lee los datos asociados con esa parte. El fichero suvido se muestra simplemente, pero también puede ser escrito en disco:

>     package auction;

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

>     public class PostMultiServlet extends HttpServlet {

>        public void init(ServletConfig config)
>     	throws ServletException  {
>          super.init(config);
>        }

>        public void service(HttpServletRequest request,
>     	HttpServletResponse response)
>     	throws ServletException, IOException {

>          response.setContentType("text/html");
>          PrintWriter out = response.getWriter();
>          if (request.getMethod().equals("POST")
>     	&& request.getContentType().startsWith(
>             "multipart/form-data")) {

>            int index = request.getContentType().indexOf(
>     				"boundary=");
>            if (index < 0) {
>              System.out.println("can't find boundary type");
>              return;
>            }

>            String boundary =
>     		request.getContentType().substring(
>     		                           index+9);
>            ServletInputStream instream =
>                                 request.getInputStream();
>            byte[] tmpbuffer = new byte[8192];
>            int length=0;
>            String inputLine=null;
>            boolean moreData=true;

>     //Skip until form data is reached
>            length = instream.readLine(
>     			tmpbuffer,
>     			0,
>     			tmpbuffer.length);
>            inputLine = new String (tmpbuffer, 0, 0,
>                                     length);

>            while(inputLine.indexOf(boundary)
>                              >0 && moreData) {
>              length = instream.readLine(
>     			tmpbuffer,
>     			0,
>     			tmpbuffer.length);
>              inputLine = new String (tmpbuffer, 0, 0,
>                                       length);
>              if(inputLine !=null)
>                System.out.println("input="+inputLine);
>                if(length<0) {
>                  moreData=false;
>                }
>              }

>              if(moreData) {
>                length = instream.readLine(
>     			tmpbuffer,
>     			0,
>     			tmpbuffer.length);
>                inputLine = new String (tmpbuffer, 0, 0,
>                                         length);

>                if(inputLine.indexOf("desc") >=0) {
>                  length = instream.readLine(
>     			tmpbuffer,
>     			0,
>     			tmpbuffer.length);
>                  inputLine = new String (tmpbuffer, 0, 0,
>                                           length);
>                  length = instream.readLine(
>     			tmpbuffer,
>     			0,
>     			tmpbuffer.length);
>                  inputLine = new String (tmpbuffer, 0, 0,
>                                           length);
>                  System.out.println("desc="+inputLine);
>          	   }
>        	 }

>              while(inputLine.indexOf(boundary)
>                                >0 && moreData) {
>                length = instream.readLine(
>     			tmpbuffer,
>     			0,
>     			tmpbuffer.length);
>                inputLine = new String (tmpbuffer, 0, 0,
>                                         length);
>              }
>              if(moreData) {
>                length = instream.readLine(
>     			tmpbuffer,
>     			0,
>     			tmpbuffer.length);
>                inputLine = new String (tmpbuffer, 0, 0,
>                                         length);

>                if(inputLine.indexOf("filename") >=0) {
>                  int startindex=inputLine.indexOf(
>                                             "filename");
>                  System.out.println("file name="+
>                             inputLine.substring(
>     			startindex+10,
>     			inputLine.indexOf("\"",
>     			startindex+10)));
>                  length = instream.readLine(
>     			tmpbuffer,
>     			0,
>     			tmpbuffer.length);
>                  inputLine = new String (tmpbuffer, 0, 0,
>                                           length);
>                }
>              }
>              byte fileBytes[]=new byte[50000];
>              int offset=0;
>              if (moreData) {
>                while(inputLine.indexOf(boundary)
>                                  >0 && moreData) {
>                  length = instream.readLine(
>     			tmpbuffer,
>     			0,
>     			tmpbuffer.length);
>                  inputLine = new String (tmpbuffer, 0, 0, length);
>                  if(length>0 && (
>                       inputLine.indexOf(boundary) <0)) {
>                    System.arraycopy(
>     			tmpbuffer,
>     			0,
>     			fileBytes,
>     			offset,
>     			length);
>                    offset+=length;
>                  } else {
>                   moreData=false;
>                  }
>                }
>              }
>     // trim last two newline/return characters
>     // before using data
>             for(int i=0;i<offset-2;i++) {
>               System.out.print((char)fileBytes[i]);
>             }
>          }
>          out.println("</body></html>");
>          out.close();
>       }
>     }

### Threads

Un servlet debe ser capaz de manejar múltipels peticiones concurrentes. Cualquier número de usuarios puede en un momento dado invocar al servlet, y mientras que el método init ejecuta siempre un único trehad, el método service es multi-thread para manejar múltiples peticiones.

Esto significa que cualquier campo estático o público accedido por el método service deberían estár restringidos a accesos de un thread. el ejemplo de abajo usa la palabra clave synchronized para restringir el acceso a un contador para que sólo pueda ser actualizado por un thread a la vez:

>       int counter
>       Boolean lock = new Boolean(true);

>       synchronized(lock){
>         counter++;
>       }

### HTTPS

Muchos servidores, navegadores, y el java Plug-In tiene la posibilidad de soportar el protocolo HTTP seguro llamado HTTPS. Este similar al HTTP excepto en que los datos on tramitidos a través de una capa de socket seguro (SSL) en lugar de una conexión de socket normal. Los navegadores web escuchan peticiones HTTP en un puerto mientras escuchan las peticiones HTTPS en otro puerto.

Los datos encriptados que son enviados a través de la red incluyen chequeos para verificar si los dato se han modificado en el tránsito. SSL también autentifica el servidor web a sus clientes proporcionando un certificado de clave pública. en el SSL 3.0 el cliente también puede autentificarse a sí mismo con el servidor, usxando de nuevo un certificado de clave pública.

La clave pública criptográfica (también llamada clave de encriptación asimétrerica) usa una pareja de claves pública y privada. Cualquier mensaje encriptado (hecho ininteligible) con la clave privada de la pareja sólo puede ser desencriptado con la correspondiente clave pública. Los certificados son sentencias firmadas digitalmente generadas por un tercera parte conocidad como "Autoridad de Certificación" Certificate Authority. Esta Autorizar necesita asegurarse de que nosotros somos quien decimos ser porque los clientes se creeran el certificado que reciban. Si es así, este certificado puede contener la clave pública de la pareja de clave pública/privada. El certificado está firmado por la clave privada de la Autoridad de Certificación, y muchos navegadores conocen las claves públicas la mayoría de las Autoridades de Certificación.

Mientras que la encriptaciónde clavepública es buena para propósitos de autentificación, no es tan rápida como la encriptación asimétrica y por eso el protocolo SSL usa ambos tipos de claves en el ciclo de vida de una conexión SSL. El cliente y el servidor empiezan una transación HTTPS con una inicialización de conexión o fase de estrechamiento de manos.

Es en ese momento en el que el servidor es autentificado usando el certificado que el cliente ha recibido. El cliente usa la clave pública del servidor para encriptar los mensajes enviados al servidor. Después de que el cliente haya sido autentificado y el algoritmo de encriptación se ha puesto de acuerdo entre las dos partes, se usan unas nuevas claves de sesión simétrica para encriptar y desencriptar las comunicaciones posteriores.

El algoritmo de encriptación puede ser uno de los más populares algoritmos como "Rivest Shamir and Adleman" (RSA) o "Data Encryption Standard" (DES). Cuando mayor sea el número de bits usados para crear la clave, mayores dificultades para poder romper las claves mediante la fuerza bruta.

HTTPS usando criptografía de clave pública y certificados nos permite proporcionar una gran privacidad a las aplicacioens que necesitan transaciones seguras. Los servidores, navegadores y Java Plug-In tienen sus propias configuraciones para permitir usar Comunicaciones SSL. En general, estos pasos requieren:

- Obtener una clave privada y un certificado firmado digitalmente con la clave pública correspondente.

- Instalar el certificado en una localización especificada por el software que estamos usando (servidor, navegador o Java Plug-In).

- Activar las características SSL y especificar nuestros ficheros de certificado y de clave privada como se explica en nuestra documentación.

Siempre que activemos las características SSL de acuerdo con los requerimientos de la aplicación dependiendo del nivel de seguridad de necesitemos. Por ejemplo no necesitamos verificar la autenticidad de los clientes para navegar por los ítems de la subasta, pero sí querremos ecriptar la información de la tarjeta de crédido y otras informaciones suministradas cuando los compradores y vendedores se registran para participar.

HTTPS puede ser usado para cualquier dato, no sólo ara páginas web HTTP. Los programas escritos en lenguaje Java pueden ser descaradoa a trravés de conexiones HTTPS, y podemos abrir una conexión con un servidor HTTPS en el Java Plug-In. Para escribir un programa en Java que use SSL, este necesita una librería SSL y un conecimiento detallado del proceso de negociación HTTPS. Nuestra librería SSL podría cubir los pasos necesarios ya que está información es restringida por el control de exportación de seguridad.

Ozito