Archivo de la categoría Tecnologia

Tutorial H2 Database

Jueves, 19 de Junio de 2008

La base de datos H2 es un base de datos relacional programada integramente en Java. Una de las características más importantes a consecuencia de esto es que podemos integrarla completamente en nuestras aplicaciones Java y acceder a a ella lanzando SQL directamente, sin tener que pasar por una conexión a través de sockets, como ocurirría con MySQL, Oracle, Postgress, etc. (aunque también es posible utilizarla a través de conexiones JDBC externas). Esto implica una mayor velocidad y una mejor integración, como ahora veremos.
Cuando trabajamos con la base de datos dentro de nuestra aplicaciones, se llama modo “embedded” (incrustado). Una vez que nuestra aplicación ha abierto la base de datos para trabajar con ella, esta queda fisicamente bloqueada, no permitiendo que otro motor H2 (en otra aplicación Java por ejemplo) pueda acceder a ella. Esto permite mantener la integridad del fichero físico de la base de datos, ya que si dos motores H2 escribieran a la vez podrían corromperla.

Usar y crear bases de datos incrustadas con H2 desde Java es realmente facil. Primero tenemos que bajarnos el último jar de http://www.h2database.com e incluirlo en nuestro CLASSPATH (o en el directorio WEB-INF/lib de nuestra aplicación WAR).

El siguiente código abre una conexión contra una base de datos, definiendo donde se encuentra físicamente en nuestro disco, sin tener que especificar ningún host, ip o puerto, ya que no hay conexión a través de sockets, sino que se accede directamenete al disco.

import java.sql.*;
public class Test {
  public static void main(String[] a) throws Exception {
    Class.forName("org.h2.Driver");
    Connection conn = DriverManager.getConnection("jdbc:h2:/home/vilches/h2/test", "sa", "");
  }
}

Si al abrir una conexión no existe físicamente los archivos de la base de datos, H2 los creará. Por ejemplo:

/home/vilches/h2/test.2.log.db
/home/vilches/h2/test.data.db
/home/vilches/h2/test.index.db
/home/vilches/h2/test.trace.db

La ruta a la base de datos elegida es /home/vilches/h2/test. Esto significa que se utilizará el directorio /home/vilches/h2/ y que en ese directorio se crearán los ficheros de la base de datos, que empezarán por test.* y tendrán distintas extensiones. De esta manera, cambiando solo el nombre del fichero es posible tener varias bases de datos el el mismo directorio, ya que empezarán con nombres distintos (por ejemplo /home/vilches/h2/test y /home/vilches/h2/otro).

Una conexión por sesión es suficiente

Una de las mayores ventajas de usar H2 de forma embebida es que podemos utilizar conexiones sin necesidad de un pool de conexiones. Podemos, por ejemplo, utilizar una única conexión para toda nuestra aplicación y no cerrarla nunca (o cerrarla al salir de la aplicación). En caso de aplicaciones Web, podemos crear una conexión por sesión con un listener que implemente HtppSessionListener:

package test;
import javax.servlet.*;
import javax.servlet.http.*;
import java.sql.*;
import java.io.*;
public class MySessionListener implements HttpSessionListener {
    public Connection openConnection() throws SQLException, ClassNotFoundException {
        Class.forName("org.h2.Driver");
        return DriverManager.getConnection("jdbc:h2:/home/vilches/h2/test", "", "sa");
    }

    public void sessionCreated(HttpSessionEvent se) {
        try {
            se.getSession().setAttribute("h2.connection", openConnection());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void sessionDestroyed(HttpSessionEvent se) {
        try {
            Connection con = (Connection)se.getSession().getAttribute("h2.connection");
            con.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Para que funcione el listener es necesario darlo de alta en el web.xml:

<listener>
    <listener-class>test.MySessionListener</listener-class>
</listener>

Ahora, desde nuestros servlets o JSPs, podemos acceder a la conexión con:

Connection con = (Connection)se.getSession().getAttribute("h2.connection");

y podremos utilizarla sin preocuparnos por cerrarla.

Abriendo la base de datos al arrancar la aplicación

Una buena practica es abrir por lo menos una conexión al arrancar la aplicación, de esta manera nos aseguramos de que H2 abre fisicamente la base de datos en el disco y la bloquea para el resto de procesos. Para hacer esto, podemos ampliar el listener anterior para que implemente también ServletContextListener, el cual tiene dos métodos que se ejecutan cuando se crea y se destruye el contexto (es decir, se arranca y se para la aplicación web).

Además, vamos a definir la ruta de la base de datos como un parámetro del contexto en el WEB-INF/web.xml llamado “h2.database.path” que leeremos en el arranque (si el parámetro no existe, cogeremos la home del usuario actual con System.getProperty("user.home")) y utilizaremos para crear la url jdbc :

package test;
import javax.servlet.*;
import javax.servlet.http.*;
import java.sql.*;
import java.io.*;

public class MySessionListener implements ServletContextListener,
    HttpSessionListener {

    // Constante para el nombre de los ficheros
    private final static String DBFILENAME = "test";

    String url;
    Connection globalConnection;

    public void contextInitialized(ServletContextEvent sce) {
        // Leemos el parametro del contexto
        String spath = sce.getServletContext().getInitParameter("h2.database.path");
        if (spath == null) {
            // Si no existe, es la home del usuario
            spath = System.getProperty("user.home");
        }
        try {
            Class.forName("org.h2.Driver");
            File dbfile = new File(spath, DBFILENAME);
            url = "jdbc:h2:file:" + dbfile.getAbsolutePath().replaceAll("\\", "/");
            globalConnection = openConnection();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public Connection openConnection() throws SQLException {
        return DriverManager.getConnection(url, "", "sa");
    }

    public void contextDestroyed(ServletContextEvent sce) {
        try {
            globalConnection.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    public void sessionCreated(HttpSessionEvent se) {
        try {
            se.getSession().setAttribute("h2.connection", openConnection());
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    public void sessionDestroyed(HttpSessionEvent se) {
        try {
            Connection con = (Connection)se.getSession().getAttribute("h2.connection");
            con.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

Para que funcione el listener es necesario darlo de alta en el web.xml y definir el parámetro de contexto con la ruta :

<context-param>
    <param-name>h2.database.path</param-name>
    <param-value>/home/vilches/h2</param-value>
</context-param>
<listener>
    <listener-class>test.MySessionListener</listener-class>
</listener>

Creando tablas al arrancar por primera vez

Ahora supongamos que queremos crear una serie de tablas cuando hemos arrancado la base de datos, pero solo la primera vez, cuando la base de datos está vacía. Para hacer esto vamos a verificar, antes de abrir la primera conexión, si la base de datos existe. Y si no existe, crearemos las tablas (después de abrir la primera conexión). Modificaremos el método contextInitialized para que quede así:

public void contextInitialized(ServletContextEvent sce) {
    // Leemos el parametro del contexto
    String spath = sce.getServletContext().getInitParameter("h2.database.path");
    if (spath == null) {
        // Si no existe, es la home del usuario
        spath = System.getProperty("user.home");
    }

    // Comprobamos si no existe la base de datos
    boolean exists = new File(spath, DBFILENAME+".data.db").exists();

    try {
        Class.forName("org.h2.Driver");
        File dbfile = new File(spath, DBFILENAME);
        url = "jdbc:h2:file:" + dbfile.getAbsolutePath().replaceAll("\\", "/");
        globalConnection = openConnection();

        // Si no existe, llamamos al metodo initDb()
        if (!exists) {
            initDb();
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}

Ahora solo queda implementar initDb() a nuestro gusto.

Ejecutando un script SQL desde Java

Una buena practica es crear un fichero de script con las sentencias SQL y ejecutarlo directamente, sin tener que implementar, una a una, las sentencias de creación de tablas en nuestro código. El siguiente código lee un fichero de script llamado squema-h2.sql.txt situado dentro de la carpeta de de clases (es decir en /WEB-INF/classes/squema-h2.sql.txt) y lo ejecuta. Dentro de este script podemos incluir sentencias para crear tablas y añadir registros. El script necesita que cada sentencia acabe en punto y coma “;”, permite partir las sentencias en más de una línea e ignora los comentarios (líneas que empiezan por “–”, “#” y “//”) y líneas en blanco.

private void initDb() throws SQLException, IOException {
    InputStream is = getClass().getClassLoader().getResourceAsStream("squema-h2.sql.txt");
    BufferedReader br = new BufferedReader(new InputStreamReader(is));
    Connection connection = null;
    try {
        connection = openConnection();
        String line = br.readLine();
        StringBuilder statement = new StringBuilder();
        while (line != null) {
            line = line.trim();
            if (!line.startsWith("--") && !line.startsWith("#") && !line.startsWith("//")) {
                statement.append(line);
                if (line.endsWith(";")) {
                    executeLine(connection, statement.toString());
                    statement = new StringBuilder();
                }
            }
            line = br.readLine();
        }
        if (statement.length() > 0) {
            executeLine(connection, statement.toString());
        }
    } finally {
        try {
            br.close();
        } catch (Exception e) {;}
        try {
            if (connection != null) connection.close();
        } catch (Exception e) {;}
    }
}

private void executeLine(Connection connection, String statement) throws SQLException {
    PreparedStatement pstmt = connection.prepareStatement(statement);
    pstmt.execute();
    pstmt.close();
    System.out.println("Ejecutando "+statement);
}

El script /WEB-INF/classes/squema-h2.sql.txt podría ser:

CREATE TABLE USUARIO (
	ID INT AUTO_INCREMENT,
	LOGIN VARCHAR(25) NOT NULL UNIQUE,
	PASSWORD VARCHAR(25) NOT NULL,
	NOMBRE VARCHAR(100) NOT NULL,
	EMAIL VARCHAR(50),
	WEB	VARCHAR(100),

	PRIMARY KEY(ID)
);

-- Esto es un comentario
// Esto también
# Y esto también lo es

insert into usuario values (null, 'admin','admin123','Alberto Vilches',
	'email@sitio.com', 'http://albertovilches.com');

(Para más información sobre la sintaxis SQL de creación de tablas de H2, consultar H2 SQL Grammar reference)

Finalmente, creamos un JSP de ejemplo que liste los registros de la tabla para ver si todo funciona correctamente:

<%@ page import="java.sql.*" %>
<html>
  <body>
  <%
      Connection con = (Connection) request.getSession().getAttribute("h2.connection");

      PreparedStatement stmt = con.prepareStatement("SELECT * FROM USUARIO");
      ResultSet rst = stmt.executeQuery();
      while (rst.next()) {
      %>

  <%=rst.getString("LOGIN")%>, mailto:<%=rst.getString("EMAIL")%><br/>

      <%
      }

  %>
  </body>
</html>

Resumen

Con esto ya tendríamos un acceso a base de datos muy ligero y rápido disponible en nuestras aplicaciones Web. La ruta de la base de datos irá definida en el parámetro “h2.database.path” en el web.xml. El listener se encargará de crear la base de datos con las todas las tablas y mantenerla siempre abierta mientras esté activa la aplicación Web. También tendremos en la sesión siempre una conexión disponible lista para usar.

Bájate el código de ejemplo completo: War de ejemplo con H2 incrustado

Crear una conexión JDBC

Jueves, 19 de Junio de 2008

Para acompañar al viejo post sobre PL-SQL Oracle desde Java (en algunos comentarios y mensajes) voy a explicar a continuación como establecer una conexión contra la base de datos sin usar un pool de conexiones.

1 Lo primero que necesitaremos son los drivers JDBC en formato jar, incluir este archivo jar en el CLASSPATH de nuestro proyecto y cargarlos en memoria:

Oracle: JDBC Download page Oracle

Class.forName("oracle.jdbc.driver.OracleDriver").newInstance();

MySql: MySQL Connector J

Class.forName("com.mysql.jdbc.Driver").newInstance();

2 Para crear la conexión, necesitamos una url JDBC, la cual contiene toda la información necesaria para conectarse:

Oracle: necesitaremos saber el host (el nombre de la máquina o su IP), el puerto de escucha del listener de Oracle (1521 por ejemplo), el servicio o sid, el usuario y la clave. Diferentes formatos de la url JDBC pueden ser:

jdbc:oracle:thin:@host:puerto:sid
jdbc:oracle:thin:usuario/clave@host:puerto:sid
jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST=host)(PORT=puerto)))(CONNECT_DATA=(SERVICE_NAME=servicio)(SERVER=SHARED)))
jdbc:oracle:thin:usuario/clave@(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST=host)(PORT=puerto)))(CONNECT_DATA=(SERVICE_NAME=servicio)(SERVER=SHARED)))

Más información en FAQ JDBC Oracle: Connections

MySql: host (nombre de la máquina o su IP), puerto de escucha (3306 por defecto), nombre de la base de datos, usuario y clave. El formato de la url es:

jdbc:mysql://host:puerto/database
jdbc:mysql://host:puerto/database?user=usuario&password=clave

(Más información en MySql JDBC Reference)

Una vez tengamos la url JDBC, podemos crear la conexión contra la base de datos:

Connection connection = DriverManager.getConnection(ulr, usuario, clave);

Si la url ya contiene el usuario y la clave (hay formatos que ya la llevan):

Connection connection = DriverManager.getConnection(ulr);

Con el objeto connection ya podemos trabajar, sin olvidarnos de que una vez hemos acabado, hay que cerrar la conexión. Esto es muy importante hacerlo, ya que si se queda abierta, estamos desaprovechando recursos, creando lo que se llama connection leaks (conexiones perdidas) con la base de datos.
La mejor forma de hacerlo es englobar todo el código en una estructura try/catch/finally y efectuando el cierre de la conexión en el finally. De esta manera nos aseguramos de que falle o no falle el código, se cerrará la conexión.

Connection connection;
try {
    // ...
    connection = DriverManager.getConnection(ulr);
    // ...
} finally {
    if (connection != null) {
        try {
            connection.close();
        } catch (SQLException e) {
			      e.printStackTrace();
        }
    }
}

Veamos ahora un par de ejemplos completo. Para Oracle definimos una url que incluye el usuario y la clave, por lo que no necesitaremos pasarle esta información en el método getConnection():

import java.sql.*;

public class OracleConnection {
    public static void main(String args[]) {

        String usuario = "vil";
        String password = "secreta";

        String host = "localhost"; // tambien puede ser una ip como "192.168.1.14"
        String puerto = "1521";
        String sid = "prueba";

        String driver = "oracle.jdbc.driver.OracleDriver";

        String ulrjdbc = "jdbc:oracle:thin:" + usuario + "/" + password + "@" + host + ":" + puerto + ":" + sid;

        Connection connection = null;
        try {
            Class.forName(driver).newInstance();
            connection = DriverManager.getConnection(ulrjdbc);

            // Ya tenemos el objeto connection creado

            ResultSet result = connection.createStatement().executeQuery("SELECT 'hola mundo' FROM DUAL");
            result.next();
            System.out.println(result.getString(1));

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (connection != null) {
                try {
                    connection.close();
                } catch (SQLException e) {
                }
            }
        }
    }
}

Y para MySQL (usaremos una url sin usuario y clave, por lo que pasaremos esta información al método getConnection):

import java.sql.*;

public class MySQLConnection {
    public static void main(String args[]) {

        String usuario = "vil";
        String password = "secreta";

        String host = "localhost"; // tambien puede ser una ip como "192.168.1.14"
        String puerto = "3306";
        String database = "prueba";

        String driver = "com.mysql.jdbc.Driver";

        String ulrjdbc = "jdbc:mysql://" + host + ":" + puerto + "/" + database;

        Connection connection = null;
        try {
            Class.forName(driver).newInstance();
            connection = DriverManager.getConnection(ulrjdbc, usuario, password);

            // Ya tenemos el objeto connection creado

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (connection != null) {
                try {
                    connection.close();
                } catch (SQLException e) {
                }
            }
        }
    }
}

Resumen para crear una conexión con cualquier base de datos:

  • Insertar en el CLASSPATH el jar con los drivers de nuestra base de datos (drivers Oracle, drivers MySQL o buscar en Google “drivers jdbc basededatos”)
  • Cargar los drivers con Class.forName(sdriver).newInstance();, siendo sdriver un String con la clase del driver.
  • Crear la conexión con DriverManager.getConnection(ulrjdbc, usuario, password);, siend urljdbc una url con el formato que define la propia base de datos (Oracle: jdbc:oracle:thin:@host:puerto:sid, MySQL: jdbc:mysql://host:puerto/database o buscar en Google “jdbc url basededatos”)
  • Cerrar siempre la conexión dentro del finally

Parando Tomcat 5/6 desde Linux

Jueves, 8 de Mayo de 2008

Con algo tan sencillo como esto:

echo SHUTDOWN | nc localhost 8005

Si queremos parar Tomcat en otra máquina, cambiar “localhost” por su nombre o IP. Este sistema de parada es mejor que un kill -9 y mucho más rápido que la parada que realiza catalina.sh stop
De hecho, podemos cambiar este script para que realice la parada de esta manera, comentando las líneas que invocan la clase org.apache.catalina.startup.Bootstrap con el parámetro stop, de esta manera:

#  "$_RUNJAVA" $JAVA_OPTS $CATALINA_OPTS 
#    -Djava.endorsed.dirs="$JAVA_ENDORSED_DIRS" -classpath "$CLASSPATH" 
#    -Dcatalina.base="$CATALINA_BASE" 
#    -Dcatalina.home="$CATALINA_HOME" 
#    -Djava.io.tmpdir="$CATALINA_TMPDIR" 
#    org.apache.catalina.startup.Bootstrap "$@" stop

echo SHUTDOWN | /bin/nc localhost 8005

El truco no es mío, lo he visto en groovygrails.de

Evaluando navegadores

Miércoles, 19 de Marzo de 2008

En anieto2k están apareciendo una serie de artículos sobre los navegadores actuales y los nuevas betas que vienen (Firefox 3 y IE8) bastante interesantes que hablan sobre la memoria consumida y compatibilidad con acid3. Copio y pego un par de gráficos interesantes que hablan por sí solos, pero parece que Safari 3.1 se lleva la palma.

acid3_beta_browsers.jpg


mem.jpg

Y aquí los artículos completos:

Como reconocer a un buen programador

Miércoles, 12 de Marzo de 2008

Traducido por Info soldier

Indicadores positivos:

  • Le apasiona la tecnología
  • Programa en su tiempo libre, es su hobby
  • Hablará largo y tendido de cualquier tema técnico si se le pregunta
  • Ha tenido proyectos personales significativos a través de los años
  • Aprende nuevas tecnologías por si solo
  • Tiene opiniones fuertes sobre cual tecnología es la mejor para varios usos
  • Se siente incomodo trabajando con tecnologías que el crea no son “correctas”
  • Puede mantener una buena conversación en cualquier variedad de temas, no tan solo aquellos que sean técnicos
  • Empezó a programar mucho antes de entrar a la universidad o al trabajo
  • Tiene proyectos personales grandes que no necesariamente aparecen en el cv
  • Conoce de una gran variedad de tecnologías (las cuales no necesariamente están en el cv)

Indicadores negativos:

  • Solo programa mientras está en el trabajo
  • No le gusta hablar de programación, aunque se le pregunte
  • Aprende nuevas tecnologías cuando la compañía lo envía a tomar cursos
  • Usa la tecnología que le indiquen, cualquier tecnología es “buena”
  • Empezó a programar en la universidad
  • Toda su experiencia en programación está en su cv
  • Enfocado en uno o dos plataformas de programación (ejemplo: todo lo que tiene que ver con aplicaciones de java) sin ninguna experiencia fuera de esa plataforma.

Original en ingles

Programadorus Tremendus

Martes, 15 de Enero de 2008

lambdaward.PNG

(Vía Secret Geek)

Un tetris en 560bytes

Lunes, 10 de Diciembre de 2007

Con este simple código, un Tetris completamente funcional, con tan solo 560 bytes.

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=shift_jis">
<body onKeyDown=K=event.keyCode><script>X=[Z=[B=A=12]];h=e=K=t=P=0;function Y()
{C=[d=K-38];c=0;for(i=4;i--*K;K-13?c+=!Z[h+p+d]:c-=!Z[h+(C[i]=p*A-Math.round(p/
A)*145)])p=B[i];!t|c+4?c-4?0:h+=d:B=C;for(f=K=i=0;i<4;f+=Z[A+p])X[p=h+B[i++]]=1
if(e=!e){if(f|B){for(l=228;i--;)Z[h+B[i]]=k=1;for(B=[[-7,-20,6,17,-9,3,6][t=++t
%7]-4,0,1,t-6?-A:-1];l--;h=5)if(l%A)l-=l%A*!Z[l];else for(P+=k++,j=l+=A;--j>A;)
Z[j]=Z[j-A]}h+=A}for(i=S="";i<240;X[i]=Z[i]|=++i%A<2|i>228)i%A?0:S+="<br>",S+=X
[i]?"■":"_";document.body.innerHTML=S+P;Z[5]||setTimeout(Y,99-P)}Y()</script>
</body>
</html>

Puedes verlo funcionando aquí: http://zapanet.info/blog/game/tetorisu.html

Abrir una url en un navegador desde Java

Miércoles, 24 de Octubre de 2007

Sacado del código fuente de H2, clase org.h2.util.StartBrowser

    public static void openURL(String url) {
        String osName = System.getProperty("os.name");
        try {
            if (osName.startsWith("Windows")) {
                Runtime.getRuntime().exec("rundll32 url.dll,FileProtocolHandler " + url);
            } else if (osName.startsWith("Mac OS X")) {
                // Runtime.getRuntime().exec("open -a safari " + url);
                // Runtime.getRuntime().exec("open " + url + "/index.html");
                Runtime.getRuntime().exec("open " + url);
            } else {
                System.out.println("Please open a browser and go to "+ url);
            }
        } catch (IOException e) {
            System.out.println("Failed to start a browser to open the url " + url);
            e.printStackTrace();
        }
    }

Sufriendo con las teclas en MacBook

Miércoles, 29 de Agosto de 2007

Las teclas que le faltan a los MacBook
El problema más gordo que me he encontrado con Mac OS son las teclas. Los MacBook no tienen tecla de Suprimir ni de Insertar (Ins/Del) y las teclas de Av.Pag, Re.Pag, Inicio y Fin comparte funcionalidad con los cursores, por lo que para pulsar Inicio por ejemplo, tienes que pulsar Fn (tecla “función”, una tecla que tienen solo los portátiles y sirve para dar más funcionalidad a ciertas teclas) e Izquierda a la vez.

Todo esto es bastante incomodo, pues como programador, necesito moverme muy rápido por el código mientras tecleo. También veo que para que funcionen las teclas de Función (F1,F2,…,F11,F12) he de combinarlas con la tecla Fn, simplemente horrible (luego veremos como cambiarlo). Y no funcionan los atajo de teclado acostumbrados de Windows, por ejemplo para copiar y pegar no funcionan los CTRL+C, CTRL+V, etc (aunque hay otros equivalentes muy parecidos) ni que decir de la histórica combinación clásica que todavía algunos usamos (Ctrl+Ins para copiar, Mayus+Supr para cortar y Ctrl+Ins para pegar) ni pensarlas.

Tampoco hay AltGr, pero porque no hace falta, en Mac la tecla Alt y AltGr es la misma tecla. Pero estamos acostumbrados a buscarla en la parte derecha del teclado, donde nos encontramos una molesta tecla Intro pequeñita que nunca hemos utilizado y que Apple ha puesto ahí para molestar.

Finalmente, además de las teclas Alt (que en Mac se llama “opción”) y Control, tenemos una nueva tecla llamada “Comando”, con el símbolo de una manzana, que sirve para casi todo. Por ejemplo:

  • Cortar, copiar y pegar es Comando+X, Comando+C y Comando+V (en Windows es igual pero con Ctrl)
  • Gardar es Comando+S (en Windows es Ctrl+S o Ctrl+G, depende de la aplicación)
  • Cerrar ventana es Comando+W (en WIndows Ctrl+F4)
  • Cerrar aplicación es Comando+Q (en Windows Alt+F4)
  • Seleccionar todo es Comando+A (en Windows Ctrl+A o Ctrl+E, depende de la aplicación)
  • Imprimir es Comando+P (en Windows Ctrl+P)
  • Deshacer es Comando+Z y rehacer Comando+Mayus+Z (en Windows Ctrl+Z y Ctrl+Mayus+Z o Ctrl+Y, depende de la aplicación)

Así que en este sentido el cambio es poco drástico, tan solo que los atajos comunes ahora son con comando en vez de con Alt, pero en esencia son muy parecidos.

Entre otras cosillas vemos que al pulsar tabulador en un formulario del sistema, este viaja por los campos de texto, pero no por los botones, lo cual es incómodo, ya que obliga a usar el ratón para pulsarlos. Finalmente, no tenemos botones de Pausa, Imprimir pantalla ni bloqueo de teclas (Num Lock).

En Mac OS todo cambia y los que venimos de PC (los switchers) tenemos que buscarnos la vida si queremos sobrevivir al cambio. Así que, tras un mes buscando trucos y programas, esto es todo lo que he ido descubriendo:

  • En preferencias del sistema, entramos en Teclado y Ratón, pestaña Teclado y activar el checkbox “Use las teclas F1-F12 para controlar las funciones del software”. Con esto nos ahorraremos tener que pulsar Fn+F1 para lanzar un simple F1. Ahora, pulsando F1 será F1 y no “subir iluminación del teclado”. Lo mismo sucederá con el resto de teclas de función.
    También podemos aprovechar para subir la velocidad de repetición del teclado y la espera hasta la repetición.
  • Para imprimir pantalla, tenemos los siguientes atajos:

    • Captura pantalla a archivo = Comando+Mayus+3
    • Captura pantalla a portapapeles = Comando+Ctrl+Mayus+3
    • Captura pantalla a archivo (solo un área) = Comando+Mayus+4
    • Captura pantalla a portapapeles (solo un área) = Comando+Ctrl+Mayus+4

    Estos atajos se pueden cambiar en preferencias, teclado y ratón, funciones rápidas de teclado.

  • La tecla Supr es Fn+BackSpace.
  • En Windows, Alt+Tab sirve para cambiar de ventana. En Mac tenemos Comando+Tab para cambiar de aplicación, ¡pero no de ventana! Esto significa que si una aplicación tiene varias ventanas, no podemos cambiar de una a otra con Comando+Tab. Para esto tenemos que usar Comando+< (la tecla que esta entre mayúsculas y la Z).
  • No hay atajo para la tecla Insert. Según Apple, si queremos sobreescribir texto (escritura destructiva), lo que tenemos que hacer es marcar el texto a borrar (con el ratón por ejemplo) y escribir encima. El problema viene si utilizamos algún programa que requiere que pulsemos la tecla Insert.
  • Hay una tecla Intro (Enter, Return) pequeña entre los cursores y la tecla de Comando que los que venimos de PC no la necesitamos porque nunca la hemos usado. Con el programa Double Command podemos mapear esta tecla con otra funcionalidad. Hay quien pone en esta tecla la función de Suprimir (lo que se denomina como “Forward delete”), pero yo prefiero que sea Alt. En Mac no hay AltGr, por lo que los caracteres especiales se introducen con el Alt normal. Con Double Command instalado, podemos elegir esta opción para que la tecla Intro sea Alt. Ahora ya podemos introducir los caracteres @#[]{} con la tecla Intro, tal y como está la tecla AltGr en los PCs, a la derecha de la tecla espacio.
    imagen-1.png

    Pero si lo que queremos es que Intro sea Suprimir, entonces elegiremos esta otra opción: “Enter key acts as forward delete” como recomiendan en Apple Esfera:

    imagen-1.png
  • Seguramente usemos Parallels para virtualizar un Windows XP bajo nuestro Mac. En este caso, para mapear esta tecla, necesitaremos usar la utilidad free KeyTweak. La instalamos, ejecutamos, pulsamos el botón Half Teach Mode. Nos saldrá una ventana, pulsamos el botón Scan a single key. En este momento, pulsamos la tecla cuya funcionalidad queremos cambiar, así que pulsamos la tecla Intro pequeña. Se añadirá con el código 541. Ahora elegimos la funcionalidad en Destination Key, seleccionamos “Right Alt” y ya tenemos nuestro desaparecida tecla AltGr

    imagen-1.png
  • En preferencias del sistema, entramos en Dashboard y Exposé y desactivamos las teclas F9 y F10. Ahora ya tenemos esas teclas libres para otros programas. En su lugar, podemos activar las esquinas de la pantalla como zonas calientes para mostrar el escritorio, etc.
  • En preferencias del sistema, entramos en Internacional. Activamos el Checkbox “Visor de Teclado” y “Mostrar el menú de teclado en la barra de menús”. Ahora en la parte superior derecha de la pantalla hay una opción nueva llamada “Mostrar visor de teclado”. Con este visor podemos ver que caracteres se introducirán al pulsar Alt, Comando, etc.
    imagen-1.png
  • En preferencias del sistema, entramos en Teclado y Ratón, pestaña Funciones rápidas de Teclado. En el radio button de abajo, elegimos “Todos los controles”, con esto conseguimos que al pulsar Tab el foco se mueva por los botones, y no solo por los campos de texto. Hay un atajo de teclado para cambiar esta opción que deberíamos desactivar, ya que si lo pulsamos sin querer, nos quedaremos sin poder viajar por los botones con la tecla Tab (y tendremos que venir a preferencias otra vez y activarlos). Este atajo se llama “Moverse por los controles/campos de texto/listas” y está asignado al atajo Ctrl+F7, desactívalo para vivir más tranquilo. También desactivaría el VoiceOver, asignado a Comando+F5. Es muy fácil pulsar esta tecla sin querer y que nuestro Mac se ponga a hablar sin parar.
    imagen-1.png
  • El botón derecho del ratón se consigue pulsando Ctrl a la vez que click. Pero lo mejor es comprarse un buen ratón con más de un botón. Yo me he comprado el Logitech MX400, que tiene drivers para Mac (hay que bajárselos de la Web). Es un buen ratón, con 7 botones y laser muy preciso.
    Ojo, con este ratón no funciona correctamente la rueda al pulsarla cuando instalas los drivers, para arreglarlo: Vete a preferencias del sistema, Logitech, y selecciona en la rueda central “Click avanzado”. En la parte inferior, elige Tipo de clic = “Clic” y número de botón = 3. Ahora ya funciona al pulsar la rueda (por ejemplo, para cerrar una pestaña en Firefox)
    imagen-9.png

Una vez configurado todo esto, ya tenemos el sistema medio controlado. Pero veremos que según qué programas utilicemos (por ejemplo, el estupendo editor de texto TextMate), el Inicio/Fin y AvPag/RePag se comportan de una manera un poco caprichosa, a lo que no estamos acostumbrados los que venimos de PC. Para arreglar esto, aquí tienes un artículo completísimo para redefinir teclas en todas las aplicaciones: Customizing the Cocoa Text System y otro Key Bindings for Switchers.

Pero si no te apetece leértelos entero, yo te los resumo. En tu carpeta de usuario (tu home), dentro de Library debes crear una carpeta llamada KeyBindings. Dentro de esta carpeta, debes crear un fichero llamado DefaultKeyBinding.dict Por ejemplo, mi usuario se llama avilches, así que tengo un fichero con esta ruta:

/Users/avilches/Library/KeyBindings/DefaultKeyBinding.dict

Si quieres que las teclas Inicio/Fin/AvPag/RePag funcionen como en Windows, el contenido de este fichero debe ser:

{
 	/* Inicio          Ir al inicio de la linea */
	"UF729"    = "moveToBeginningOfLine:";     

	/* Fin             Ir al final de la linea */
	"UF72B"    = "moveToEndOfLine:";           

	/* Mayus-Inicio    Ir seleccionando al inicio de la linea */
 	"$UF729"   = "moveToBeginningOfLineAndModifySelection:";

 	/* Mayus-Fin       Ir seleccionando al final de la linea */
	"$UF72B"   = "moveToEndOfLineAndModifySelection:";

	/* Ctrl-Inicio     Ir al inicio del documento */
	"^UF729"   = "moveToBeginningOfDocument:"; 

	/* Ctrl-Fin        Ir al final del documento */
	"^UF72B"   = "moveToEndOfDocument:";       

	/* Ctrl-May-Inicio Ir seleccionando al inicio del documento */
	"^$UF729"  = "moveToBeginningOfDocumentAndModifySelection:";

	/* Ctrl-May-Fin    Ir seleccionando al final del documento */
	"^$UF72B"  = "moveToEndOfDocumentAndModifySelection:";

/***** Tecla AvPag RePag *****/
/* En portatil AvPag = Fn+Arriba, RePag = Fn+Abajo */

	/* RePag           Retrocede pagina */
	"UF72C"    = "pageUp:";                    

	/* AvPag           Avance pagina */
	"UF72D"    = "pageDown:";                  

	/* Alt-RePag       Scroll Retrocede pagina */
	"~UF72C"   = "scrollPageUp:";              

	/* Alt-AvPag       Scroll Avance pagina */
	"~UF72D"   = "scrollPageDown:";
}

Si te sabe a poco, aquí te puedes descargar un mapeado supercompleto de teclas, personalizado por mi (el original lo he sacado de aqui, pero ojo, tiene algunos errores que he tenido que corregir) con algunos atajos en castellano, como Ctrl+G guardar y he simulado el copiar y pegar clásico. Échale un vistazo y borra los atajos que no te gusten. Muchos no funcionan si la aplicación donde se usan ya los redefine por su cuenta con alguna funcionalidad propia o si los usa el sistema. Por ejemplo Ctrl+S, en algunos programas servirá para otra cosa, así que no funcionará. Otro ejemplo, Ctrl+F4 sirve para cambiar a la siguiente ventana activa y esta definido por el sistema en preferencias, ratón y teclado, funciones rápidas de teclado. Si no lo desactivas previamente en el sistema, no servirá de nada redefinirlo. Escríbeme un comentario si quieres redefinir algo y no sabes como o no te funciona.
Lo que seguro que funciona correctamente sin tocar nada son los movimientos de los cursores, Inicio, Fin, AvPag y RePag combinados con Ctrl y Mayus, lo cual ya ayuda bastante a la hora de escribir.

Si te animas a redefinir tus propias teclas, te pueden ser útil estos programas:

  • Key Codes, te dice el código de la tecla pulsada
  • KeyBinding Editor, para redefinir teclas sin tener que tocar ficheros a mano. Yo lo hago a mano porque este editor no me funciona, si lo consigues hacer funcionar, olvídate de tener que editar ficheros, este programa te lo hace todo automáticamente: eliges la tecla y la función y te genera el fichero con el mapeo.

De todas formas, conviene antes estudiarse todas las teclas que hay existentes, desactivar las que no nos gusten y redefinir las nuevas, si es que no existen. Un par de chuletas siempre nos vendrán bien:

Mucho de estos trucos los he sacado de la web Mac OS X Hints, otros por internet, en foros y demás. Todos estos cambios en la configuración de tu Mac te harán la vida más fácil si vienes de PC. Mañana hablaré de aplicaciones, widgets y programas útiles y básicos para Mac que los switchers necesitamos.

Más captchas

Lunes, 20 de Agosto de 2007

Uno muy bueno e interesante es el que propone My Java Server cuando te intentas registrar. Debes programar el método de una clase en la propia página y debe pasar unas pruebas (testcases) y devolver siempre un resultado correcto en todas.
Ellos mismo te indican como debe funcionar el método. El código que programas se compila y ejecuta cada cierto tiempo y los resultados de la compilación y las pruebas se muestran en la propia página, todo con Ajax muy chulo.
Se supone que la idea es que solo un programador pueda darse de alta y así evitar que se abuse del sistema. El problema que le veo es que el “desafío” de programación es siempre el mismo, en cuanto un solo programador publique en Internet la solución (no es muy difícil, en 5 minutos si sabes Java lo tienes), cualquiera podría copiar y pegarla para darse de alta sin tener ni idea. Por lo menos es curioso y, tecnológicamente, interesante.

Y otro un poco más difícil, el de Ruder BoÅ¡kovic Institute que he visto gracias a Gaussianos. En teoría, para alguien con conocimientos en matemáticas son sencillos, pero para el resto de mortales… buf, yo desde luego algunos no los paso. Pero si te atascas no hay problema, puedes elegir otro más sencillo.

captcha-chungo.jpg