<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-9553291</id><updated>2012-02-02T23:36:27.659Z</updated><title type='text'>La luna ilumina por igual a culpables e inocentes</title><subtitle type='html'>Programación en general y tonterías en particular.</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://rincew.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://rincew.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Javier J.</name><uri>http://www.blogger.com/profile/00715175463383886462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>75</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-9553291.post-3836411377062850338</id><published>2011-09-09T22:43:00.004Z</published><updated>2011-09-09T23:14:25.967Z</updated><title type='text'>Cómo utilizar el API de Paypal en Java (III)</title><content type='html'>&lt;strong&gt;4. Poniendo el código en el proyecto&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;He creado un sencillo proyecto con un único paquete y he puesto en él los dos ficheros de código generados por el wizard de Paypal. Como se puede ver tienen algunos fallitos, pero nada que sea difícil de corregir.&lt;br /&gt;&lt;br /&gt;Vamos a empezar añadiendo al código el botón de pago por Paypal (creado por el wizard). Cuando el cliente pulsa este botón, significa que quiere pagar mediante Paypal, así que tendremos que hacer la operación &lt;em&gt;SetExpressCheckout&lt;/em&gt;.&lt;br /&gt;&lt;br /&gt;Por no complicar el ejemplo, utilizaré el formulario vacío creado por el wizard. He añadido el formulario al servlet &lt;em&gt;EjemploPaypalServlet &lt;/em&gt;y he modificado la URL de la etiqueta &lt;em&gt;action &lt;/em&gt;del formulario. El código completo se muestra a continuación.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;package ejemplopaypal;&lt;br /&gt;&lt;br /&gt;import java.io.IOException;&lt;br /&gt;import javax.servlet.http.*;&lt;br /&gt;&lt;br /&gt;@SuppressWarnings("serial")&lt;br /&gt;public class EjemploPaypalServlet extends HttpServlet {&lt;br /&gt;public void doGet(HttpServletRequest req, HttpServletResponse resp)&lt;br /&gt;throws IOException {&lt;br /&gt;HttpSession session = req.getSession(true);&lt;br /&gt;session.setAttribute("Payment_Amount", "30.00");&lt;br /&gt;&lt;br /&gt;resp.setContentType("text/html");&lt;br /&gt;resp.getWriter().println("-- Código del formulario aquí --");&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Al pulsar el botón, se ejecutarà el código que invoca la operación de la API que ya generó el wizard (archivo expresscheckout.java), pero hay que modificar este archivo un poco para que funcione.&lt;br /&gt;&lt;br /&gt;Además, se ha añadido al servlet una variable de sesión que guarda el total del pedido que facturará Paypal.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;5. Operación SetExpressCheckout&lt;br /&gt;&lt;br /&gt;&lt;/strong&gt;El archivo expresschecout.java es un Servet que realiza una operación utilizando los métodos del paypalfunctions.java, en concreto, el método: CallShortcutExpressCheckout (paymentAmount, returnURL, cancelURL);&lt;br /&gt;&lt;br /&gt;Lo primero que tenemos que hacer es añadirle la declaración del paquete. En mi caso es esta: package ejemplopaypal;&lt;br /&gt;Una vez añadida esta línea, el código ya no da más errores sintácticos.&lt;br /&gt;Para poder invocar a CallShortcutExpressCheckout necesitamos tres parámetros: la cuantía total de la factura, la URL a la que paypal nos enviará si el cliente confirma el pago y la URL la que nos enviará si surge un error. Los dos últimos parámetros ya los tenemos bien configurados gracias a las URLs que pusimos en el wizard.&lt;br /&gt;La cantidad, se toma de una variable de sesión de tipo String con clave Payment_Amount. Ya vimos en el código de EjemploPaypalServlet como ponerlo.&lt;br /&gt;&lt;br /&gt;Para aprender un poco como funciona Paypal, os aconsejo que hagáis una modificación al código para que os muestre todo lo que Paypal responde al invocar al método CallShortcutExpressCheckout . Para eso, podemos modificar el código del archivo expresscheckout.java) añadiendo las siguientes líneas:&lt;br /&gt;&lt;br /&gt;&lt;blockquuote&gt;&lt;br /&gt;// Buscar esta línea&lt;br /&gt;HashMap nvp = ppf.CallShortcutExpressCheckout (paymentAmount, returnURL, cancelURL);&lt;br /&gt;&lt;br /&gt;//* -- Añadir&lt;br /&gt;for (Object o: nvp.entrySet()) {&lt;br /&gt;Map.Entry e = (Map.Entry)o;&lt;br /&gt;System.out.println(e.getKey() + ": " + e.getValue());&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;String strAck = nvp.get("ACK").toString();&lt;br /&gt;&lt;/blockquuote&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Nota: Google Application Engine soporta las sesiones de Servlets, pero este soporte no está activdo por defecto. Para que funcione es necesario añadir la etiqueta &lt;em&gt;sessions-enabled &lt;/em&gt;con un valor a &lt;em&gt;true &lt;/em&gt;&lt;br /&gt;&lt;br /&gt;Aún no podemos ejecutar el sistema, ya que &lt;em&gt;paypalfunctions &lt;/em&gt;tiene errores que hay que corregir. Eso es justo lo que haremos a continuación.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9553291-3836411377062850338?l=rincew.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rincew.blogspot.com/feeds/3836411377062850338/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9553291&amp;postID=3836411377062850338' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/3836411377062850338'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/3836411377062850338'/><link rel='alternate' type='text/html' href='http://rincew.blogspot.com/2011/09/como-utilizar-el-api-de-paypal-en-java.html' title='Cómo utilizar el API de Paypal en Java (III)'/><author><name>Javier J.</name><uri>http://www.blogger.com/profile/00715175463383886462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9553291.post-1563782341306786778</id><published>2011-08-30T08:54:00.004Z</published><updated>2011-08-30T09:09:59.714Z</updated><title type='text'>Cómo utilizar el API de Paypal en Java  (II)</title><content type='html'>&lt;strong&gt;2.5. Creando una tienda de prueba&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;Antes de continuar, vamos a crear una cuenta vendedora para usarla en nuestro ejemplo. Lo primero es ir al sandbox (https://developer.paypal.com/) y hacer login con el usuario y clave que registrado en el paso anterior (ver la primera entrada de este tutorial).&lt;br /&gt;&lt;br /&gt;Allí, vamos a l opción API Credentials y creamos una nueva cuenta asegurándonos que es una cuenta de tipo vendedor (Seller). Cuando tengamos el código hecho, comprobaremos que nuestra cuenta compradora ha pagado y nuestra cuenta vendedora ha recibido el dinero. Pero eso será al final del tutorial.&lt;br /&gt;&lt;br /&gt;Una vez hecho esto, veremos los datos que necesitamos para acceder a la API. Vamos a copiarlos que los necesitaremos más adelante. Un detalle, comprobad que las cuentas están &lt;em&gt;enabled&lt;/em&gt;, si alguna os aparece como &lt;em&gt;disable&lt;/em&gt;, pulsad en esa misma palabra (“&lt;em&gt;disabled&lt;/em&gt;”) para activarla.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;3. Creando el código con el wizard de Paypal&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;Por suerte, Paypal puede generar el código que necesitamos gracias a su wizard. La URL es: https://www.paypal-labs.com/integrationwizard/&lt;br /&gt;&lt;br /&gt;En el wizard, elegimos la opción 1, así crearemos el código necesario para el flujo de Express Checkout (consulta la documentación de desarrolladores de Paypal, ahí viene bien explicado).&lt;br /&gt;&lt;br /&gt;En cuanto a lenguaje, elegimos “Java SDK”, como URLs yo usaré: http://localshost:8888/sucess y http://localshost:8888/cancel. Si tu contenedor de Servlets (Tomcat, Jetty, etc.) está configurado para usar una URL distinta, usa dicha URL. &lt;br /&gt;Ahora, copia el código  HTML generado y guárdalo. &lt;br /&gt;&lt;br /&gt;Copia los dos archivos .java y guárdalos también. Veamos lo que son. El archivo expresscheckout.java es el servlet que ejecuta la primera operación del API &lt;em&gt;SetExpressCheckout &lt;/em&gt;y redirige al servidor de Paypal. El archivo paypalfunctions.java es un objeto que contiene métodos para invocar operacons del API de Paypal que vamos a necesitar.&lt;br /&gt;&lt;br /&gt;S quieres puedes continuar generando el código para elr estod e los psos. Pero, con lo que ya tenemos, podemos montar la primera operación (SetExpresscheckout) e, incluso, las otras dos operaciones escirbiéndo algo de código por nosotros mismos.&lt;br /&gt;&lt;br /&gt;Si quieres puedes continuar generando el código para el resto de los pasos. Pero, con lo que ya tenemos, podemos montar la primera operación e, incluso, las otras dos operaciones escribiendo algo de código por nosotros mismos.&lt;br /&gt;&lt;br /&gt;Manos a la obra (en la siguiente entrega).&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9553291-1563782341306786778?l=rincew.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rincew.blogspot.com/feeds/1563782341306786778/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9553291&amp;postID=1563782341306786778' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/1563782341306786778'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/1563782341306786778'/><link rel='alternate' type='text/html' href='http://rincew.blogspot.com/2011/08/como-utilizar-el-api-de-paypal-en-java_30.html' title='Cómo utilizar el API de Paypal en Java  (II)'/><author><name>Javier J.</name><uri>http://www.blogger.com/profile/00715175463383886462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9553291.post-1115586797562011757</id><published>2011-08-27T09:29:00.003Z</published><updated>2011-08-27T09:37:23.052Z</updated><title type='text'>Cómo utilizar el API de Paypal en Java</title><content type='html'>El objetivo de este tutorial es crear un sistema Java muy básico que sea capaz de realizar un pago utilizando Paypal. Nuestro sistema se conectará con Paypal, le dirá lo que tiene que cobrar, nos enviará a la página de Paypal y recibirá el resultado de la operación.&lt;br /&gt;&lt;br /&gt;Lo que necesitas para seguir este tutorial es el J2EE de Java (cualquier versión vale), ya que en este tutorial usaremos Servlets. Además, necesitarás un servidor capaz de ejecutar los Servlets. Cualquier Tomcat o Jetty debería servir. En mi caso concreto he utilizado Google Application Engine, no por nada en especial sino porque es lo que tengo más a amno ahora.&lt;br /&gt;&lt;br /&gt;Empezamos.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;1. Un poco de teoría&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;Lo que vamos a implementar es un pago express, o express checkout, el cuál es uno de los servicios que Paypal ofrece. Para hacer este pago sólo tenemos que hacer tres operaciones con (y una de ella no es obligatoria). Veámoslas:&lt;br /&gt;&lt;br /&gt;&lt;em&gt;SetExpressCheckout&lt;/em&gt;: Prepara un nuevo pago. La llamaremos justo antes de enviar a un cliente a Paypal. El servidor nos devolverá el token que identifica el pago.&lt;br /&gt;&lt;br /&gt;&lt;em&gt;GetExpressCheckout (opcional)&lt;/em&gt;: Nos permite obtener información sobre el cliente.&lt;br /&gt;&lt;br /&gt;&lt;em&gt;DoExpressCheckoutPayment&lt;/em&gt;: Concluye y confirma el pago.&lt;br /&gt;&lt;br /&gt;Para comunicarnos con Paypal (y hacer las tres operaciones anteriores) tenemos dos opciones, utilizar la API NVP y la API SOAP. NVP son la siglas de Name-Value Pair (par clave-valor) y eso es exactamente en lo que consiste, en construir conjuntos de pares clave valor, con la información que Paypal necesita y mandárselo. En este ejemplo usaremos la API NVP.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;2. Creando un usuario de sandbox&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;Para hacer pruebas necesitamos usuarios de prueba: un usuario que represente la tienda a la que pagamos y usuarios que representen los clientes que hacen el pago. Algunos de ejemplos ya vienen configurados para hacer pagos a una tienda imaginaria, así que vamos a empezar creando un cliente imaginario. Este cliente será el que usemos en Paypal para pagar.  Para crear los clientes de pruebas podemos usar esta URL https://developer.paypal.com/&lt;br /&gt;&lt;br /&gt;La manera de trabajar es registrarnos en el sandbox para, mediante nuestra cuenta, generar los usuarios de prueba que necesitemos. El proceso no es complejo. La cuenta de correo debe ser auténtica ya que nos mandarán un mensaje para activarla.&lt;br /&gt;&lt;br /&gt;Una vez activa, entramos con nuestro correo y contraseña y ya podemos crear nuestra primera test account. En mi caso he creado una cuenta de comprador (buyer)  preconfigurada&lt;br /&gt;&lt;br /&gt;Ojo que las cuentas se crean con el mismo dominio que la dirección e correo original y variantes aleatorias. Con este método se pueden crear todas las que se quieran.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Ahora sí vámonos de cabeza al código.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;em&gt;Continuará en un par de días.&lt;/em&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9553291-1115586797562011757?l=rincew.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rincew.blogspot.com/feeds/1115586797562011757/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9553291&amp;postID=1115586797562011757' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/1115586797562011757'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/1115586797562011757'/><link rel='alternate' type='text/html' href='http://rincew.blogspot.com/2011/08/como-utilizar-el-api-de-paypal-en-java.html' title='Cómo utilizar el API de Paypal en Java'/><author><name>Javier J.</name><uri>http://www.blogger.com/profile/00715175463383886462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9553291.post-2067679018145203851</id><published>2011-08-09T10:22:00.002Z</published><updated>2011-08-09T10:24:46.352Z</updated><title type='text'>Nueva página sobre Test-Driven Development y testing: www.portaltdd.org</title><content type='html'>Actualizo este blog para comentar que estoy participando en la página Portal TDD (www.portaltdd.org)&lt;br /&gt;&lt;br /&gt;El objetivo de este portal es publicar referencias a toda la información que aparece en Internet sobre Test-Driven Development y pruebas del softwareen general. &lt;br /&gt;También queremos publicar artículos prácticos y, más adelante, intentar organizar encuentros y promover reuniones para intercambiar experiencias.&lt;br /&gt;&lt;br /&gt;Espero veros por allí.&lt;br /&gt;&lt;br /&gt;Un saludo.&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9553291-2067679018145203851?l=rincew.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/2067679018145203851'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/2067679018145203851'/><link rel='alternate' type='text/html' href='http://rincew.blogspot.com/2011/08/nueva-pagina-sobre-test-driven.html' title='Nueva página sobre Test-Driven Development y testing: www.portaltdd.org'/><author><name>Javier J.</name><uri>http://www.blogger.com/profile/00715175463383886462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-9553291.post-9001158621556263144</id><published>2008-07-31T22:01:00.001Z</published><updated>2008-07-31T22:03:14.370Z</updated><title type='text'>Seguridad en aplicaciones web y modelos de navegación</title><content type='html'>Saludos.&lt;br /&gt;&lt;br /&gt;Recientemente, por motivos de trabajo, ha caído en mis manos un manual de buenas prácticas de seguridad para el desarrollo de aplicaciones. Después de ojearlo me han pedido unas ideas de cómo poder evaluar que, en el proceso de desarrollo de una aplicación, se está poniendo en práctica todo lo que viene en dicho manual.&lt;br /&gt;&lt;br /&gt;Pensando en ello me ha vuelto a quedar patente la importancia, sobre todo en aplicaciones web, de contar con buenos modelos de referencia y, sobre todo, con un buen modelo navegacional. Un modelo navegacional representa (como no puede ser de otra manera) la navegación del sistema. Cuando hablo de navegación no hablo de en qué pantalla estoy y a qué otra pantalla puedo ir. Eso vendrá más adelante y es solo una parte de la navegación.&lt;br /&gt;&lt;br /&gt;Los modelos de navegación con los que trabajo (más información en &lt;a href="http://www.iwt2.org/"&gt;www.iwt2.org&lt;/a&gt;), y muchos otros propuestos por empresas e investigadores, son el pegamento de modelos de información, funcionales y de actores / roles, junto con algunos añadidos más.&lt;br /&gt;&lt;br /&gt;Un buen modelo navegacional nos dice con precisión cuáles son los actores / roles que interactúan con la aplicación y qué funcionalidad (por ejemplo definida mediante casos de uso) tiene disponible cada uno, qué información y qué campos de dicha información es accesible para cada actor y qué información no lo es (por ejemplo definida mediante prototipos de visualización o diagramas de clases conceptuales), y, por supuesto, a qué otras secciones de la aplicación pueden acceder o no. Todo esto combinado con información sobre validaciones, permisos, etc. incluida en los requisitos funcionales (por ejemplo mediante los mecanismos de precondiciones y postcondicione sutilizados por muchos autores) nos proporcionan abundante información de seguridad&lt;br /&gt;Por supuesto, luego habrá muchas más reglas a tener en cuenta en diseño y en codificación (verificación de parámetros y controles de seguridad en todas las capas, almacenamiento de información encriptada, etc.), pero ya contamos con una buena base de seguridad en nuestros modelos sin necesidad de realizar ninguna inversión en herramientas o técnicas específicas de seguridad.&lt;br /&gt;&lt;br /&gt;A divertirse.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9553291-9001158621556263144?l=rincew.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rincew.blogspot.com/feeds/9001158621556263144/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9553291&amp;postID=9001158621556263144' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/9001158621556263144'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/9001158621556263144'/><link rel='alternate' type='text/html' href='http://rincew.blogspot.com/2008/07/seguridad-en-aplicaciones-web-y-modelos.html' title='Seguridad en aplicaciones web y modelos de navegación'/><author><name>Javier J.</name><uri>http://www.blogger.com/profile/00715175463383886462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9553291.post-8624819983025121901</id><published>2008-07-16T07:46:00.001Z</published><updated>2008-07-16T07:49:15.986Z</updated><title type='text'>Extendiendo PMD con nuevos renderers</title><content type='html'>PMD es una herramienta de verificación que código Java que permite aplicar un conjunto de reglas (actualmente tiene aproximadamente 240 reglas) y generar un informe con los resultados. En una entrada anterior (http://rincew.blogspot.com/2008/02/escribiendo-reglas-en-pmd.html) conté como escribir una nueva regla. En esta entrada voy a dar unas ideas de cómo desarrollar un nuevo informe de resultados.&lt;br /&gt;&lt;br /&gt;Los encargados de procesar los resultados de la verificación se llaman renderers. PMD trae un conjunto de renderers ya construidos para mostrar los resultados como texto, o como HTML, o CSV, o XML. Es útil crear un renderer propio cuando se quiere hacer un procesado adicional, cuando queremos generar un nuevo tipo de resultado, por ejemplo un informa en RTF o añadir el resultado a una BBDD, o cuando queremos ampliar alguno de los renderers ya existentes para incluya información adicional. Hay que recalcar que todas las acciones anteriores pueden hacerse e otra manera, por ejemplo utilizando transformaciones XSLT o las opciones de importación de CSV de la mayoría de sistemas de BBDD.&lt;br /&gt;&lt;br /&gt;En PMD, un renderer es una clase que implementa la interfaz Renderer del paquete net.sourceforge.pmd.renderers. Esta interfaz permite que un rederer se comporte como una máquina de estados. Al inicializar el renderer se invoca el método start(), cada vez que se ha aplicado un nuevo conjunto de reglas se invoca al método renderFileReport(Report report) y una vez que se ha acabado el proceso se invoca al método end(). Existen más métodos en esta interfaz, sin embargo no será necesario utilizarlos en este ejemplo.&lt;br /&gt;&lt;br /&gt;Como es habitual en muchas librerías y herramientas, PMD ya ofrece una implementación base de esta interfaz en la clase AbstractRenderer (que se mantiene por motivos de compatibilidad con versiones antiguas). A pesar de su nombre, esta clase no es abstracta ni ninguno de sus métodos son abstractos. Tampoco usaremos esta clase como base para los renderers, en su lugar utilizaremos su hija OnTheFlyRenderer, la cuál sí tiene tres métodos abstractos: start, renderFileViolations y end. El funcionamiento de start y end es el mismo que el definido anteriormente, sin embargo, para cada conjunto de reglas, el método invocado será el método renderFileViolations(Iterator&lt;IRuleViolation&gt; violations)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Este método, recibe un iterador con todas las violaciones detectadas para un conjunto de reglas concreto. Una violación es un objetivo de una clase que implementa la interfaz IRuleViolation. A partir de los métodos get de esta interfaz podemos conocer toda la información que PMD maneja del error como el nombre del archivo (método getFilename), la clase y el método de la violación (getClassName y getMethodName), etc. &lt;br /&gt;Además, también podemos obtener toda la información referente a la regla que se ha incumplido con el método getRule. Este método devuelve un objeto que implementa la interfaz Rule y que tiene varios métodos get para conocer el nombre de la regla (getName), su prioridad (getPriority), etc.&lt;br /&gt;A modo de ejemplo, a continuación se muestra un nuevo renderer que también genera una salida de texto plano pero con una información distinta y un orden distinto al renderer de texto que viene de serie en PMD.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;import net.sourceforge.pmd.IRuleViolation;&lt;br /&gt;import net.sourceforge.pmd.PMD;&lt;br /&gt;import net.sourceforge.pmd.renderers.OnTheFlyRenderer;&lt;br /&gt;import java.io.IOException;&lt;br /&gt;import java.io.Writer;&lt;br /&gt;import java.util.Iterator;&lt;br /&gt;&lt;br /&gt;public class RendererTexto extends OnTheFlyRenderer {&lt;br /&gt;&lt;br /&gt; &lt;br /&gt;    public void start() throws IOException {&lt;br /&gt;       &lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public void renderFileViolations(Iterator&lt;IRuleViolation&gt; violations) &lt;br /&gt;            throws IOException &lt;br /&gt;    {&lt;br /&gt;        Writer writer = getWriter();&lt;br /&gt;        StringBuffer buf = new StringBuffer();&lt;br /&gt;        &lt;br /&gt;        while (violations.hasNext()) {&lt;br /&gt;            IRuleViolation rv = violations.next();&lt;br /&gt;            buf.append(PMD.EOL);&lt;br /&gt;            buf.append(rv.getFilename());&lt;br /&gt;            buf.append(':');&lt;br /&gt;            buf.append(Integer.toString(rv.getBeginLine()));&lt;br /&gt;            buf.append('\t');&lt;br /&gt;            buf.append(rv.getRule().getName());&lt;br /&gt;            buf.append(": ");&lt;br /&gt;            buf.append(rv.getDescription());&lt;br /&gt;            &lt;br /&gt;            writer.write(buf.toString());&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public void end() throws IOException {&lt;br /&gt;        getWriter().write("Ok.");&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Como heredamos de OnTheFlyRenderer solo nos tenemos que preocupar de los tres métodos antes mencionados. En concreto, en el método start no hemos nada. En el método renderFileViolations recorremos cada una de las violaciones mostrando el nombre del fichero y la línea donde ocurre, el nombre de la regla y su descripción. En el método end escribimos la cadena de texto Ok para indicar que el proceso terminó con éxito.&lt;br /&gt;Por último, será necesario indicar a PMD que utilice el nuevo renderer. Para ello es necesario que la clase esté disponible en el classpath del PMD (por ejemplo poniendo el nuevo renderer en una archivo jar y modificando el archivo pmd.bat para que añada dicho jar al classpath en cada ejecución).&lt;br /&gt;Una vez hecho, solo es necesario indicar el nombre de la clase (indicando su paquete) como segundo parámetro. Por ejemplo, si la clase mostrada más arriba está guardada en el paquete pmdext, para utilizar habría que escribir:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;PMD c:\src pmdext. RendererTexto basic&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Con la línea anterior se verificarían todas las reglas de conjunto de regla basic en todo el código Java dentro de la carpeta src.&lt;br /&gt;En verdad, el mundo de los renderes en PMD es un poco más complejo, ya que no hemos hablado de los writers (aunque se usan en el ejemplo de más arriba). Además, el report no solo nos informa de las violaciones de las reglas sino de los errores que han podido seguir a la hora de procesar un archivo o de las supresiones, es decir, bloques de código marcados para ser ignorados por PMD.&lt;br /&gt;Sin embargo, con estas nociones básicas no es difícil estudiar el código de la clase OnTheFlyRenderer y ver los detalles que no hemos mencionado. &lt;br /&gt;Como comentario final, quiero mencionar que el diseño del mecanismo de renders de PMD no es todo lo bueno y homogéneo que pudiera ser, por suerte es bastante sencillo de usar. Ya se ha anunciado que para la versión 5 se van a hacer cambios de código importantes y se va a romper la compatibilidad hacia atrás, por lo que tengo esperanzas de que mejoren este mecanismo.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9553291-8624819983025121901?l=rincew.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rincew.blogspot.com/feeds/8624819983025121901/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9553291&amp;postID=8624819983025121901' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/8624819983025121901'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/8624819983025121901'/><link rel='alternate' type='text/html' href='http://rincew.blogspot.com/2008/07/extendiendo-pmd-con-nuevos-renderers.html' title='Extendiendo PMD con nuevos renderers'/><author><name>Javier J.</name><uri>http://www.blogger.com/profile/00715175463383886462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9553291.post-2242566984652932847</id><published>2008-03-21T14:38:00.003Z</published><updated>2008-03-21T14:41:48.973Z</updated><title type='text'>Como aplicar inclusiones y extensiones a las plantillas de casos de uso</title><content type='html'>Saludos.&lt;br /&gt;&lt;br /&gt;Muchos de nosotros utilizamos diagramas UML de casos de uso (ver más abajo) y, además, definimos cada caso de uso con plantillas de texto (ver más abajo). Sin embargo, en más de una ocasión ambas representaciones no son consistentes, es decir, hay una información en una de las representaciones que no está en la otra. Esto pasa, por ejemplo, a la hora de definir relaciones de inclusión y extensión. Ambas se definen en los diagramas de casos de uso pero casi nunca se incluyen en las plantillas.&lt;br /&gt;A continuación expongo cómo incluyo yo dichas relaciones en mis plantillas para que sean consistentes con lo que se cuenta en el diagrama de casos de uso.&lt;br /&gt;Como una inclusión debe realizarse siempre, la coloco en la secuencia principal, esto es, en los pasos que se ejecutan salvo algún error o circunstancia anómala. Con esto, además, estoy indicando en qué momento debe realizarse otro caso de uso.&lt;br /&gt;Una excepción, por el contrario, puede realizarse o no dependiendo de una condición (llamada punto de extensión). Si bien los puntos de extensión pueden añadirse a los diagramas de casos de uso en la mayoría de herramientas, yo no suelo hacerlo porque, en cuanto hay más de uno, ya no se sabe qué punto de extensión corresponde a cada extensión. En cambio, añadirlo a la plantilla es muy sencillo. Sólo hay que colocar el punto de extensión y el caso de uso a realizar como un paso de la secuencia alternativa. Si los pasos de la secuencia alternativa están asociados a pasos de la secuencia principal entonces, además, estamos indicando en qué momento  concreto debe evaluarse el punto de extensión para decidir si se realiza la extensión o no.&lt;br /&gt;Veamos un ejemplo imaginario. Teneos el caso de uso dar de alta nuevo proyecto, en el cuál, además de introducir la información de cada proyecto, debemos indicar el supervisor del proyecto y el grupo de trabajo encargado.  Un proyecto debe tener siempre un encargado, y el comportamiento para seleccionar uno lo definiremos en otro caso de uso. Además, si no encontramos un grupo de trabajo adecuado, podremos crear un nuevo grupo sobre la marcha (mediante otro caso de uso). Con todo esto tenemos tres casos de uso, una inclusión y una extensión. El diagrama correspondiente se muestra a continuación.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://2.bp.blogspot.com/_3cfkt5aAWkM/R-PI-bpb5uI/AAAAAAAAAAw/CDY3nlfclJg/s1600-h/EjemploCasosUso.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://2.bp.blogspot.com/_3cfkt5aAWkM/R-PI-bpb5uI/AAAAAAAAAAw/CDY3nlfclJg/s400/EjemploCasosUso.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5180204971305199330" /&gt;&lt;/a&gt;&lt;br /&gt; &lt;br /&gt;Veamos ahora la plantilla del caso de uso dar de alta un nuevo proyecto. Esta plantilla ha sido simplificada al máximo para este ejemplo.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://1.bp.blogspot.com/_3cfkt5aAWkM/R-PItLpb5tI/AAAAAAAAAAo/PpaUuPEf1Yk/s1600-h/TablaRequisito.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://1.bp.blogspot.com/_3cfkt5aAWkM/R-PItLpb5tI/AAAAAAAAAAo/PpaUuPEf1Yk/s400/TablaRequisito.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5180204674952455890" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Como se puede observar, las inclusiones y extensiones indicadas en el diagrama de casos de uso también están en la plantilla. Además, se ha indicado información adicional (cuando aparecen las relaciones, cuál es el punto de extensión de la extensión) de manera clara y sencilla sin necesidad de complicar el diagrama de casos de uso.&lt;br /&gt;&lt;br /&gt;Feliz semana.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9553291-2242566984652932847?l=rincew.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rincew.blogspot.com/feeds/2242566984652932847/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9553291&amp;postID=2242566984652932847' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/2242566984652932847'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/2242566984652932847'/><link rel='alternate' type='text/html' href='http://rincew.blogspot.com/2008/03/como-aplicar-inclusiones-y-extensiones.html' title='Como aplicar inclusiones y extensiones a las plantillas de casos de uso'/><author><name>Javier J.</name><uri>http://www.blogger.com/profile/00715175463383886462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_3cfkt5aAWkM/R-PI-bpb5uI/AAAAAAAAAAw/CDY3nlfclJg/s72-c/EjemploCasosUso.jpg' height='72' width='72'/><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9553291.post-374364068496828683</id><published>2008-02-22T12:43:00.002Z</published><updated>2008-02-22T12:45:57.291Z</updated><title type='text'>Uso de ant y CPD</title><content type='html'>La herramienta de detección de copy&amp;paste de PMD (http://pmd.sourceforge.net/) permite detectar fragmentos de código copiados. Esta herramienta, al igual que PMD, también incorpora una tarea ant para poder ser invocada desde dicha herramienta. Sin embargo, no está tan documentada como la tarea ant para CPD. Un ejemplo de su uso se muestra a continuación.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://4.bp.blogspot.com/_3cfkt5aAWkM/R77DtxKMVuI/AAAAAAAAAAg/0fcYAnbdtCM/s1600-h/CapturaCPD.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://4.bp.blogspot.com/_3cfkt5aAWkM/R77DtxKMVuI/AAAAAAAAAAg/0fcYAnbdtCM/s400/CapturaCPD.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5169784613325526754" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Como cuenta de tokens se ha elegido 50 para que detecte bloques de código repetido de entre 5 o 6 líneas ( si tienen muchos caracteres)  hasta 10 u 11 líneas (si tienen pocos caracteres).&lt;br /&gt;&lt;br /&gt;(La captura de pantalla está tomada de Microsoft Word, por eso aparecen algunas palabras subralladas).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9553291-374364068496828683?l=rincew.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rincew.blogspot.com/feeds/374364068496828683/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9553291&amp;postID=374364068496828683' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/374364068496828683'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/374364068496828683'/><link rel='alternate' type='text/html' href='http://rincew.blogspot.com/2008/02/uso-de-ant-y-cpd.html' title='Uso de ant y CPD'/><author><name>Javier J.</name><uri>http://www.blogger.com/profile/00715175463383886462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_3cfkt5aAWkM/R77DtxKMVuI/AAAAAAAAAAg/0fcYAnbdtCM/s72-c/CapturaCPD.jpg' height='72' width='72'/><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9553291.post-9198243491783602242</id><published>2008-02-14T18:19:00.005Z</published><updated>2008-02-14T18:34:59.077Z</updated><title type='text'>Escribiendo reglas en PMD</title><content type='html'>Saludos.&lt;br /&gt;&lt;br /&gt;PMD (http://pmd.sourceforge.net/) es un programa para la verificación estática de código Java. PMD contiene bastantes reglas de programación (como no utilizar variables ni muy cortas ni muy largas o no anidar una sentencia if dentro de otra) y su función es buscar qué fragmentos de código incumplen dichas reglas.&lt;br /&gt;&lt;br /&gt;A continuación veremos cómo añadir una nueva regla a PMD. Esta herramienta ofrece dos alternativas para las nuevas reglas: implementarlas como código Java o mediante XPath. En este ejemplo usaremos la segunda alternativa. La regla a implementar será: no se permiten genéricos de tipo &lt;em&gt;Object&lt;/em&gt;, es decir, no se permiten declaraciones como &lt;em&gt;List[Object]&lt;/em&gt; o &lt;em&gt;Set[Object]&lt;/em&gt; (uso corchetes en vez de los símbolos de mayor y menor). Puede que esta regla no sea muy útil ya que el declarar genéricos que contienen Objects es tan poco práctico que difícilmente alguien lo va a usar, pero es un buen ejemplo.&lt;br /&gt;&lt;br /&gt;Lo primero que tenemos que hacer es ejecutar la herramienta de diseño de reglas que viene con PMD (la propia herramienta viene con un script para ejecutarla). Después, escribimos un fragmento de código que incumpla la regla (como el de la figura 1) y generamos su árbol de sintaxis abstracta (muy similar a un documento XML).&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Figura 1.&lt;/strong&gt;&lt;br /&gt;&lt;a href="http://2.bp.blogspot.com/_3cfkt5aAWkM/R7SHUxKMVsI/AAAAAAAAAAQ/wji6jX_J0Rc/s1600-h/PMDImg01.jpg"&gt;&lt;img id="BLOGGER_PHOTO_ID_5166903463364024002" style="DISPLAY: block; MARGIN: 0px auto 10px; CURSOR: hand; TEXT-ALIGN: center" alt="" src="http://2.bp.blogspot.com/_3cfkt5aAWkM/R7SHUxKMVsI/AAAAAAAAAAQ/wji6jX_J0Rc/s400/PMDImg01.jpg" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Ahora, tenemos que escribir una expresión XPath que encuentre sólo aquellos nodos del árbol que incumplen la regla. Para ello, tenemos que buscar aquellas declaraciones de tipo clase o interfaz con un argumento que es un tipo clase object (se puede ver un ejemplo en la figura 1)&lt;br /&gt;&lt;br /&gt;Una posible expresión en XPath se muestra a continuación: &lt;em&gt;//ReferenceType/ClassOrInterfaceType/TypeArguments/TypeArgument/ReferenceType/ClassOrInterfaceType[@Image='Object']&lt;/em&gt;&lt;br /&gt;&lt;br /&gt;Para probarla, la escribimos en el cuadro de la parte superior derecha y la ejecutamos. En el cuadro que está justo debajo vemos las líneas del código Java que incumplen la regla. En la figura 1 se puede observar como la expresión detecta toda las líneas que declaran un genérico de tipo &lt;em&gt;Object&lt;/em&gt;.&lt;br /&gt;&lt;br /&gt;Para poder utilizar esta regla debemos incluirla en un &lt;em&gt;ruleset&lt;/em&gt;, el cuál es un archivo XML que define un conjunto de reglas a verificar. Para no modificar ninguno de los archivos de PMD vamos a crear un nuevo &lt;em&gt;ruleset&lt;/em&gt;. La cabecera se muestra a continuación.&lt;br /&gt;&lt;br /&gt;&lt;em&gt;[ruleset name="My custom rules" xmlns="http://pmd.sf.net/ruleset/1.0.0" nonamespaceschemalocation="http://pmd.sf.net/ruleset_xml_schema.xsd" schemalocation="http://pmd.sf.net/ruleset/1.0.0 http://pmd.sf.net/ruleset_xml_schema.xsd" xsi="http://www.w3.org/2001/XMLSchema-instance"]&lt;br /&gt;&lt;br /&gt;….&lt;br /&gt;&lt;br /&gt;[/ruleset]&lt;/em&gt;&lt;br /&gt;&lt;br /&gt;(De nuevo uso corchetes en vez de los símbolos de mayor y menor).&lt;br /&gt;&lt;br /&gt;La propia herramienta de diseño de reglas es capaz de generar el código XML de la regla. Dicho código (figura 2) debe incluirse entre las etiquetas &lt;em&gt;ruleset&lt;/em&gt;.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Figura 2.&lt;/strong&gt;&lt;br /&gt;&lt;a href="http://1.bp.blogspot.com/_3cfkt5aAWkM/R7SIghKMVtI/AAAAAAAAAAY/4ynMpz3--7M/s1600-h/PMDImg02.jpg"&gt;&lt;img id="BLOGGER_PHOTO_ID_5166904764739114706" style="DISPLAY: block; MARGIN: 0px auto 10px; CURSOR: hand; TEXT-ALIGN: center" alt="" src="http://1.bp.blogspot.com/_3cfkt5aAWkM/R7SIghKMVtI/AAAAAAAAAAY/4ynMpz3--7M/s400/PMDImg02.jpg" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Ahora, ya es posible utilizar nuestro &lt;em&gt;ruleset &lt;/em&gt;como parámetro de PMD para que aplique la regla que hemos definido.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9553291-9198243491783602242?l=rincew.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rincew.blogspot.com/feeds/9198243491783602242/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9553291&amp;postID=9198243491783602242' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/9198243491783602242'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/9198243491783602242'/><link rel='alternate' type='text/html' href='http://rincew.blogspot.com/2008/02/escribiendo-reglas-en-pmd.html' title='Escribiendo reglas en PMD'/><author><name>Javier J.</name><uri>http://www.blogger.com/profile/00715175463383886462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_3cfkt5aAWkM/R7SHUxKMVsI/AAAAAAAAAAQ/wji6jX_J0Rc/s72-c/PMDImg01.jpg' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9553291.post-116222059176620790</id><published>2006-10-30T15:01:00.000Z</published><updated>2006-10-30T15:03:11.793Z</updated><title type='text'>Combinando el modo grabar/reproducir con el modo API de código de Selenium</title><content type='html'>Saludos.&lt;br /&gt;&lt;br /&gt;Selenium (http://www.openqa.org/) es una herramienta open-source estupenda para realizar pruebas de sistema / aceptación de aplicaciones web. Selenium incluye una herramienta que se integra con Firefox y que permite grabar todo lo que hagamos con Firefox, guardarlo en un archivo, y reproducirlo después. Además, Selenium también incluye la posibilidad de escribir nuestras pruebas directamente en Java, C#, Python y Ruby.&lt;br /&gt;&lt;br /&gt;Para un proyecto real, estábamos muy interesados en utilizar Selenium y su herramienta para grabar / reproducir pruebas. Sin embargo, queríamos hacer dos cosas que no se pueden hacer con la filosofía de grabar / reproducir: la primera es añadir código adicional que compruebe el estado de la aplicación y el estado de la base de datos y la segunda ejecutar todas las pruebas grabadas automáticamente cada cierto tiempo.&lt;br /&gt;&lt;br /&gt;Por suerte el código de una prueba cuando se graba es muy similar al código que habría que escribir en un lenguaje, por ejemplo en Java o CSharp. A continuación incluyo el código de una sencilla prueba &lt;br /&gt;&lt;br /&gt;&lt;em&gt;Código 1. Prueba grabada con Firefox.&lt;/em&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;class NewTest&lt;br /&gt;  def test_foo&lt;br /&gt;    open "/links_jsp/Default.jsp"&lt;br /&gt;    assertTitle "Links"&lt;br /&gt;    clickAndWait "//a[contains(@href, 'LinkNew.jsp')]"&lt;br /&gt;    assertTitle "Links"&lt;br /&gt;    type "name", "Prueba"&lt;br /&gt;    type "link_url", "Prueba"&lt;br /&gt;    type "description", "URL de prueba"&lt;br /&gt;    clickAndWait "//input[@value='Insert']"&lt;br /&gt;    assertTitle "Links"&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;em&gt;Código 2. Prueba escrita en Java&lt;/em&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;import com.thoughtworks.selenium.*;&lt;br /&gt;import junit.framework.*;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;public class Selenium_InsertarEnlace extends TestCase {&lt;br /&gt;&lt;br /&gt;    private Selenium sel;&lt;br /&gt;&lt;br /&gt;    public void setUp() {&lt;br /&gt;        sel = new DefaultSelenium("localhost",&lt;br /&gt;            4444, "*firefox", "http:/localhost:8080");&lt;br /&gt;        sel.start();&lt;br /&gt;    }&lt;br /&gt;    &lt;br /&gt;    public void testInsertarEnlace() {&lt;br /&gt;        sel.open("/links_jsp/Default.jsp");&lt;br /&gt;        assertEquals("Links", sel.getTitle());&lt;br /&gt;        sel.click("//a[contains(@href, 'LinkNew.jsp')]");&lt;br /&gt;        sel.waitForPageToLoad("5000");&lt;br /&gt;        sel.type("name", "Prueba");&lt;br /&gt;        sel.type("link_url", "Prueba");&lt;br /&gt;        sel.type("description", "URL de prueba");&lt;br /&gt;        sel.click("//input[@value='Insert']");&lt;br /&gt;        sel.waitForPageToLoad("5000");&lt;br /&gt;        assertEquals("Links", sel.getTitle());&lt;br /&gt;    }&lt;br /&gt;    &lt;br /&gt;    public void tearDown() {&lt;br /&gt;        sel.stop();&lt;br /&gt;    }&lt;br /&gt;    &lt;br /&gt;    public static void main(String[] args) {&lt;br /&gt;      junit.textui.TestRunner.run(new TestSuite(Selenium_InsertarEnlace.class));&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;em&gt;Código 3. Prueba escrita en CSharp&lt;/em&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;using Selenium;&lt;br /&gt;using NUnit.Framework;&lt;br /&gt;namespace MyTests {&lt;br /&gt;    [TestFixture]&lt;br /&gt;    public class Selenium_InsertarEnlace {&lt;br /&gt;        private ISelenium sel;&lt;br /&gt;        &lt;br /&gt;        [SetUp]&lt;br /&gt;        public void SetUp() {&lt;br /&gt;            sel = new DefaultSelenium("localhost",&lt;br /&gt;                4444, "*firefox", "http:/localhost:8080");&lt;br /&gt;            sel.Start();&lt;br /&gt;        }&lt;br /&gt;        &lt;br /&gt;        [Test]&lt;br /&gt;        public void testGoogle() {&lt;br /&gt;            sel.Open("/links_jsp/Default.jsp");&lt;br /&gt;            Assert.AreEqual("Links", sel.getTitle());&lt;br /&gt;            sel.Click("//a[contains(@href, 'LinkNew.jsp')]");&lt;br /&gt;            sel.WaitForPageToLoad("5000");&lt;br /&gt;            sel.Type("name", "Prueba");&lt;br /&gt;            sel.Type("link_url", "Prueba");&lt;br /&gt;     sel.Type("description", "URL de prueba");&lt;br /&gt;                 sel.Click("//input[@value='Insert']");&lt;br /&gt;            sel.WaitForPageToLoad("5000");&lt;br /&gt;            Assert.AreEqual("Links", sel.getTitle());&lt;br /&gt;        }&lt;br /&gt;        &lt;br /&gt;        [TearDown]&lt;br /&gt;        public void TearDown() {&lt;br /&gt;            sel.stop();&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Para ejecutar la prueba en Java, es necesario tener en ejecución el servidor de Selenium ("java -jar selenium-server.jar") y tener en el PATH la ruta a Firefox. Al ejecutar la prueba en Java (necesitamos "selenium-java-client-driver.jar") se abrirá una nueva instancia del Firefox y, en la salida estándar veremos el resultado de la prueba. La prueba en CSharp no la hemos ejecutado, pero debería ser igual de sencillo.&lt;br /&gt;&lt;br /&gt;Ahora es posible añadir a la prueba todas las comprobaciones adicionales que queramos como, por ejemplo, conectar con la BBDD para asegurarnos que el enlace está ahí.&lt;br /&gt;&lt;br /&gt;Estamos escribiendo un sencillo script para traducir automáticamente el código de una prueba capturada a código Java. Si alguien está interesado no tiene más que escribirme.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9553291-116222059176620790?l=rincew.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rincew.blogspot.com/feeds/116222059176620790/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9553291&amp;postID=116222059176620790' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/116222059176620790'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/116222059176620790'/><link rel='alternate' type='text/html' href='http://rincew.blogspot.com/2006/10/combinando-el-modo-grabarreproducir.html' title='Combinando el modo grabar/reproducir con el modo API de código de Selenium'/><author><name>Javier J.</name><uri>http://www.blogger.com/profile/00715175463383886462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9553291.post-116178888488226046</id><published>2006-10-25T15:07:00.000Z</published><updated>2006-10-25T15:08:04.893Z</updated><title type='text'>Aplicaciones autoadministradas y autoreparadas</title><content type='html'>Después de asistir a la conferencia de un gurú (o eso dijeron) de la arquitectura de software en un congreso, me llevo la idea de que el desarrollo de aplicaciones que sean capaces de administrase ellas solitas (y de paso repararse) es un buen nicho de mercado.&lt;br /&gt;&lt;br /&gt;Esto no es, ni mucho menos, ciencia-ficción. Si no me equivoco la plataforma Java ya incorpora desde hace tiempo interfaces de monitorización de componentes bajo el nombre de JMX.&lt;br /&gt;&lt;br /&gt;Para poder hacer esto, dado que la tecnología existe, lo que hay que hacer es plantearlo desde el comienzo del desarrollo de la aplicación e incluir en nuestra aplicación, módulos que monitoricen el funcionamiento de la misma y que tengan la capacidad de tomar las medidas correctivas necesarias. Esto puede venderse muy bien, ya que, como nos comentó el gurú, por cada dólar gastado en adquirir una aplicación (esto incluye el desarrollo a medida), se gastan nueve dólares en su administración y mantenimiento (en euros será menos, seguro). Si podemos desarrollar aplicaciones autoadministradas, podemos reducir el gasto durante toda la vida útil de la aplicación de nuestros clientes 9 veces, lo cuál es bastante interesante.&lt;br /&gt;&lt;br /&gt;Además una aplicación que sea capaz de arreglarse a sí misma, es una aplicación que no tiene por qué detenerse nunca, lo cuál es muy beneficioso en ciertos nichos de mercado.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9553291-116178888488226046?l=rincew.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rincew.blogspot.com/feeds/116178888488226046/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9553291&amp;postID=116178888488226046' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/116178888488226046'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/116178888488226046'/><link rel='alternate' type='text/html' href='http://rincew.blogspot.com/2006/10/aplicaciones-autoadministradas-y.html' title='Aplicaciones autoadministradas y autoreparadas'/><author><name>Javier J.</name><uri>http://www.blogger.com/profile/00715175463383886462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9553291.post-116126726985565092</id><published>2006-10-19T14:12:00.000Z</published><updated>2006-10-19T14:14:29.870Z</updated><title type='text'>Presentaciones "De casos de uso a casos de prueba" en mi web</title><content type='html'>Saludos.&lt;br /&gt;&lt;br /&gt;He puesto en mi web el 95% de las presentaciones que voy a usar en el seminario "De casos de uso a casos de prueba" que impartiré el martes 24 en el evento SoloRequisitos. La dirección es:&lt;br /&gt;&lt;br /&gt;http://www.lsi.us.es/~javierj/cursos.htm&lt;br /&gt;&lt;br /&gt;Después del evento publicaré el 100% de las transparencias, aunque el 5% que falta son las menos importantes.&lt;br /&gt;&lt;br /&gt;Feliz otoño&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9553291-116126726985565092?l=rincew.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rincew.blogspot.com/feeds/116126726985565092/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9553291&amp;postID=116126726985565092' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/116126726985565092'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/116126726985565092'/><link rel='alternate' type='text/html' href='http://rincew.blogspot.com/2006/10/presentaciones-de-casos-de-uso-casos.html' title='Presentaciones &quot;De casos de uso a casos de prueba&quot; en mi web'/><author><name>Javier J.</name><uri>http://www.blogger.com/profile/00715175463383886462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9553291.post-116119004097554986</id><published>2006-10-18T16:46:00.000Z</published><updated>2006-10-18T16:47:20.986Z</updated><title type='text'>Eclipse. Caballo ganador en la investigación</title><content type='html'>Saludos.&lt;br /&gt;&lt;br /&gt;A menudo nos encontramos comparativas entre Eclipse y NetBeans y muchas veces nos da la impresión (al menos a mí) de que ambas plataformas tienen una lucha feroz por imponerse en el mundo de la empresa.&lt;br /&gt;sin embargo en el mundo de la investigación y las universidades no es así. Por lo que he podido ver en un reciente congreso, todo el mundo usa Eclipse y desarrolla sus ideas a partir de Eclipse. En ese sentido, el EMF es una herramienta de uso casi inevitable. Netbeans no aparece por ningún sitio ni se le espera.&lt;br /&gt;&lt;br /&gt;¡Dios mío, yo uso Netbeans!. ¿De dónde me puedo descargar Eclipse?.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9553291-116119004097554986?l=rincew.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rincew.blogspot.com/feeds/116119004097554986/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9553291&amp;postID=116119004097554986' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/116119004097554986'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/116119004097554986'/><link rel='alternate' type='text/html' href='http://rincew.blogspot.com/2006/10/eclipse-caballo-ganador-en-la.html' title='Eclipse. Caballo ganador en la investigación'/><author><name>Javier J.</name><uri>http://www.blogger.com/profile/00715175463383886462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9553291.post-116101806693563010</id><published>2006-10-16T16:58:00.000Z</published><updated>2006-10-16T17:01:06.950Z</updated><title type='text'>Evitando el lado oscuro de las pruebas (y II)</title><content type='html'>Como ya comentamos hace tiempo en este blog, las pruebas de código tipo JUnit tienen un lado oscuro. Si el código cambia, las pruebas pueden quedar inservibles, lo cuál supone un desperdicio de tiempo y recursos.&lt;br /&gt;&lt;br /&gt;La solución que propuse fue la de intentar automatizar lo máximo posible la generación de pruebas. Así, si el código cambiaba, simpleente se volvía a ejecutar el programa que generaba un nuevo conjunto de pruebas.&lt;br /&gt;&lt;br /&gt;Otra posible solución que se ha comentado en el taller de pruebas es la verificacón estática del código. Es decir, revisar el código para buscar errores antes de ejecutarlo. Según comentaron, las revisiones estáticas de código pueden resolver cerca de un 80% de los errores básicos. Un artículo muy bueno al respecto puede encontrase en (http://in2test.lsi.uniovi.es/pris2006/).&lt;br /&gt;&lt;br /&gt;Feliz otoño.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9553291-116101806693563010?l=rincew.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rincew.blogspot.com/feeds/116101806693563010/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9553291&amp;postID=116101806693563010' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/116101806693563010'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/116101806693563010'/><link rel='alternate' type='text/html' href='http://rincew.blogspot.com/2006/10/evitando-el-lado-oscuro-de-las-pruebas.html' title='Evitando el lado oscuro de las pruebas (y II)'/><author><name>Javier J.</name><uri>http://www.blogger.com/profile/00715175463383886462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9553291.post-115830471569486392</id><published>2006-09-15T07:13:00.000Z</published><updated>2006-09-15T07:18:35.710Z</updated><title type='text'>Reglas para escribir buenos casos de uso</title><content type='html'>Saludos a todos.&lt;br /&gt;&lt;br /&gt;Desde principios de verano estoy colaborando con la empresa Icinetic (www.icinetic.com) aplicando algunas de mis ideas sobre pruebas para mejorar su proceso de fabricación de software y también desarrollando nuevos servicios.&lt;br /&gt;&lt;br /&gt;Uno de los primeros pasos para poner en marcha un proceso de prueba es tener buenos casos de uso. Cuanto mejor sean los casos de uso, más fácil será fabricar el software y elaborar buenas pruebas.  En Icinetic tienen su propio conjunto de reglas para desarrollar buenos casos de uso y, además, me han dado permiso para difundirlas. Estas reglas son:&lt;br /&gt;&lt;br /&gt;1. El caso de uso se inicia con la acción de un actor.&lt;br /&gt;&lt;br /&gt;2. El caso de uso tiene una precondición y una postcondición&lt;br /&gt;&lt;br /&gt;3. Mostrar todas las excepciones posibles, así como los flujos alternativos. Al menos “datos incorrectos” y  “error al guardar los datos”&lt;br /&gt;&lt;br /&gt;4. Mantener el mismo nivel de abstracción.&lt;br /&gt;&lt;br /&gt;5. Rastreabilidad hacia el requisito/s de información y el objetivo/s por el que se ha incluido el caso de uso.&lt;br /&gt;&lt;br /&gt;6. Comprobar que las relaciones entre casos de uso (include y extends) sean consecuentes con diagramas de casos de uso.&lt;br /&gt;&lt;br /&gt;7. Comprobar que los actores que inician los casos de uso sean consecuentes con los diagramas de caso de uso&lt;br /&gt;&lt;br /&gt;8. En las eliminaciones comprobar revisando los requisitos de información si se debería eliminar más elementos en cascada. En tal caso detallarlo en la postcondición.&lt;br /&gt;&lt;br /&gt;En general, este conjunto de reglas me parece muy bueno. A continuación incluyo los comentarios que les hice a ellos para que el conjunto de reglas fueran aún mejor.&lt;br /&gt;&lt;br /&gt;Completamente de acuerdo con las reglas 1, 4, 6 y 8.&lt;br /&gt;No estoy muy convencido de que un caso de uso deba tener siempre una precondición y una postcondición. Si no identificamos claramente una pre o postcondición vale más la pena no ponerla que forzar la situación.&lt;br /&gt;Por regla general, una precondición expresa el estado que debe tener el sistema para poder ejecutar un caso de uso y una postcondición el estado en el que queda el sistema después de ejecutar un caso de uso. Por ejemplo, no se me ocurre ninguna postcondición para un caso de uso de realizar una búsqueda ya que no cambia nada en el sistema. Poner por poner es para nada.&lt;br /&gt;La regla 5 puede hacerse más genérica. Es buena idea que cualquier requisito sea rastreable, y no solo los requisitos de almacenamiento de información.&lt;br /&gt;La regla 7 me parece un poco ambigua. Cuando, en un diagrama de casos de uso hay más de un actor que participa en un caso de uso, no hay, que yo conozca, ninguna notación para saber cuál es el actor que comienza el caso de uso. Yo les propuse escribir esta regla de una forma más genérica: “comprobar que los actores participantes en un caso de uso son consecuentes con los diagramas de casos de uso”.&lt;br /&gt;&lt;br /&gt;Además, les propuse añadir las siguientes reglas, la mayoría sacadas sobre todo del libro “Writting Effective Use Cases” de Cockburn.&lt;br /&gt;&lt;br /&gt;9. Si el caso de uso se inicia por la acción de un actor, al final de dicho caso de uso el actor debe obtener un resultado &lt;em&gt;(salvo que el caso de uso sea una inclusión o extensión de otro caso de uso. Entonces, probablemente, no se inicie con a acción de un actor.)&lt;/em&gt;&lt;br /&gt;10. Comprobar que el caso de uso sea como un partido de tenis (El actor hace… El sistema hace…) y como un partido de fútbol &lt;em&gt;(se sabe en todo momento qué pasa y quién lo hace)&lt;/em&gt;&lt;br /&gt;&lt;br /&gt;11. Un caso de uso de 3 pasos o menos o de más de 10 pasos probablemente no sea un buen caso de uso &lt;em&gt;(salvo que el caso de uso sea una inclusión o extensión de otro caso de uso&lt;/em&gt;).&lt;br /&gt;&lt;br /&gt;12. Todas las acciones similares deben describirse con las mismas frases. Por ejemplo, enunciar todas las inserciones de datos con la frase “El actor X introduce los datos….”. No permitir variantes como “inserta datos”, “añade datos”, “incluye datos”, ni siquiera “introducir datos”, etc. Siempre igual, por muy aburrido que resulte.&lt;br /&gt;&lt;br /&gt;Por cierto, la regla 12 es muy útil cuando varias personas distintas escriben casos de uso y también a la hora de utilizar herramientas software de análisis de casos de uso y de generación de pruebas. &lt;br /&gt;&lt;br /&gt;Como resumen de todo lo visto: un buen requisito y caso de uso debe tener estas características: completo, correcto, no ambiguo, validado, verificable y rastreable.&lt;br /&gt;&lt;br /&gt;a. Un requisito completo es aquel que lo cuenta todo, si hay un limite dice cuál es, si hay algo que hacer dice el qué, etc.&lt;br /&gt;&lt;br /&gt;b. Un requisito correcto es aquel que está bien escrito, si faltas de ortografía, ni errores sintácticos y que, además, sigue correctamente las reglas para definir requisitos que se estén usando.&lt;br /&gt;&lt;br /&gt;c. Un requisito validado es un requisito que ha sido visto y comprendido por el cliente &lt;em&gt;(y ha dado su visto bueno)&lt;/em&gt;.&lt;br /&gt;&lt;br /&gt;d. Un requisito no ambiguo es aquel requisito que tiene el mismo significado para todo el que lo lea.&lt;br /&gt;&lt;br /&gt;e. Un requisito verificable es un requisito que expresa cosas que se pueden probar. Un ejemplo de requisitos no verificables son los que dependen de que una persona haga algo determinado &lt;em&gt;(llevar un paquete, comprobar que los códigos estén correctos, etc.)&lt;/em&gt;.&lt;br /&gt;&lt;br /&gt;f. Un requisito rastreable es un requisito que se sabe de dónde viene y se sabe a dónde va.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;P.D.&lt;br /&gt;Autonota. Ahora que retomo la actividad, sería buena idea terminar el curso de Cacuts, a ser posible, utilizando la herramienta Cactus.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9553291-115830471569486392?l=rincew.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rincew.blogspot.com/feeds/115830471569486392/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9553291&amp;postID=115830471569486392' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/115830471569486392'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/115830471569486392'/><link rel='alternate' type='text/html' href='http://rincew.blogspot.com/2006/09/reglas-para-escribir-buenos-casos-de.html' title='Reglas para escribir buenos casos de uso'/><author><name>Javier J.</name><uri>http://www.blogger.com/profile/00715175463383886462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9553291.post-115753351280734496</id><published>2006-09-06T09:01:00.000Z</published><updated>2006-09-06T09:05:12.816Z</updated><title type='text'>Publicaciones en mi web.</title><content type='html'>Saludos.&lt;br /&gt;&lt;br /&gt;He publicado en mi web una página con referencias a todas las propuestas para probar casos de uso que conozco. El enlace es http://www.lsi.us.es/~javierj/approaches.htm&lt;br /&gt;&lt;br /&gt;También he publicado un texto dónde se describe brevemente propuestas para especificar pruebas y para implementarlas, tanto para aplicaciones de escritorio como para aplicaciones web. El enlace es http://www.lsi.us.es/~javierj/investigacion_ficheros/EICP.pdf.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Feliz septiembre.&lt;br /&gt;PD: aún está pendiente terminar la serie de pruebas con Cactus.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9553291-115753351280734496?l=rincew.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rincew.blogspot.com/feeds/115753351280734496/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9553291&amp;postID=115753351280734496' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/115753351280734496'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/115753351280734496'/><link rel='alternate' type='text/html' href='http://rincew.blogspot.com/2006/09/publicaciones-en-mi-web.html' title='Publicaciones en mi web.'/><author><name>Javier J.</name><uri>http://www.blogger.com/profile/00715175463383886462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9553291.post-115590046041841477</id><published>2006-08-18T09:16:00.000Z</published><updated>2006-08-18T11:27:40.493Z</updated><title type='text'>La desidia del verano</title><content type='html'>Saludos a todos.&lt;br /&gt;&lt;br /&gt;Se supone que el verano, con las vacaciones, sería una época estupendapara escribir un montón de entradas interesantes.&lt;br /&gt;&lt;br /&gt;Pues no. La verdad es que, aunque tengo tiempo libre, en verano se apetece escribir nada. Espero volver en septiembre.&lt;br /&gt;&lt;br /&gt;Feiz vacaciones a todos.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9553291-115590046041841477?l=rincew.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rincew.blogspot.com/feeds/115590046041841477/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9553291&amp;postID=115590046041841477' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/115590046041841477'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/115590046041841477'/><link rel='alternate' type='text/html' href='http://rincew.blogspot.com/2006/08/la-desidia-del-verano.html' title='La desidia del verano'/><author><name>Javier J.</name><uri>http://www.blogger.com/profile/00715175463383886462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9553291.post-115253278294535992</id><published>2006-07-10T11:58:00.000Z</published><updated>2006-07-10T11:59:42.960Z</updated><title type='text'>Un ejemplo de diseño de pruebas con Cactus (II).</title><content type='html'>Un millón de disculpas por la tardanza, continuamos:&lt;br /&gt;&lt;br /&gt;Necesitaremos definir los siguientes elementos para cada caso de prueba: acciones a realizar, valores de prueba, resultado esperado. Por comodidad, vuelvo a poner el comportamiento que el servlet debe tener:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;1. El servlet recibe una petición de login con un nombre de usuario y una clave.&lt;br /&gt;  1.1. Si el usuario ya se ha registrado, el servlet informa de ello y termina.&lt;br /&gt;  1.2. Si el usuario ya lo intentó 3 veces, el servlet informa de ello y termina.&lt;br /&gt;2. En caso contrario, el servlet comprueba el nombre y la clave recibida.&lt;br /&gt;  2.1. Si el nombre es válido y la clave coincide, el servlet registra al usuario, informa de ello y termina.&lt;br /&gt;  2.2. Si el nombre no es válido o la clave no coincide, el servlet incrementa el número de intentos e informa de ello.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Comencemos.&lt;br /&gt;&lt;br /&gt;Las acciones a realizar serán  todas las combinaciones posibles, tal y como se muestra a continuación.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;c.a) 1 -&gt; 1.1&lt;br /&gt;c.b) 1 -&gt; 1.2&lt;br /&gt;c.c) 1 -&gt; 2 -&gt; 2.1&lt;br /&gt;c.d) 1 -&gt; 2 -&gt; 2.2&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;La combinación c.a) representa el escenario en el que un usuario solicita el acceso pero ya se había validado. La combinación c.c) representa el escenario en que un usuario solicita el acceso e introduce un nombre y clave correctos, etc.&lt;br /&gt;&lt;br /&gt;Además, estás combinaciones pueden repetirse varias veces, tal y como se muestra a continuación:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;a) 1 -&gt; 1.1 -&gt; 1 -&gt; 1.1 -&gt; ... (hasta que la sesión expire)&lt;br /&gt;b) 1 -&gt; 1.2 -&gt; 1 -&gt; 1.2 -&gt; ... (hasta que la sesión expire)&lt;br /&gt;c) 1 -&gt; 2 -&gt; 2.2 -&gt; 1 -&gt; 2 -&gt; 2.1&lt;br /&gt;d) 1 -&gt; 2 -&gt; 2.2 -&gt; 1 -&gt; 2 -&gt; 2.2 -&gt; 1 -&gt; 2 -&gt; 2.1&lt;br /&gt;e) 1 -&gt; 2 -&gt; 2.2 -&gt; 1 -&gt; 2 -&gt; 2.2 -&gt; 1 -&gt; 2 -&gt; 2.2 -&gt; 1 -&gt; 1.2 -&gt; 1 -&gt; 1.2 -&gt; ... (hasta que la sesión expire)&lt;br /&gt;f) 1 -&gt; 2 -&gt; 2.2 -&gt; 1 -&gt; 2 -&gt; 2.2 -&gt; 1 -&gt; 2 -&gt; 2.1 -&gt; 1 -&gt; 1.1 -&gt; 1 -&gt; 1.1 -&gt; ... (hasta que la sesión expire)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Las repeticiones de combinaciones, en principio, no las tendremos en cuenta, aunque sería interesante probar algunas, por ejemplo la c, la d, la e y la f.&lt;br /&gt;&lt;br /&gt;Pasemos ahora a los valores de prueba. En primer lugar identificamos los valores o variables y su dominio: &lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Nombre de usuario (Cadena de texto)&lt;br /&gt;Clave (Cadena de texto)&lt;br /&gt;Número de intentos (Entero)&lt;br /&gt;Está validado (Booleano)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;A continuación dividimos el dominio en distintas categorías. Podemos definir, de manera informal, una categoría como un subconjunto de los valores del dominio para los cuales el sistema siempre presenta el mismo comportamiento. Algunos ejemplos de categorías se muestran a continuación:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Nombre de usuario  Correcto / Incorrecto&lt;br /&gt;Clave    Correcto / Incorrecto&lt;br /&gt;Número de intentos  Menor de 3 / igual a 3 *&lt;br /&gt;Registrado   Cierto / Falso&lt;br /&gt;&lt;br /&gt;*- Debemos investigar si es posible que el número de intentos sea mayor que 3 o menor que 0.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;No todas las categorías pueden tomarse siempre. En general, las categorías posibles estarán restringidas por el camino que estemos probando. Por ejemplo para la secuencia c) (la secuencia de acceso correcto), el número de intentos debe ser menos que tres, el usuario no debe estar registrado y el nombre y la clave deben ser correctos. A continuación se expresan las restricciones de cada una de las combinaciones.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;c.a) Registrado = Cierto.&lt;br /&gt;c.b) Registrado = Falso &amp;&amp; Numero de intentos = 3&lt;br /&gt;c.c) Registrado = Falso &amp;&amp; Numero de intentos &lt; 3 &amp;&amp; Nombre = Correcto &amp;&amp; Clave = Correcto&lt;br /&gt;c.d) Registrado = Falso &amp;&amp; Numero de intentos &lt; 3 &amp;&amp; ( Nombre = Incorrecto || Clave = Incorrecto )&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;A partir de estas restricciones vamos a construir los escenarios de prueba. Un escenario de prueba es una combinación concreta, con un conjunto de valores de prueba concretos que cumplen las restricciones. Cada escenario de prueba es una prueba candidata, es decir, puede convertirse en un caso de prueba. En total, para las cuatro combinaciones del principio hemos identificado 10 escenarios de prueba:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Escenario 1:&lt;br /&gt;Camino a)&lt;br /&gt;Registrado = Cierto.&lt;br /&gt;&lt;br /&gt;Escenario 2:&lt;br /&gt;Camino b)&lt;br /&gt;Registrado = Falso &lt;br /&gt;Numero de intentos = 3&lt;br /&gt;&lt;br /&gt;Escenario 3:&lt;br /&gt;Camino c)&lt;br /&gt;Registrado = Falso &lt;br /&gt;Numero de intentos = 0&lt;br /&gt;&lt;br /&gt;Escenario 4:&lt;br /&gt;Camino c)&lt;br /&gt;Registrado = Falso &lt;br /&gt;Numero de intentos = 2&lt;br /&gt;Nombre = correcto&lt;br /&gt;Clave = correcto&lt;br /&gt;&lt;br /&gt;Escenario 5:&lt;br /&gt;Camino d)&lt;br /&gt;Registrado = Falso &lt;br /&gt;Numero de intentos = 0&lt;br /&gt;Nombre = correcto&lt;br /&gt;Clave = incorrecto&lt;br /&gt;&lt;br /&gt;Escenario 6:&lt;br /&gt;Camino d)&lt;br /&gt;Registrado = Falso &lt;br /&gt;Numero de intentos = 0&lt;br /&gt;Nombre = incorrecto &lt;br /&gt;Clave = correcto&lt;br /&gt;&lt;br /&gt;Escenario 7:&lt;br /&gt;Camino d)&lt;br /&gt;Registrado = Falso &lt;br /&gt;Numero de intentos = 0&lt;br /&gt;Nombre = incorrecto &lt;br /&gt;Clave = incorrecto &lt;br /&gt;&lt;br /&gt;Escenario 8:&lt;br /&gt;Camino d)&lt;br /&gt;Registrado = Falso &lt;br /&gt;Numero de intentos = 2&lt;br /&gt;Nombre = correcto&lt;br /&gt;Clave = incorrecto&lt;br /&gt;&lt;br /&gt;Escenario 9:&lt;br /&gt;Camino d)&lt;br /&gt;Registrado = Falso &lt;br /&gt;Numero de intentos = 2&lt;br /&gt;Nombre = incorrecto &lt;br /&gt;Clave = correcto&lt;br /&gt;&lt;br /&gt;Escenario 10:&lt;br /&gt;Camino d)&lt;br /&gt;Registrado = Falso &lt;br /&gt;Numero de intentos = 2&lt;br /&gt;Nombre = incorrecto &lt;br /&gt;Clave = incorrecto&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Aún nos falta decidir cuáles de estos escenarios se implementarán como pruebas con Cactus y definir el resultado esperado de cada escenario. Eso lo veremos en la siguiente entrada, que espero que no tarde tanto como esta.&lt;br /&gt;&lt;br /&gt;Feliz verano.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9553291-115253278294535992?l=rincew.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rincew.blogspot.com/feeds/115253278294535992/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9553291&amp;postID=115253278294535992' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/115253278294535992'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/115253278294535992'/><link rel='alternate' type='text/html' href='http://rincew.blogspot.com/2006/07/un-ejemplo-de-diseo-de-pruebas-con.html' title='Un ejemplo de diseño de pruebas con Cactus (II).'/><author><name>Javier J.</name><uri>http://www.blogger.com/profile/00715175463383886462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9553291.post-115012996132028812</id><published>2006-06-12T16:30:00.000Z</published><updated>2006-06-12T16:34:03.290Z</updated><title type='text'>Un ejemplo de diseño de pruebas con Cactus (I).</title><content type='html'>Saludos y disculpas por la tardanza.&lt;br /&gt;&lt;br /&gt;Vamos a ver un sencillo ejemplo donde vamos a diseñar un conjunto de pruebas para un servlet que controla el acceso mediante nombre y clave (el login de toda la vida) y las vamos a implementar con la herramienta Cactus de Apache.  Por supuesto, las ideas que veamos pueden aplicarse a otras plataformas de desarrollo web y otras herramientas.&lt;br /&gt;Cactus es una herramienta que nos permite escribir pruebas para código que se ejecute en un servidor, como servlets, JSP, filtros, EJB, etc. Básicamente, una prueba escrita en Cactus se divide en dos partes: la parte cliente y la parte servidor. La parte cliente se ejecuta en nuestra máquina mientras que la parte servidor se ejcuta en el servidor. Más adelante veremos como usarla, ahora vamos a describir el problema y diseñar un conjunto de pruebas.&lt;br /&gt;&lt;br /&gt;Vamos a probar el servlet de acceso. Lo primero que necesitamos es el funcionamiento esperado del servlet. Dicho funcionamiento se describe a continuación:&lt;br /&gt;&lt;br /&gt;1. El servlet recibe una petición de login con un nombre de usuario y una clave.&lt;br /&gt;1.1. Si el usuario ya se ha registrado, el servlet informa de ello y termina.&lt;br /&gt;1.2. Si el usuario ya lo intentó 3 veces, el servlet informa de ello y termina.&lt;br /&gt;2. En caso contrario, el servlet comprueba el nombre y la clave recibida.&lt;br /&gt;2.1. Si el nombre es válido y la clave coincide, el servlet registra al usuario, informa de ello y termina.&lt;br /&gt;2.2. Si el nombre no es válido o la clave no coincide, el servlet incrementa el número de intentos e informa de ello.&lt;br /&gt;&lt;br /&gt;El código del servlet que vamos a probar no lo necesitamos ni para diseñar las pruebas ni para codificarlas. De todas amneras dicho código se muestra a continuación:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;import javax.servlet.*;&lt;br /&gt;import javax.servlet.http.*;&lt;br /&gt;&lt;br /&gt;public class AccessServlet extends HttpServlet {&lt;br /&gt;  &lt;br /&gt;  private String user ="test";&lt;br /&gt;  private String passwd = "test";&lt;br /&gt;  &lt;br /&gt;  protected void doPost(HttpServletRequest req, HttpServletResponse resp) &lt;br /&gt;    throws ServletException, java.io.IOException  &lt;br /&gt;  {&lt;br /&gt;    HttpSession session;&lt;br /&gt;    ServletOutputStream  out;&lt;br /&gt;    Boolean validUser;&lt;br /&gt;    Integer numTries;&lt;br /&gt;    String pUser, pPass;&lt;br /&gt;    &lt;br /&gt;    out = resp.getOutputStream();&lt;br /&gt;    &lt;br /&gt;    // 1. Obtengo la sesion.&lt;br /&gt;    session = req.getSession();&lt;br /&gt;    &lt;br /&gt;    // 2. Miro si el usuario ya está validado&lt;br /&gt;    validUser = (Boolean) session.getAttribute("validUser");&lt;br /&gt;    if (validUser != null) {&lt;br /&gt;     out.println("&lt;p&gt;"+"Ya eres un usuario válid");&lt;br /&gt;      out.flush();&lt;br /&gt;      return;&lt;br /&gt;    }&lt;br /&gt;    &lt;br /&gt;    // 3. Comprobamos el número máximo de intentos&lt;br /&gt;    numTries = (Integer) session.getAttribute("numTries");&lt;br /&gt;    if (numTries == null) {&lt;br /&gt;      numTries = new Integer(0);&lt;br /&gt;    }&lt;br /&gt;    &lt;br /&gt;    if (numTries.intValue() &gt;= 3) {&lt;br /&gt;     out.println("&lt;p&gt;"+"Demasiados intentos");&lt;br /&gt;     out.flush();&lt;br /&gt;      return;&lt;br /&gt;    }&lt;br /&gt;     &lt;br /&gt;    // 4. Recuperamos los parámetros.&lt;br /&gt;    pUser = req.getParameter("f_user");&lt;br /&gt;    pPass = req.getParameter("f_password");&lt;br /&gt;     &lt;br /&gt;    // 5. Comprobamos si el nombre y la clave son válidos&lt;br /&gt;    if ( pUser.equals(user) &amp;&amp; pPass.equals(passwd) ) {&lt;br /&gt;     session.setAttribute("validUser", new Boolean(true) );&lt;br /&gt;     out.println("&lt;p&gt;"+"Eres un usuario válido");&lt;br /&gt;     out.flush();&lt;br /&gt;      return;&lt;br /&gt;    }&lt;br /&gt;     &lt;br /&gt;    // 6. Si no, incrementamos en 1 el número de intentos y lo guardamos en la sesión&lt;br /&gt;    numTries = new Integer(numTries.intValue()+1);  &lt;br /&gt;    session.setAttribute("numTries", numTries );    &lt;br /&gt;    out.println("&lt;p&gt;"+"Los datos de acceso no son válidos.");&lt;br /&gt;    out.flush();&lt;br /&gt;     &lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Por simplicidad, se ha incluido el nombre y la clave válidos en el propio servlet. Si consultáramos con una base de datos, las pruebas que vamos a diseñar a continuación serán exactamente iguales. Sin embargo la arquitectura de pruebas (por ejemplo el setUp() de un caso de prueba) será un poco más complejo.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;En la próxima entrada diseñaremos pruebas para probar este servlet y las codificaremos con Cactus.&lt;br /&gt;&lt;br /&gt;PD: en el código de ejemplo faltan los println para las etiquetas HTML y BODI.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9553291-115012996132028812?l=rincew.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rincew.blogspot.com/feeds/115012996132028812/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9553291&amp;postID=115012996132028812' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/115012996132028812'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/115012996132028812'/><link rel='alternate' type='text/html' href='http://rincew.blogspot.com/2006/06/un-ejemplo-de-diseo-de-pruebas-con.html' title='Un ejemplo de diseño de pruebas con Cactus (I).'/><author><name>Javier J.</name><uri>http://www.blogger.com/profile/00715175463383886462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9553291.post-114795622446178455</id><published>2006-05-18T12:42:00.000Z</published><updated>2006-05-18T12:43:44.476Z</updated><title type='text'>Extrayendo fragmentos de frases con expresiones regulares</title><content type='html'>Saludos.&lt;br /&gt;&lt;br /&gt;Recientemente, desarrollando unas herramientas de prueba, me he encontrado la necesidad de extraer fragmentos de frases compuestas. Lo que buscaba era poder hacer algo así. Si tengo, por ejemplo, estas dos frases.&lt;br /&gt;&lt;br /&gt;1: El niño cogió la pelota con las manos.&lt;br /&gt;2: El niño de azul cogió la pelota grande y morada con las dos manos.&lt;br /&gt;&lt;br /&gt;Y quiero aplicar el siguiente patrón.&lt;br /&gt;&lt;br /&gt;El $1 cogió $2 con $3&lt;br /&gt;&lt;br /&gt;El resultado que quiero es:&lt;br /&gt;&lt;br /&gt;1: $1 = "niño", $2 = "la pelota", $3 = "las manos"&lt;br /&gt;2: $1 = "niño de azul", $2 = "la pelota grande y morada", $3 = "las dos manos"&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Por suerte encontré una manera rápida y fácil de hacer esto en Java con expresiones regulares y Jakarta RegExp (http://jakarta.apache.org/regexp/index.html). Con esta herramienta escribo el patrón utilizando una expresión regular y encerrando entre paréntesis las expresiones que luego quiero recuperar. Así, el patrón anterior se expresaría de la siguiente forma:&lt;br /&gt;&lt;br /&gt;El (.+) cogió (.+) con (.+)&lt;br /&gt;&lt;br /&gt;El código para aplicar las frases anteriores al patrón se muestra a continuación.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;br /&gt;import org.apache.regexp.*;&lt;br /&gt;&lt;br /&gt;public class DemoBlog {&lt;br /&gt;  &lt;br /&gt;  public static void main(String[] args) {&lt;br /&gt;    &lt;br /&gt;    String f1 = "El niño cogió la pelota con las manos.";&lt;br /&gt;    String f2 = "El niño de azul cogió la pelota grande y morada con las dos manos.";&lt;br /&gt;    String re00 = "El (.+) cogió (.+) con (.+)";&lt;br /&gt;&lt;br /&gt;    RE re = new RE(re00);&lt;br /&gt;    System.out.println("Match: " + re.match(f1) );&lt;br /&gt;    System.out.println("Match: " + re.match(f2) );&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;El código anterior nos muestra por consola que ambas frases casan con la expresión regular.&lt;br /&gt;&lt;br /&gt;Ahora, para extraer la parte de la frase que casa con cada una de las expresiones entre paréntesis del patrón, solo tenemos que llamar a getParen(índice), con índice = 1 para obtener el primer paréntesis e índice = 3 para el último. Por ejemplo:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    System.out.println("$1 = " + re.getParen(1) );&lt;br /&gt;    System.out.println("$2 = " + re.getParen(2) );&lt;br /&gt;    System.out.println("$3 = " + re.getParen(3) );&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Además, índice = 0 nos devuelve todo el texto (en este caso la frase completa). Si no queremos saber a priori cuanto paréntesis tenemos, podemos ir llamando a getParen(índice) hasta que este nos devuelva null.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Las clases de expresiones regulares (java.util.regexp) que viene a partir del SE 1.4 (y que comentamos en el post de pruebas con JUnit y expresiones regulares) no permiten hacer lo mismo. O, al menos, yo no he encontrado ninguna manera de hacerlo.&lt;br /&gt;&lt;br /&gt;También estuve mirando el proyecto ORO de Jakarta (http://jakarta.apache.org/oro/index.html), por si tuviera algo que me permitiera hacer esto mismo de una manera más potente. Pero en un primer vistazo no he visto nada interesante.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9553291-114795622446178455?l=rincew.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rincew.blogspot.com/feeds/114795622446178455/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9553291&amp;postID=114795622446178455' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/114795622446178455'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/114795622446178455'/><link rel='alternate' type='text/html' href='http://rincew.blogspot.com/2006/05/extrayendo-fragmentos-de-frases-con.html' title='Extrayendo fragmentos de frases con expresiones regulares'/><author><name>Javier J.</name><uri>http://www.blogger.com/profile/00715175463383886462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9553291.post-114710558206464424</id><published>2006-05-08T16:25:00.000Z</published><updated>2006-05-08T16:26:22.086Z</updated><title type='text'>Rompiendo una lanza a favor de las empresas de informática</title><content type='html'>Saludos.&lt;br /&gt;&lt;br /&gt;Algunas veces escuchamos, o leemos, comentarios de que en tal o cual empresa se escriben aplicaciones deficientes y código malo. La causa principal, dicen, es la falta de tiempo. n las empresas hay que hacerlo ya, y por eso no hay tiempo para hacer una buena planificación y un buen diseño.&lt;br /&gt;&lt;br /&gt;No creo que eso sea una buena justificación para hacer mal código. A continuación juego de abogado del diablo y expongo el por qué.&lt;br /&gt;&lt;br /&gt;Dudo mucho que a un arquitecto le pidan el diseño de una torre o unos chalets y le digan que el tiempo no importa, que tarde lo que le apetezca. Lo mismo les pasa a los albañiles, seguro que ellos no tienen tiempo para planificar cómo van a  poner los ladrillos. Sin embargo, los edificios no se caen, ni la gente deja de usarlos porque son inhabitables, ni hay que llamar constantemente a arquitectos y albañiles para que pongan parches. &lt;br /&gt;&lt;br /&gt;A nadie se le ocurre que, por mucha prisa que haya en levantar un edificio, los albañiles comiencen a poner ladrillos por su cuenta. ¿por qué?. Pues porque los arquitectos, los albañiles, y tantos otros profesionales, saben hacerlo rápido y hacerlo bien. &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Esto es algo que, según mi opinión, los informáticos aún no sabemos hacer. Si no hay tiempo para hacer un buen diseño, hay que hacer un buen diseño sobre la marcha.&lt;br /&gt;&lt;br /&gt;Cuando escribamos el código debemos Ser capaces de identificar rápidamente las necesidades y decidir qué clases construir.  Debemos tener muy en cuenta algunos conceptos de diseño fundamentes como las clases tímidas o la encapsulación (pedir a los objetos que hagan operaciones, en vez de pedirles información para hacerlo nosotros). Tampoco debemos olvidar los patrones, por ejemplo, utilizar siempre el patrón iterador (IEnumerator en .NET) para recorrer colecciones de objetos, o implementarlo para nuestras propias colecciones. Si Java y .NET o hacen, ¿por qué no nosotros?. Tampoco hay que escribir tanto código.&lt;br /&gt;&lt;br /&gt;Desde mi opinión, la mayoría de las personas que empiezan en las empresas de informática, vengan de la universidad, de FP, de cursos de formación o autodidactas, no saben hacer estas cosas (incluso algunas que llevan muchos años si no se esfuerzan en mejorar). Aunque también tengo mi opinión de por qué pasa esto, no voy a entrar en ese debate.&lt;br /&gt;&lt;br /&gt;Si nos formamos adecuadamente y ponemos en práctica lo aprendido nuestro código no va a ser bueno a la primera y cometeremos errores. Pero seguro que esos errores serán más pequeños y fáciles de arreglar (encapsular / desacoplar, encapsular / desacoplar, encapsular / desacoplar,....). Seguro que nuestro código es muchísimo mejor y seguro que &lt;br /&gt;&lt;br /&gt;podremos ir convenciendo a nuestros jefes de que, si le dedicamos algo de tiempo al principio, nuestro código será aún mejor, seremos más productivos y más rápidos.&lt;br /&gt;&lt;br /&gt;En resumen, si sabemos hacerlo bien, hacerlo rápido sólo implica pensar más rápido. Hacerlo mal nunca es hacerlo rápido.&lt;br /&gt;&lt;br /&gt;Termino con otra opinión que, en principio, puede parecer que no tenga mucha relación pero creo que la tiene. Un sueldo bajo no debe ser excusa para hacer mal nuestro trabajo.&lt;br /&gt;&lt;br /&gt;PD:&lt;br /&gt;&lt;br /&gt;Por supuesto, esto es una opinión personal algo "extrema" y provocadora. Ni esto es la causa de todos los males ni su solución. Por descontado cualquier otra opinión a favor o en contra es bienvenida.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9553291-114710558206464424?l=rincew.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rincew.blogspot.com/feeds/114710558206464424/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9553291&amp;postID=114710558206464424' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/114710558206464424'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/114710558206464424'/><link rel='alternate' type='text/html' href='http://rincew.blogspot.com/2006/05/rompiendo-una-lanza-favor-de-las.html' title='Rompiendo una lanza a favor de las empresas de informática'/><author><name>Javier J.</name><uri>http://www.blogger.com/profile/00715175463383886462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9553291.post-114615358768213559</id><published>2006-04-27T15:55:00.000Z</published><updated>2006-04-27T15:59:47.700Z</updated><title type='text'>Un plan de pruebas para una aplicación gráfica.</title><content type='html'>Saludos a todos.&lt;br /&gt;&lt;br /&gt;Recientemente me he visto involucrado en un interesante proyecto para desarrollar una aplicación de escritorio con una interfaz gráfica en Java. Esta aplicación permitirá diseñar formularios de una manera gráfica. Estos formularios se almacenan archivos XML,  son específicos de una empresa y utilizan un juego de controles predeterminados y bien documentados.&lt;br /&gt;&lt;br /&gt;La aplicación no es vital. De hecho, sus futuros usuarios llevan ya tiempo creando y trabajando con estos formularios. Sin embargo, ahora estos formularios se construyen a mano con editores de texto. Esto presenta varios problemas: no se puede ver el resultado o los cambios a medida que se hacen y hay que conocer un número muy alto de etiquetas y atributos. &lt;br /&gt;&lt;br /&gt;A continuación expongo un resumen preliminar de cómo creo que debe ser un plan de pruebas y, en concreto, la parte que aborda las pruebas del sistema.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;em&gt;&lt;strong&gt;Pruebas del sistema.&lt;/strong&gt;&lt;/em&gt;&lt;br /&gt;Las pruebas del sistema (según Métrica v3) ejercitan a fondo el sistema completo para verificar que todo funciona bien. Dentro de las pruebas del sistema se encuentran varios tipos de prueba como pruebas funcionales, de carga, de seguridad, de usabilidad, de stress, etc. En este caso, yo me voy a centrar solo en las pruebas funcionales, esto es, las pruebas que verifican que el sistema hace lo que debe hacer a través de su interfaz externa, en este sistema, una interfaz de usuario gráfica.&lt;br /&gt;&lt;br /&gt;Lo primero que vamos a definir es qué se puede hacer con la aplicación:&lt;br /&gt;&lt;br /&gt;1. Crear un nuevo formulario.&lt;br /&gt;2. Modificar un formulario existente.&lt;br /&gt;2.1. Añadir nuevos controles.&lt;br /&gt;2.2. Eliminar controles&lt;br /&gt;2.3. Modificar las propiedades de los controles&lt;br /&gt;2.4. Modificar la posición de los controles.&lt;br /&gt;&lt;br /&gt;Una vez definida la funcionalidad a probar, hay que determinar los valores de prueba. Estos valores de prueba son, sin duda, los formularios. Dividimos los formularios en distintas categorías.&lt;br /&gt;&lt;br /&gt;- Formularios básicos: Un único componente.&lt;br /&gt;- Formulario simples: Entre dos y cuatro componentes.&lt;br /&gt;- Formularios medios: entre cinco y ocho componentes.&lt;br /&gt;- Formularios grandes: entre 9 y 14 componentes.&lt;br /&gt;- Formularios enormes: quince o más componentes.&lt;br /&gt;&lt;br /&gt;También será necesario, cuando haya más información sobre el proyecto, estudiar las características de cada componente que puede haber en un formulario y decidir si es necesario diseñar pruebas específicas para probar características particulares de componentes.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;em&gt;&lt;strong&gt;Diseño de pruebas.&lt;/strong&gt;&lt;/em&gt;&lt;br /&gt;&lt;br /&gt;Ahora, se trata de combinar las operaciones y los tipos de formularios para construir pruebas. Combinando las 5 operaciones a realizar sobre un formulario con los 5 tipos de formularios posibles tenemos un total de 25 tipos de pruebas iniciales. Algunos ejemplos son:&lt;br /&gt;&lt;br /&gt;1. Crear un formulario básico.&lt;br /&gt;2. Añadir un nuevo control a un formulario medio.&lt;br /&gt;3. Eliminar un control de un formulario enorme.&lt;br /&gt;&lt;br /&gt;Estas pruebas se repetirán varias veces con distintos tipos de controles. Además, estas pruebas se combinarán entre ellas para construir pruebas mayores. Por ejemplo, si combinamos las pruebas 2 y 3 para un formulario enorme obtenemos una prueba que añade nuevos controles y después elimina otros controles ya existentes.&lt;br /&gt;&lt;br /&gt;Estimamos que, en las primeras iteraciones, la herramienta tendrá pocas opciones y pocos controles. Así, en las primeras iteraciones deben primar las pruebas de creación de formularios básicos y simples. Por la misma razón, en las últimas iteraciones deben primar las pruebas de modificaciones de formularios grandes y enormes.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;em&gt;&lt;strong&gt;Herramientas.&lt;/strong&gt;&lt;/em&gt;&lt;br /&gt;&lt;br /&gt;Queremos usar dos herramientas para realizar estas pruebas.&lt;br /&gt;&lt;br /&gt;La primera herramienta nos permitirá definir una secuencia de interacciones con la interfaz gráfica (mover el ratón, hacer click, etc.) y ejecutarlas sobre nuestra aplicación. Además, esa herramienta debe permitir comprobar que los resultados de las interacciones son los esperados (un cambio en la pantalla, la aparición de una nueva ventana, etc.).&lt;br /&gt;En un post anterior ya hemos comentado algunas de las herramientas que podemos utilizar para implementar este tipo de pruebas en sistemas Java, tales como Marathon (http://marathonman.sourceforge.net/) o Abbot (http://abbot.sourceforge.net/).&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Hemos visto que queremos construir nuevas pruebas a partir de pruebas básicas (por ejemplo una prueba que sea crear un formulario nuevo, insertar tres controles, guardarlo, cargarlo, insertar dos controles más, borrar un control, modificar otro, etc). Esto presenta algunos problemas:&lt;br /&gt;1) Estas pruebas son largas de codificar (perdemos recursos) y, cuanto más largas sean, más tendremos que escribir más errores podemos tener.&lt;br /&gt;2) El número de posibles combinaciones de añadidos y modificaciones es muy alto.&lt;br /&gt;3) Si hay algún cambio en la interfaz o bien damos por perdidas las pruebas que nos ha constado mucho tiempo implementar, o bien las modificamos con lo que perdemos más tiempo aún.&lt;br /&gt;&lt;br /&gt;La solución es una herramienta que coja unas pocas pruebas simples, las combine y genere tantas pruebas complejas como queramos. Existen trabajos que hablan de como combinar pruebas pero no conozco ninguna herramienta que pueda hacerlo.&lt;br /&gt;&lt;br /&gt;Como opino que las ventajas superan a los inconvenientes, nos planteamos construir una herramienta ad-hoc para construir pruebas a partir de otras. Una vez que tengamos decida una herramienta concreta y sepamos qué formato tienen las pruebas (por ejemplo archivos de código Python o archivos XML) podremos desarrollarla.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;em&gt;&lt;strong&gt;Siguientes pasos.&lt;/strong&gt;&lt;/em&gt;&lt;br /&gt;&lt;br /&gt;Como hemos comentado al principio, esto es una aproximación preliminar. A medida que el desarrollo del sistema avance, se definan con más precisión la funcionalidad del sistema, se obtengan formularios de ejemplo por parte de los clientes, etc. Este plan de pruebas y las pruebas diseñadas se irán completando. Así, durante cada iteración se dispondrá de un conjunto de pruebas del sistema listas para verificar que todo va bien.&lt;br /&gt;&lt;br /&gt;La información necesaria para seguir completando este plan es:&lt;br /&gt;- Una descripción detallada de los componentes.&lt;br /&gt;- Los casos de uso.&lt;br /&gt;- Formularios, a ser posible, reales.&lt;br /&gt;&lt;br /&gt;Ojo, las pruebas contempladas aquí no son suficientes para desarrollar una buena fase de pruebas del sistema. Además se deben probar la correcta implementación de los casos de uso (http://es.wikipedia.org/wiki/UML#Ejemplo_de_diagrama_de_caso_de_uso) del sistema. Por ejemplo, probablemente exista un caso de uso que sea cargar formulario de archivo y otro que sea guardar formulario en archivo dónde se explicará como tiene que comportarse la aplicación al realizar esas operaciones. Por tanto, debemos desarrollar también algunas pruebas que comprueben no solo que los formularios se cargar y guardan correctamente y dichas operaciones se comportan como dicen los casos de uso.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Si este proyecto sigue adelante y, además, hay la oportunidad de poner estas ideas en prácticas, os lo comentaré.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9553291-114615358768213559?l=rincew.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rincew.blogspot.com/feeds/114615358768213559/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9553291&amp;postID=114615358768213559' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/114615358768213559'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/114615358768213559'/><link rel='alternate' type='text/html' href='http://rincew.blogspot.com/2006/04/un-plan-de-pruebas-para-una-aplicacin_27.html' title='Un plan de pruebas para una aplicación gráfica.'/><author><name>Javier J.</name><uri>http://www.blogger.com/profile/00715175463383886462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9553291.post-114483555402620217</id><published>2006-04-12T09:48:00.000Z</published><updated>2006-04-12T09:52:34.026Z</updated><title type='text'>Andalucía, tierra de OpenCMS</title><content type='html'>Saludos a todos.&lt;br /&gt;&lt;br /&gt;En una entrada anterior comentada que, en Andalucía (al sur de España), las tencologías dominantes en la industria, sin duda, son Java y Oracle (http://rincew.blogspot.com/2005_10_01_rincew_archive.html). De un tiempo a esta parte, me he dado cuenta que varias empresas (de las más importantes, al menos en mi opninión) están utilizando OpenCMS como plataforma de desarrollo de portales (http://www.opencms.org/). &lt;br /&gt;&lt;br /&gt;No conozco la herramienta, así que ignoro si es la mejor, pero si varias empresas importantes y la administración pública la usan, desde luego hay que tenerla en cuenta.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9553291-114483555402620217?l=rincew.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rincew.blogspot.com/feeds/114483555402620217/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9553291&amp;postID=114483555402620217' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/114483555402620217'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/114483555402620217'/><link rel='alternate' type='text/html' href='http://rincew.blogspot.com/2006/04/andaluca-tierra-de-opencms.html' title='Andalucía, tierra de OpenCMS'/><author><name>Javier J.</name><uri>http://www.blogger.com/profile/00715175463383886462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9553291.post-114416158983005496</id><published>2006-04-04T14:36:00.000Z</published><updated>2006-04-04T14:39:49.850Z</updated><title type='text'>Referencias sobre generación de pruebas a partir de casos de uso.</title><content type='html'>Saludos.&lt;br /&gt;&lt;br /&gt;Para quién le interese. A continuación pongo algunos enlaces sobre documentación para generar pruebas a partir de casos de uso.&lt;br /&gt;&lt;br /&gt;En mi página web ha puesto los reusltados de un enlace comparativo con casos prácticos:&lt;br /&gt;http://www.lsi.us.es/~javierj/publications.html&lt;br /&gt;(descargar "Generación de casos de prueba a partir de la especificación funcional")&lt;br /&gt;&lt;br /&gt;En STORM es posible encontrar varias listas de herramientas de prueba. Aunque no conozco ninguna herramienta de acceso público para generar pruebas, sí&lt;br /&gt;se pueden encontrar algunas herramientas muy interesante para implementar las pruebas. Se puede encontrar alguna herramienta de libre descarga para&lt;br /&gt;TSL, pero está a añados luz de distancia de WinRunner.&lt;br /&gt;http://www.mtsu.edu/~storm/&lt;br /&gt;&lt;br /&gt;Algunos artículos que pueden ser interesantes. Son fáciles de encontrar en Google. Si alguien no los localiza que no dude en pedírmelos:&lt;br /&gt;&lt;br /&gt;Axel Ruder et-al. 2004. A Model-based Approach to Improve System Testing of Interactive Applications. ISSTA’04. Boston, USA.&lt;br /&gt;L. Briand, Y. Labiche. 2002. A UML-Based Approach to System Testing. Carleton University Inner Report. &lt;br /&gt;Heumann , Jim,  2002. Generating Test Cases from Use Cases. Journal of Software Testing Professionals.&lt;br /&gt;Nebut, C. Fleurey, et-al. 2003. Requirements by contract allow automated system testing. Procedings of the 14th International symposium of Software Reliability Engineering (ISSRE'03). Denver, Colorado. EEUU.&lt;br /&gt;&lt;br /&gt;UCTSystem. http://www.irisa.fr/triskell/results/ISSRE03/UCTSystem/UCTSystem.html&lt;br /&gt;&lt;br /&gt;Espero que los disfruteis.&lt;br /&gt;&lt;br /&gt;Si alguien tiene interés en el tema y quiere más información que no dude en comunicármelo.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9553291-114416158983005496?l=rincew.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rincew.blogspot.com/feeds/114416158983005496/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9553291&amp;postID=114416158983005496' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/114416158983005496'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/114416158983005496'/><link rel='alternate' type='text/html' href='http://rincew.blogspot.com/2006/04/referencias-sobre-generacin-de-pruebas.html' title='Referencias sobre generación de pruebas a partir de casos de uso.'/><author><name>Javier J.</name><uri>http://www.blogger.com/profile/00715175463383886462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9553291.post-114303007727411611</id><published>2006-03-22T12:17:00.000Z</published><updated>2006-03-22T12:21:17.290Z</updated><title type='text'>Materiales de cursos</title><content type='html'>Saludos.&lt;br /&gt;&lt;br /&gt;Estoy poniendo en mi página web materiales relacionados sobre algunos y seminarios que he tenido la suerte de poder impartir. Por si a alguien le interesa la dirección es:&lt;br /&gt;http://www.lsi.us.es/~javierj/cursos.htm&lt;br /&gt;&lt;br /&gt;Aún hay poco material. Espero ir subiendo más cosas a lo largo de estas semanas. &lt;br /&gt;&lt;br /&gt;Nos leemos.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9553291-114303007727411611?l=rincew.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rincew.blogspot.com/feeds/114303007727411611/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9553291&amp;postID=114303007727411611' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/114303007727411611'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/114303007727411611'/><link rel='alternate' type='text/html' href='http://rincew.blogspot.com/2006/03/materiales-de-cursos.html' title='Materiales de cursos'/><author><name>Javier J.</name><uri>http://www.blogger.com/profile/00715175463383886462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9553291.post-114138233403582419</id><published>2006-03-03T10:37:00.000Z</published><updated>2006-03-03T10:38:54.053Z</updated><title type='text'>Trabajando con HipersonicDB</title><content type='html'>Saludos.&lt;br /&gt;&lt;br /&gt;Existen varias bases de datos libres implementadas en Java. Una de ellas, la que uso habitualmente, es HipersonicSQL DB (http://hsqldb.org/). &lt;br /&gt;&lt;br /&gt;¿Por qué utilizar una base de datos pequeña escrita en Java cuando existen sistemas como MySQL, PostgreSQL o Firebird?. En mi caso, la respuesta es comodidad. Puedo distribuir junto con mis programas todo el servidor de bases de datos (sólo son 200-300 KB de más) evitando que los usuarios tengan que instalar ni configurar nada. En las primeras etapas de desarrollo es una opción muy interesante, ya que podemos tener nuestra base de datos funcionando de manera autónoma sin depender de la instalación y configuración de ningún servidor.&lt;br /&gt;Además, HSQLDB es bastante completa soportando vistas, integridad referencial, transacciones, consultas batch, etc. (verificar esto).&lt;br /&gt;&lt;br /&gt;HipersonicSQL tiene varios modos de funcionamiento. Los dos principales son local y servidor. En el primer modo, el servidor se inicia y termina a la misma vez que el programa. En el segundo, hemos de iniciar y terminar el servidor a mano (como en cualquier otro sistema). La principal diferencia entre ambos es su  cadena de conexión, como se muestra a continuación&lt;br /&gt;&lt;br /&gt;Modo servidor: jdbc:hsqldb:hsql://localhost/base_de_datos&lt;br /&gt;Modo local: jdbc:hsqldb:ruta_a_la_base_de_datos&lt;br /&gt;&lt;br /&gt;A continuación pongo el código de un sencillo ejemplo que se conecta a una base de datos en modo local, realiza una consulta y muestra el resultado. Lo único necesario para que este ejemplo funcione (además de la base de datos), es tener hsqldb.jar en el CLASSPATH.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;import java.sql.*;&lt;br /&gt;public class SQLStatement  {&lt;br /&gt; public static void main(String args[]) {&lt;br /&gt;  try {&lt;br /&gt;   Class.forName("org.hsqldb.jdbcDriver");&lt;br /&gt;&lt;br /&gt;  } catch(java.lang.ClassNotFoundException e) {&lt;br /&gt;   System.err.println(e.getMessage());&lt;br /&gt;  }&lt;br /&gt;  try {&lt;br /&gt;      Connection con = DriverManager.getConnection("jdbc:hsqldb:./testdb/testdb", &lt;br /&gt;          "sa", "");&lt;br /&gt;&lt;br /&gt;      String query = "select COF_NAME from COFFEES";&lt;br /&gt;      Statement stmt = con.createStatement();       &lt;br /&gt;   ResultSet rs = stmt.executeQuery(query);&lt;br /&gt;   ResultSetMetaData rsmd = rs.getMetaData();&lt;br /&gt;   int numberOfColumns = rsmd.getColumnCount();&lt;br /&gt;      System.out.println("Columns: "+numberOfColumns);&lt;br /&gt;   while (rs.next()) {&lt;br /&gt;    System.out.println("Fila " + rowCount + ":  ");&lt;br /&gt;    for (int i = 1; i &lt;= numberOfColumns; i++) {&lt;br /&gt;     System.out.print("   Columna " + i + ":  ");&lt;br /&gt;     System.out.println(rs.getString(i));&lt;br /&gt;    }&lt;br /&gt;    System.out.println("");&lt;br /&gt;   }&lt;br /&gt;      stmt.execute("Shutdown");&lt;br /&gt;   stmt.close();&lt;br /&gt;   con.close();&lt;br /&gt;&lt;br /&gt;  } catch(SQLException ex) {&lt;br /&gt;   System.err.println(ex.getMessage());&lt;br /&gt;  } &lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;No todos son ventajas trabajando con HSQLDB. Por ejemplo, las bases de datos de la versión 1.7 no son compatibles con la versión 1.8, lo cuál es malo. Sin embargo, tampoco he encontrado la necesidad de actualizar, por lo que he seguido trabajando con el 1.7 sin problemas.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9553291-114138233403582419?l=rincew.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rincew.blogspot.com/feeds/114138233403582419/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9553291&amp;postID=114138233403582419' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/114138233403582419'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/114138233403582419'/><link rel='alternate' type='text/html' href='http://rincew.blogspot.com/2006/03/trabajando-con-hipersonicdb.html' title='Trabajando con HipersonicDB'/><author><name>Javier J.</name><uri>http://www.blogger.com/profile/00715175463383886462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9553291.post-113924770408746069</id><published>2006-02-06T17:41:00.000Z</published><updated>2006-02-06T17:41:44.110Z</updated><title type='text'>Mutaciones y Java.</title><content type='html'>Saludos. &lt;br /&gt;&lt;br /&gt;Una técnica de prueba de código bastante antigua (de los 70) y famosa son las mutaciones. Sin embargo, esta técnica no suele ser muy conocida. Voy a hacer un breve resumen de ella y después comentaré una herramienta para Java.&lt;br /&gt;&lt;br /&gt;A grandes rasgos, la técnica de las mutaciones surge de la idea de que todos los fallos de un programa vienen motivados por pequeños cambios en el código, como un error al escribir un número, un operador matemático o una comprobación. Obviamente, un fallo grande es un conjunto de fallos pequeños.&lt;br /&gt;&lt;br /&gt;La técnica de las mutaciones propone un conjunto de operadores para obtener código mutado: por ejemplo cambiar un '&gt;' por un '&lt;' o un '+' por un '-'. Con esto, el código mutado que se genera es erróneo (aunque no siempre, puede dar la casualidad de que siga estando bien).&lt;br /&gt;&lt;br /&gt;¿Pero para qué sirve el código mutado?. Principalmente tiene dos usos: 1) guiar la construcción de pruebas, 2) validar un conjunto de pruebas. Comentémoslos brevemente.&lt;br /&gt;&lt;br /&gt;En primer lugar, el código mutado nos dice qué prueba hemos de construir. Por ejemplo, si hemos cambiado un '+' por un '-', debemos construir una prueba que sea capaz de detectar ese error. A más mutaciones, más pruebas y más fiable será nuestro código.&lt;br /&gt;&lt;br /&gt;Además, las mutaciones pueden indicarnos lo bueno que es un conjunto de pruebas ya existente. Así, generamos un conjunto de mutaciones, ejecutamos las pruebas sobre el conjunto y, según el número de mutaciones detectadas podemos tener una idea de la eficacia de las pruebas.&lt;br /&gt;&lt;br /&gt;Como ya he comentado, esta es una técnica antigua, por lo que ya existen muchos trabajos que describen operadores "mutacionales" para obtener mutaciones de código en muchos lenguajes como Fortran, C, o Java. También existen varias herramientas, aunque, personalmente, no conozco ninguna muy famosa, ni libre, ni fácil de usar.&lt;br /&gt;&lt;br /&gt;La última que he visto (MuJava http://www.isse.gmu.edu/faculty/ofut/mujava/) es gratuita pero no libre. El trabajo con esta herramienta se divide en dos partes: en primer lugar la herramienta es capaz de generar ella sola un conjunto de mutantes a partir de las clases y métodos originales. Después, la herramienta permite ejecutar un conjunto de pruebas sobre los mutantes y detectar los mutantes eliminados (aquellos detectados con alguna prueba). Por desgracia, las pruebas hemos de escribirlas a mano. A la hora de la verdad, la herramienta se comía la primera letra de mis clases y solo era capaz de generar un montón de NullPointerException. Lástima que no sea libre y pueda corregir el código.&lt;br /&gt; &lt;br /&gt;Aunque las herramientas que conozco no permitan sacar todo el jugo de la técnica de mutaciones, creo que los fundamentos son muy importantes para cualquier que escriba pruebas para código. Conocer los operadores para crear mutaciones nos va a dar muchas buenas ideas sobre qué pruebas escribir para verificar que nuestro código funciona y siga funcionando en el futuro.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9553291-113924770408746069?l=rincew.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rincew.blogspot.com/feeds/113924770408746069/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9553291&amp;postID=113924770408746069' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/113924770408746069'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/113924770408746069'/><link rel='alternate' type='text/html' href='http://rincew.blogspot.com/2006/02/mutaciones-y-java.html' title='Mutaciones y Java.'/><author><name>Javier J.</name><uri>http://www.blogger.com/profile/00715175463383886462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9553291.post-113751430973878211</id><published>2006-01-17T16:09:00.000Z</published><updated>2006-01-17T16:11:49.753Z</updated><title type='text'>Herramientas libres para pruebas de sistema/aceptación en Java.</title><content type='html'>&lt;p&gt;Saludos. &lt;/p&gt;&lt;br /&gt;&lt;p&gt;Una de las cosas que he estado haciendo aprovechando la tranquilidad de las navidades es ojear herramienta que me permitan hacer &lt;br /&gt;pruebas del sistema/aceptaci&amp;oacute;n sobre aplicaciones Java. A continuaci&amp;oacute;n os cuento un resumen superficial de mis opiniones. &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;strong&gt;El problema: &lt;/strong&gt;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;Quiero escribir una prueba que verifique que una aplicaci&amp;oacute;n tipo bloc de notas carga correctamente un archivo del sistema. La &lt;br /&gt;aplicaci&amp;oacute;n elegida fue, al principio, el Notepad que se incluye como ejemplo en el paquete J2SE. Como me dio problemas con &lt;br /&gt;algunas herramientas, al final hice las pruebas con Stylepad, que tambi&amp;eacute;n se incluye como ejemplo en J2SE. &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;strong&gt;Las herramientas: &lt;/strong&gt;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;Lo que buscaba es una herramienta que me permitiera indicar las mismas secuencias que har&amp;iacute;a una persona que cargara un archivo. Es decir: pulsar en el men&amp;uacute; file &amp;gt; open, navegar hasta una carpeta predeterminada, seleccionar en un archivo concreto y pulsar el bot&amp;oacute;n open. Para evitar tener que pelearme con lenguajes o archivos XML, busqu&amp;eacute; herramientas que me permitan grabar y reproducir todas esas acciones. &lt;br&gt;&lt;br /&gt;Las elegidas fueron 3: Abbot (link) , JFCUnit (link)y Marathon (link). &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;strong&gt;Abbot: &lt;/strong&gt;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;Abbot sirve tanto para probar componentes de manera aislada como para grabar y reproducir una secuencia de acciones. La herramienta viene con un editor (llamado Costello) muy completo que facilita la tarea de grabar secuencias, construir casos de prueba, y reproducirlas. No fue nada dif&amp;iacute;cil echarla a andar. &lt;br&gt;&lt;br /&gt;Con el editor ejecut&amp;eacute; la aplicaci&amp;oacute;n y captur&amp;eacute; todas las pulsaciones de rat&amp;oacute;n perfectamente. El editor, adem&amp;aacute;s, registr&amp;oacute; todos los componentes (JMenuBar, JPane, JTextPane, etc. ) implicados en la secuencia. A la hora de a&amp;ntilde;adir asertos, no tuve m&amp;aacute;s que elegir el componente, elegir la propiedad que quer&amp;iacute;a comprobar y el valor al que iba a comparara. Realmente sencillo. &lt;br&gt;&lt;br /&gt;Ahora vienen las cosas malas. Abbot almacena los casos de prueba en un XML bastante complejo, lo que hace dif&amp;iacute;cil hacer pruebas sin grabar/reproducir. Adem&amp;aacute;s, el editor siempre se colgaba con bastante rapidez. La descripci&amp;oacute;n de errores es muy poco clara, ya que se limita simplemente a mostrar el texto de la excepci&amp;oacute;n, lo cual no tiene ninguna utilidad para m&amp;iacute; que no conoc&amp;iacute;a como estaba hecha la herramienta por dentro. &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;strong&gt;JFCUnit: &lt;/strong&gt;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;JFCUnit permite realizar pruebas unitarias de interfaces gr&amp;aacute;ficas de usuario y pruebas del sistema. A diferencia de Abbot y Marathon, esta herramienta no trae ning&amp;uacute;n editor que permita capturar/reproducir. Para grabar y reproducir hay que escribir un par de programas en programa Java, f&amp;aacute;cil de hacer una vez que se investiga a fondo en la documentaci&amp;oacute;n y ejemplos. Adem&amp;aacute;s, es necesario modificar el c&amp;oacute;digo de la prueba a mano para a&amp;ntilde;adir los asertos. Adem&amp;aacute;s, la prueba no fue capaz de grabar mi interacci&amp;oacute;n con el FileDialog para seleccionar el archivo a abrir. &lt;br&gt;&lt;br /&gt;Las pruebas tambi&amp;eacute;n se almacenan en XML. Aunque la documentaci&amp;oacute;n de JFCUnit es muy completa, falta una buena referencia de las etiquetas y sus atributos. algo indispensable ya que no dispone de un editor. &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;strong&gt;Marathon: &lt;/strong&gt;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;Esta herramienta s&amp;oacute;lo sirve para pruebas de sistema/aceptaci&amp;oacute;n, no permitirnedo escribir pruebas para componentes de manera individual. En esta herramienta las pruebas nos e guardan en XML sino en Python (y se procesan con JPython). Esto hace que el c&amp;oacute;digo sea muy compacto, muy legible y que tengamos toda la potencia de Python a nuestra disposici&amp;oacute;n. &lt;br&gt;&lt;br /&gt;Aunque el editor no es tan completo como el editor de Abbot, incluye un men&amp;uacute; contextual sobre la aplicaci&amp;oacute;n a prueba que permite a&amp;ntilde;adir comprobaciones al mismo tiempo que se graba. Sin embargo tambi&amp;eacute;n presenta problemas. El m&amp;aacute;s importante es que, al igual que en la herramienta anterior, no se ha capturado la interacci&amp;oacute;n con el di&amp;aacute;logo para abrir un archivo. La documentaci&amp;oacute;n, aunque muy buena para empezar, se queda muy corta cuando quieres profundizar. &lt;/p&gt;&lt;br /&gt;&lt;p&gt;&amp;nbsp; &lt;/p&gt;&lt;br /&gt;&lt;p&gt;&lt;strong&gt;Conclusiones: &lt;/strong&gt;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;En una primera aproximaci&amp;oacute;n, no creo que ninguna de estas tres sea la herramienta definitiva. La herramienta que m&amp;aacute;s me ha gustado es Marathon, sin duda por trabajar en Python. He encontrado mucho m&amp;aacute;s c&amp;oacute;modo entender c&amp;oacute;digo en Python que lenguajes propios de etiquetas. Marathon, adem&amp;aacute;s, es capaz de ocultar los detalles de la interfaz gr&amp;aacute;fica mejor que las dem&amp;aacute;s. Esto es muy &amp;uacute;til cuando queremos escribir las pruebas antes de tener la interfaz gr&amp;aacute;fica, o para evitar tener que volver a grabar todas las pruebas por alg&amp;uacute;n cambio en la interfaz. &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Estas navidades tambi&amp;eacute;n estuve investigando algunas herramientas para probar aplicaciones web. En un futuro post, dentro de un mes m&amp;aacute;s o menos, hablar&amp;eacute; de ellas. Por supuesto escribir&amp;eacute; m&amp;aacute;s mensajes mientras.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9553291-113751430973878211?l=rincew.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rincew.blogspot.com/feeds/113751430973878211/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9553291&amp;postID=113751430973878211' title='12 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/113751430973878211'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/113751430973878211'/><link rel='alternate' type='text/html' href='http://rincew.blogspot.com/2006/01/herramientas-libres-para-pruebas-de.html' title='Herramientas libres para pruebas de sistema/aceptación en Java.'/><author><name>Javier J.</name><uri>http://www.blogger.com/profile/00715175463383886462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>12</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9553291.post-113639126837292828</id><published>2006-01-04T16:13:00.000Z</published><updated>2006-01-04T16:14:28.400Z</updated><title type='text'>Probando excepciones con JUnit.</title><content type='html'>Saludos.&lt;br /&gt;&lt;br /&gt;Escribir una prueba con JUnit que compruebe si salta o no una excepción es muy sencillo. Por ejemplo, supongamos que queremos probar un método con la  &lt;br /&gt;siguiente cabecera:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;public Empleado obtenerEmpleado(String DNI)&lt;br /&gt;  throws DNIErroneo&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Vamos a escribir, en primer lugar, una prueba que compruebe que cuando le indicamos un DNI correcto, no lanza la excepción:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;public void testDNICorrectoNoExcepcion() {&lt;br /&gt; try {&lt;br /&gt;  // Nota: es un DNI inventado. Supongamos que es correcto&lt;br /&gt;  obtenerEmpleado("18665230F");&lt;br /&gt; } catch (DNIErroneo e) {&lt;br /&gt;  fail();&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Como se puede ver, si surge una excepción se ejecuta fail(), lo cuál termina el caso de prueba con error. Si no, termina normalmente.&lt;br /&gt;&lt;br /&gt;Ahora vamos a escribir una prueba que verifique lo contrario. Le indicaremos al método un DNI erróneo y verificaremos que nos devuelve una excepción.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;public void testDNIErroneoExcepcion() {&lt;br /&gt; try {&lt;br /&gt;  // Nota: es un DNI inventado. Supongamos que es correcto&lt;br /&gt;  obtenerEmpleado("18665230F");&lt;br /&gt; } catch (DNIErroneo e) {&lt;br /&gt;  return;&lt;br /&gt; }&lt;br /&gt; fail();&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Ahora, fail() se ejecutará si no ha aparecido la la excepción de DNI erróneo. Si aparee cualquier otra excepción, la prueba también fallará.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9553291-113639126837292828?l=rincew.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rincew.blogspot.com/feeds/113639126837292828/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9553291&amp;postID=113639126837292828' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/113639126837292828'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/113639126837292828'/><link rel='alternate' type='text/html' href='http://rincew.blogspot.com/2006/01/probando-excepciones-con-junit.html' title='Probando excepciones con JUnit.'/><author><name>Javier J.</name><uri>http://www.blogger.com/profile/00715175463383886462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9553291.post-113569791687843084</id><published>2005-12-27T15:37:00.000Z</published><updated>2005-12-27T15:38:36.896Z</updated><title type='text'>Utilizando Drools II. Caso práctico 2/2</title><content type='html'>&lt;p&gt;Como ya hemos visto, Drools es un motor de reglas de negocio. &amp;iquest;Cu&amp;aacute;ndo podemos utilizar Drools?. Pues,por ejemplo:&lt;/p&gt;&lt;br /&gt;  &lt;ul&gt;&lt;br /&gt;    &lt;li&gt;      Cuando la l&amp;oacute;gica de negocio sea compleja pero se pueda expresar mediante cl&amp;aacute;usulas IF-THEN.&lt;/li&gt;&lt;br /&gt;    &lt;li&gt;Cuando vaya a haber muhcos cambios.&lt;/li&gt;&lt;br /&gt;    &lt;li&gt;Cuando el cliente no tiene clara su l&amp;oacute;gica de negocio.&lt;/li&gt;&lt;br /&gt;    &lt;li&gt;Cuando el cliente se exprese de manera natural mediante IF-THEN.&lt;/li&gt;&lt;br /&gt;  &lt;/ul&gt;&lt;br /&gt;&lt;p&gt;B&amp;aacute;sicamente, al utilizar Drools tendremos que escribir dos elementos: las reglas de negocio y el sistema que utilizar&amp;aacute; dichas reglas (a a trav&amp;eacute;s de Drools). Drools permite escribir reglas en lenguaje Java, Python o Groovy. En este ejemplo vamos a escribir las reglas utilizando Java. Las reglas se almacenan en un archivo DRL, que no es m&amp;aacute;s que un archivo XML con la definici&amp;oacute;n de las reglas. La estructura (simplificada) de un archivo DRL se muestra a continuaci&amp;oacute;n&lt;/p&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;  &lt;li&gt;Imports de elementos de paquetes.&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;Declaraci&amp;oacute;n de funciones auxiliares.&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;Nombre del conjunto de reglas.&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;Reglas&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;p&gt;Para cada regla podemos indicar:&lt;br&gt;&lt;br /&gt;  &lt;/p&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;  &lt;li&gt;Nombre de la regla.&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;Par&amp;aacute;metros (los nombres son importantes).&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;Conciones para que la regla se dispare.&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;Consecuencias.&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;p&gt;A continuaci&amp;oacute;n se muestra un ejemplo de un archivo DRL completo para el ejemplo que expusimos en el post aterior. Concretamente este archivo DRL incluye una regla por la que, si el precido de un pedido es superior a 100, le descuenta el 5%.&lt;/p&gt;&lt;br /&gt;&lt;pre&gt;&amp;lt;?xml version=&amp;quot;1.0&amp;quot;?&amp;gt;&lt;br&gt;&amp;lt;rule-set name=&amp;quot;BusinessRulesSample&amp;quot;&lt;br /&gt;   xmlns=&amp;quot;http://drools.org/rules&amp;quot;&lt;br /&gt;   xmlns:java=&amp;quot;http://drools.org/semantics/java&amp;quot;&lt;br /&gt;   xmlns:xs=&amp;quot;http://www.w3.org/2001/XMLSchema-instance&amp;quot;&lt;br /&gt;   xs:schemaLocation=&amp;quot;http://drools.org/rules rules.xsd&lt;br /&gt;   http://drools.org/semantics/java java.xsd&amp;quot;&amp;gt;&lt;br /&gt; &lt;br&gt; &amp;lt;java:import&amp;gt; java.lang.Object &amp;lt;/java:import&amp;gt;&lt;br /&gt; &amp;lt;java:import&amp;gt; java.lang.String &amp;lt;/java:import&amp;gt;&lt;br /&gt; &amp;lt;java:import&amp;gt; ebr.beans.Pedido &amp;lt;/java:import&amp;gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt; &amp;lt;rule name=&amp;quot;Calculo del total del pedido&amp;quot;&amp;gt;&lt;br /&gt; &amp;lt;parameter identifier=&amp;quot;pedido&amp;quot;&amp;gt;&lt;br /&gt; &amp;lt;class&amp;gt;ebr.beans.Pedido&amp;lt;/class&amp;gt;&lt;br /&gt; &amp;lt;java:condition&amp;gt;&lt;br /&gt;   pedido.getCalculoTotalOriginal() &amp;gt;= 100.0&lt;br /&gt; &amp;lt;/java:condition&amp;gt;&lt;br /&gt; &amp;lt;java:consequence&amp;gt;&lt;br /&gt;   pedido.setTotalFinal(pedido.getCalculoTotalOriginal() * 0.95);&lt;br /&gt; &amp;lt;/java:consequence&amp;gt;&lt;br /&gt; &amp;lt;/rule&amp;gt;&lt;br /&gt; &lt;/pre&gt;&lt;br /&gt;Para disparar esta regla vamos a escribir, en prime lugar, los beans que van a guardar la informaci&amp;oacute;n &lt;br /&gt;del workspace y un sencillo programa que utilice el motor. Uno de los beans guardar&amp;aacute; informaci&amp;oacute;n sobre cada producto (nombre cantidad y precio) y el otro guardar&amp;aacute; la lista de productos. El sgeundo bean se muestra a continuaci&amp;oacute;n.&lt;br&gt;&lt;br /&gt;&lt;pre&gt;import java.util.*;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;public class Pedido {&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt; private List entradas;&lt;br /&gt; private double totalFinal; &lt;/pre&gt;&lt;br /&gt;&lt;pre&gt; public Pedido() {&lt;br /&gt;   entradas = new ArrayList();&lt;br /&gt;   this.totalFinal = -1.0;&lt;br /&gt; }&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt; public void addEntrada (Entrada e) {&lt;br /&gt;   this.entradas.add(e);&lt;br /&gt; }&lt;br /&gt;   &lt;br /&gt; public List getEntradas() {&lt;br /&gt;   return this.entradas;&lt;br /&gt; }&lt;br /&gt;   &lt;br /&gt; public void getEntradas(List entradas) {&lt;br /&gt;   this.entradas = entradas;&lt;br /&gt; }&lt;br /&gt;   &lt;br /&gt; public double getCalculoTotalOriginal() {&lt;br /&gt;   Entrada e;&lt;br /&gt;   double total = .0;&lt;br /&gt;   Iterator it = this.entradas.iterator();&lt;br /&gt;   &lt;br /&gt;   while(it.hasNext()) {&lt;br /&gt;     e = (Entrada) it.next();&lt;br /&gt;     total += e.getCantidad();&lt;br /&gt;   }&lt;br /&gt;   &lt;br /&gt;   return total;&lt;br /&gt; }&lt;br /&gt;   &lt;br /&gt; public void setTotalFinal(double total) {&lt;br /&gt;   this.totalFinal = total;&lt;br /&gt; }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;Para trabajar con Drools necesitamos importar los sigientes elementos:&lt;/p&gt;&lt;br /&gt;&lt;pre&gt;  import java.io.IOException;&lt;br /&gt;   import org.drools.DroolsException;&lt;br /&gt;   import org.drools.RuleBase;&lt;br /&gt;   import org.drools.WorkingMemory;&lt;br /&gt;   import org.drools.event.DebugWorkingMemoryEventListener;&lt;br /&gt;   import org.drools.io.RuleBaseLoader;&lt;br /&gt;   import org.xml.sax.SAXException;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;Lo primero que tenemos que hacer es crear un nuevo pedido:&lt;/p&gt;&lt;br /&gt;&lt;pre&gt;  Entrada e1 = new Entrada(&amp;quot;A&amp;quot;, 10, 5);&lt;br /&gt;   Entrada e2 = new Entrada(&amp;quot;B&amp;quot;, 6, 6);&lt;br /&gt;   Pedido pedido = new Pedido();&lt;br /&gt;   pedido.addEntrada (e1);&lt;/pre&gt;&lt;br /&gt;En este pedido tenemos 10 unidades del producto A cada una a un precio de 5 y 6 unidades del B a un &lt;br /&gt;precio de 6. A continuaci&amp;oacute;n creamos nuestro motor de reglas para que procese el archivo de reglas visto m&amp;aacute;s arriba:&lt;br /&gt;&lt;pre&gt;  BREngine br = new BREngine();&lt;br /&gt;   RuleBase businessRules = RuleBaseLoader.loadFromUrl(&lt;br /&gt;     BREngine.class.getResource( &amp;quot;rules01.drl&amp;quot; ) );&lt;/pre&gt;&lt;br /&gt;Limpiamos el espacio de trabajo e instalamos un listener que ser&amp;aacute; avisado cada vez que Drools ejecute una regla. Este listener mostrar&amp;aacute; un mnesaje por la salida est&amp;aacute;ndar.&lt;br /&gt;&lt;pre&gt;  WorkingMemory workingMemory = businessRules.newWorkingMemory();&lt;br /&gt;   workingMemory.addEventListener(new DebugWorkingMemoryEventListener());&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;A continuaci&amp;oacute;n a&amp;ntilde;adimos el pedido:&lt;/p&gt;&lt;br /&gt;&lt;pre&gt; workingMemory.assertObject(fact);&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;Y a trabajar:&lt;/p&gt;&lt;br /&gt;&lt;pre&gt; workingMemory.fireAllRules();&lt;br /&gt; &lt;/pre&gt;&lt;br /&gt;&lt;p&gt;&lt;em&gt;Nota, los try / catch de todas las pisbles excepciones han sido omitios.&lt;/em&gt;&lt;br&gt;&lt;br /&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9553291-113569791687843084?l=rincew.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rincew.blogspot.com/feeds/113569791687843084/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9553291&amp;postID=113569791687843084' title='9 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/113569791687843084'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/113569791687843084'/><link rel='alternate' type='text/html' href='http://rincew.blogspot.com/2005/12/utilizando-drools-ii-caso-prctico-22.html' title='Utilizando Drools II. Caso pr&amp;aacute;ctico 2/2'/><author><name>Javier J.</name><uri>http://www.blogger.com/profile/00715175463383886462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>9</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9553291.post-113501556193317100</id><published>2005-12-19T18:05:00.000Z</published><updated>2005-12-19T18:06:01.946Z</updated><title type='text'>Cumplimos un año. Gracias !!!!!!</title><content type='html'>Saludos a todos.&lt;br /&gt;&lt;br /&gt;Recientemente este blog ha cumplido un año. Sinceramente, cuando empecé no esperaba que fuera capaz de mantenerlo todo este tiempo, no por falta d einetrés sino de tiempo.&lt;br /&gt;&lt;br /&gt;Os agradezco de todo corazón vuestro tiempo a todos los que de vez en cuando venís por aquí. Lamento no poder actualizar esto con tanta frecuencia como me gustaría pero espero que, lo que ponga, os sea de utilidad.&lt;br /&gt;&lt;br /&gt;Felices fiesta a todos.&lt;br /&gt;&lt;br /&gt;(PD: sí, sí, está pendiente la continuación del post de Drools, a ver si llegan ya las vacaciones y tengo tiempo de ponerme con ello).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9553291-113501556193317100?l=rincew.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rincew.blogspot.com/feeds/113501556193317100/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9553291&amp;postID=113501556193317100' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/113501556193317100'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/113501556193317100'/><link rel='alternate' type='text/html' href='http://rincew.blogspot.com/2005/12/cumplimos-un-ao-gracias.html' title='Cumplimos un año. Gracias !!!!!!'/><author><name>Javier J.</name><uri>http://www.blogger.com/profile/00715175463383886462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9553291.post-113336199323574773</id><published>2005-11-30T14:43:00.000Z</published><updated>2005-11-30T14:46:33.250Z</updated><title type='text'>Drools I. Introducción a los motores de reglas de negocios.</title><content type='html'>Saludos.&lt;br /&gt;&lt;br /&gt;Drools (http://drools.org) es una implementación libre de un motor de reglas de negocio compatible con la especificación JSR-94 Rules Engine API (http://www.jcp.org/en/jsr/detail?id=94). ¿Y qué es un motor de reglas de negocio?. Es un componente que, a partir de una información inicial y un conjunto de  &lt;br /&gt;reglas, detectas qué reglas deben aplicarse en un instante determinado y cuáles son los resultados de esas reglas. &lt;br /&gt;&lt;br /&gt;Uno de los puntos fuertes de los motores de reglas de negocios es que hablan el mismo lenguaje que el dominio del problema. Un ejemplo típico: supongamos que estamos hablando con un cliente para el desarrollo de una tienda virtual.  &lt;br /&gt;Algunas de las cosas que nuestro cliente nos pediría pudieran ser: &lt;br /&gt;&lt;br /&gt;Si la cuantía del pedido supera cierta cantidad o si es un cliente de confianza, entonces se le aplica un descuento. Si se compra más de N productos del mismo tipo se le aplica un descuento. Entre las fechas X e Y, un producto está en oferta y, si se lleva 2, se le añade un 3º gratis&lt;br /&gt;&lt;br /&gt;En una aplicación normal, todo lo anterior deberíamos codificarlo en métodos de clases que se ejecutaran al procesar un pedido, lo cual no siempre es sencillo. Con un motor, solo tenemos que indicarle las reglas de negocio, e ir indicándole los hechos. En este ejemplo, tendríamos que suministrarle al motor la información sobre los pedidos, clientes y productos mediante clases JavaBean.&lt;br /&gt;&lt;br /&gt;Otra ventaja es a la hora de realizar cambios o mantenimiento. Si nuestro cliente desea cambiar el descuento por pedido, o el descuento por número de productos, o las fechas o tipos de productos (o quitar todos los descuentos), con un motor de reglas de negocio sólo es necesario cambiar las reglas, sin necesidad de modificar código, recompilar, ni siquiera de parar la aplicación.&lt;br /&gt;&lt;br /&gt;Básicamente un motor de reglas de negocio está compuesto de tres elementos: un conjunto de reglas, el espacio de trabajo (o el conocimiento que tiene), y el procesador de reglas. Las reglas son sentencias de la forma IF-THEN, de tal manera que si se cumplen todas las condiciones del IF se ejecutan todas las acciones del THEN. El espacio de trabajo es donde se guarda el conocimiento (objetos Java) que el motro utilizará para decidir que reglas deben activarse.&lt;br /&gt;&lt;br /&gt;Dentro de las reglas pueden existir conflictos. Un conflicto es cuando varias reglas distintas pueden activarse para el mismo conjunto de hechos y, la aplicación de dichas reglas, pueden tener resultados contradictorios. Algunas de las estrategias para resolver conflictos son: asignarle una prioridad a cada regla, estrategias FIFO o LIFO, aplicarlas en el orden en que se declararon o aplicarlas en orden aleatorio.&lt;br /&gt;&lt;br /&gt;Existen otros motores de reglas de negocio para java, por ejemplo JESS (http://herzberg.ca.sandia.gov/jess/), Mandarax (http://mandarax.sourceforge.net/) o OpenRules (http://java-source.net/open-source/rule-engines/openrules). También hay  &lt;br /&gt;motores de reglas de negocio para plataforma .NET como Quick Rules, JRules o Blaze.&lt;br /&gt;&lt;br /&gt;Los motores de reglas de negocio pueden utilizarse, por ejemplo, para desarrollar prototipos rápidos o, incluso, para implementar la lógica de la aplicación. También pueden utilizarse como parte de un sistema de workflow.&lt;br /&gt;&lt;br /&gt;En el próximo mensaje pondré un ejemplo de como implementar el caso práctico que hemos visto.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9553291-113336199323574773?l=rincew.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rincew.blogspot.com/feeds/113336199323574773/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9553291&amp;postID=113336199323574773' title='26 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/113336199323574773'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/113336199323574773'/><link rel='alternate' type='text/html' href='http://rincew.blogspot.com/2005/11/drools-i-introduccin-los-motores-de.html' title='Drools I. Introducción a los motores de reglas de negocios.'/><author><name>Javier J.</name><uri>http://www.blogger.com/profile/00715175463383886462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>26</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9553291.post-113216682736836704</id><published>2005-11-16T18:46:00.000Z</published><updated>2005-11-16T18:48:56.170Z</updated><title type='text'>Extendiendo JUnit para que verifique expresiones regulares</title><content type='html'>Saludos.&lt;br /&gt;&lt;br /&gt;A veces, escribiendo pruebas unitarias con JUnit, hemos de comprobar resultados complejos. &lt;br /&gt;Utilizando el soporte para expresiones regulares de J2SE a partir de la versión 4 que comentamos en el mensaje anterior, vamos a escribir un nuevo método assert, por ejemplo assertRegExp, que nos permita validar una cadena respecto de una expresión regular. Una posible implementación de este método se muestra a continuación.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt; public static void assertRegExp(String message, String regexp, String s) {&lt;br /&gt;  if (message==null)&lt;br /&gt;   message="";&lt;br /&gt;  if (!Pattern.matches(regexp, s))&lt;br /&gt;   fail(message+" ["+regexp+"] over ["+s+"]");&lt;br /&gt; }&lt;br /&gt; &lt;br /&gt; public static void assertRegExp(String regexp, String s) {&lt;br /&gt;  assertRegExp(null, regexp, s);&lt;br /&gt; }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Para que funcione, ambos métodos deben estar en una clase que herede de Assert, TestCase o TestDecorator.&lt;br /&gt;&lt;br /&gt;Veamos un ejemplo. Supongamos un método "String getDNI()" para el que queremos escribir una prueba que verifique que el resultado está compuesto de 8 dígitos y una letra mayúscula. Una posible prueba utilizando nuestro método assertRegExp se muestra a continuación.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;String dni = persona.getDNI();&lt;br /&gt;String expReg = "[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][A-Z]";&lt;br /&gt;assertRegExp("DNI incorrecto", expReg, dni);&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9553291-113216682736836704?l=rincew.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rincew.blogspot.com/feeds/113216682736836704/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9553291&amp;postID=113216682736836704' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/113216682736836704'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/113216682736836704'/><link rel='alternate' type='text/html' href='http://rincew.blogspot.com/2005/11/extendiendo-junit-para-que-verifique.html' title='Extendiendo JUnit para que verifique expresiones regulares'/><author><name>Javier J.</name><uri>http://www.blogger.com/profile/00715175463383886462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9553291.post-113034315002155469</id><published>2005-10-26T16:08:00.000Z</published><updated>2005-10-26T16:12:30.030Z</updated><title type='text'>Soporte de expresiones regulares en Java</title><content type='html'>Saludos.&lt;br /&gt;&lt;br /&gt;Existen muy buenas herramientas libres para trabajar con expresiones regulares en Java, como (http://lucene.apache.org/). Sin embargo, a partir de la versión 1.4, el kit estándar de Java incluye su propio soporte para expresiones regulares.&lt;br /&gt;En el paquete 'java.util.regexp' encontramos las clases Matcher y Pattern. La primera clase permite aplicar una expresión sobre una cadena de texto. La segunda clase representa una expresión regular compilada para ganar en eficiencia.&lt;br /&gt;&lt;br /&gt;La manera más sencilla y rápida de comprobar si una cadena satisface una expresión regular es mediante el método estático 'boolean Pattern.matches(cadena, expresión regular)'. Es posible conseguir el mismo efecto invocando el método matches sobre un objeto String. A continuación se muestra un sencillo ejemplo.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;import java.util.regex.*;&lt;br /&gt;&lt;br /&gt;public class EjemploRegExp {&lt;br /&gt;&lt;br /&gt;  // Números de 2 cifras que empiecen por 1 o 2&lt;br /&gt;  static String re = "\\b[1][0-9]|[2][0-9]";&lt;br /&gt;  static String cadena1  = "10";&lt;br /&gt;  static String cadena2  = "110";&lt;br /&gt;&lt;br /&gt;  public static void main(String[] args) {&lt;br /&gt;    if (Pattern.matches(cadena1, re)) &lt;br /&gt;      System.out.println(cadena1+" cumple: "+re);&lt;br /&gt;    else&lt;br /&gt;      System.out.println(cadena1+" no cumple: "+re);&lt;br /&gt;    if (cadena2.matches(re)) &lt;br /&gt;      System.out.println(cadena2+" cumple: "+re);&lt;br /&gt;    else&lt;br /&gt;      System.out.println(cadena2+" no cumple: "+re);&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;Además, la clase String también nos ofrece la manera de dividir un String mediante una expresión regular con el método: &lt;br /&gt;&lt;br /&gt;String[] split(String regex)&lt;br /&gt;String[] split(String regex, int limit)&lt;br /&gt;&lt;br /&gt;En el próximo mensaje veremos una aplicación práctica extendiendo la herramienta JUnit (www.junit.org) para que soporte expresiones regulares.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9553291-113034315002155469?l=rincew.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rincew.blogspot.com/feeds/113034315002155469/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9553291&amp;postID=113034315002155469' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/113034315002155469'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/113034315002155469'/><link rel='alternate' type='text/html' href='http://rincew.blogspot.com/2005/10/soporte-de-expresiones-regulares-en.html' title='Soporte de expresiones regulares en Java'/><author><name>Javier J.</name><uri>http://www.blogger.com/profile/00715175463383886462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9553291.post-112894205022943834</id><published>2005-10-10T10:59:00.000Z</published><updated>2005-10-10T11:00:50.250Z</updated><title type='text'>Andalucía, tierra de SUN y Oracle</title><content type='html'>La Junta de Andalucía es uno de los principales generadores de empleo en el mercado de la informática, bien de manera directa, bien creando y participando en empresas, o bien contratando trabajos a empresas privadas. &lt;br /&gt;Por este motivo es muy importante para quien quiera trabajar en Andalucía conocer las tecnologías que la Junta de Andalucía ha decidido utilizar. En este caso y, hoy por hoy, la Junta desarrolla una apuesta clara por Java y Oracle para sus nuevos sistemas web y estos sistemas son los que salen a concurso.&lt;br /&gt;Ya sabéis, todos los informáticos de Andalucía a estudiar SUN Y Oracle.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9553291-112894205022943834?l=rincew.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rincew.blogspot.com/feeds/112894205022943834/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9553291&amp;postID=112894205022943834' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/112894205022943834'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/112894205022943834'/><link rel='alternate' type='text/html' href='http://rincew.blogspot.com/2005/10/andaluca-tierra-de-sun-y-oracle.html' title='Andalucía, tierra de SUN y Oracle'/><author><name>Javier J.</name><uri>http://www.blogger.com/profile/00715175463383886462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9553291.post-111683900823742147</id><published>2005-05-23T09:02:00.000Z</published><updated>2005-05-23T09:03:28.243Z</updated><title type='text'>El lado oscuro de las pruebas.</title><content type='html'>Saludos.&lt;br /&gt;&lt;br /&gt;De un tiempo a esta parte, con la aparición de metodologías extremas, diseño guiado a pruebas y herramientas tipo jUnit, se han puesto de moda las pruebas. Parece que, cuantas más pruebas tenga un programa, mejor. Pero tener un número elevado de pruebas tiene un lado oscuro que puede traer problemas.&lt;br /&gt;&lt;br /&gt;Si las pruebas son pequeñas y simples, las podemos escribir en un momento, pero si aumenta su tamaño (y no podemos dividirlas en pruebas más pequeñas) se hacen difíciles de escribir. Además a más código más errores, por lo que tenemos que perder tiempo depurándolas. Si nuestra prueba tiene mucho código o es compleja, ¿cómo podemos asegurarnos de que nuestra prueba funciona adecuadamente? ¿Tenemos que escribir una prueba para probar la prueba?.&lt;br /&gt;&lt;br /&gt;Esto es malo, pero podemos encontrar algo todavía peor. Supongamos que tenemos una clase y 10 pruebas que dependen de dicha clase. Con el uso se ha descubierto que hay una manera mejor de crear objetos de esa clase cambiando los constructores. Ese cambio supondría, no solo cambiar en el código donde se crean objetos de esa clase (muy pocos si programamos bien 8) ) sino tener que modificar esas 10 pruebas. &lt;br /&gt;Además habría que repasarlas para comprobar que sigan teniendo sentido después de la modificación y, probablemente, escribir pruebas nuevas.&lt;br /&gt;&lt;br /&gt;En este caso el problema no es muy grande, pero si cambiamos (o refactorizamos) mucho código, el número de casos de casos de prueba a modificar aumenta, con el consiguiente gasto de tiempo para modificarlas. Recordemos que las pruebas no son parte del sistema (no se la entregamos al cliente ni, probablemente, el cliente aprecie su utilidad), por lo que no debemos gastar demasiado tiempo con ellas.&lt;br /&gt;&lt;br /&gt;Esto nos puede llevar a dos decisiones igual de malas: o bien no modificar el código para seguir aprovechando las pruebas que ya tenemos, o, lo más común, modificar el código y tener un conjunto de pruebas obsoletas e inservibles.&lt;br /&gt;&lt;br /&gt;Este problema no es exclusivo en las pruebas de código "tipo jUnit". También sucede lo mismo trabajando, por ejemplo, con robots de prueba que capturan pulsaciones de teclado o ratón y luego las reproducen. En este caso, un mínimo cambio en la interfaz gráfica puede obligar a modificar (o a volver a grabar) un buen número de pruebas.&lt;br /&gt;&lt;br /&gt;En resumen. Cuanto más pruebas, por ejemplo pruebas de código, tengamos, más atados estamos a dicho código y menos ganas de cambiar nada tenemos..&lt;br /&gt;&lt;br /&gt;¿Cuál puede ser una la solución?. Creo que una buena alternativa sería generar las pruebas de manera automática. Si tuviéramos un programa que permitiera apretar un botón y obtener un buen conjunto de pruebas (e incluso ejecutarlas), no nos preocuparíamos a lo hora de cambiar el código. En cada cambio descartaríamos todas las pruebas obsoletas y generaríamos pruebas nuevas fácilmente.&lt;br /&gt;&lt;br /&gt;Esto es muy fácil de decir pero muy difícil de poner en práctica (por no decir imposible). Espero seguir hablando de esto en futuras entradas.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9553291-111683900823742147?l=rincew.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rincew.blogspot.com/feeds/111683900823742147/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9553291&amp;postID=111683900823742147' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/111683900823742147'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/111683900823742147'/><link rel='alternate' type='text/html' href='http://rincew.blogspot.com/2005/05/el-lado-oscuro-de-las-pruebas.html' title='El lado oscuro de las pruebas.'/><author><name>Javier J.</name><uri>http://www.blogger.com/profile/00715175463383886462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9553291.post-111506212408709794</id><published>2005-05-02T19:24:00.000Z</published><updated>2005-05-02T19:28:44.090Z</updated><title type='text'>Texto con formato e imágenes en aplicaciones web</title><content type='html'>Saludos.&lt;br /&gt;&lt;br /&gt;Recientemente he estado haciendo una miniconsultoría (en plan "Amigetes Conculting") que me ha llevado a buscar editores web, o editores de texto lo más completos posibles para integrarlos en una aplicación PHP. A continuación comento lo que he encontrado.&lt;br /&gt;&lt;br /&gt;En el siguiente enlace se puede encontrar una lista muy completa con opciones para todo tipo de plataformas y lenguajes: http://www.htmlarea.com/&lt;br /&gt;&lt;br /&gt;De todos los que he visto, el que más me ha gustado para PHP (también disponible para ASP.NET) es Spaw, que se distribuye bajo licencia GPL para usos no comerciales (www.solmetra.com/spaw/ y sourceforge.net/projects/spaw/).&lt;br /&gt;&lt;br /&gt;Sin embargo, aunque es un componente bastante evolucionado y cuenta con un foro bastante dinámico (en SourceForge), yo no he consegido hacerlo funcionar. Una lástima.&lt;br /&gt;&lt;br /&gt;Por ese motivo he optado por htmlArea (http://www.dynarch.com/projects/htmlarea/) que, si bien no es tan vistoso ni icluye tantas opciones, cumple sobradamente. Además está íntegramente en JavaScript, por lo que se adapta a casi cualquier plataforma.&lt;br /&gt;&lt;br /&gt;También existen plug-in que lo extienden, por ejemplo a la hora de incluir imágenes  &lt;br /&gt;(http://www.zhuo.org/htmlarea/). Su licencia es BSD.&lt;br /&gt;&lt;br /&gt;HtmlArea lo utilizan aplicaciones tan importante como la plataforma de e-Learning Moodle.&lt;br /&gt;&lt;br /&gt;Ya no hay excusas para crear formularios que no admitan texto con formato.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9553291-111506212408709794?l=rincew.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rincew.blogspot.com/feeds/111506212408709794/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9553291&amp;postID=111506212408709794' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/111506212408709794'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/111506212408709794'/><link rel='alternate' type='text/html' href='http://rincew.blogspot.com/2005/05/texto-con-formato-e-imgenes-en.html' title='Texto con formato e imágenes en aplicaciones web'/><author><name>Javier J.</name><uri>http://www.blogger.com/profile/00715175463383886462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9553291.post-111255334526713927</id><published>2005-04-03T18:30:00.001Z</published><updated>2005-04-03T19:05:44.416Z</updated><title type='text'>Lo mejor de la semana</title><content type='html'>Saludos.&lt;br /&gt;&lt;br /&gt;Sin duda lo mejor que esta semana he encontrado en la red es esta colección de enlaces a tiras cómicas.&lt;br /&gt;&lt;br /&gt;La tira de Linux Hispano&lt;br /&gt;http://www.linuxhispano.net/tira/tira.html&lt;br /&gt;&lt;br /&gt;Raulito el Friki&lt;br /&gt;http://recurrente.afraid.org/myblog/?q=ultima&lt;br /&gt;&lt;br /&gt;La tira de Bit y Byte&lt;br /&gt;http://tira.emezeta.com/&lt;br /&gt;&lt;br /&gt;Tira Frikis&lt;br /&gt;http://www.geocities.com/tirasfrikis/&lt;br /&gt;&lt;br /&gt;Tira Ecol&lt;br /&gt;http://tira.escomposlinux.org/&lt;br /&gt;&lt;br /&gt;Un millón de gracias a sus autores por su tiempo y esfuerzo.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9553291-111255334526713927?l=rincew.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rincew.blogspot.com/feeds/111255334526713927/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9553291&amp;postID=111255334526713927' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/111255334526713927'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/111255334526713927'/><link rel='alternate' type='text/html' href='http://rincew.blogspot.com/2005/04/lo-mejor-de-la-semana.html' title='Lo mejor de la semana'/><author><name>Javier J.</name><uri>http://www.blogger.com/profile/00715175463383886462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9553291.post-111230229358348013</id><published>2005-03-31T20:47:00.000Z</published><updated>2005-03-31T20:51:33.583Z</updated><title type='text'>Una pregunta en el aire</title><content type='html'>Saludos.&lt;br /&gt;&lt;br /&gt;El otro día, leyendo una página web sobre una aplicación encontré lo siguiente:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style:italic;"&gt;Robusto: Más de 230 pruebas Junit.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;La primera pregunta que se plantea es bastante obvia: ¿Podemos medir el grado de robustez o fiabilidad de un sistema mediante el número de pruebas?.&lt;br /&gt;&lt;br /&gt;No. Un ejemplo muy claro, aunque tal vez un poco extremo. Imaginemos el  &lt;br /&gt;siguiente código (que sirve tanto para C como para Java).&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;int factorial(int n) {&lt;br /&gt; if (n == 1)&lt;br /&gt;   return n;&lt;br /&gt; return n * factorial(n-1);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Supongamos que escribo 100 pruebas para comprobar los 100 primeros factoriales. ¿Puedo asegurar que el código anterior es robusto?. No. en cuanto reciba un 0 o un número negativo fallará a pesar de sus 100 (o 11000) pruebas.&lt;br /&gt;&lt;br /&gt;Otra pregunta, tal vez no tan obvia, es si la robustez o fiabilidad de un sistema está garantizada únicamente por pruebas unitarias.&lt;br /&gt;&lt;br /&gt;No. Un ejemplo muy claro lo encontramos en las aplicaciones web. Con pruebas  &lt;br /&gt;tipo JUnit, u otra herramienta similar, difícilmente podremos comprobar la respuesta del sistema a un gran número de peticiones concurrentes de distintos clientes, ni si el sistema es capaz de seguir funcionando o se colapsa.&lt;br /&gt;&lt;br /&gt;La gran pregunta que habría que responder es: ¿Como podemos garantizar la robustez del sistema?. Lo cual nos lleva a plantearnos: ¿y qué es la robustez?.&lt;br /&gt;&lt;br /&gt;Habrá que seguir con esto.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9553291-111230229358348013?l=rincew.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rincew.blogspot.com/feeds/111230229358348013/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9553291&amp;postID=111230229358348013' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/111230229358348013'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/111230229358348013'/><link rel='alternate' type='text/html' href='http://rincew.blogspot.com/2005/03/una-pregunta-en-el-aire.html' title='Una pregunta en el aire'/><author><name>Javier J.</name><uri>http://www.blogger.com/profile/00715175463383886462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9553291.post-111169589861292651</id><published>2005-03-24T20:22:00.000Z</published><updated>2005-03-24T20:24:58.613Z</updated><title type='text'>Probando exhaustivamente</title><content type='html'>El siguiente ejemplo está sacado de un libro sobre pruebas bastante famoso. &lt;br /&gt;Nos piden un programa para clasificar un triángulo en equilátero, isósceles o escaleno. Para que tres lados a, b y c formen un triángulo debe cumplirse que s (s = (a +b +c) / 2) sea mayor estricto que a, b y c.&lt;br /&gt;Una posible solución se muestra a continuación.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;br /&gt; static final int NOTRIANGULO = 0;&lt;br /&gt; static final int EQUILATERO = 0;&lt;br /&gt; static final int ISOSCELES = 0;&lt;br /&gt; static final int ESCALENO = 0;&lt;br /&gt; &lt;br /&gt; public static int tipoTriangulo(int a, int b, int c) {&lt;br /&gt;  int s;&lt;br /&gt;  &lt;br /&gt;  s = (a+b+c) / 2;&lt;br /&gt;  if ( (s &lt;= a) || (s &lt;= b) || (s &lt;= c)) &lt;br /&gt;   return NOTRIANGULO;&lt;br /&gt;  &lt;br /&gt;  if ( (a==b) &amp;&amp; (b==c) )&lt;br /&gt;   return EQUILATERO;&lt;br /&gt;  &lt;br /&gt;  if ( (a==b) || (b==c) || (a==c) )&lt;br /&gt;   return ISOSCELES; &lt;br /&gt;  &lt;br /&gt;  return ESCALENO;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Sin embargo, lo más interesante es plantear que pruebas necesitamos para verificar a fondo que el código anterior funciona. En este caso, una prueba va a ser un conjunto de valores concretos y el resultado esperado, por ejemplo:&lt;br /&gt;&lt;br /&gt;(3, 3, 3) : EQUILATERO&lt;br /&gt;(3, 3, 4) : ISOSCELES&lt;br /&gt;(3, 4, 5) : ESCALENO&lt;br /&gt;(2, 2, 6) : NOTRIANGULO&lt;br /&gt;&lt;br /&gt;¿Cuantas pruebas necesitamos para tener la certeza que hemos probado todas las posibles combinaciones?. ¿Cómo obtenemos esas pruebas?. Una técnica clásica que podemos utilizar es analizar las estructuras de control. Esta es una técnica de caja blanca.&lt;br /&gt;Por ejemplo, en el caso de un triángulo isósceles, vemos que ha de cumplirse una de tres condiciones posibles. Por tanto, en este caso hemos de verificar las tres condiciones. Las pruebas resultantes se muestran a continuación:&lt;br /&gt;&lt;br /&gt;(3, 3, 4), (3, 4, 3), (4, 3, 3) : ISOSCELES&lt;br /&gt;&lt;br /&gt;Aplicando este mismo proceso al resto de los casos obtenemos el resto de pruebas. Sin embargo esto aún no es suficiente. Por ejemplo: ¿qué sucede cuando un lado es 0 o negativo, o cuando un lado vale el valor máximo que un tipo int puede almacenar?. ¿Es posible introducir lados decimales o lados que no sean valores numéricos?. Todas estas circunstancias deberían ser probadas también.&lt;br /&gt;En este caso, para completar el conjunto de pruebas, podemos aplicar una técnica de prueba de caja negra llamada análisis de valores límites. Por ejemplo, para verificar que el programa sigue funcionando cuando uno de sus lados es 0 las pruebas serán:&lt;br /&gt;&lt;br /&gt;(0, 3, 3), (3, 0, 3), (3, 3, 0) : NOTRIANGULO&lt;br /&gt;&lt;br /&gt;Calculando todas las posibles combinaciones se obtiene aproximadamente 30 casos de prueba distintos, lo cual da una medida de lo importante que es probar convenientemente cualquier código.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9553291-111169589861292651?l=rincew.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rincew.blogspot.com/feeds/111169589861292651/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9553291&amp;postID=111169589861292651' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/111169589861292651'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/111169589861292651'/><link rel='alternate' type='text/html' href='http://rincew.blogspot.com/2005/03/probando-exhaustivamente.html' title='Probando exhaustivamente'/><author><name>Javier J.</name><uri>http://www.blogger.com/profile/00715175463383886462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9553291.post-111152435950453616</id><published>2005-03-22T20:41:00.000Z</published><updated>2005-03-22T20:45:59.506Z</updated><title type='text'>Enlaces sobre refactorización</title><content type='html'>Saludos.&lt;br /&gt;&lt;br /&gt;Para aquellos interesados en tener a mano información breve y funcional sobre refactorizaciones, puede echarle un vistazo a estos dos enlaces publicados en Agile Spain (www.agile-spain.com/):&lt;br /&gt;&lt;br /&gt;En el primero nos muestran una tabla con malos olores y enlaces a las refactorizaciones recomendadas para eliminarlas:&lt;br /&gt;http://wiki.java.net/bin/view/People/SmellsToRefactorings&lt;br /&gt;&lt;br /&gt;En el segundo nos explican las refactorizaciones mediante sencillos (muy sencillos) diagramas parecidoa a UML:&lt;br /&gt;http://www.refactoring.be/thumbnails.html&lt;br /&gt;&lt;br /&gt;Además de estos enlaces no hay que dejar de visitar el blog en español: &lt;br /&gt;http://www.programacion.com/blogs/14_refactoring&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9553291-111152435950453616?l=rincew.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rincew.blogspot.com/feeds/111152435950453616/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9553291&amp;postID=111152435950453616' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/111152435950453616'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/111152435950453616'/><link rel='alternate' type='text/html' href='http://rincew.blogspot.com/2005/03/enlaces-sobre-refactorizacin.html' title='Enlaces sobre refactorización'/><author><name>Javier J.</name><uri>http://www.blogger.com/profile/00715175463383886462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9553291.post-111092735838899836</id><published>2005-03-15T22:53:00.000Z</published><updated>2005-03-15T22:55:58.390Z</updated><title type='text'>Pruebas con aspectos</title><content type='html'>Saludos.&lt;br /&gt;&lt;br /&gt;Nos han publicado en JavaHispano un trabajo que presentamos el año pasado en un estupendo workshop. El trabajo versa sobre como aplicar programación orientada a aspectos para escribir pruebas unitarias, y que ventajas aporta frente a la clásica herramienta JUnit. Los pocos ejemplos están escritos en Java y AspectJ.&lt;br /&gt;&lt;br /&gt;Para quien tenga interés, el enlace es: http://www.javahispano.org/articles.article.action?id=96&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9553291-111092735838899836?l=rincew.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rincew.blogspot.com/feeds/111092735838899836/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9553291&amp;postID=111092735838899836' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/111092735838899836'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/111092735838899836'/><link rel='alternate' type='text/html' href='http://rincew.blogspot.com/2005/03/pruebas-con-aspectos.html' title='Pruebas con aspectos'/><author><name>Javier J.</name><uri>http://www.blogger.com/profile/00715175463383886462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9553291.post-111063322159315279</id><published>2005-03-12T12:35:00.000Z</published><updated>2005-03-12T13:13:41.593Z</updated><title type='text'>Fin de la operación limpieza.</title><content type='html'>Saludos.&lt;br /&gt;&lt;br /&gt;Aunque aún quedan algunos ejercicios con semáforos, la operación limpieza queda oficalmente clausurada. Si alguien quiere los ejercicios que faltan puede pedírmelos por correo electrónico.&lt;br /&gt;Dentro de unos pocos meses empezaré otra operación de limpieza, pero esta vez para ejercicios básicos en Java.&lt;br /&gt;Mientras tanto retomaré la idea original de este blog e iré escribiendo comentarios referentes a Java y reflexiones pruebas del software.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9553291-111063322159315279?l=rincew.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rincew.blogspot.com/feeds/111063322159315279/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9553291&amp;postID=111063322159315279' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/111063322159315279'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/111063322159315279'/><link rel='alternate' type='text/html' href='http://rincew.blogspot.com/2005/03/fin-de-la-operacin-limpieza.html' title='Fin de la operación limpieza.'/><author><name>Javier J.</name><uri>http://www.blogger.com/profile/00715175463383886462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9553291.post-110857216318110092</id><published>2005-02-16T16:39:00.000Z</published><updated>2005-02-16T16:47:34.250Z</updated><title type='text'>Sincroniazación entre procesos</title><content type='html'>El siguiente porgrama crea 2 hijos. Uno de ellos pondrá los números del 1 al 5 en una variable compartida y el otro leerá esos valores y los mostrará por pantalla.&lt;br /&gt;&lt;br /&gt;El primer procso pone el número 1 en la variable compartida, después, el segundo proceso lee el valor 1 y lo muestra por pantalla. Después el primer proceso pone el número 2 en la variable compartida, y así hasta el 5.&lt;br /&gt;&lt;br /&gt;Para sincronizar ambos procesos se han utilizado dos grupos de un semáforo cada uno.&lt;br /&gt;&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt;&lt;br /&gt;#include &lt; errno.h&gt;&lt;br /&gt;#include &lt; stdio.h&gt;&lt;br /&gt;#include &lt; unistd.h&gt;&lt;br /&gt;#include &lt; wait.h&gt;&lt;br /&gt;#include &lt; sys/ipc.h&gt;&lt;br /&gt;#include &lt; sys/sem.h&gt;&lt;br /&gt;#include &lt; sys/stat.h&gt;&lt;br /&gt;#include &lt; sys/types.h&gt;&lt;br /&gt;&lt;br /&gt;#include "sv.h"&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;union senum {&lt;br /&gt;  int val;&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;int main() {&lt;br /&gt;&lt;br /&gt;  pid_t pid;&lt;br /&gt;  int c, r;&lt;br /&gt;  int num;&lt;br /&gt;  int sem_prod, sem_cons;&lt;br /&gt;  struct sembuf arriba = {0,1,0};&lt;br /&gt;  struct sembuf abajo = {0,-1,0};&lt;br /&gt;  union senum arg;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;  sv_init();&lt;br /&gt;&lt;br /&gt;  sem_prod = semget(IPC_PRIVATE, 1, IPC_CREAT | IPC_EXCL | S_IRUSR | S_IWUSR);&lt;br /&gt;  sem_cons = semget(IPC_PRIVATE, 1, IPC_CREAT | IPC_EXCL | S_IRUSR | S_IWUSR);&lt;br /&gt;  if ((sem_prod == -1) || (sem_cons==-1)) {&lt;br /&gt;    perror("Error creando semáforos");&lt;br /&gt;    return -1;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  arg.val = 1;&lt;br /&gt;  r = semctl(sem_prod, 0, SETVAL, arg);&lt;br /&gt;  if (r == -1) {&lt;br /&gt;    perror("Error incializando el semáforo del productor - ");&lt;br /&gt;    //Borrar los semáforos&lt;br /&gt;    return -1;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  for (c = 0; c &lt; 2; c++) {&lt;br /&gt;    pid = fork();&lt;br /&gt;    if (pid == -1) {&lt;br /&gt;      perror("Error creando procesos - ");&lt;br /&gt;      // Borrar semáforos.&lt;br /&gt;      return -1;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    if (pid == 0) {&lt;br /&gt;&lt;br /&gt;      // Productor&lt;br /&gt;      if (c==0) {&lt;br /&gt; for (num = 1; num &lt; 6; num++) {&lt;br /&gt;   // Mi turno de producir&lt;br /&gt;   r = semop(sem_prod, &amp;abajo, 1);&lt;br /&gt;   if (r == -1) {&lt;br /&gt;     perror("Error bajando el semáforo del productor - ");&lt;br /&gt;     // Eliminar semáforos&lt;br /&gt;     return -1;&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   printf("Valor producido: %i\n", num);&lt;br /&gt;   sv_set(num);&lt;br /&gt;&lt;br /&gt;   // El turno de consumir&lt;br /&gt;   r = semop(sem_cons, &amp;arriba, 1);&lt;br /&gt;   if (r == -1) {&lt;br /&gt;     perror("Error subiendo el semáforo del consumidor - ");&lt;br /&gt;     // Eso&lt;br /&gt;     return -1;&lt;br /&gt;   }&lt;br /&gt; }&lt;br /&gt; exit(0);&lt;br /&gt;      }&lt;br /&gt;&lt;br /&gt;      // Consumidor&lt;br /&gt;      if (c==1) {&lt;br /&gt; for (num = 0; num &lt; 5; num++) {&lt;br /&gt;   // Voy a consumir 5 números&lt;br /&gt;   r = semop(sem_cons, &amp;abajo, 1);&lt;br /&gt;   if (r == -1) {&lt;br /&gt;     perror("Error bajando semáforo del consumidor - ");&lt;br /&gt;     return -1;&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   printf("Valor consumido: %i\n", sv_get() );&lt;br /&gt;&lt;br /&gt;   // Hora de producir&lt;br /&gt;   r = semop(sem_prod, &amp;arriba, 1);&lt;br /&gt;   if (r == -1) {&lt;br /&gt;     perror("Error subiendo semáforo del productos - ");&lt;br /&gt;     return -1;&lt;br /&gt;   }&lt;br /&gt; }&lt;br /&gt; exit(0);&lt;br /&gt;      }&lt;br /&gt;&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  pid = wait(&amp;r);&lt;br /&gt;&lt;br /&gt;  while ( (pid != -1) ||&lt;br /&gt;   ( (pid == -1) &amp;&amp; (errno == EINTR)) )&lt;br /&gt;    pid = wait(&amp;r);&lt;br /&gt;&lt;br /&gt;  r = semctl(sem_prod, 0, IPC_RMID);&lt;br /&gt;  if (r==-1) {&lt;br /&gt;    perror("Error eliminando semáforo productor.");&lt;br /&gt;  }&lt;br /&gt;  r = semctl(sem_cons, 0, IPC_RMID);&lt;br /&gt;  if (r == -1) {&lt;br /&gt;    perror("Error eliminando semáforo consumidor. ");&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  sv_finish();&lt;br /&gt;&lt;br /&gt;  printf("Fin.\n");&lt;br /&gt;  return 0;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/PRE&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9553291-110857216318110092?l=rincew.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rincew.blogspot.com/feeds/110857216318110092/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9553291&amp;postID=110857216318110092' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/110857216318110092'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/110857216318110092'/><link rel='alternate' type='text/html' href='http://rincew.blogspot.com/2005/02/sincroniazacin-entre-procesos.html' title='Sincroniazación entre procesos'/><author><name>Javier J.</name><uri>http://www.blogger.com/profile/00715175463383886462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9553291.post-110857183387638573</id><published>2005-02-16T16:30:00.000Z</published><updated>2005-02-16T16:38:59.676Z</updated><title type='text'>Acceso a un recurso compartido</title><content type='html'>Saludos.&lt;br /&gt;&lt;br /&gt;El siguinte código crea cuatro procesos hijos. Cada hijo incrementará una variable compartida en 5 unidades. Para garantizar la exclusión mútua sobre la variable compartida se utiliza un grupo de un semáforo que funciona como semáforo binario.&lt;br /&gt;&lt;br /&gt;Hay que cabiar todas las " de los includes por mayor y menor excepto el include de sv.h.&lt;br /&gt;&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt;/**&lt;br /&gt;Implementa un  semáforo binario&lt;br /&gt;utilizando directamente las funciones del sistema&lt;br /&gt;**/&lt;br /&gt;&lt;br /&gt;#include "sv.h"&lt;br /&gt;&lt;br /&gt;#include "stdio.h"&lt;br /&gt;#include "wait.h&gt;&lt;br /&gt;#include "unistd.h"&lt;br /&gt;#include "sys/types.h"&lt;br /&gt;&lt;br /&gt;#include "sys/stat.h"&lt;br /&gt;#include "sys/ipc.h"&lt;br /&gt;#include "sys/sem.h"&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;// Incrementa en 5 la variable compartida&lt;br /&gt;#define INC 5&lt;br /&gt;&lt;br /&gt;void inc(int);&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;// Esta unión hay que declararla&lt;br /&gt;union semun {&lt;br /&gt;  int val;&lt;br /&gt;  struct semid_ds *buf;&lt;br /&gt;  ushort *array;&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;int main() {&lt;br /&gt;  int c;&lt;br /&gt;  int hijos = 4;&lt;br /&gt;  pid_t pid;&lt;br /&gt;&lt;br /&gt;  int semaforo;&lt;br /&gt;  union semun arg;&lt;br /&gt;  int r;&lt;br /&gt;  // { semaforo, valor, banderas }&lt;br /&gt;  struct sembuf arriba = {0, 1, 0};&lt;br /&gt;  struct sembuf abajo = {0, -1, 0};&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;  sv_init();&lt;br /&gt;  printf("Valor de inicio: %i\n", sv_val());&lt;br /&gt;&lt;br /&gt;  // Crear e inicializar un semaforo binario&lt;br /&gt;  // para garantizar la exclusion mutua.&lt;br /&gt;  semaforo = semget(42, 1, IPC_CREAT | IPC_EXCL | S_IRUSR | S_IWUSR);&lt;br /&gt;  if (semaforo == -1) {&lt;br /&gt;    perror ("Error en la creación del semáforo - ");&lt;br /&gt;    return -1;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  arg.val = 1;&lt;br /&gt;  r = semctl(semaforo, 0, SETVAL, arg);&lt;br /&gt;  if (r == -1) {&lt;br /&gt;    perror ("Error inicializando semáforo - ");&lt;br /&gt;    // Hay que eliminarlo.&lt;br /&gt;    return -1;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;  for (c = 0; c &lt; hijos; c++) {&lt;br /&gt;    pid = fork();&lt;br /&gt;    if (pid == -1) {&lt;br /&gt;      perror("Error en fork - ");&lt;br /&gt;      exit(-1);&lt;br /&gt;    }   &lt;br /&gt;    if (pid == 0) {&lt;br /&gt;   &lt;br /&gt;      // Decrementar semaforo&lt;br /&gt;      r = semop(semaforo, &amp;abajo, 1);&lt;br /&gt;      if (r == -1) {&lt;br /&gt; perror("Error decrementando semáforo - ");&lt;br /&gt; // Eliminar el semáforo&lt;br /&gt; return -1;&lt;br /&gt;      }&lt;br /&gt;&lt;br /&gt;      inc(c);&lt;br /&gt;&lt;br /&gt;      // Incrementar semaforo&lt;br /&gt;      r = semop(semaforo, &amp;arriba, 1);&lt;br /&gt;      if (r == -1) {&lt;br /&gt; perror("Error incrementando semáforo - ");&lt;br /&gt; // Es necesario eliminarlo&lt;br /&gt; return -1;&lt;br /&gt;      }&lt;br /&gt;&lt;br /&gt;      exit(0);&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  while ( wait(NULL)!=-1 );&lt;br /&gt;&lt;br /&gt;  printf("-------------------------------\n");&lt;br /&gt;  printf("Valor esperado: %i\n", hijos  * INC);&lt;br /&gt;  printf("Valor real: %i", sv_val());&lt;br /&gt;&lt;br /&gt;  // Borrar semaforo&lt;br /&gt;  r = semctl(semaforo, 0, IPC_RMID);&lt;br /&gt;  if (r == -1)&lt;br /&gt;  {&lt;br /&gt;    perror("Error elminando semáforo - ");&lt;br /&gt;    return -1;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  sv_finish();&lt;br /&gt;  printf("\n");&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;//----------------------------&lt;br /&gt;void inc(int id) {&lt;br /&gt;  int c;&lt;br /&gt;  int tmp;&lt;br /&gt;  for(c=0; c &lt; INC; c++) {&lt;br /&gt;    tmp = sv_get();&lt;br /&gt;    if ( (id == 0) &amp;&amp; (c &lt; 2)) sleep(1);&lt;br /&gt;    tmp++;&lt;br /&gt;    sv_set(tmp);&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;/PRE&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9553291-110857183387638573?l=rincew.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rincew.blogspot.com/feeds/110857183387638573/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9553291&amp;postID=110857183387638573' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/110857183387638573'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/110857183387638573'/><link rel='alternate' type='text/html' href='http://rincew.blogspot.com/2005/02/acceso-un-recurso-compartido.html' title='Acceso a un recurso compartido'/><author><name>Javier J.</name><uri>http://www.blogger.com/profile/00715175463383886462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9553291.post-110841971849888683</id><published>2005-02-14T22:16:00.000Z</published><updated>2005-02-14T22:21:58.500Z</updated><title type='text'>Libería para implementar una variable compartida</title><content type='html'>A continuación se incluye el código de una librería que permite crear y manipular una variable compartida, esto es, una variable común para todos los procesos que utilicen esta librería. Así, los cambios que haga un proceso sobre esta variable, serán visibles para los demás procesos.&lt;br /&gt;&lt;br /&gt;Esta librería es un buen ejemplo de como utilizar el mecanismo IPC de memoria compartida.&lt;br /&gt;&lt;br /&gt;Esta librería se utilizará en ejemplos posteriores con semáforos.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;sv.h&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt;#ifndef _SV_&lt;br /&gt;#define _SV_&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt;Gestiona una variable entera mediante memoria compartida.&lt;br /&gt;La variable es común a todos los procesos creados&lt;br /&gt;después de llamar a sv_init();&lt;br /&gt;**/&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt;Reserva la memoria compartida e inicializa la variable &lt;br /&gt;a 0.&lt;br /&gt;-1 si error.&lt;br /&gt;**/&lt;br /&gt;int sv_init();&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt;Libera la memoria compartida.&lt;br /&gt;-1 si error.&lt;br /&gt;**/&lt;br /&gt;int sv_finish();&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt;Devuelve el valor de la variable común&lt;br /&gt;sv_get es un alias para sv_vsl&lt;br /&gt;**/&lt;br /&gt;int sv_val();&lt;br /&gt;int sv_get();&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt;Incrementa la variable entera con el valor &lt;br /&gt;indicado como parámetro y devuelve su nuevo valor.&lt;br /&gt;**/&lt;br /&gt;int sv_inc(int);&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt;Asigna un valor a la variable común&lt;br /&gt;Devuelve el valor asignado.&lt;br /&gt;**/&lt;br /&gt;int sv_set(int);&lt;br /&gt;&lt;br /&gt;#endif&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/PRE&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;sv.c&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt;#include "stdio.h"&lt;br /&gt;#include "sys/ipc.h"&lt;br /&gt;#include "sys/shm.h"&lt;br /&gt;#include "sys/stat.h"&lt;br /&gt;#include "sys/types.h"&lt;br /&gt;&lt;br /&gt;int s_id;&lt;br /&gt;int *sv = NULL;&lt;br /&gt;&lt;br /&gt;//---------------------------------&lt;br /&gt;int sv_init() {&lt;br /&gt;  int tam = sizeof(int);&lt;br /&gt;  s_id = shmget(IPC_PRIVATE, tam, IPC_CREAT | IPC_EXCL | S_IRUSR | S_IWUSR);&lt;br /&gt;  if (s_id == -1)&lt;br /&gt;    return -1;&lt;br /&gt;  sv = (int *)shmat(s_id, NULL, 0);&lt;br /&gt;  if (sv == (int *)-1) {&lt;br /&gt;    sv = NULL;&lt;br /&gt;    return -1;&lt;br /&gt;  }&lt;br /&gt;  (*sv)=0;&lt;br /&gt;  return 1;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;//--------------------------------&lt;br /&gt;int sv_inc(int val) {&lt;br /&gt;  int tmp;&lt;br /&gt;  if (sv == NULL)&lt;br /&gt;    return -1;&lt;br /&gt;  tmp = (*sv);&lt;br /&gt;  tmp += val;  &lt;br /&gt;  (*sv)= tmp;&lt;br /&gt;  return (tmp);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;//--------------------------------&lt;br /&gt;int sv_val() {&lt;br /&gt;  if (sv == NULL)&lt;br /&gt;    return 0;&lt;br /&gt;  return (*sv);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;//--------------------------------&lt;br /&gt;int sv_get() {&lt;br /&gt;  return sv_val();&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;//--------------------------------&lt;br /&gt;int sv_set(int val) {&lt;br /&gt;  (*sv) = val;&lt;br /&gt;  return val;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;//--------------------------------&lt;br /&gt;int sv_finish() {&lt;br /&gt;  if (s_id == -1)&lt;br /&gt;    return -1;&lt;br /&gt;  return shmctl(s_id, IPC_RMID, 0);&lt;br /&gt;}&lt;br /&gt;&lt;/PRE&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9553291-110841971849888683?l=rincew.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rincew.blogspot.com/feeds/110841971849888683/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9553291&amp;postID=110841971849888683' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/110841971849888683'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/110841971849888683'/><link rel='alternate' type='text/html' href='http://rincew.blogspot.com/2005/02/libera-para-implementar-una-variable.html' title='Libería para implementar una variable compartida'/><author><name>Javier J.</name><uri>http://www.blogger.com/profile/00715175463383886462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9553291.post-110841934937949280</id><published>2005-02-14T22:11:00.000Z</published><updated>2005-02-14T22:15:49.383Z</updated><title type='text'>Mensaje con preaviso</title><content type='html'>Saludos.&lt;br /&gt;&lt;br /&gt;El siguiente código crea un proceso hijo que enviará cuatro mensajes al proceso padre, el cual los mostrará por pantalla. Para evitar que cualquier de los dos procesos se quede esperando en la tubería, el padre, antes de leer un mensaje de la tubería, avisará al hijo con una señal SIGUSR1. El hijo, al recibir la señal, pondrá un mensaje en la tubería y esperará a la siguiente señal.&lt;br /&gt;&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt;#include "stdio.h"&lt;br /&gt;#include "unistd.h"&lt;br /&gt;#include "signal.h"&lt;br /&gt;#include "wait.h"&lt;br /&gt;#include "sys/types.h"&lt;br /&gt;&lt;br /&gt;#define SIZE 256&lt;br /&gt;&lt;br /&gt;int tub[2];&lt;br /&gt;char msg[SIZE];&lt;br /&gt;&lt;br /&gt;void manejador_hijo(int);&lt;br /&gt;&lt;br /&gt;int main() {&lt;br /&gt;&lt;br /&gt;  int c = 0;&lt;br /&gt;  int status;&lt;br /&gt;  pid_t pid;&lt;br /&gt;&lt;br /&gt;  pipe(tub);  &lt;br /&gt;  pid = fork();&lt;br /&gt;  if (pid == -1) {&lt;br /&gt;    perror("Error bifurcando proceso - ");&lt;br /&gt;    return -1;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  if (pid == 0) {&lt;br /&gt;    if (signal (SIGUSR1, manejador_hijo) == SIG_ERR) {&lt;br /&gt;      perror("Error instalando manejador - ");&lt;br /&gt;      exit(-1);&lt;br /&gt;    }&lt;br /&gt;    close(tub[0]);&lt;br /&gt;    while (1)&lt;br /&gt;      pause();&lt;br /&gt;&lt;br /&gt;    printf("Hijo, terminación inesperada.\n");&lt;br /&gt;    exit(-1);&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  close(tub[1]);&lt;br /&gt;  while(c &lt; 4) {&lt;br /&gt;    sleep(1);&lt;br /&gt;    if ( kill(pid, SIGUSR1) == -1) {&lt;br /&gt;      perror("Error enviando señal al hijo.");&lt;br /&gt;      return -1;&lt;br /&gt;    } &lt;br /&gt;&lt;br /&gt;    if (read(tub[0], msg, SIZE) == -1) {&lt;br /&gt;      perror("Error leyendo de la tuberia - ");&lt;br /&gt;      return -1;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    printf("%i: %s\n", c, msg);&lt;br /&gt;    c++;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  close(tub[0]);&lt;br /&gt;  &lt;br /&gt;  if (kill(pid, SIGTERM) == -1) {&lt;br /&gt;    perror("Error terminando hijo.");&lt;br /&gt;    return -1;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  while( pid != wait(&amp;status) );&lt;br /&gt;&lt;br /&gt;  printf("Fin\n");&lt;br /&gt;  return 0;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;//--------------------------------------------------&lt;br /&gt;&lt;br /&gt;void manejador_hijo(int sig)&lt;br /&gt;{&lt;br /&gt;  if ( signal (SIGUSR1, manejador_hijo) == SIG_ERR) {&lt;br /&gt;    perror("Error reinstalando el controlador.");&lt;br /&gt;    exit(-1);&lt;br /&gt;  }&lt;br /&gt;  strcpy(msg, "Mensaje del hijo.");&lt;br /&gt;  if ( write(tub[1], msg, strlen(msg)) == -1) {&lt;br /&gt;    perror("Error escribiendo en la tuberia.");&lt;br /&gt;    exit(-1);&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/PRE&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9553291-110841934937949280?l=rincew.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rincew.blogspot.com/feeds/110841934937949280/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9553291&amp;postID=110841934937949280' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/110841934937949280'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/110841934937949280'/><link rel='alternate' type='text/html' href='http://rincew.blogspot.com/2005/02/mensaje-con-preaviso.html' title='Mensaje con preaviso'/><author><name>Javier J.</name><uri>http://www.blogger.com/profile/00715175463383886462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9553291.post-110820508833910455</id><published>2005-02-12T10:42:00.000Z</published><updated>2005-02-12T10:44:48.340Z</updated><title type='text'>Mensaje repetitivo</title><content type='html'>El siguiente código muestra un mensaje por pantalla drante 2 segundos, pasado estetiempo recibe una señal SIGALRM y el mensaje cambia. Este proceso se repite infinitas veces, o hasta que alguna variable se desborde.&lt;br /&gt;&lt;br /&gt;Hay que cambiar las " de los includes por los signos de mayor y menor.&lt;br /&gt;&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt;#include "unistd.h"&lt;br /&gt;#include "signal.h"&lt;br /&gt;#include "string.h"&lt;br /&gt;#include "sys/types.h"&lt;br /&gt;&lt;br /&gt;void sig_alarm(int);&lt;br /&gt;&lt;br /&gt;char men[50];&lt;br /&gt;int num_alarm = 0;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;int main()&lt;br /&gt;{&lt;br /&gt;  if ( signal(SIGALRM, sig_alarm) == SIG_ERR) {&lt;br /&gt;    perror("Error instalando manejador - ");&lt;br /&gt;    return -1;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  sprintf(men, "Alarma %i", num_alarm);&lt;br /&gt;  alarm(2);&lt;br /&gt;&lt;br /&gt;  while(1) {&lt;br /&gt;    printf("%s\n", men);&lt;br /&gt;    pause();&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  // Se ejcutará alguna vez esta línea ??&lt;br /&gt;  printf("Se acabó.\n");&lt;br /&gt;&lt;br /&gt;  return 0;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;//---------------------------&lt;br /&gt;&lt;br /&gt;void sig_alarm(int sig) {&lt;br /&gt;  &lt;br /&gt;  if ( signal(SIGALRM, sig_alarm) == SIG_ERR)  {&lt;br /&gt;    perror("Error instalando manejador - ");&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  alarm(2);&lt;br /&gt;  num_alarm++;&lt;br /&gt;  sprintf(men, "Alarma %i", num_alarm);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;/PRE&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9553291-110820508833910455?l=rincew.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rincew.blogspot.com/feeds/110820508833910455/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9553291&amp;postID=110820508833910455' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/110820508833910455'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/110820508833910455'/><link rel='alternate' type='text/html' href='http://rincew.blogspot.com/2005/02/mensaje-repetitivo.html' title='Mensaje repetitivo'/><author><name>Javier J.</name><uri>http://www.blogger.com/profile/00715175463383886462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9553291.post-110820492993892347</id><published>2005-02-12T10:40:00.000Z</published><updated>2005-02-12T10:42:09.940Z</updated><title type='text'>Matar al hijo perezoso</title><content type='html'>El siguiente programa lanza un proceso hijo, y si después de X segundos el proceso sige activo, lo mata con una señal SIGKILL. &lt;br /&gt;&lt;br /&gt;Es conveniente jugar con el valor del sleep(..) del hijo para ver el comportamiento de este programa.&lt;br /&gt;&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt;#include "unistd.h"&lt;br /&gt;#include "signal.h"&lt;br /&gt;#include "wait.h"&lt;br /&gt;#include "sys/types.h"&lt;br /&gt;&lt;br /&gt;int main()&lt;br /&gt;{&lt;br /&gt;  pid_t pid, pid_hijo;&lt;br /&gt;  int status;&lt;br /&gt;  int sleep_padre = 2;&lt;br /&gt;  int sleep_hijo = 1;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;  if ( (pid_hijo=fork()) == -1) {&lt;br /&gt;    perror("Error en el fork - ");&lt;br /&gt;    return -1;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  if (pid_hijo == 0) {&lt;br /&gt;    sleep(sleep_hijo);&lt;br /&gt;    printf("Termina hijo.\n");&lt;br /&gt;    exit(0);&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  sleep(sleep_padre);&lt;br /&gt;  pid = waitpid(pid_hijo, &amp;status, WNOHANG);&lt;br /&gt;&lt;br /&gt;  printf("pid = %ld / pid_hijo = %ld / status = %i\n", pid, pid_hijo, status);&lt;br /&gt;&lt;br /&gt;  if (kill(pid_hijo,0)==0) {&lt;br /&gt;    printf("Hijo aún en activo. Lo termino\n");&lt;br /&gt;    kill(pid_hijo, SIGTERM);&lt;br /&gt;    waitpid(pid_hijo, NULL, 0);&lt;br /&gt;  }&lt;br /&gt;  printf("Termina padre.\n");&lt;br /&gt;&lt;br /&gt;  return 0;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;/PRE&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9553291-110820492993892347?l=rincew.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rincew.blogspot.com/feeds/110820492993892347/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9553291&amp;postID=110820492993892347' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/110820492993892347'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/110820492993892347'/><link rel='alternate' type='text/html' href='http://rincew.blogspot.com/2005/02/matar-al-hijo-perezoso.html' title='Matar al hijo perezoso'/><author><name>Javier J.</name><uri>http://www.blogger.com/profile/00715175463383886462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9553291.post-110820477606170811</id><published>2005-02-12T10:36:00.000Z</published><updated>2005-02-12T10:39:36.063Z</updated><title type='text'>Contar señales SIGUSR1</title><content type='html'>Saludos.&lt;br /&gt;&lt;br /&gt;El siguiente programa queda a la espera de recibir una señal. Cunado recibe una señal SIGUSR1 incrementa un contador, y cuando recibe una señal SIGTERM muestra el contador y termina.&lt;br /&gt;&lt;br /&gt;Una variante muy sencilla es añadirle un manejador para SIGUSR2 de tal manera que, al recibir esta señal, muestre el valor del contador pero continue su ejecución.&lt;br /&gt;&lt;br /&gt;Para ejecutar un programa o comando en sgeundo plano, sin que deje la consola ocupada, hay que añadir &amp; al final del nombre del programa o comando.&lt;br /&gt;&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt;#include "signal.h"&lt;br /&gt;#include "unistd.h"&lt;br /&gt;&lt;br /&gt;void manejadorUSR1(int sig);&lt;br /&gt;void manejadorTERM(int sig);&lt;br /&gt;&lt;br /&gt;int cuenta = 0;&lt;br /&gt;&lt;br /&gt;int main()&lt;br /&gt;{&lt;br /&gt;&lt;br /&gt;  if ( signal(SIGUSR1, manejadorUSR1) == SIG_ERR) {&lt;br /&gt;    perror("Error instalando manejadorUSR1 - ");&lt;br /&gt;    return -1;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  if ( signal(SIGTERM, manejadorTERM) == SIG_ERR) {&lt;br /&gt;    perror("Error instalando manejadorTERM - ");&lt;br /&gt;    return -1;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  while(1)&lt;br /&gt;    sleep(5);&lt;br /&gt;&lt;br /&gt;  return 0;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;//-------------------------&lt;br /&gt;void manejadorUSR1(int sig) {&lt;br /&gt;&lt;br /&gt;  if ( signal(SIGUSR1, manejadorUSR1) == SIG_ERR) {&lt;br /&gt;    perror("Error instalamdno manejadorUSR1 - ");&lt;br /&gt;    exit(0);&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  cuenta++;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;//---------------------------&lt;br /&gt;void manejadorTERM(int sig) {&lt;br /&gt;&lt;br /&gt;  printf("Señales USR1 recibidas: %i \n", cuenta);&lt;br /&gt;  exit(0);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;/PRE&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9553291-110820477606170811?l=rincew.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rincew.blogspot.com/feeds/110820477606170811/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9553291&amp;postID=110820477606170811' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/110820477606170811'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/110820477606170811'/><link rel='alternate' type='text/html' href='http://rincew.blogspot.com/2005/02/contar-seales-sigusr1.html' title='Contar señales SIGUSR1'/><author><name>Javier J.</name><uri>http://www.blogger.com/profile/00715175463383886462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9553291.post-110786544019788050</id><published>2005-02-08T13:22:00.000Z</published><updated>2005-02-08T12:24:00.196Z</updated><title type='text'>Sincronización padre-hijo con señales</title><content type='html'>El siguiente programa muestra los números del 1 al 6 en orden creciente por pantalla. Los números impares los muestra el padre y los números pares los muestra el hijo.&lt;br /&gt;&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt;#include "signal.h&gt;&lt;br /&gt;#include "unistd.h&gt;&lt;br /&gt;#include "sys/types.h&gt;&lt;br /&gt;&lt;br /&gt;void manejadorPadre(int sig);&lt;br /&gt;void manejadorHijo(int sig);&lt;br /&gt;&lt;br /&gt;int cuenta = 0;&lt;br /&gt;&lt;br /&gt;int main() {&lt;br /&gt;&lt;br /&gt;  pid_t pid;&lt;br /&gt;&lt;br /&gt;  if ( (pid = fork()) == -1) {&lt;br /&gt;    perror("Error en el fork - ");&lt;br /&gt;    return -1;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  if (pid == 0) {&lt;br /&gt;    printf("Hijo instala manejador.\n");&lt;br /&gt;    if ( signal(SIGUSR1, manejadorHijo) == SIG_ERR) {&lt;br /&gt;      perror("Error instalando manejadorHijo - ");&lt;br /&gt;      exit(-1);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    printf("Hijo entra en el bucle\n");&lt;br /&gt;    while(1)&lt;br /&gt;      pause();&lt;br /&gt;    exit(0);&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  // sleep(1);&lt;br /&gt;  printf("Uno\n");&lt;br /&gt;&lt;br /&gt;  if ( signal(SIGUSR1, manejadorPadre) == SIG_ERR) {&lt;br /&gt;    perror("Error instalando manejadorHijo - ");&lt;br /&gt;    exit(-1);&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  while(cuenta &lt; 2) {&lt;br /&gt;    kill(pid, SIGUSR1);&lt;br /&gt;    pause();&lt;br /&gt;  }&lt;br /&gt;  kill(pid, SIGUSR1);&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;  printf("Esperando al hijo.\n");&lt;br /&gt;  wait(NULL);&lt;br /&gt;  return 0;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;//--------------------------&lt;br /&gt;void manejadorHijo(int sig)&lt;br /&gt;{&lt;br /&gt;&lt;br /&gt;  static int contador = 0;&lt;br /&gt;&lt;br /&gt;  if ( signal(SIGUSR1, manejadorHijo) == SIG_ERR) {&lt;br /&gt;      perror("Error instalando manejadorHijo - ");&lt;br /&gt;      exit(-1);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;  if (contador == 0){&lt;br /&gt;    printf("Dos\n");&lt;br /&gt;    contador++;&lt;br /&gt;    kill(getppid(), SIGUSR1);&lt;br /&gt;  } else if (contador == 1) {&lt;br /&gt;    printf("Cuatro\n");&lt;br /&gt;    contador++;&lt;br /&gt;    kill(getppid(), SIGUSR1);&lt;br /&gt;  } else {&lt;br /&gt;    printf("Seis\n");&lt;br /&gt;&lt;br /&gt;    // Restauro el manejador original&lt;br /&gt;    if (signal(SIGUSR1, SIG_DFL) == NULL) &lt;br /&gt;      perror("Error restaurando manejador original en el hijo - ");&lt;br /&gt;&lt;br /&gt;    exit(0);&lt;br /&gt;  };&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;//---------------------------------&lt;br /&gt;void manejadorPadre(int sig)&lt;br /&gt;{&lt;br /&gt;&lt;br /&gt;  if ( signal(SIGUSR1, manejadorPadre) == SIG_ERR) {&lt;br /&gt;      perror("Error instalando manejadorPadre - ");&lt;br /&gt;      exit(-1);&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  if (cuenta == 0) {&lt;br /&gt;    printf("Tres\n");&lt;br /&gt;    cuenta++;&lt;br /&gt;  } else if (cuenta == 1) {&lt;br /&gt;    printf("Cinco\n");&lt;br /&gt;    cuenta++;&lt;br /&gt;    // Restauro el manejador original&lt;br /&gt;    if (signal(SIGUSR1, SIG_DFL) == NULL) &lt;br /&gt;      perror("Error restaurando manejador original en el padre - ");&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/PRE&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9553291-110786544019788050?l=rincew.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rincew.blogspot.com/feeds/110786544019788050/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9553291&amp;postID=110786544019788050' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/110786544019788050'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/110786544019788050'/><link rel='alternate' type='text/html' href='http://rincew.blogspot.com/2005/02/sincronizacin-padre-hijo-con-seales.html' title='Sincronización padre-hijo con señales'/><author><name>Javier J.</name><uri>http://www.blogger.com/profile/00715175463383886462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9553291.post-110786527966474139</id><published>2005-02-08T13:16:00.000Z</published><updated>2005-02-08T12:21:19.663Z</updated><title type='text'>Listado ordenado.</title><content type='html'>El siguiente programa crea dos procesos hijos. El primer hijo ejecuta el comando "ls -l" y le pasa el resultado al segundo hijo. El segundo hijo ejecuta "sort -n +4" y redirecciona la salida al archivo "lsord.txt".&lt;br /&gt;&lt;br /&gt;En otras palabras, el resultado de este programa es el mismo que el de ejecutar la siguiente expresión:&lt;br /&gt;&lt;br /&gt;$ ls -l | sort -n +4 &gt; lsord.txt&lt;br /&gt;&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt;// Sustituir las "s por mayor y menos&lt;br /&gt;#include "stdio.h"&lt;br /&gt;#include "stdlib.h&gt;&lt;br /&gt;#include "unistd.h&gt;&lt;br /&gt;#include "fcntl.h&gt;&lt;br /&gt;#include "errno.h&gt;&lt;br /&gt;#include "sys/types.h&gt;&lt;br /&gt;#include "sys/wait.h&gt;&lt;br /&gt;&lt;br /&gt;int main()&lt;br /&gt;{&lt;br /&gt;  int fd[2];&lt;br /&gt;  int status;&lt;br /&gt;  int fd_salida;&lt;br /&gt;&lt;br /&gt;  pipe(fd);&lt;br /&gt;&lt;br /&gt;  if ( (fd_salida=open("lsord.txt", O_WRONLY | O_CREAT | O_TRUNC, 0600)) == -1) {&lt;br /&gt;    perror("Error - ");&lt;br /&gt;    return -1;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  if (fork()==0)&lt;br /&gt;  {&lt;br /&gt;    // El hijo&lt;br /&gt;    dup2(fd[1], STDOUT_FILENO);&lt;br /&gt;    close(fd[0]);&lt;br /&gt;    close(fd[1]);&lt;br /&gt;    execlp("ls", "ls ", "-l", NULL);&lt;br /&gt;    perror("Exec falló en ls");&lt;br /&gt;    exit(-1);&lt;br /&gt;  }&lt;br /&gt;  if (fork()==0) {&lt;br /&gt;    // El otro hijo&lt;br /&gt;    dup2(fd[0], STDIN_FILENO);&lt;br /&gt;    close(fd[0]);&lt;br /&gt;    close(fd[1]);&lt;br /&gt;    dup2(fd_salida, 1);&lt;br /&gt;    close(fd_salida);&lt;br /&gt;    execlp("sort", "sort", "-n", "+4", NULL);&lt;br /&gt;    perror("Exec falló en sort");&lt;br /&gt;    exit(-1);&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  close(fd[0]);&lt;br /&gt;  close(fd[1]);&lt;br /&gt;&lt;br /&gt;  // El padre espera a que terminen los hijos&lt;br /&gt;  status = wait(NULL);&lt;br /&gt;  while ( (status != -1) ||&lt;br /&gt;	  (status == -1 &amp;&amp; errno == EINTR))&lt;br /&gt;    status = wait(NULL);&lt;br /&gt;  &lt;br /&gt;  exit(0);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;/PRE&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9553291-110786527966474139?l=rincew.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rincew.blogspot.com/feeds/110786527966474139/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9553291&amp;postID=110786527966474139' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/110786527966474139'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/110786527966474139'/><link rel='alternate' type='text/html' href='http://rincew.blogspot.com/2005/02/listado-ordenado.html' title='Listado ordenado.'/><author><name>Javier J.</name><uri>http://www.blogger.com/profile/00715175463383886462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9553291.post-110769320053600553</id><published>2005-02-06T13:29:00.000Z</published><updated>2005-02-06T12:33:20.536Z</updated><title type='text'>Familia numerosa.</title><content type='html'>El siguiente código crea tres procesos hijos y, cada proceso hijo, crea tres nuevos procesos nietos.&lt;br /&gt;Este código se puede simplificar mucho utilizando &lt;em&gt;exit(int)&lt;/em&gt;.&lt;br /&gt;&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt;#include "unistd.h"&lt;br /&gt;#include "sys/types.h"&lt;br /&gt;&lt;br /&gt;int main()&lt;br /&gt;{&lt;br /&gt;  int i, padre, prim_padre;&lt;br /&gt;&lt;br /&gt;  prim_padre = padre = 1;&lt;br /&gt;&lt;br /&gt;  for(i=0; i&lt;3; i++) {&lt;br /&gt;    if (padre == 1) {&lt;br /&gt;      if (fork() == 0) {&lt;br /&gt;	// Proceso hijo&lt;br /&gt;	if (fork()==0) {&lt;br /&gt;	  // Proceso nieto&lt;br /&gt;	  printf("Nieto %i con padre %ld\n", getpid(), getppid());&lt;br /&gt;	} else {&lt;br /&gt;	  printf("Hijo %i con padre %ld\n", getpid(), (long)getppid() );&lt;br /&gt;	}&lt;br /&gt;	padre = 0;&lt;br /&gt;      } else {&lt;br /&gt;	sleep(2);&lt;br /&gt;	if (prim_padre) {&lt;br /&gt;	  printf("Padre %i\n", getpid());&lt;br /&gt;	  prim_padre = 0;&lt;br /&gt;	}&lt;br /&gt;      }&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;  return 0;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/PRE&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9553291-110769320053600553?l=rincew.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rincew.blogspot.com/feeds/110769320053600553/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9553291&amp;postID=110769320053600553' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/110769320053600553'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/110769320053600553'/><link rel='alternate' type='text/html' href='http://rincew.blogspot.com/2005/02/familia-numerosa.html' title='Familia numerosa.'/><author><name>Javier J.</name><uri>http://www.blogger.com/profile/00715175463383886462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9553291.post-110769281990527130</id><published>2005-02-06T13:25:00.000Z</published><updated>2005-02-06T12:26:59.906Z</updated><title type='text'>Ejemplo de tuberías</title><content type='html'>En el siguiente código, un proceso padre envía un mensaje a un proceso hijo mediante una tubería y el proceso hijo lo muestra por pantalla.&lt;br /&gt;&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt;#include "unistd.h"&lt;br /&gt;#include "sys/types.h"&lt;br /&gt;&lt;br /&gt;int main()&lt;br /&gt;{&lt;br /&gt;  int tub[2];&lt;br /&gt;  char msg[256];&lt;br /&gt;  pid_t pid;&lt;br /&gt;  int status;&lt;br /&gt;&lt;br /&gt;  pipe(tub);&lt;br /&gt;  pid = fork();&lt;br /&gt;&lt;br /&gt;  if (pid !=0)    {&lt;br /&gt;    // El padre&lt;br /&gt;    write(tub[0], "Soy padre.\n", 12);&lt;br /&gt;  }&lt;br /&gt;  else {&lt;br /&gt;    // El hijo&lt;br /&gt;    read(tub[1], msg, 256) ;&lt;br /&gt;    printf("Hijo &gt; %s", msg);&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/PRE&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9553291-110769281990527130?l=rincew.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rincew.blogspot.com/feeds/110769281990527130/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9553291&amp;postID=110769281990527130' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/110769281990527130'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/110769281990527130'/><link rel='alternate' type='text/html' href='http://rincew.blogspot.com/2005/02/ejemplo-de-tuberas.html' title='Ejemplo de tuberías'/><author><name>Javier J.</name><uri>http://www.blogger.com/profile/00715175463383886462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9553291.post-110769267834172981</id><published>2005-02-06T13:22:00.000Z</published><updated>2005-02-06T12:24:38.343Z</updated><title type='text'>Copiar entrada en salida.</title><content type='html'>El siguiente código espera una entrada por la consola y muestra dicha entrada en la salida estándar.&lt;br /&gt;&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt;#include "stdio.h"&lt;br /&gt;#include "stdlib.h"&lt;br /&gt;#include "sys/file.h"&lt;br /&gt;&lt;br /&gt;#define TAM_BUF 255&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;int main(int argc, char *argv[])&lt;br /&gt;{&lt;br /&gt;  char buf[TAM_BUF];&lt;br /&gt;  int readed;&lt;br /&gt;&lt;br /&gt;  readed = read(0, buf, TAM_BUF);&lt;br /&gt;  while(readed &gt; 0)&lt;br /&gt;  {&lt;br /&gt;    write(1, buf, readed);&lt;br /&gt;    readed = read(0, buf, TAM_BUF);&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  &lt;br /&gt;  printf("Hello, world!\n");&lt;br /&gt;&lt;br /&gt;  return EXIT_SUCCESS;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;/PRE&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9553291-110769267834172981?l=rincew.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rincew.blogspot.com/feeds/110769267834172981/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9553291&amp;postID=110769267834172981' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/110769267834172981'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/110769267834172981'/><link rel='alternate' type='text/html' href='http://rincew.blogspot.com/2005/02/copiar-entrada-en-salida.html' title='Copiar entrada en salida.'/><author><name>Javier J.</name><uri>http://www.blogger.com/profile/00715175463383886462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9553291.post-110769241135232625</id><published>2005-02-06T13:17:00.000Z</published><updated>2005-02-06T12:20:11.353Z</updated><title type='text'>Escribir un mensaje por la salida estándar</title><content type='html'>Saludos.&lt;br /&gt;&lt;br /&gt;El siguiente código escribe un mensaje por la salida estándar utilizando el mecanismo de descriptores de archivos.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;#include "stdio.h"&lt;br /&gt;#include "stdlib.h"&lt;br /&gt;&lt;br /&gt;#include "sys/file.h"&lt;br /&gt;&lt;br /&gt;#define TAM_BUF 255&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;int main(int argc, char *argv[])&lt;br /&gt;{&lt;br /&gt;  char buf[TAM_BUF];&lt;br /&gt;  &lt;br /&gt;  strcpy(buf, "Soy tocon.\n");&lt;br /&gt;  write(1, buf, 10);&lt;br /&gt;  &lt;br /&gt;  printf("Hello, world!\n");&lt;br /&gt;&lt;br /&gt;  return EXIT_SUCCESS;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9553291-110769241135232625?l=rincew.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rincew.blogspot.com/feeds/110769241135232625/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9553291&amp;postID=110769241135232625' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/110769241135232625'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/110769241135232625'/><link rel='alternate' type='text/html' href='http://rincew.blogspot.com/2005/02/escribir-un-mensaje-por-la-salida.html' title='Escribir un mensaje por la salida estándar'/><author><name>Javier J.</name><uri>http://www.blogger.com/profile/00715175463383886462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9553291.post-110759986932243199</id><published>2005-02-05T10:36:00.000Z</published><updated>2005-02-05T10:37:49.323Z</updated><title type='text'>Adivinanza 2</title><content type='html'>¿Cuántas veces aparece por pantalla la palabra "Proceso" y que valor tiene en cada aparición la variable p?&lt;br /&gt;&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt;#include "stdio.h"&lt;br /&gt;#include "sys/types.h"&lt;br /&gt;#include "unistd.h"&lt;br /&gt;&lt;br /&gt;int main(void) {&lt;br /&gt; &lt;br /&gt;  int c;&lt;br /&gt;  int p=0;&lt;br /&gt;  for (c = 0; c &lt; 3 ; c++ )&lt;br /&gt;  {&lt;br /&gt;     fork();&lt;br /&gt;  }&lt;br /&gt;  p++;&lt;br /&gt;  printf("Proceso %d\n", p);&lt;br /&gt;}&lt;br /&gt;&lt;/PRE&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9553291-110759986932243199?l=rincew.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rincew.blogspot.com/feeds/110759986932243199/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9553291&amp;postID=110759986932243199' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/110759986932243199'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/110759986932243199'/><link rel='alternate' type='text/html' href='http://rincew.blogspot.com/2005/02/adivinanza-2.html' title='Adivinanza 2'/><author><name>Javier J.</name><uri>http://www.blogger.com/profile/00715175463383886462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9553291.post-110759976586617126</id><published>2005-02-05T10:34:00.000Z</published><updated>2005-02-05T10:36:05.866Z</updated><title type='text'>Adivinanza 1</title><content type='html'>¿Cuantas veces aparece por pantalla la palabra "Proceso"?.&lt;br /&gt;&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt;// Cambiar las " por mayor y menor.&lt;br /&gt;#include "stdio.h"&lt;br /&gt;#include "sys/types.h"&lt;br /&gt;#include "unistd.h"&lt;br /&gt;&lt;br /&gt;int main(void) {&lt;br /&gt; &lt;br /&gt;  int c;&lt;br /&gt;  for (c = 0; c &lt; 3 ; c++ )&lt;br /&gt;  {&lt;br /&gt;     fork();&lt;br /&gt;  }&lt;br /&gt;  printf("Proceso\n");&lt;br /&gt;}&lt;br /&gt;&lt;/PRE&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9553291-110759976586617126?l=rincew.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rincew.blogspot.com/feeds/110759976586617126/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9553291&amp;postID=110759976586617126' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/110759976586617126'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/110759976586617126'/><link rel='alternate' type='text/html' href='http://rincew.blogspot.com/2005/02/adivinanza-1.html' title='Adivinanza 1'/><author><name>Javier J.</name><uri>http://www.blogger.com/profile/00715175463383886462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9553291.post-110759960277113271</id><published>2005-02-05T10:31:00.000Z</published><updated>2005-02-05T10:33:22.773Z</updated><title type='text'>Redireccionar salia de errores</title><content type='html'>Saludos.&lt;br /&gt;&lt;br /&gt;El siguiente programa redirecciona la salida estándar de errores de la pantalla hasta un fichero y después simula un error.&lt;br /&gt;&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt;// Cambiar las " de los include por mayor y menos.&lt;br /&gt;#include "fcntl.h"&lt;br /&gt;#include "stdio.h"&lt;br /&gt;#include "unistd.h"&lt;br /&gt;#include "sys/file.h"&lt;br /&gt;#include "sys/types.h"&lt;br /&gt;&lt;br /&gt;int main()&lt;br /&gt;{&lt;br /&gt;  int fp;&lt;br /&gt;  int fpdup;&lt;br /&gt;&lt;br /&gt;  fp = open("errores.txt", O_WRONLY | O_CREAT | O_TRUNC, 00600);&lt;br /&gt;  if (fp == -1)&lt;br /&gt;  {&lt;br /&gt;    perror("Error abriendo fichero.\n");&lt;br /&gt;    return -1;&lt;br /&gt;  };&lt;br /&gt;&lt;br /&gt;  fpdup=dup2(fp, 2);&lt;br /&gt;  printf("%i / %i\n", fp, fpdup);&lt;br /&gt;&lt;br /&gt;  close(fp);&lt;br /&gt;&lt;br /&gt;  perror("Error simulado.\n");&lt;br /&gt;&lt;br /&gt;  return 0;&lt;br /&gt;}&lt;br /&gt;&lt;/PRE&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9553291-110759960277113271?l=rincew.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rincew.blogspot.com/feeds/110759960277113271/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9553291&amp;postID=110759960277113271' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/110759960277113271'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/110759960277113271'/><link rel='alternate' type='text/html' href='http://rincew.blogspot.com/2005/02/redireccionar-salia-de-errores.html' title='Redireccionar salia de errores'/><author><name>Javier J.</name><uri>http://www.blogger.com/profile/00715175463383886462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9553291.post-110759944467572456</id><published>2005-02-05T10:28:00.000Z</published><updated>2005-02-05T10:30:44.676Z</updated><title type='text'>Mostrar argumentos de la línea de comandos.</title><content type='html'>Saludos.&lt;br /&gt;&lt;br /&gt;El siguiente código muestra uno a uno y numerados los argumentos de la línea de comandos. También podría hacerse con un bucle for hasta el valor de la variable argc.&lt;br /&gt;&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt;#include "stdio.h"&lt;br /&gt;&lt;br /&gt;int main(int argc, char *argv[])&lt;br /&gt;{&lt;br /&gt;  int i = 0;&lt;br /&gt;  while(argv[i]!=NULL) {&lt;br /&gt;    printf("%i : %s \n", i, argv[i]);&lt;br /&gt;    i++;&lt;br /&gt;  }&lt;br /&gt;return 0;&lt;br /&gt;}&lt;br /&gt;&lt;/PRE&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9553291-110759944467572456?l=rincew.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rincew.blogspot.com/feeds/110759944467572456/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9553291&amp;postID=110759944467572456' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/110759944467572456'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/110759944467572456'/><link rel='alternate' type='text/html' href='http://rincew.blogspot.com/2005/02/mostrar-argumentos-de-la-lnea-de.html' title='Mostrar argumentos de la línea de comandos.'/><author><name>Javier J.</name><uri>http://www.blogger.com/profile/00715175463383886462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9553291.post-110736937395284258</id><published>2005-02-02T18:34:00.000Z</published><updated>2005-02-02T18:36:13.953Z</updated><title type='text'>Listar la línea de argumentos</title><content type='html'>Saludos.&lt;br /&gt;&lt;br /&gt;El siguiente código lista todos los argumentos introducidos en la línea de comandos a continuación del nombre del programa y los numera. También podría hacerse mediante un bucle for con el valor de argc.&lt;br /&gt;&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt; * Este programa prueba si argv termina en NULL&lt;br /&gt; **/ &lt;br /&gt;#include &lt;stdio.h&gt;&lt;br /&gt;&lt;br /&gt;int main(int argc, char *argv[])&lt;br /&gt;{&lt;br /&gt;  int i = 0;&lt;br /&gt;  while(argv[i]!=NULL) {&lt;br /&gt;    printf("%i : %s \n", i, argv[i]);&lt;br /&gt;    i++;&lt;br /&gt;  }&lt;br /&gt;return 0;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;/PRE&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9553291-110736937395284258?l=rincew.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rincew.blogspot.com/feeds/110736937395284258/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9553291&amp;postID=110736937395284258' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/110736937395284258'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/110736937395284258'/><link rel='alternate' type='text/html' href='http://rincew.blogspot.com/2005/02/listar-la-lnea-de-argumentos.html' title='Listar la línea de argumentos'/><author><name>Javier J.</name><uri>http://www.blogger.com/profile/00715175463383886462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9553291.post-110699880958656092</id><published>2005-01-29T11:38:00.000Z</published><updated>2005-01-29T11:41:17.243Z</updated><title type='text'>Comprara dos archivos.</title><content type='html'>Saludos.&lt;br /&gt;&lt;br /&gt;El siguiente programa permite comparar dos archivos de texto binários. Aunque funciona la mayoría de las veces, tiene bastantes errores de diseño y, con ciertas condiciones especiales falla.&lt;br /&gt;&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt;#include &lt;unistd.h&gt;&lt;br /&gt;#include &lt;sys/file.h&gt;&lt;br /&gt;&lt;br /&gt;#define SIZE 256&lt;br /&gt;&lt;br /&gt;// Devuelve 1 si son distintos y 0 si son iguales.&lt;br /&gt;int cmpArrays(char a1[], char a2[], int size);&lt;br /&gt;&lt;br /&gt;int main(int argc, char *args[])&lt;br /&gt;{&lt;br /&gt;  char buff1[SIZE], buff2[SIZE];&lt;br /&gt;  int fd1, fd2;&lt;br /&gt;  int readed1, readed2;&lt;br /&gt;&lt;br /&gt;  fd1 = open(args[1], O_RDONLY);&lt;br /&gt;  fd2 = open(args[2], O_RDONLY);&lt;br /&gt;&lt;br /&gt;  while ( (readed1=read(fd1, buff1, SIZE)) &gt;0) {&lt;br /&gt;    readed2=read(fd2, buff2, SIZE);&lt;br /&gt;    if (readed1 != readed2) {&lt;br /&gt;      printf("\nArchivos de distinto tamaño.\n");&lt;br /&gt;      return 0;&lt;br /&gt;    }&lt;br /&gt;    if (cmpArrays(buff1, buff2, readed1)) {&lt;br /&gt;      printf("\nArchivos con distinto contenido.\n");&lt;br /&gt;      return 0;&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  close(fd1);&lt;br /&gt;  close(fd2);&lt;br /&gt;&lt;br /&gt;  printf("\nArchivos iguales.\n");&lt;br /&gt;&lt;br /&gt;  return 0;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;//------------------------------------&lt;br /&gt;&lt;br /&gt;int cmpArrays(char a1[],char a2[], int size)&lt;br /&gt;{&lt;br /&gt;  int c;&lt;br /&gt;&lt;br /&gt;  for (c=0; c &lt; size;c++)&lt;br /&gt;    if (a1[c] != a2[c])&lt;br /&gt;      return 1;&lt;br /&gt;&lt;br /&gt;  return 0;&lt;br /&gt;}&lt;br /&gt;&lt;/PRE&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9553291-110699880958656092?l=rincew.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rincew.blogspot.com/feeds/110699880958656092/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9553291&amp;postID=110699880958656092' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/110699880958656092'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/110699880958656092'/><link rel='alternate' type='text/html' href='http://rincew.blogspot.com/2005/01/comprara-dos-archivos.html' title='Comprara dos archivos.'/><author><name>Javier J.</name><uri>http://www.blogger.com/profile/00715175463383886462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9553291.post-110699869110459305</id><published>2005-01-29T11:36:00.000Z</published><updated>2005-01-29T11:38:11.103Z</updated><title type='text'>Guardar la entrada estándar en un fichero.</title><content type='html'>Saludos.&lt;br /&gt;&lt;br /&gt;El siguiente código va leyendo la entrada estándar y guardando lo leido en un archivo buffer.f&lt;br /&gt;&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt;&lt;br /&gt;#include &lt;unistd.h&gt;&lt;br /&gt;#include &lt;fcntl.h&gt;&lt;br /&gt;#include &lt;sys/types.h&gt;&lt;br /&gt;&lt;br /&gt;int main()&lt;br /&gt;{&lt;br /&gt;  int fd;&lt;br /&gt;  char *buffer[256];&lt;br /&gt;  int b_leidos, b_escritos;&lt;br /&gt;&lt;br /&gt;  fd = open("buffer.f", O_WRONLY | O_APPEND);&lt;br /&gt;  if (fd == -1)&lt;br /&gt;  {&lt;br /&gt;    printf("\nCreando fichero 'buffer.f'\n");&lt;br /&gt;    fd = open("buffer.f", O_WRONLY | O_CREAT, 00600);&lt;br /&gt;    if (fd == -1)&lt;br /&gt;    {&lt;br /&gt;      perror("Error creando 'buffer.f'\n");&lt;br /&gt;      return -1;&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  b_leidos = read(0, buffer, 256);&lt;br /&gt;  while (b_leidos &gt; 0)&lt;br /&gt;  {&lt;br /&gt;    b_escritos = write(fd, buffer, b_leidos);&lt;br /&gt;    if (b_escritos == -1)&lt;br /&gt;    {&lt;br /&gt;      perror("Error escribiendo.\n");&lt;br /&gt;      return -1;&lt;br /&gt;    }&lt;br /&gt;    b_leidos = read(0, buffer, 256);&lt;br /&gt;  }&lt;br /&gt;  close(fd);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;/PRE&gt; &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9553291-110699869110459305?l=rincew.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rincew.blogspot.com/feeds/110699869110459305/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9553291&amp;postID=110699869110459305' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/110699869110459305'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/110699869110459305'/><link rel='alternate' type='text/html' href='http://rincew.blogspot.com/2005/01/guardar-la-entrada-estndar-en-un.html' title='Guardar la entrada estándar en un fichero.'/><author><name>Javier J.</name><uri>http://www.blogger.com/profile/00715175463383886462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9553291.post-110648490537992169</id><published>2005-01-23T13:48:00.000Z</published><updated>2005-01-23T12:55:05.380Z</updated><title type='text'>Funciones potencial y factorial</title><content type='html'>Saludos.&lt;br /&gt;&lt;br /&gt;Como recordatorio de la manera de hacer bibliotecas de funciones y de las técnicas recursivas, se propuso como ejercicio implementar una biblioteca de funciones con una función potencial y otra factorial. A continuación se muestra dicha biblioteca con versiones recursivas y no recursivas.&lt;br /&gt;&lt;br /&gt;Próximamente más.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;pot_fac.h&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;#ifndef __pot_fac__&lt;br /&gt;#define __pot_fac__&lt;br /&gt;&lt;br /&gt;int potencial_nr(int, int);&lt;br /&gt;int potencial_r(int, int);&lt;br /&gt;int factorial_nr(int);&lt;br /&gt;int factorial_r(int);&lt;br /&gt;&lt;br /&gt;#endif&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:Courier New;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;pot_fac.c&lt;br /&gt;&lt;/strong&gt;&lt;/span&gt;&lt;pre&gt;&lt;br /&gt;#include "pot_fac.h"&lt;br /&gt;&lt;br /&gt;int potencial_nr(int valor, int potencia)&lt;br /&gt;{&lt;br /&gt;int i, resultado;&lt;br /&gt;resultado = 1;&lt;br /&gt;for (i=0; i&lt;potencia; i="valor-1;" potencia="="&gt; 0; i--)&lt;br /&gt;resultado *= i;&lt;br /&gt;return resultado;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;//---------------------------------------------&lt;br /&gt;int factorial_r(int valor)&lt;br /&gt;{&lt;br /&gt;if (valor == 1)&lt;br /&gt;return 1;&lt;br /&gt;return valor * factorial_r(valor-1);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;span style="font-family:Courier New;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style="font-size:130%;"&gt;prueba_pot_fac.c &lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;#include "pot_fac.h"&lt;br /&gt;&lt;br /&gt;int main() {&lt;br /&gt;&lt;br /&gt;  int resultado;&lt;br /&gt;&lt;br /&gt;  resultado=potencial_nr(4, 3);&lt;br /&gt;  printf("4 elevado a 3 %i (no resursivo)\n", resultado);&lt;br /&gt;&lt;br /&gt;  resultado=potencial_r(3, 3);&lt;br /&gt;  printf("3 elevado a 3 %i (resursivo)\n", resultado);&lt;br /&gt;&lt;br /&gt;  resultado=factorial_nr(4);&lt;br /&gt;  printf("4 factorial %i (no resursivo)\n", resultado);&lt;br /&gt;&lt;br /&gt;  resultado=factorial_r(6);&lt;br /&gt;  printf("6 factorial %i (resursivo)\n", resultado);&lt;br /&gt;&lt;br /&gt;  return 0;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9553291-110648490537992169?l=rincew.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rincew.blogspot.com/feeds/110648490537992169/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9553291&amp;postID=110648490537992169' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/110648490537992169'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/110648490537992169'/><link rel='alternate' type='text/html' href='http://rincew.blogspot.com/2005/01/funciones-potencial-y-factorial.html' title='Funciones potencial y factorial'/><author><name>Javier J.</name><uri>http://www.blogger.com/profile/00715175463383886462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9553291.post-110648430158894143</id><published>2005-01-23T13:25:00.000Z</published><updated>2005-01-23T12:45:01.586Z</updated><title type='text'>Contar caracteres de una frase</title><content type='html'>Saludos.&lt;br /&gt;&lt;br /&gt;Este ejercicio se propuso para que los alumnos repasaran sus conocimientos de manejo de cadenas en C. El objetivo es introducir una frase en la línea de comandos. El programa devolverá el número de veces que se repite cada una de las letras en la frase.&lt;br /&gt;&lt;br /&gt;Próximamente más.&lt;br /&gt;&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt;#include "stdlib.h"&lt;br /&gt;#include "string.h"&lt;br /&gt;&lt;br /&gt;// Variables globales;&lt;br /&gt;char caracteres[29];&lt;br /&gt;int car_index = 0;&lt;br /&gt;int repeticiones[29];&lt;br /&gt;&lt;br /&gt;// Prototipos&lt;br /&gt;void incCaracter(char caracter)&lt;br /&gt;{&lt;br /&gt;  int pos = -1;&lt;br /&gt;  int i;&lt;br /&gt;&lt;br /&gt;  for (i=0; i&lt; car_index; i++)&lt;br /&gt;    if (caracteres[i] == caracter)&lt;br /&gt;      pos = i;&lt;br /&gt;&lt;br /&gt;  if (pos == -1) {&lt;br /&gt;    //car_index++;&lt;br /&gt;    caracteres[car_index] = caracter;&lt;br /&gt;    repeticiones[car_index] = 0;&lt;br /&gt;    pos = car_index;&lt;br /&gt;    car_index++;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  repeticiones[pos] ++;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;//-----------------------------------------------------&lt;br /&gt;int main(int argc, char *argv[])&lt;br /&gt;{&lt;br /&gt;  char cadena[255];&lt;br /&gt;  int i = 0;&lt;br /&gt;  &lt;br /&gt;  if (argc &lt; 2) {&lt;br /&gt;    printf("Parámetros insuficientes. \n");&lt;br /&gt;    return -1;&lt;br /&gt;  }&lt;br /&gt;  strcpy(cadena, argv[1]);&lt;br /&gt; &lt;br /&gt;  while (cadena[i] != '\0')&lt;br /&gt;  {&lt;br /&gt;    incCaracter(cadena[i]);&lt;br /&gt;    i++;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  printf("Estadísticas de &lt;%s&gt;: \n", cadena);&lt;br /&gt;  for (i=0; i &lt; car_index; i++)&lt;br /&gt;    printf("%c : %i \n", caracteres[i], repeticiones[i]);&lt;br /&gt;&lt;br /&gt;  return 0;&lt;br /&gt;}&lt;br /&gt;  &lt;br /&gt;&lt;/PRE&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9553291-110648430158894143?l=rincew.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rincew.blogspot.com/feeds/110648430158894143/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9553291&amp;postID=110648430158894143' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/110648430158894143'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/110648430158894143'/><link rel='alternate' type='text/html' href='http://rincew.blogspot.com/2005/01/contar-caracteres-de-una-frase.html' title='Contar caracteres de una frase'/><author><name>Javier J.</name><uri>http://www.blogger.com/profile/00715175463383886462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9553291.post-110615557579875197</id><published>2005-01-19T17:22:00.000Z</published><updated>2005-01-19T17:26:15.796Z</updated><title type='text'>Operación Limpieza</title><content type='html'>Aviso a navegantes.&lt;br /&gt;&lt;br /&gt;Por estas fechas termino de impartir una serie de clases sobre programación C básica en sistemas Unix/Linux (Fedora y SunOS). Por este motivo voy a ir publicando aquí en los próximos días el código de los ejemplos y problemas extra que he ido proponiendo a mis alumnos.&lt;br /&gt;&lt;br /&gt;También pondré de vez en cuando algún otro post como los que hay más abajo para que esto no se convierta en un monográfico C bajo Unix.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9553291-110615557579875197?l=rincew.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rincew.blogspot.com/feeds/110615557579875197/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9553291&amp;postID=110615557579875197' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/110615557579875197'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/110615557579875197'/><link rel='alternate' type='text/html' href='http://rincew.blogspot.com/2005/01/operacin-limpieza.html' title='Operación Limpieza'/><author><name>Javier J.</name><uri>http://www.blogger.com/profile/00715175463383886462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9553291.post-110587318987362393</id><published>2005-01-16T10:55:00.000Z</published><updated>2005-01-16T10:59:49.873Z</updated><title type='text'>Convertir double a caddena</title><content type='html'>Saludos.&lt;br /&gt;&lt;br /&gt;El otro día me preguntaron como pasar un tipo double a cadena con un número determinado de decimales, por ejemplo 2, en Java. Una posible solución se muestra a continuación:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;br /&gt;import java.text.DecimalFormat ;&lt;br /&gt;&lt;br /&gt;public class Decimal {&lt;br /&gt;&lt;br /&gt;public static void main(String args[]) {&lt;br /&gt;&lt;br /&gt;double d1 = 123456789.123456789;&lt;br /&gt;double d2 = 1.7976931348623157d;&lt;br /&gt;double d3 = 4415961481999.03D;&lt;br /&gt;DecimalFormat df = new DecimalFormat ("##############.##");&lt;br /&gt;&lt;br /&gt;System.out.println ("d1:"+df.format(d1));&lt;br /&gt;System.out.println ("d2:"+df.format(d2));&lt;br /&gt;System.out.println ("d3:"+df.format(d3));&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt;Salida:&lt;br /&gt;&gt;java Decimal&lt;br /&gt;d1:123456789,12&lt;br /&gt;d2:1,8&lt;br /&gt;d3:4415961481999,03&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;/blockquote&gt;&lt;blockquote&gt;&lt;/blockquote&gt;&lt;em&gt;&lt;/em&gt;&lt;strong&gt;&lt;/strong&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9553291-110587318987362393?l=rincew.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rincew.blogspot.com/feeds/110587318987362393/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9553291&amp;postID=110587318987362393' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/110587318987362393'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/110587318987362393'/><link rel='alternate' type='text/html' href='http://rincew.blogspot.com/2005/01/convertir-double-caddena.html' title='Convertir double a caddena'/><author><name>Javier J.</name><uri>http://www.blogger.com/profile/00715175463383886462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9553291.post-110570522682372049</id><published>2005-01-14T13:16:00.000Z</published><updated>2005-01-14T12:20:26.823Z</updated><title type='text'>Pruebas como indicadores del avance de un proyecto.</title><content type='html'>¿Cuándo finaliza la construcción de un programa?. ¿Cuánto hemos avanzado esta semana?. ¿Vamos más rápidos o más lentos que el mes pasado?.&lt;br /&gt;&lt;br /&gt;Todas estas preguntas se refieren a la velocidad con la que se crea o construye un programa y, en muchas ocasiones no son fáciles de responder. Una primera idea podría ser pensar que el avance de un programa tiene relación con las líneas de código escritas o número de clases implementadas, pero nada más lejos de la realidad. Es posible, por ejemplo, escribir 100.000 líneas de código o 50 clases en una semana y que el proyecto no avance ni un ápice, porque, por ejemplo, el código no funcione correctamente y necesite una depuración a fondo, o no pueda incorporarse al resto del programa o tenga que ser reescrito o, simplemente, se haya escrito código para una característica o función que no va a tener el sistema, por lo que no habrá servido de nada. &lt;br /&gt;&lt;br /&gt;Una aproximación más fiable, pero aún no perfecta desde mi punto de vista, es relacionar el avance del proyecto con el cumplimiento de los requisitos. Un requisito es una característica que el sistema debe tener o algo que debe permitir hacer. A medida que vayamos escribiendo código, el sistema permitirá hacer cada vez más cosas, hasta que el sistema sea capaz de hacer todo lo que le piden los requisitos, momento en el que estará terminado. Sin embargo a veces es difícil cuantificar, a partir del código, el cumplimiento de requisitos. Por ejemplo, en un sistema típico de gestión comercial, si empleamos dos semanas en construir las bases de datos y las clases de acceso a las bases de datos (algo que será necesario para todos los requisitos), ¿qué grado de cumplimiento de los requisitos hemos conseguido un 1%, un 10%?. ¿Algún requisito habrá avanzado más que otro?. ¿Cuánto nos queda para satisfacer todos los requisitos?. &lt;br /&gt;&lt;br /&gt;La mejor aproximación desde mi punto de vista, y que aúna características de las dos anteriores, es medir el avance del proyecto en función de la superación de pruebas. El proyecto estará terminado cuando sea capaz de superar todas las pruebas que lo verifiquen. Una semana se avanzará más que otra si ha sido capaz de superar todas las pruebas de la semana anterior y un número mayor de nuevas pruebas. Esta aproximación nos permite además, medir el desarrollo del código, mediante pruebas unitarias, y medir el cumplimiento de requisitos, mediante pruebas del sistema o pruebas de aceptación. En el momento en que ambas medidas alcancen el máximo, el sistema hará todo o que debe hacer sin errores en su código.&lt;br /&gt;&lt;br /&gt;Un motivo más que justifica escribir pruebas para nuestros proyectos: no solo nos garantizan el cumplimiento de requisitos y la ausencia de errores, sino que nos permiten conocer el estado de nuestro proyecto, a que ritmo avanza y cuanto queda para su conclusión.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9553291-110570522682372049?l=rincew.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rincew.blogspot.com/feeds/110570522682372049/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9553291&amp;postID=110570522682372049' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/110570522682372049'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/110570522682372049'/><link rel='alternate' type='text/html' href='http://rincew.blogspot.com/2005/01/pruebas-como-indicadores-del-avance-de.html' title='Pruebas como indicadores del avance de un proyecto.'/><author><name>Javier J.</name><uri>http://www.blogger.com/profile/00715175463383886462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9553291.post-110503642959548763</id><published>2005-01-06T18:28:00.000Z</published><updated>2005-01-06T18:33:49.596Z</updated><title type='text'>Límite de procesos en Unix/Linux</title><content type='html'>Saludos.&lt;br /&gt;&lt;br /&gt;Preparando una clase de programación en C Unix/Linux he intentado hacer un ejemplo donde un gran número de procesos accedieran simultáneamente a un trozo de memoria compartida y los resultados no fueran los esperados. Con esto pretendía ilustrar la necesidad de garantizar la exclusión mútua.&lt;br /&gt;&lt;br /&gt;No lo he conseguido. Mi Knoppix solo me ha permitido lanzar 487 procesos simultáneamente, mientra que SunOS me dejaba algunos más, hasta 496, 497 más o menos. Por desgracia con este número de procesos todo funcionaba a la perfección y, aunque no garantizara la exclusión mútua, ningún proceso se pisaba a otro.&lt;br /&gt;&lt;br /&gt;Tnedré que escribir un programa donde fuerce la situación con unos sleep estratégicamente colocados. &lt;br /&gt;&lt;br /&gt;Es una alegría que los Linux/Unix funcionen bien incluso cuando uno quiere que fallen.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9553291-110503642959548763?l=rincew.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rincew.blogspot.com/feeds/110503642959548763/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9553291&amp;postID=110503642959548763' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/110503642959548763'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/110503642959548763'/><link rel='alternate' type='text/html' href='http://rincew.blogspot.com/2005/01/lmite-de-procesos-en-unixlinux.html' title='Límite de procesos en Unix/Linux'/><author><name>Javier J.</name><uri>http://www.blogger.com/profile/00715175463383886462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9553291.post-110431565340729560</id><published>2004-12-29T10:20:00.000Z</published><updated>2004-12-29T10:20:53.406Z</updated><title type='text'>Felicitación al mayor genio de la informática.</title><content type='html'>No quiero dejar pasar la ocasión de, en estas fiestas navideñas, felicitar al mayor genio de la informática de toda la historia, un visionario, un profeta adelantado a su tiempo, sin el cual esta ciencia y arte a la vez probablemente no existiría. No me refiero a Bill Gates, ni a Alan Turing, nia Dennis Ritchie, ni a Linus Torvarld, ni tampoco a Von Newman. Hablo del hombre que debe ser por méritos propios patrono de los informáticos (por encima de santa Tecla), el cual se merece todos los parabienes y homenajes, el hombre que tendría que tener una escultura en todas las escuelas de informática del mundo, el hombre que ha conseguido que en todas las casa y oficina hay un ordenador, y a veces, hasta más de uno, el hombre sin el cual no existiría Internet ni el 99% de los programas existentes en la actualidad..&lt;br /&gt;&lt;br /&gt;Feliz navidad y próspero año nuevo para el hombre que inventó el copy&amp;paste.&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9553291-110431565340729560?l=rincew.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rincew.blogspot.com/feeds/110431565340729560/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9553291&amp;postID=110431565340729560' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/110431565340729560'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/110431565340729560'/><link rel='alternate' type='text/html' href='http://rincew.blogspot.com/2004/12/felicitacin-al-mayor-genio-de-la.html' title='Felicitación al mayor genio de la informática.'/><author><name>Javier J.</name><uri>http://www.blogger.com/profile/00715175463383886462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9553291.post-110374247514698571</id><published>2004-12-22T19:06:00.000Z</published><updated>2004-12-22T19:07:55.146Z</updated><title type='text'>DumpTable</title><content type='html'>Saludos.&lt;br /&gt;&lt;br /&gt;Actualmente estoy realizando un trabajo sobre herramientas de álgebra relacional, por lo que he tenido la necesidad de disponer de una herramienta que me permitiera volcar el contenido de una tabla de una base de datos en un archivo de texto que siguiera un formato concreto. Por ejemplo, a partir de una tabla, crear un archivo de texto con sentencias INSERT para cada registro de la tabla.&lt;br /&gt;&lt;br /&gt;Por ese motivo he creado una sencilla herramienta llamada DumpTable que está a disposición de quien la quiera en la página de proyectos JavaHispano.net (http://javahispano.net/projects/dumptable/).&lt;br /&gt;&lt;br /&gt;Para algunos alumnos de facultades que tienen que sufrir la herramienta WinRDBI en prácticas, puede resultarles de ayuda ya que pueden generar bases de datos para esta herramienta a partir de una tabla de una base de datos con lo cual se ahorran tener que pelearse con la incómoda interfaz de usuario y tener que introducir los registros a mano.&lt;br /&gt;&lt;br /&gt;También incluyo documentación sobre su diseño, principalmente diagramas UML, por lo que puede ser interesante como ejemplo pedagógico (a imitar o a evitar, eso lo dejo a consideración de quien lo vea).&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9553291-110374247514698571?l=rincew.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rincew.blogspot.com/feeds/110374247514698571/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9553291&amp;postID=110374247514698571' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/110374247514698571'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/110374247514698571'/><link rel='alternate' type='text/html' href='http://rincew.blogspot.com/2004/12/dumptable.html' title='DumpTable'/><author><name>Javier J.</name><uri>http://www.blogger.com/profile/00715175463383886462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9553291.post-110313793170506341</id><published>2004-12-15T19:09:00.000Z</published><updated>2004-12-15T19:12:11.706Z</updated><title type='text'>Proyectos open-source en Rediris y Knoppix.</title><content type='html'>Saludos.&lt;br /&gt;&lt;br /&gt;En el servidor sunsite de rediris (http://sunsite.rediris.es/) se pueden encontrar gran cantidad de mirrors actualizados de muchos proyectos open-source interesantes (entre ellos varias distribuciones de Linux). Aparte de la ventaja de tenerlos todos en el mismo sitio, las velocidades de descarga de los archivos son bastante interesantes.&lt;br /&gt;&lt;br /&gt;En cocnreto, en la carpeta de Knoppix (http://sunsite.rediris.es/mirror/knoppix/), he encontrado (además de las distribuciones), algunas utilidades interesantes y  una lista con todos los paquetes que Knoppix incorpora, y la verdad es que me he llevado más de una grata sorpresa (por ejemplo, Knoppix es LAMP). Espero ir escribiendo más mensajes próximamente hablando de Knoppix y sus posibilidades.&lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9553291-110313793170506341?l=rincew.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rincew.blogspot.com/feeds/110313793170506341/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9553291&amp;postID=110313793170506341' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/110313793170506341'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/110313793170506341'/><link rel='alternate' type='text/html' href='http://rincew.blogspot.com/2004/12/proyectos-open-source-en-rediris-y.html' title='Proyectos open-source en Rediris y Knoppix.'/><author><name>Javier J.</name><uri>http://www.blogger.com/profile/00715175463383886462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9553291.post-110287568111382869</id><published>2004-12-12T18:09:00.000Z</published><updated>2004-12-12T18:21:21.113Z</updated><title type='text'>Insertar un registro en MySQL con Java.</title><content type='html'>Saludos.&lt;br /&gt;&lt;br /&gt;Hace poco un alumno me comentó como podía insertar un registro en una base de datos en MySQL. Por si a alguien le resulta de utilidad, copio a continuación el código que le envié de ejemplo:&lt;br /&gt;&lt;br /&gt;Lo primero que se necesita (además de tener NySQL instalado y en ejecución), es un driver JDBC, por ejemplo MySQL Connector que se puede descargar de la página de MySQL (www.mysql.com). Junto con el driver se distribuye también documentación y archivos de ejemplo.&lt;br /&gt;&lt;br /&gt;A continuación se incluye el código fuente de una clase más sencilla que las que acompañan al driver y que muestra como insertar un registro. Para que dicha clase funcione es necesario que el JAR (por ejemplo "mysql-connector-java-3.0.16-ga-bin.jar") se encuentre en el CLASSPATH.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;import java.sql.Connection;&lt;br /&gt;import java.sql.DatabaseMetaData;&lt;br /&gt;import java.sql.DriverManager;&lt;br /&gt;import java.sql.PreparedStatement;&lt;br /&gt;import java.sql.ResultSet;&lt;br /&gt;import java.sql.SQLException;&lt;br /&gt;import java.sql.Statement;&lt;br /&gt;&lt;br /&gt;import java.util.Properties;&lt;br /&gt;&lt;br /&gt;public class InsertarRegistro  {&lt;br /&gt;&lt;br /&gt;    protected Connection conn = null;&lt;br /&gt;    protected Statement stmt = null;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;    /**&lt;br /&gt;     * Conecta con la base de datos 'cliente' de un servidor&lt;br /&gt;     * MySQL local.&lt;br /&gt;     */&lt;br /&gt;    public void conectar() throws Exception {&lt;br /&gt;        Class.forName("com.mysql.jdbc.Driver").newInstance();&lt;br /&gt;        this.conn = DriverManager.getConnection("jdbc:mysql:///Clientes");&lt;br /&gt;        this.stmt = conn.createStatement();&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    /**&lt;br /&gt;     * @throws Exception DOCUMENT ME!&lt;br /&gt;     */&lt;br /&gt;    public void desconectar() throws Exception {&lt;br /&gt;        if (this.stmt != null) {&lt;br /&gt;            try {&lt;br /&gt;                this.stmt.close();&lt;br /&gt;            } catch (SQLException SQLE) {&lt;br /&gt;                ;&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;        if (this.conn != null) {&lt;br /&gt;            try {&lt;br /&gt;                this.conn.close();&lt;br /&gt;            } catch (SQLException SQLE) {&lt;br /&gt;                ;&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    protected void insertarUnCliente() throws SQLException {&lt;br /&gt;          String sql = "INSERT INTO Clientes VALUES ('', 'entidad', " + &lt;br /&gt;                                                        "'actividad'," + &lt;br /&gt;                                                        "'direccion', " + &lt;br /&gt;                                                        "'localidad', " +&lt;br /&gt;                                                        "'codigo_postal', " + &lt;br /&gt;                                                        "'telefono', '', '', '');";&lt;br /&gt;          stmt.execute(sql);          &lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    &lt;br /&gt;    public static void main(String[] args) {&lt;br /&gt;      InsertarRegistro ie = new InsertarRegistro();&lt;br /&gt;      try {&lt;br /&gt;        ie.conectar();&lt;br /&gt;        ie.insertarUnCliente();&lt;br /&gt;        ie.desconectar();&lt;br /&gt;      }  catch (Exception e) {&lt;br /&gt;        e.printStackTrace();&lt;br /&gt;      }&lt;br /&gt;      System.out.println("Ok!");&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9553291-110287568111382869?l=rincew.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rincew.blogspot.com/feeds/110287568111382869/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9553291&amp;postID=110287568111382869' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/110287568111382869'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/110287568111382869'/><link rel='alternate' type='text/html' href='http://rincew.blogspot.com/2004/12/insertar-un-registro-en-mysql-con-java.html' title='Insertar un registro en MySQL con Java.'/><author><name>Javier J.</name><uri>http://www.blogger.com/profile/00715175463383886462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9553291.post-110271162430588665</id><published>2004-12-10T20:41:00.000Z</published><updated>2004-12-10T20:47:04.306Z</updated><title type='text'>Java pierde popularidad.</title><content type='html'>Saludos.&lt;br /&gt;&lt;br /&gt;La notia ya tiene algunas semanas y ya ha sido comentada en otras páginas con mucha audencia, pero no me resisto a omentarlo aquí.&lt;br /&gt;Al parecer, en una web (&lt;a href="http://www.tiobe.com/tpci.htm"&gt;http://www.tiobe.com/tpci.htm&lt;/a&gt;) se dedican a calcular la popularidad de los distintos lenguajes de programación. En principio me pare que utilizan un método muy acertado (independientemente de su eficacia o no), que es, a grandes rasgos, contar enlaces en el Google.&lt;br /&gt;Lo que me llama la atención no es tanto que Java baje posiciones, aunque se queda en el segundo puesto, sino que sea desbancado por el C de toda la vida (sin clases ni objetos), inventado allá por 1974, más o menos.&lt;br /&gt;También me ha llamdo la atención ver el poco tirón que ese informe le adjudica a C#, un lenguaje que yo pensaba que estaba teniendo mucho tirón de la mano de Microsoft.&lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9553291-110271162430588665?l=rincew.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rincew.blogspot.com/feeds/110271162430588665/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9553291&amp;postID=110271162430588665' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/110271162430588665'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/110271162430588665'/><link rel='alternate' type='text/html' href='http://rincew.blogspot.com/2004/12/java-pierde-popularidad.html' title='Java pierde popularidad.'/><author><name>Javier J.</name><uri>http://www.blogger.com/profile/00715175463383886462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9553291.post-110269867795269619</id><published>2004-12-10T17:07:00.000Z</published><updated>2004-12-10T17:11:17.953Z</updated><title type='text'>Mensaje inagural</title><content type='html'>Saludos a todos.&lt;br /&gt;&lt;br /&gt;Este blog tratará de temas d eprogramación y relacionados con la informática y, especialmente, de lenguaje Java. En este blog espero ir escribiendo mensajes útiles que rcojan las herramientas, enlaces y experiencia de mi trabajo cotidiano.&lt;br /&gt;&lt;br /&gt;Por cierto, el tírulo es un pequeño homenaje al juego Warcraft III, en versión doblada al español.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9553291-110269867795269619?l=rincew.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rincew.blogspot.com/feeds/110269867795269619/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9553291&amp;postID=110269867795269619' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/110269867795269619'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9553291/posts/default/110269867795269619'/><link rel='alternate' type='text/html' href='http://rincew.blogspot.com/2004/12/mensaje-inagural.html' title='Mensaje inagural'/><author><name>Javier J.</name><uri>http://www.blogger.com/profile/00715175463383886462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry></feed>
