Ejemplo de aplicación web con Hibernate que funciona usando Netbeans IDE y JSTL (JSP(X))

4.12.09. Por ooscarr (ooscarr)

Al final de este tutorial conocerás la dinámica del desarrollo de una aplicación web manejando la persistencia de la base de datos con Hibernate y cómo NetBeans IDE te ayuda a escribir el código y consultar la base de datos. También escribirás una aplicación web JSP que haga consultas a esta base de datos a través usando XML.

Este ejemplo de Hibernate es famoso, está documentado en varias páginas, pero todas tienen el código malo y es frustrante aprender así. Así que lo corregí y lo traduje al español sacando cosas de uno y de otro mismo tutorial que encontré en ¡chino, portugués, inglés y ruso!

Soporte para Hibernate en aplicaciones web está disponible desde NetBeans IDE 6.1. Desde NetBeans IDE 6.5, también puedes hacer ingeniería inversa a las tablas de una BD a archivos de mapeo y clases Java correspondientes usando un asistente intuitivo.

Pasos previos

Antes de empezar necesitarás:

  • Instalar NetBeans IDE. Al momento de escribir esto, anda dando vueltas la versión 6.8 RC2.
  • Instalar MySQL o Java DB, o tener acceso a conectarte a una de estas bases de datos con permiso para acceder o crear la base de datos Sakila. La base de datos Sakila está disponible en estos dos tipos de bases de datos, pero Hibernate funciona con cualquier JDBC, por ejemplo PostgreSQL y Oracle.
  • Importar la base de datos de ejemplo Sakila (a la que le haremos consultas en este ejemplo).

Una vez obtenido esto, asegúrate de configurar NetBeans IDE para poder acceder a la base de datos Sakila desde el panel "Prestaciones" (Menú Ventana > Prestaciones.

Instalar el complemento de Hibernate

El plug-in, si no lo tienes ya instalado, lo obtienes

  1. yendo, en el menú, a Herramientas > Complementos.
  2. En la ficha Plugins disponibles, selecciona la casilla del módulo Hibernate y presiona Instalar.

  3. Siguiente
  4. Acepta los términos de todos los contratos de licencia y presiona el botón de Instalar.
  5. Después que se descargue y se instale, presiona Terminar.
  6. Y listo, el módulo para Hibernate (o Hibernar) se activará solo; o sino, reinicia NetBeans IDE.

Crear Proyecto nuevo

Primero: lo típico. Creamos un Proyecto nuevo de tipo Aplicación web.

Asistente de Nuevo proyecto de NetBeans, bajo la categoría Web está la opción Aplicación web.

Para nuestro ejemplo, le damos el nombre DVDStore.

Elegimos un servidor como Tomcat o Glassfish, Siguiente y marcamos la opción Hibernate y seleccionamos la base de datos jdbc:mysql://localhost:3306/sakila, en nuestro caso, de la lista que, supongo, ya habías configurado anteriormente como indiqué en los pasos previos de este tutorial.

Asistente de Nuevo proyecto de NetBeans, después de marcar la casilla que habilita Hibernate 3.2.5 abajo selecciona la base de datos Sakila instalada en localhost.
Terminar

Fíjate que en:

  • DVDStore
    • Libraries

se agregaron automáticamente las librerías necesarias para utilizar Hibernate y conectar a la base de datos.

Configurar proyecto

hibernate.cfg.xml

Abre el archivo hibernate.cfg.xml que está en la ruta:

  • DVDStore
    • Source Packages
      • <paquete predeterminado>
        • hibernate.cfg.xml

Una vez abierto en NetBeans IDE, en el modo Diseño, en la parte Propiedades opcionales, Propiedades de configuración, pon Agregar... y añade la propiedad:

Nombre de la propiedad:hibernate.show_sql
Valor de la propiedad:true

Com el asistente de diseño de XML de configuración en NetBeans IDE se puede seleccionar los valores en una lista desplegable sin tocar el código XML.

Dentro de las mismas Propiedades opcionales del modo Diseño de NetBeans IDE, más abajo, en Propiedades varias, Agrega la propiedad:

Nombre de la propiedad:hibernate.current_session_context_class
Valor de la propiedad:thread

Después de esto, si ves el archivo hibernate.cfg.xml en el modo Operador XML, quedaría un código más o menos así, pero con otras contraseñas, por supuesto:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
  <session-factory>
    <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
    <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
    <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/sakila</property>
    <property name="hibernate.connection.username">root</property>
    <property name="hibernate.connection.password">######</property>
    <property name="hibernate.current_session_context_class">thread</property>
    <property name="hibernate.show_sql">true</property>
  </session-factory>
</hibernate-configuration>

Guarda los cambios del archivo.

Asistente de ingeniería inversa de Hibernate

Agreguemos un nuevo archivo, y seleccionemos Hibernar > Asistente de ingeniería inversa de Hibernate

Asistente para la creación de nuevo archivo en NetBeans IDE. El primer paso es seleccionar la categoría Hibernar y luego, en la columna derecha, Asistente de ingeniería inversa de Hibernate

En el siguiente paso dejamos los valores por defecto (asegurándonos que pertenezca al proyecto DVDStore). Siguiente.

Se cargarán automáticamente los nombres de las tablas de la base de datos Sakila (elegida cuando creamos el proyecto), y seleccionaremos las tablas:

  • actor
  • category
  • film
  • film_actor
  • film_category
  • language

Asistente de ingeniería inversa de Hibernate tiene 2 columnas: Tablas disponibles (con las tablas en la base de datos) y Tablas seleccionadas (con las tablas que se utilizarán en el proyecto)
Para pasar tablas de una columnas a la otra, existen los botones Agregar > y < Eliminar.

Si marcas la casilla Incluir tablas relacionadas algunas otras tablas se incluirán automáticamente. Eso está bien.

Al presionar Terminar se va a crear un archivo hibernate.reveng.xml parecido a este:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-reverse-engineering PUBLIC "-//Hibernate/Hibernate Reverse Engineering DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-reverse-engineering-3.0.dtd">
<hibernate-reverse-engineering>
  <schema-selection match-catalog="sakila"/>
  <table-filter match-name="film"/>
  <table-filter match-name="language"/>
  <table-filter match-name="category"/>
  <table-filter match-name="actor"/>
  <table-filter match-name="film_category"/>
  <table-filter match-name="film_actor"/>
</hibernate-reverse-engineering>

No lo edites, cíerralo.

Archivos de mapas de Hibernate y POJOs de la base de datos

Crea un Archivo nuevo.... Categoría Hibernar, tipo Archivos de mapas de Hibernate y POJOs de la base de datos.

Asistente para la creación de nuevo archivo en NetBeans IDE. El primer paso es seleccionar la categoría Hibernar y luego, en la columna derecha, Archivos de mapas de Hibernate y POJOs de la base de datos.

Debes ponerle nombre a la clase donde estarán los mapas de Hibernate. Para que este tutorial funcione, ponle dvdrental

En el asistente de nuevo archivo de mapas de Hibernate y POJOs de la base de datos, en el paso de la Generación de código, debes seleccionar hibernate.cfg.xml como archivo de configuración de Hibernate e hibernate.reveng.xml como archivo de ingeniería inversa.

...y presiona el botón Terminar. Se deberían generar los siguientes archivos:

  • DVDStore
    • Source Packages
      • dvdrental
        • Actor.hbm.xml
        • Actor.java
        • Category.hbm.xml
        • Category.java
        • Film.hbm.xml
        • Film.java
        • FilmActor.hbm.xml
        • FilmActor.java
        • FilmActorId.java
        • FilmCategory.hbm.xml
        • FilmCategory.java
        • FilmCategoryId.java
        • Language.hbm.xml
        • Language.java

HibernateUtil.java

Lo último sería crear un Archivo Nuevo..., Categoría Hibernar, Tipo HibernateUtil.java.

Al crear el nuevo archivo HibernateUtil.java, en el asistente de nuevo archivo de NetBeans IDE, por favor fijarse en cambiar el nombre del archivo propuesto NewHibernateUtil por HibernateUtil sin el New y agregarlo al paquete dvdrental
Terminar

Empecemos

FilmHelper.java

Crea una nueva Clase Java (Archivo Nuevo..., Categoría Java, tipo Clase Java) y llámale FilmHelper y ponlo dentro del paquete dvdrental.

Creación de una nueva clase Java llamada FilmHelper en el paquete dvdrental utilizando NetBeans IDE 6.8 para Mac OS X.
Terminar

Agrega el siguiente código (en negrita) para crear una sesión de Hibernate.

package dvdrental;

public class FilmHelper {

 Session session = null;

 public FilmHelper() {
  this.session = HibernateUtil.getSessionFactory().getCurrentSession();
 }

}

Van a aparecer unos errores que se arreglan usando el menú contextual (botón secundario del ratón) y eligiendo la opción Reparar importaciones.

Menú contextual aparece cuando uno hace click sobre el código fuente.
La opción Reparar importaciones en inglés se llama Fix import.

Si dejamos el valor por defecto, quedaría el código de FilmHelper.java de la siguiente manera:

package dvdrental;

import org.hibernate.Session;

public class FilmHelper {

 Session session = null;

 public FilmHelper() {
  this.session = HibernateUtil.getSessionFactory().getCurrentSession();
 }
 
}

Consultas HQL

Probemos algo. Haz click con el botón secundario sobre el archivo Source Packages/<paquete predeterminado>/hibernate.cfg.xml y selecciona la opción Ejecutar la consulta HQL del menú contextual.

Opción Ejecutar la consulta HQL aparece en el menú contextual sólo cuando se hace click sobre el archivo hibernate.cfg.xml.

En la barra de herramientas del editor de consultas HQL, selecciona la Sesión: hibernate.cfg y escribe lo siguiente en la primera caja de texto grande:

from Film

Y presiona el botón que está al lado de la lista desplegable que debería estar mostrando hibernate.cfg como opción seleccionada para hacer la consulta y obtener un resultado como el siguiente.

El Editor de consultas HQL de NetBeans IDE tiene dos partes: arriba para escribir la consultas y abajo para mostrar los resultados o equivalente SQL.

Ahora prueba con una consulta un poquito más complicada:

from Film as film where film.filmId between 100 and 200

Con las consultas probadas, obteniendo los resultados esperados, podemos utilizarlas en la clase helper.

Hibernate

Edita el archivo FilmHelper.java y agrégale el siguiente método (marcado con negrita):

package dvdrental;

import java.util.List;
import org.hibernate.Query;
import org.hibernate.Session;

public class FilmHelper {

 Session session = null;

 public FilmHelper() {
  this.session = HibernateUtil.getSessionFactory().getCurrentSession();
 }
 
  // Obtiene los films donde el id de film está entre un cierto rango especificado por las variables startID y endID

 public List getFilmTitles(int startID, int endID) {
  List<Film> filmList = null;
  try {
   org.hibernate.Transaction tx = session.beginTransaction();
   Query q = session.createQuery("from Film as film where film.filmId between '" + startID + "' and '" + endID + "'");
   filmList = (List<Film>) q.list();
  } catch (Exception e) {
   e.printStackTrace();
  }
  return filmList;
 }
 
}

Repara las importaciones (Fix Import) si es necesario, utilizando las clases de hibernate.

Asistente para reparar importaciones en el código Java de NetBeans IDE. Instrucciones Import: Query org.hibernate.Query y List java.util.List.

para que quede completamente igual como está en el código de ejemplo anterior.

Vuelve a abrir el Editor de Consultas HQL y haz la siguiente query:

from Actor as actor where actor.actorId in (select filmActor.actor.actorId from FilmActor as filmActor where filmActor.film.filmId='10')

Esta consulta obtiene los actores de la película del filmid = 10.

Ahora súmale el siguiente método (marcado con negrita) al archivo FilmHelper.java:

package dvdrental;

import java.util.List;
import org.hibernate.Query;
import org.hibernate.Session;

public class FilmHelper {

 Session session = null;

 public FilmHelper() {
  this.session = HibernateUtil.getSessionFactory().getCurrentSession();
 }

  // Obtiene los films donde el id de film está entre un cierto rango especificado por las variables startID y endID

 public List getFilmTitles(int startID, int endID) {
  List<Film> filmList = null;
  try {
   org.hibernate.Transaction tx = session.beginTransaction();
   Query q = session.createQuery("from Film as film where film.filmId between '" + startID + "' and '" + endID + "'");
   filmList = (List<Film>) q.list();
  } catch (Exception e) {
   e.printStackTrace();
  }
  return filmList;
 }

 // Obtiene los actores en un film particular
 public List getActorsByID(int filmId) {
  List<Actor> actorList = null;
  try {
   org.hibernate.Transaction tx = session.beginTransaction();
   Query q = session.createQuery("from Actor as actor where actor.actorId in (select filmActor.actor.actorId from FilmActor as filmActor where filmActor.film.filmId='" + filmId + "')");
   actorList = (List<Actor>) q.list();

  } catch (Exception e) {
   e.printStackTrace();
  }

  return actorList;
 }

}

Repara las importaciones si es necesario. Guarda.

Ahora agrega 3 métodos más y algunos métodos que inventé para hacer uso de beans en el código JSPX.


package dvdrental;

import java.util.List;
import org.hibernate.Query;
import org.hibernate.Session;

public class FilmHelper {

 Session session = null;

 public FilmHelper() {
  this.session = HibernateUtil.getSessionFactory().getCurrentSession();
 }

 // Obtiene los films donde el id de film está entre un cierto rango especificado por las variables startID y endID
 public List getFilmTitles(int startID, int endID) {
  List<Film> filmList = null;
  try {
   org.hibernate.Transaction tx = session.beginTransaction();
   Query q = session.createQuery("from Film as film where film.filmId between '" + startID + "' and '" + endID + "'");
   filmList = (List<Film>) q.list();
  } catch (Exception e) {
   e.printStackTrace();
  }
  return filmList;
 }

 // Obtiene los actores en un film particular
 public List getActorsByID(int filmId) {
  List<Actor> actorList = null;
  try {
   org.hibernate.Transaction tx = session.beginTransaction();
   Query q = session.createQuery("from Actor as actor where actor.actorId in (select filmActor.actor.actorId from FilmActor as filmActor where filmActor.film.filmId='" + filmId + "')");
   actorList = (List<Actor>) q.list();

  } catch (Exception e) {
   e.printStackTrace();
  }

  return actorList;
 }

 // Obtiene una lista de categorías de acuerdo al filmId
 public Category getCategoryByID(int filmId) {
  List<Category> categoryList = null;
  try {
   org.hibernate.Transaction tx = session.beginTransaction();
   Query q = session.createQuery("from Category as category where category.categoryId in (select filmCat.category.categoryId from FilmCategory as filmCat where filmCat.film.filmId='" + filmId + "')");
   categoryList = (List<Category>) q.list();

  } catch (Exception e) {
   e.printStackTrace();
  }

  return categoryList.get(0);
 }

 // Obtiene un solo film de acuerdo al filmId
 public Film getFilmByID(int filmId) {

  Film film = null;

  try {
   org.hibernate.Transaction tx = session.beginTransaction();
   Query q = session.createQuery("from Film as film where film.filmId=" + filmId);
   film = (Film) q.uniqueResult();
  } catch (Exception e) {
   e.printStackTrace();
  }

  return film;
 }


 // Obtiene el idioma del film de acuerdo a un langId
 public String getLangByID(int langId) {

  Language language = null;

  try{
   org.hibernate.Transaction tx = session.beginTransaction();
   Query q = session.createQuery("from Language as lang where lang.languageId=" + langId);
   language = (Language) q.uniqueResult();
  } catch (Exception e) {
   e.printStackTrace();
  }

  return language.getName();
 }



 //Métodos para no poner código java en el JSPX
 int startId;
 int endId;

 int filmId;
 int langId;

 public void setAttributeStartID(int startId){
  this.startId = startId;
 }
 public void setAttributeEndID(int endId){
  this.endId = endId;
 }
 public List getFilmTitlesList(){
  return getFilmTitles(startId,endId);
 }
 public void setAttributeFilmByID(int filmId){
  this.filmId = filmId;
 }
 public List getActorsID() {
  return getActorsByID(filmId);
 }
 public Category getCategoryID() {
  return getCategoryByID(filmId);
 }
 public Film getFilmID() {
  return getFilmByID(filmId);
 }
 public void setAttributeLangByID(int langId){
  this.langId = langId;
 }
 public String getLangID() {
  return getLangByID(langId);
 }

}

JSP(X)

Hurra. Por fin llegamos a las páginas web. A mi me gusta escribir las JSP con JSTL así que antes de seguir deberás...

Instalar la librería JSTL

  1. Entra a las propiedades del proyecto (botón secundario del ratón sobre DVDStore, opción Propiedades).

  2. En la sección Libraries hay un botón Add Library.... Presiónalo.
  3. Si no te aparece inmediatamente, Importa la librería (o biblioteca también le llaman) JSTL 1.1 o superior.
  4. Selecciona la librería JSTL y añádela al proyecto.
  5. Aceptar

Nuevo archivo JSPX

Primero elimina el archivo index.jsp del proyecto que está en la carpeta DVDStore/Web Pages/ del proyecto.

Agrega un nuevo archivo JSPX y cambia la página por defecto.

En el archivo index.jspx escribe lo siguiente.

index.jspx
<?xml version="1.0" encoding="UTF-8"?>
<!-- 
    Document   : index
    Created on : 01-dic-2009, 21:43:16
    Author     : ooscarr
-->
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" version="2.0"
     xmlns:c="http://java.sun.com/jsp/jstl/core"
     xmlns:fn="http://java.sun.com/jsp/jstl/functions">

 <jsp:output
  omit-xml-declaration="no"
  doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN"
  doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"
  doctype-root-element="html"/>

 <jsp:directive.page import="dvdrental.*"/>
 <jsp:directive.page import="java.util.List"/>

 <jsp:directive.page contentType="text/html" pageEncoding="UTF-8"/>

 <html xmlns="http://www.w3.org/1999/xhtml">
  <head>
   <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
   <title>DVDStore</title>
  </head>
  <body>


   <c:set var="startID" value="1"/>
   <c:set var="endID" value="10"/>
   <c:set var="prev_startID" value="1"/>
   <c:set var="prev_endID" value="10"/>
   
   <c:set var="FILM_RECORD_COUNT" value="1000"/>
   
   <c:set var="RECORD_START_PAGE" value="false"/>
   <c:set var="RECORD_END_PAGE" value="false"/>


   <c:if test="${not empty param.startid}">
    <c:set var="startID" value="${param.startid}"/>
   </c:if>
   <c:if test="${not empty param.endid}">
    <c:set var="endID" value="${param.endid}"/>
   </c:if>

   <jsp:useBean id="helper" class="dvdrental.FilmHelper"/>
   <!-- Invento para pasar filmID a FilmHelper.java -->
   <c:set target="${helper}" property="attributeStartID" value="${startID}"/>
   <c:set target="${helper}" property="attributeEndID" value="${endID}"/>
   <c:set var="filmTitles" value="${helper.filmTitlesList}"/>

   <c:if test="${startID == 1}">
    <c:set var="RECORD_START_PAGE" value="true"/>
   </c:if>
   <c:if test="${endID == FILM_RECORD_COUNT}">
    <c:set var="RECORD_END_PAGE" value="true"/>
   </c:if>

   <c:set var="prev_startID" value="${startID-10}"/>
   <c:set var="prev_endID" value="${endID-10}"/>

   <c:set var="startID" value="${endID+1}"/>
   <c:set var="endID" value="${endID+10}"/>

   <c:set var="filmTitlesSize" value="${fn:length(filmTitles)}"/>


   <table>
    <thead>

     <c:choose>
      <c:when test="${RECORD_START_PAGE}">
       <tr>
        <td class="NEXT"> </td>
        <td class="NEXT"> </td>
        <td class="NEXT"> </td>
        <td class="NEXT">
         <a class="NEXT" href="index.jspx?startid=${startID}&amp;endid=${endID}">Next</a></td>
       </tr>
      </c:when>
      <c:when test="${RECORD_END_PAGE}">
       <tr>
        <td class="NEXT"> </td>
        <td class="NEXT"> </td>
        <td class="NEXT">
         <a class="NEXT" href="index.jspx?startid=${prev_startID}&amp;endid=${prev_endID}">Prev</a></td>
        <td class="NEXT"> </td>
       </tr>
      </c:when>
      <c:otherwise>
       <tr>
        <td class="NEXT"> </td>
        <td class="NEXT"> </td>
        <td class="NEXT">
         <a class="NEXT" href="index.jspx?startid=${prev_startID}&amp;endid=${prev_endID}">Prev</a></td>
        <td class="NEXT">
         <a class="NEXT" href="index.jspx?startid=${startID}&amp;endid=${endID}">Next</a>
        </td>
       </tr>
      </c:otherwise>
     </c:choose>


     <tr><th>Title</th><th>Description</th><th> </th><th> </th></tr>
    </thead>
    <tbody>

     <c:forEach items="${filmTitles}" var="i">
      <c:set var="film" value="${i}"/>
      <c:set var="filmID" value="${film.filmId}"/>
      <tr>
       <td class="COL1">
        <a href="browse.jspx?id=${filmID}">${film.title}</a>
       </td>
       <td class="COL2">${film.description}</td>
       <td class="COL2">
        <a href="browse.jspx?id=${filmID}">More</a>
       </td>
       <td class="COL2">
        <a href="rent.jsp?id=${filmID}">Rent</a>
       </td>
      </tr>
     </c:forEach>

    </tbody>
   </table>

  </body>
 </html>
</jsp:root>

Ya lo puedes hacer correr si quieres probarlo.

Si no te gusta escribir las JSP con JSTL, puedes revisar el código oficial donde está lo mismo pero en clave Java clásico.

browse.jspx

Crea otro archivo JSP con sintaxis XML que se llame browse con el siguiente código:

<?xml version="1.0" encoding="UTF-8"?>
<!-- 
    Document   : browse
    Created on : 29-nov-2009, 19:47:16
    Author     : ooscarr
-->
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" version="2.0"
     xmlns:c="http://java.sun.com/jsp/jstl/core">

 <jsp:output
  omit-xml-declaration="no"
  doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN"
  doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"
  doctype-root-element="html"/>

 <jsp:directive.page import="dvdrental.*"/>
 <jsp:directive.page import="java.util.List"/>

 <jsp:directive.page contentType="text/html" pageEncoding="UTF-8"/>

 <html xmlns="http://www.w3.org/1999/xhtml">
  <head>
   <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
   <title>DVDStore browse</title>
  </head>
  <body>
   
   <!-- Get title ID -->
   <c:set var="filmID" value="1"/>
   <c:if test="${not empty param.id}">
    <c:set var="filmID" value="${param.id}"/>
   </c:if>

   <c:set var="startPage">false</c:set>
   <c:set var="endPage">false</c:set>

   <jsp:useBean id="helper" class="dvdrental.FilmHelper"/>

   <!-- Invento para pasar filmID a FilmHelper.java -->
   <c:set target="${helper}" property="attributeFilmByID" value="${filmID}"/>

   <jsp:useBean id="film" class="dvdrental.Film"/>
   <c:set var="film" value="${helper.filmID}"/>

   <c:set var="filmTitle" value="${film.title}"/>
   <c:set var="filmDescription" value="${film.description}"/>

   <!-- Get Actors -->
   <c:set var="actors" value="${helper.actorsID}"/>

   <!-- Get Category -->
   <c:set var="category" value="${helper.categoryID}"/>

   <c:set var="totalCast">
    <c:forEach items="${actors}" var="i">${i.firstName} ${i.lastName}, </c:forEach>
   </c:set>

   <!-- Invento para pasar langId a FilmHelper.java -->
   <c:set target="${helper}" property="attributeLangByID" value="${film.languageByLanguageId.languageId}"/>
   <!-- Obtiene el idioma usando el langId entregado anteriormente -->
   <c:set var="language" value="${helper.langID}"/>
   
   <c:set var="filmLength" value="${film.length}"/>
   <c:set var="filmRating" value="${film.rating}"/>
   <c:set var="filmYear" value="${film.releaseYear}"/>
   
   <c:set var="rentalDuration" value="${film.rentalDuration}"/>
   <c:set var="rentalRate" value="${film.rentalRate}"/>

   <c:set var="specialFeatures" value="${film.specialFeatures}"/>


   <table>
    <tr>
     <td class="RENT"> </td>
     <td class="RENT">
      <a class="RENT" href="browse.jspx?id=${filmID}">Arrendar</a>
     </td>
    </tr>
    <tr>
     <th class="TITLE">Título</th>
     <td class="TITLE">${filmTitle}</td>
    </tr>
    <tr>
     <th class="COL1">Descripción</th>
     <td class="COL2">${filmDescription}</td>
    </tr>
    <tr>
     <td class="COL1"> </td>
     <td class="COL2"> </td>
    </tr>
    <tr>
     <th class="COL1">Género</th>
     <td class="COL2">${catName}</td>
    </tr>
    <tr>
     <td class="COL1"> </td>
     <td class="COL2"> </td>
    </tr>
    <tr>
     <th class="COL1">Reparto</th>
     <td class="COL2">${totalCast}</td>
    </tr>
    <tr>
     <td class="COL1"> </td>
     <td class="COL2"> </td>
    </tr>
    <tr>
     <th class="COL1">Duración de la película</th>
     <td class="COL2">${filmLength} <abbr title="minutos">mins.</abbr></td>
    </tr>
    <tr>
     <th class="COL1">Idioma</th>
     <td class="COL2">${language}</td>
    </tr>
    <tr>
     <th class="COL1">Calificación cinematográfica</th>
     <td class="COL2">${filmRating}</td>
    </tr>
    <tr>
     <th class="COL1">Año</th>
     <td class="COL2">${filmYear}</td>
    </tr>
    <tr>
     <td class="COL1"> </td>
     <td class="COL2"> </td>
    </tr>
    <tr>
     <th class="COL1">Características especiales</th>
     <td class="SPECIAL">${specialFeatures}</td>
    </tr>
    <tr>
     <td class="COL1"> </td>
     <td class="COL2"> </td>
    </tr>
    <tr>
     <th class="COL1">Precio Arriendo</th>
     <td class="COL2">US$ ${rentalRate}</td>
    </tr>
    <tr>
     <th class="COL1">Duración de Arriendo</th>
     <td class="COL2">${rentalDuration} días</td>
    </tr>
   </table>

   </body>
 </html>
</jsp:root>

Ejecuta la aplicación en el servidor, visita la página (en mi máquina se está ejecutando en http://localhost:8084/DVDStore/) y Voilà!. Funciona o te equivocaste.

Conclusiones

A pesar de que este ejemplo sólo hace lecturas a la base de datos, me puedo imaginar que escribir debe ser igual de sencillo ya que autmáticamente se generan la clases bean para insertar valores (set).

Con la persistencia ya no me tengo que preocupar de hacer la conexión y desconexión en cada página, la contraseña está guardada de manera más segura y estandarizada, y el acceso a las tablas se hace sencillo manejándolas como clases (un editor de Java ayuda a completar el código) y no andan las consultas SQL dando vueltas por todo el código, lo que ofrece mayor abstracción entre los distintos niveles del código otorgando flexibilidad a cambios.

Si se me ocurre cambiar la base de datos (agregar una columna, modificar el nombre de una tabla o las relaciones y tipos de datos), habría que regenerar los mapas de Hibernate y volver a compilar todo.

También, si nos fijamos en todo lo que hay que saber sobre programación Java, se hace más difícil la programación, aunque supongo y reconozco que con el tiempo se debe volver más sencillo con la experiencia. Obvio.

Mejoras en el rendimiento... No sé. Supongo que al tener todas las consultas guardadas... Y con Hibernate preocupado de las conexiones y desconexiones... Me imagino que sí hay alguna mejora, lo desconozco.

En el próximo capítulo...

Esto fue cómo leer desde una base de datos con Hibernate, prometo escribir sobre cómo escribir en la base de datos usando Hibernate y hacer este mismo ejemplo usando JPA, que ya lo tengo hecho, pero hay que desarrollarlo y darle formato. Hasta el próximo mes.

Referencias

Artículos relacionados

Etiquetas: , , , ,

El Modelo OSI y el error de capa 8 ¬¬

29.11.09. Por ooscarr (ooscarr)

Sería bueno explicar qué significa el chistecito del error de capa 8 para los incautos. Lo sospechaba. Aprovecho aquí también de conocer los otros 7 niveles.

El chistecito del error de capa 8 viene del Modelo OSI, que de acuerdo a Wikipedia:

El modelo de referencia de Interconexión de Sistemas Abiertos (OSI, Open System Interconnection) fue el modelo de red descriptivo creado por la Organización Internacional para la Estandarización lanzado en 1984. Es decir, fue un marco de referencia para la definición de arquitecturas de interconexión de sistemas de comunicaciones.

7 capas del modelo OSI

Este modelo presenta 7 capas:

Pirámide que enumera las 7 capas del modelo OSI con el Nivel Físico (Capa 1) como base y el Nivel de Aplicación (Capa 7) en la parte superior.

Capa 1: Capa Física
Se refiere al medio físico de transporte como los cables o las ondas, por ejemplo. O también en el proceso de codificar la información para adaptarla al medio físico en que se transportará.
Capa 2: Capa de enlace de datos
La capa de enlace de datos se ocupa del direccionamiento físico, de la topología de la red, del acceso a la red, de la notificación de errores, de la distribución ordenada de tramas y del control del flujo.
Capa 3: Capa de red
Los enrutadores o routers y/o la saturación de la red o paquete.
Capa 4: Capa de transporte
Es cuando se dividen la información en paquetes para ser transportados por la Capa 3, la que se encarga de la comunicación independientemente del medio de transporte de la información.
Capa 5: Capa de sesión
Esta capa es la que se encarga de mantener y controlar el diálogo establecido entre los dos computadores que estan transmitiendo datos de cualquier índole.
Capa 6: Capa de presentación
El objetivo de la capa de presentación es encargarse de la representación de la información, de manera que aunque distintos equipos puedan tener diferentes representaciones internas de caracteres (ASCII, Unicode, EBCDIC), números (little-endian tipo Intel, big-endian tipo Motorola), sonido o imágenes, los datos lleguen de manera reconocible.
Capa 7: Capa de aplicación
Son los protocolos de comunicación como HTTP, SSH, POP, etc. Cabe aclarar que el usuario normalmente no interactúa directamente con el nivel de aplicación. Suele interactuar con programas que a su vez interactúan con el nivel de aplicación pero ocultando la complejidad subyacente.

Error de capa 8 y capa 9

La capa 8 es un chiste usado para referiste a una capa "usuario" o "política" inexistente. Error Capa 8: Error del Usuario. También es conocido como el "error 505" entre el teclado y el asiento. O sea, tú.

"Error Capa 9: Error del Jefe del Usuario" XD

Nerd.

Referencias

Etiquetas: ,

Ejecutar Chrome OS como si fuera otra aplicación (abrir imagen VMWare en VirtualBox)

25.11.09. Por ooscarr (ooscarr)

Con el software opensource de virtualización de escritorios VirtualBox puedes probar ya el nuevo sistema operativo de Google completamente gratis y fácilmente en una ventana gracias a la posibilidad de abrir imágenes VMWware. Jump the shark.

Pestaña de aplicaciones de Chrome OS corriendo en VirtualBox.

Instrucciones

  1. Consigue una imagen de Chrome OS ya compilada (anda una imagen VMware dando vueltas por ahí).

  2. Mueve el archivo .vmdk a una carpeta personal, por ejemplo Mis documentos o ~/Library/VirtualBox/HardDisks o en cualquier lugar que no se te pierda.

  3. Abre VirtualBox y agrega una Máquina > Nueva...

  4. Dale un nombre, en Sistema Operativo elije la opción Linux y en Versión: Other Linux. Siguiente.

    Asistente de Nueva máquina virtual en VirtualBox.

  5. Especifica la memoria base. 256MB es lo mínimo recomendado, luego Siguiente.

  6. En el paso siguiente, marca la casilla de Disco duro de arranque (Primario maestro), selecciona la opción Usar un disco duro existente y busca la imagen VMWare de Chrome OS que conseguiste.

    Agregando imagen VMWare dentro del asistente de Nueva máquina virtual en VirtualBox.

  7. Siguiente. Terminar. Ahora selecciona la nueva máquina virtual de la lista que tiene VirtualBox e iníciala.

  8. Ahí está. El usuario y contraseña son los mismos que usas para iniciar sesión en Google.

También puedes bootear Chrome OS desde un pendrive USB si lo vas a usar en serio para obtener un rendimiento mayor y ver por qué es una buena idea.

Bonus track

Probé el WebOS de Palm en VirtualBox

En http://developer.palm.com/ puedes descargar, previo registro, un instalador del WebOS (el sistema operativo que usa el Palm pre) para VirtualBox gratis también para jugar.

Referencias

Relacionados

Etiquetas: , , , ,

Importar la base de datos de ejemplo MySQL (Sakila DB Schema) con Netbeans IDE es fácil

22.11.09. Por ooscarr (ooscarr)

Con el plug-in de Netbeans, instalar la base de datos de ejemplo Sakila es sencillo (con los permisos adecuados).

La base de datos de ejemplo Sakila fue desarrollada por Mike Hillyer, un pasado miembro del equipo de documentación de MySQL AB, y su intención fue la de proveer un esquema estándar que pueda ser usado para ejemplos en libros, tutoriales, artículos, ejemplos, y así en adelante. La base de datos de ejemplo Sakila también sirve para destacar las últimas características de MySQL tales como Vistas, Procedimientos Almacenados, y Disparadores (Triggers).

Scheme de Sakila DB hecho con MySQL Workbench
Diagrama relacional del Scheme de Sakila DB (ver más grande)

Instalar el plug-in de Sakila es muy sencillo, sólo vamos al menú Herramientas > Complementos, en el panel que se abre, existe una sección llamada Plugins disponibles.

En el panel de Plugins de Netbeans IDE, hay que marcar la opción Sakila Sample Database

Para instalarlo, espera que cargue la larga lista de plugins disponibles, marca la casilla de Sakila Sample Database y presiona el botón Instalar que está debajo. Sigue el proceso de instalación, reinicia Netbeans IDE si es necesario.

Luego, en el panel de Servicios (si no lo ves, elije el menú Ventana > Prestaciones) y con el menú contextual sobre la parte de Bases de datos elije la opción Registrar servidor MySQL... si todavía no lo has hecho.

Ya, cuando te hayas conectado al servidor MySQL como root u otra cuenta con los suficientes permisos, selecciónala y en el menú contextual elije la nueva opción Crear base de datos....

Para crear base de datos MySQL con el plugin de Sakila de Netbeans IDE se necesita tener permisos de root.

En el cuadro de diálogo que se abre, seleccionamos sakila en el primer campo, y en el segundo seleccionamos al usuario que tendrá todo el poder sobre esta nueva base de datos.

En el cuadro de diálogo de Crear nueva base de datos MySQL en Netbeans IDE, también aparecen otras bases de datos de ejemplo además de Sakila.

Ya, con eso, después de esperar que se creen todas las tablas y se llenen de datos de ejemplo, podemos conectarnos y empezar a utilizarlas para nuestros experimentos y lecciones.

Netbeans IDE cuenta con un administrador de bases de datos basado en ODBC.

Referencias

Etiquetas: , , , ,

Exportar a formato Excel con JSP de forma bien básica (TSV)

19.8.09. Por ooscarr (ooscarr)

En este tutorial voy a mostrar la manera de generar un archivo compatible con Microsoft Excel que consiste en valores separados por espacios tabuladores muy elemental y sin gráficos o estilos.

Primero hay que partir diciendo qué opciones de formatos compatibles con Excel hay:

CSV
Es el más conocido, y consiste en un archivo de texto plano, generalmente con la extención .csv donde los valores de cada celda van separados por una coma (,) en el caso de las columnas, y un salto de línea para las filas.
TSV
TSV es una variación al anterior CSV, donde las columnas van separadas por un espacio tabulador y las filas por un salto de línea. Esto permite que se puedan introducir celdas con valores que incluyan una coma y no sean confundidas por un salto de columna, por ejemplo.
XLS
El formato Microsoft Excel tradicional que es un archivo binario para Windows donde se guardan en hojas, gráficos y macros. Mucho tiempo su especificación fue cerrada, pero desde que se comenzó a documentar el formato con ingeniería inversa y debido a las presiones de las cortes para aceptar su formato como un estándar, Microsoft se vio obligada a publicar su estructura de funcionamiento bien particular.
XLSX
Esta es la última especificación Office Open XML que Microsoft no sé cómo logró estandarizar, y que consiste en una serie de archivos XML y otros, organizados en una carpeta todo comprimido en un archivo ZIP (al igual que el estándar OpenDocument que también es soportado por Microsoft Office 2007 pero que no es de propiedad de Microsoft).

Y otros más menos importantes.

El formato

El formato que voy a exportar desde el JSP en este tutorial es el TSV, pero con extensión .xls. El archivo siguiente:

Uno Due Tre Quattro
Uno Dos Tres Cuatro
One Two
Ichi Ni San Shi
Odin Dva Tri

Se vería así en la planilla de cálculo:

ABCD
1UnoDueTreQuattro
2UnoDosTresCuatro
3OneTwo
4IchiNiSanShi
5OdinDvaTri
Más información »

Etiquetas: , , , , ,

Cómo instalar un .war en Tomcat desde la página Tomcat manager

2.8.09. Por ooscarr (ooscarr)

Para una tarea tuve que hacer un manual de cómo instalar un archivo .war en el viejo Tomcat. Super sencillo y básico, no se asombren.

No a la guerra.

En computación, un archivo WAR (el cual sus iníciales significan “archivo de aplicación web”) es un archivo JAR utilizado para distribuir una colección de JavaServer Pages, servlets, clases Java, archivos XML, librerías de tags y páginas web estáticas (HTML y archivos relacionados) que juntos constituyen una aplicación web.

1. Abrir la página inicial de Tomcat

Tomcat manager viene incluido con tomcat cuando se instala, para acceder a él se ingresa a la página del servidor donde está ejecutándose Tomcat.

Página de $CATALINA_HOME de Tomcat desplegándose en la URL http://localhost:2348/
Usualmente se encuentra en el http://localhost:8080/ o en el mismo puerto del servidor externo.

2. Entrar a Tomcat Manager

En dicha página en la columna de Administración izquierda se encuentra un link a Tomcat Manager

Inmediatamente preguntará por la constraseña, que por defecto es

usuario
tomcat
contraseña
tomcat

Página de Tomcat Manager típica
En la página de Tomcat manager, aparece una lista con todas las aplicaciónes que Tomcat tiene instaladas o ejecutándose (incluyéndose a sí mismo).

3. Desplegar aplicación

Casi al final, aparece un formulario llamado Desplegar.

En la parte donde dice Seleccione archivo WAR a deplegar, es cosa se buscar el archivo WAR, subirlo al servidor con el botón Desplegar...

Formulario de despliegue de aplicación web en Tomcat Manager típico

Y si no había otra aplicación con el mismo contexto, debería aparecer un mensaje de OK en la parte superior de la página al terminar la transferencia.

Advertencias

  • No todos los .war funcionan en todas las versiones de Tomcat

Referencias

Etiquetas: , ,

Accediendo a otro servidor de una red por SSH

27.7.09. Por ooscarr (ooscarr)

Terminal

El comando para conectarse al puerto del servidor que está en la Universidad y que no podemos acceder desde la casa (a través de otro servidor dentro de la red).

Primer caso

En la Universidad tenemos un servidor Windows 2003 que ofrece el servicio de Tomcat por el puerto 8080 como es normal. http://serrano.unap.cl:8080/. Pero la red de la Universidad tiene el puerto 8080 bloqueado para afuera de la U y no podemos conectarnos desde la casa.

Por medio de SSH, nosotros nos conectamos a otro servidor con Linux que está en la misma red, http://acinfo.unap.cl/, por el puerto de siempre, el 22, y a través de esta conexión accedemos al servidor del puerto 8080.

Diagrama con un notebook (localhost:2348) conectándose a un servidor con linux (acinfo.unap.cl:22) que a su vez está conectado a un servidor con Tomcat (serrano.unap.cl:8080)
Algo más o menos así.

En el Terminal, esto se hace con el comando

ssh ooscarr@acinfo.unap.cl -L 2348:serrano.unap.cl:8080

ó

ssh ooscarr@acinfo.unap.cl -L 2348/serrano.unap.cl/8080

Reemplazamos el ooscarr por tu nombre de usuario, por supuesto, y así accedemos al puerto 8080 del otro servidor como si estuviéramos en la mismísima U.

Página de $CATALINA_HOME de Tomcat desplegándose en la URL http://localhost:2348/
¡A través de la dirección http://localhost:2348/ accedemos a http://serrano.unap.cl:8080/!

Fin.

No, mentira. Segundo caso

Pongámonos en un segundo caso. Acaban de instalar Tomcat en el servidor acinfo.unap.cl:8080 así que ya no hace falta conectarse al serrano.unap.cl:8080. El puerto 8080 del servidor también está bloqueado,

Diagrama con un notebook (localhost:2348) conectándose a un servidor con linux (acinfo.unap.cl:8080)
Ahora sin el serrano.

En este caso, bastaría cambiar serrano.unap.cl por localhost ó acinfo.unap.cl así

ssh ooscarr@acinfo.unap.cl -L 2348:localhost:8080

ó

ssh ooscarr@acinfo.unap.cl -L 2348:acinfo.unap.cl:8080

ó

ssh ooscarr@acinfo.unap.cl -L 2348/acinfo.unap.cl/8080

ó

ssh -N -p 22 ooscarr@acinfo.unap.cl -L 2348:acinfo.unap.cl:8080

Y el mismo resultado.

Página de $CATALINA_HOME de Tomcat desplegándose en la URL http://localhost:2348/
¡A través de la dirección http://localhost:2348/ accedemos a http://acinfo.unap.cl:8080/.

Podríamos conectarnos a ambos servidores, utilizando un puerto distinto al 2348 para el segundo servidor.

¿Y si uso Windows, cómo lo hago?

Bueno, para esos casos, mi amigo Fernando (el Puma) hizo un mini-tutorial en su blog.

Referencias

Artículos relacionados

Etiquetas: , , , ,

Ejecuta otro sistema operativo en una ventana sin reiniciar con VirtualBox

9.4.09. Por ooscarr (ooscarr)

VirtualBox

Se me acabó el período de prueba de Parallels (US$79,99), y ya que también se estaba acabando el período de descarga de Windows 7 beta (12 de febrero de 2009), me animé a probar Virtualbox, un proyecto opensource para correr máquinas virtuales del que se estaba hablando harto en el mundo mac.

En un mundo dominado por el sistema operativo Windows, hay un par de cosas en las que dependo de este SO que son: un programa que me piden en la Universidad y que no puedo ejecutar con wine, y un pendrive que viene diseñado para ser reconocido sólo por Windows (debería cambiarlo).

Personalmente, participé en dos presentaciones de Virtualbox donde pude aclarar mis dudas por medio de demostraciones, haciendo preguntas y viendo las de otras personas, en vivo y en directo con los desarrolladores de Sun; y esa demostración me convenció de que Virtualbox es una herramienta ya madura que puedo considerar y recomendar aceptablemente. La uso a menudo.

windows 7 corriendo en VirtualBox bajo Mac OS X Leopard

Con respecto a las máquinas virtuales, hay algunas palabras claves que hay que conocer:

Máquina virtual
Es una manera especial de correr diferentes sistemas operativos dentro del SO que estás ocupando. Por ejemplo, dentro de una ventana como cualquier otro programa.
Host o Anfitrión
Se refiere al computador donde se está ejecutando Virtualbox, o sea, el real.
Guest o invitado
Corresponde a la máquina virtual. El sistema operativo invitado cree que se está ejecutando sobre hardware real y funciona.
Tecla anfitrión o Host key
Esto es específico de Virtualbox. Es la tecla o combinación de teclas para intercambiar el puntero y el teclado entre ambos sistemas operativos. Si aparece la palabra izquierda, significa que es la tecla del lado izquierdo, no siempre la flechita . Es un problema de traducción.
Más información »

Etiquetas: , , ,

Llegaron los APNG (y no son estándar)

1.3.09. Por ooscarr (ooscarr)

Animación de pelota rebotando

Pobre MNG... Un formato de animaciones basado en el PNG. Cuando salió nadie lo pescó. No estaba terminado cuando se masificó el GIF gracias a Netscape, a pesar de que era un formato propietario. Sin embargo, MNG poseía mayor compresión, transparencias alpha, y perfiles de color.

Ahora Mozilla Firefox 3 (cuyo motor se basó en el de Netscape) incluye soporte para los archivos APNG dejando botado al estándar MNG. La principal razón fue la menor cantidad de código en comparación con el MNG para poder implementarlo. Otra ventaja de APNG es que los archivos, de extensión PNG, se degradan agraciadamente en los navegadores que no soportan el formato. No así con el MNG, que hasta se está viendo la posibilidad de darle su propio MIME type.

Pero el APNG no es un estándar. Al principio se tuvo la inteción de definir una especificación simple para animaciones en PNG al estilo GIF, pero los desarrolladores de la época decidieron expandirlo a algo más complejo que resultó en un nuevo formato: el MNG.

Después llegaron unos turcos, estudiaron el PNG, inventaron la forma de incluir animación después de poner el primer frame como si fuera un PNG normal, mandaron un parche a Mozilla, a todos les gustó, se propuso como estándar, fue rechazado, el formato se quedó en el código y hoy (marzo de 2009) ya se encuentra implementado en los navegadores Mozilla Firefox 3 y Opera 9.5.

Así que ya llegaron. Estándar o no, sirven para pequeñas cosas como ese remolino animado que indica que algo está cargando, por ejemplo, donde no se sabe si el fondo será oscuro, claro, de color... Que todos hemos visto con bordes pixelados en formato GIF o con un cuadrado negro detrás para disimular la necesidad del canal alpha.

Cargando...

¿Llegará el MNG?

Eso todavía se discute.

(el APNG) es un MNG simplificado. Como la mitad de las costosas definiciones están fuera. Hay un soporte avanzado para disponer del método "restaurar-al-previo" del GIF. El "perfil de simplicidad" se fue.

¿Por qué existe este tema? MNG es un estándar inmensamente complicado que nadie quiere implementar. Encuentra a alguien que lo implemente y a alguien que le importe y con eso tendrás media posibilidad. Nada que se diga en este foro va a cambiar eso.

Y por los méritos del APNG, bueno, es extremadamente simple por una razón. La compatibilidad hacia atrás con PNGs no-animados era una meta admirable, pero si se empuja al APNG hacia un nuevo MIME y una nueva extensión de archivo, sería menos útil en la web, lo cual sería ciertamente una vergüenza. Dicho eso, esto es lo mismo que en cualquier debate de estandarización. De cualquier forma, nadie puede usar realmente esta cosa hasta alrededor de 5-10 años desde hoy, cuando existan múltiples implementaciones (si es que van a alguna parte).

Yo, por mi parte, no puedo utilizar MNG porque los navegadores no tienen soporte para el formato, no tengo un software donde pueda verlos (existe una compilación especial de Firefox que los muestra y plug-ins para otros navegadores), no está definido el MIME type aún (se está usando video/x-mng por ahora) y, sinceramente, no creo que llegue el día en que Internet Explorer no haga problemas con esto.

Este navegador no soporta MNG

Ejemplo de APNG

Como prueba de concepto, he convertido el logotipo animado de ElBlog al formato APNG. Por eso no se mueve ni en Safari ni en IE, al igual que la pelota saltarina que acompaña este post.

Referencias

Etiquetas: , ,

Editando el httpd.conf de Apache 2 para agregar un alias

14.12.08. Por ooscarr (ooscarr)

Digamos que en mi servidor Apache (versión 2) quiero que un usuario ya existente (http://acinfo.unap.cl/~ofernandez/) se pueda acceder también desde otra dirección (http://acinfo.unap.cl/ooscarr/). O cómo modificar el archivo de configuración de Apache httpd.conf en UNIX, BSD, Linux, OS X, etc.

Apache

Ubica al archivo httpd.conf

El archivo httpd.conf de Apache, si no lo conocen, es el archivo que contiene las opciones de configuración más importantes del servidor. Las líneas que comienzan por # son ignoradas al momento de ser leídas por el servidor.

En UNIX, Linux y Mac OS X Leopard, el archivo típicamente se encuentra en

/etc/apache2/httpd.conf

pero también podría estar en

/etc/httpd/conf/httpd.conf

o en

/usr/local/apache/conf/httpd.conf

según donde lo hayas instalado.

Modificar el archivo con los permisos de super usuario

El archivo debe estar bloqueado para su modificación por usuarios normales.

Una alternativa es usando el editor vi

El editor por líneas de comandos vi viene generalmente incorporado en todos los sistemas basados en UNIX, parece que es parte de la especificación.

Con él, basta acceder al archivo con permisos de súper usuario con el comando

sudo vi /etc/apache2/httpd.conf

o lo que sea, y poner la contraseña cuando la pida para abrir vi.

Una vez visualizado el archivo, con las teclas

i
se puede insertar texto.
esc
sirve para dejar de insertar y poder escribir comandos como
:w
(dos puntos doble ve) para guardar (escribir) el archivo
:q
(dos puntos qu) para salir de vi.

Los dos últimos comandos también se pueden juntar en uno solo tecleando :wq, por ejemplo.

Creando un alias

Se pueden agregar estas instrucciones en nuevas líneas al final del archivo, pero sería más ordenado buscar la sección encerrada por las etiquetas

<IfModule alias_module>
 #
...
 #
</IfModule>

y agregar el alias ahí dentro.

Se pueden usar expresiones regulares y otros trucos. Pero en mi caso sólo quiero que cuando llegue la dirección con /ooscarr el servidor Apache entregue lo que tiene el usuario /~ofernandez; por lo que mi instrucción sería...

En una configuración típica (llámese Linux)

Alias /ooscarr /home/ofernandez/public_html

En OS X

Alias /ooscarr /Users/ofernandez/Sites

Agregar una sección <Directory>

En los comentarios del archivo httpd.conf también dice:

 # Si incluye un / final en /rutaweb entonces el servidor
 # requerirá que esté presente en el URL.  También seguramente
 # necesitará proveer una sección <Directory> para permitir el acceso a
 # la ruta del sistema de archivos.

Reiniciar Apache

Para que todo esto tenga efecto, o para que se vuelva a leer el archivo httpd.conf con las nuevas modificaciones, habría que reiniciar el servidor.

En UNIX

sudo apache2ctl restart

En OS X

En Mac OS X, sólo hay que ir a las Preferencias, Compartir y apagar y volver prender la opción de Compartir web.

Voilà!

Ahora cuando entro a http://localhost/ooscarr/ puedo ver lo mismo que tengo en http://localhost/~ofernandez/ y todos sus subdirectorios.

Referencias

Etiquetas: ,

Configurar página en OpenOffice.org

1.11.08. Por ooscarr (ooscarr)

OpenOffice.org

Para los que vienen de Microsoft Office, se les hace difícil saber cómo cambiar el tamaño de la hoja en OOo.

Para cambiar el tamaño de la hoja, la orientación de las páginas o el color de fondo; sólo hay que ir al menú Formato > Página... y en la pestaña Página cambiar el menú desplegable Formato.

Mitad del panel de formato de página en OpenOffice.org para mac
El tamaño Carta se llama Letter.

Lamentablemente no sé elegir el tamaño de papel por defecto, viene predeterminado para tamaño Oficio, así que tengo que hacer esto cada vez que creo un documento nuevo. :-(

Artículos relacionados

Etiquetas: , ,

Tip: números de página en OpenOffice.org Writer

31.10.08. Por ooscarr (ooscarr)

OpenOffice.org

Siempre me lo preguntan, así que aquí voy: Cómo agregar los números de página automáticamente a un documento de texto en OOo. Y lo más importante: Cómo hacer que no aparezca el número de página en la primera plana para usarla de portada.

Pie de página

Normalmente, el número de página se pone en el pie de página o en la cabecera. Estos elemento se repiten en cada página automáticamente, así que primero vamos al menú Insertar > Pie de página > Predeterminado.

Menú Insertar, opción Pie de página Predeterminado

Y todo lo que vaya en esa parte de la página se repetirá en todas las páginas del documento.

Insertar el número de página

El número de página es tan simple como Insertar > Campos > Número de página. Este dato se puede insertar en cualquier parte, incluso dentro de un párrafo entre medio de texto.

Menú Insertar Campo Número de página

Y el número de la página aparecerá con fondo gris indicando que es un dato que se genera automáticamente. No se puede editar sino es por las opciones especiales para ese tipo de dato.

Número 1 oscurecido en la esquina inferior, cerca del borde de la hoja del documento que se está editando

Por la naturaleza del pie de página, esto se repetirá en todas las páginas cambiando el número de página automáticamente. Inclusive en la primera página.

Quitar el número de la 1ra plana

Bueno, y para no incluir el número de página en la primera página (o primera plana) solamente; hay que mostrar el panel de estilos, elegir el pequeño botoncito Estilos de página y hacer doble click en el estilo Primera página CUANDO SE TENGA EL CURSOR EN LA PRIMERA PÁGINA.

Para los cieguitos (sin ofender), también se puede con el menú Formato > Página, ficha Administrar, estilo Primera página

Y eso no más. Así era. Y deja de crear otro documento sólo para la portada.

Artículos relacionados

Etiquetas: , ,

Tip: Buscar en un sitio específico con Google

30.8.08. Por ooscarr (ooscarr)

Por medio de un simple javascript se puede crear un buscador personalizado en nuestro navegador favorito.

En Google, y en muchos otros buscadores, se pueden restringir las búsquedas a un dominio determinado usando la palabra reservada site:. Por ejemplo, en el caso de que queramos buscar el término firefox en cualquier página dentro del ooscarr.com que Google tenga en su índice, la consulta sería algo así:

firefox site:ooscarr.com

Marcador mágico

También se puede crear un marcador para buscar automáticamente en cualquier sitio que se esté visitando.

Pop-up preguntando el término a buscar en un dominio específico

Para buscar dentro de cualquier dominio al que pertenece la página que se tenga en frente con un par de clicks sin tener que tipear la parte site:ejemplo.com en Google, podemos

  1. crear cualquier marcador (o favorito, o bookmark) presionando la clásica combinación de teclas Control D. Si tienes mac, command D.

    Firefox: Marcadores > Marcar esta página
    En las nuevas versiones de Firefox, dice Marcar esta página

  2. Luego vamos donde se editan los marcadores o favoritos,

    Firefox: Marcadores > Organizar marcadores...

  3. y editamos la dirección por el siguiente código:

    javascript:Qr=prompt('Buscar%20en%20el%20sitio%20por','');if(Qr)location.href='http://www.google.com/search?q=site:'+encodeURIComponent(window.location.hostname)+'+'+escape(Qr);

    Y le cambiamos el nombre por algo más descriptivo como Buscar en este dominio

    Captura de pantalla de la Biblioteca de marcadores de Firefox 3 para Mac OS X

También se puede agregar rápidamente arrastrando el siguiente link

site:

a la barra de marcadores o favoritos. O agregarlo directamente haciendo click con el botón derecho para ver el menú contextual.

Esto funciona en todos los navegadores que conozco con soporte para javascript y funciona para cualquier dominio automáticamente.

Referencias

Etiquetas: , , , , , ,

Tip: Dejar de guardar pestañas al cerrar Firefox

14.8.08. Por ooscarr (ooscarr)
Mozilla Firefox

Ahora Firefox 3 te da la opción de guardar las pestañas que tienes abiertas antes de cerrar la aplicación y poder restaurarlas después (mañana) cuando vuelvas a abrir Firefox.

Para hacer esto, sólo debes elegir Salir desde la barra de menú (Archivo o Firefox) y elegir la opción indicada. También existe la opción de guardar siempre las pestañas abiertas al momento de salir automáticamente y sin preguntar nada.

Cuadro de diálogo Salir de Firefox para Mac OS X - ¿Desea que Firefox guarde sus pestañas para la próxima vez que se inicie?

Si marcaste No preguntar la próxima vez y no sabes cómo hacer que vuelva a preguntar (como yo), la opción está en las Preferencias eligiendo cualquiera de las otras 2 opciones Al iniciar Firefox en la sección Principal.

Captura de pantalla del Panel de preferencias de Mozilla Firefox 3 para Mac OS X

¿Seré yo el tonto al que le costó tanto encontrar cómo volver a hacer aparecer el diálogo o fue una falla de diseño de interfaz? Porque si para hacer desaparecer el diálogo tuve que marcar una casilla, lo que menos me esperaba era hacerlo volver desplegando un cuadro de selección relacionado. Es lógico pero no obvio.

Referencias

Etiquetas: , , , ,

Tip: Agregar carpeta al PATH

31.7.08. Por ooscarr (ooscarr)
terminal

Los paths son una lista de directorios que son buscados cuando escribes el nombre de un programa en las líneas de comandos.

Permanentemente

Para agregar permanentemente, se edita el archivo /etc/paths con permisos de superusuario así:

  1. Abrir el terminal
  2. tipear:
    sudo vi /etc/paths
  3. Escribir la contraseña si la pide
  4. Se abrirá una interfaz llamada vi, presionar la tecla i para insertar texto
  5. En la última línea, agregramos la ruta (path, por eso se llama path) al directorio
  6. esc
  7. En mi caso guardo el archivo presionando dos veces la combinación de teclas shift Z
  8. Listo

Temporalmente

También se puede agregar una ruta al PATH temporalmente mientras no cerremos la ventana. El método depende de qué sistema de comandos estén usando.

Con csh o tcsh

Si están usando el csh o el tcsh, los comandos serían:

setenv PATH ${PATH}:/ruta/a/la/carpeta

Con sh o bash

Y si están usando el sh o el bash, sería:

export PATH=$PATH:/ruta/a/la/carpeta

Estos últimos comandos se deshacen cuando se cierra la ventana del terminal.

Algunos sistemas como linux tienen métodos adicionales de agregar los PATHs, y otros (como Windows) tienen los PATHs ubicados en otras partes (Propiedades del Sistema).

Referencias

Etiquetas: , , , ,

Optimizando para la web con GIMP

29.7.08. Por ooscarr (ooscarr)
GIMP

Aunque no lo crean, el software GIMP sí tiene un optimizador de imágenes para la web. Existe un plug-in llamado Save for web cuya instalación todavía es un poco complicada.

¿Qué es Save for Web?

Save for web es un plug-in para GIMP (2.4) que agrega un elemento al menú Archivo que abre un optimizador del tamaño de archivo para la web como ha existido en otros programas comerciales como Adobe Photoshop y Fireworks por muchos años, y que no viene incluido por defecto en GIMP (en estas fechas estamos en la versión 2.4.6).

Archivo > Save for Web...
Por ahora está en inglés.

¿Cómo lo instalo?

Bueno, eso depende de en qué computador estés trabajando.

En Windows

Para Windows ya viene compilado (32 bits), así que sólo hay que

  1. Descargar el archivo
  2. Descomprimirlo
  3. Copiar el archivo webexport.exe a la carpeta:
    C:\Documents and Settings\TU_NOMBRE\.gimp-2.4\plug-ins
  4. Y volver a abrir el programa

En Linux

En linux hay que compilarlo primero, o sea.

  1. Si no lo tienes, instalar el paquete libgimp2.0-dev (Ubuntu, Debian)
  2. Descargar el archivo
  3. Descomprimir el archivo
  4. Entrar a la carpeta desde la consola y tipear:
    ./configure
  5. make
  6. make install
  7. Y volver a abrir el programa

En Mac OS X

En Mac se suponía que era igual que en Linux, pero no supe dónde encontrar la librería libgimp2.0-dev.

Pero en los foros encontré el plug-in ya compilado

Así que sólo habría que

  1. Descomprimir el archivo
  2. Copiar el archivo webexport a la carpeta:
    ~/Library/Application Support/Gimp/plug-ins/
  3. Y volver a abrir el programa

¿Y ahora qué?

Ahora, cada vez que quieras optimizar una imagen para la web, vas al menú Archivo > Save for Web... y se abrirá un panel especial como éste:

Save for Web panel

Lo malo es que esto todavía no tiene soporte para el canal alpha en los PNG 8. :-(

Referencias

Etiquetas: , , ,

Publicidad