Archivo de la categoría ‘Soluciones’

Ejecutar comandos Linux desde Java (I)

Domingo, 16 de Agosto de 2009

Muchas veces hay una necesidad de ejecutar un comando Linux (o Windows) desde Java. Las razones pueden ser varias, a veces conviene usar una cosa que ya está disponible en forma de comando (imaginaros el prototipo de un programa con el presupuesto aún sin aprobar) o a lo mejor simplemente porque una utilidad de consola consume mucho menos recursos que Java (supongamos que tenemos que recodificar avi -> fla muchos videos que nos han subido). El caso es que Java sirve perfectamente para ello, teniendo en cuenta ciertas consideraciones.

Veamos el código:


Process proc = Runtime.getRuntime().exec("ls -la");
InputStream is = proc.getInputStream();
int size;
String s;
int exCode = proc.waitFor();
StringBuffer ret = new StringBuffer();
while((size = is.available()) != 0) {
byte[] b = new byte[size];
is.read(b);
s = new String(b);
ret.append(s);
}
System.out.println(ret.toString());

La primera línea nos crea un proceso dentro de nuestro Runtime, con el comando ls -la dentro. Acto seguido obtenemos un InputStream para poder ver la salida estándar del programa. Y aquí viene la primera consideración, en el momento de lanzar el proceso con Runtime.getRuntime().exec() arranca un subproceso que sigue a su ritmo completamente. Quiero decir, si nada más arrancar el proceso intentamos leer lo que ha devuelto tendremos un String vacío, ya que de momento el processo no ha sacado nada por la salida estándar.

La solución para eso es esperar hasta que el proceso termine. Esto lo hacemos con la siguiente línea: proc.waitFor(); Lo que hace es que detiene el Thread dentro del cual hemos lanzado el proceso y espera hasta que termine el proceso (cuidado con los bloqueos). Como resultado devuelve un número entero con el código de terminación del proceso lanzado. Normalmente el código 0 significa OK, en caso de que se haya producido algún error lo normal es que devuelva otro valor.

Las siguientes líneas simplemente leen lo que el proceso ha enviado por la salida estándar y sacan el resultado por pantalla.

Otra consideración de la que no debemos olvidarnos es que un proceso tiene los siguientes flujos (dos de ellos para leer y uno para escribir):

  1. STDIN – flujo de entrada estándar. Mediante este flujo podemos enviarle datos al comando. Se obtiene mediante Process.getOutputStream().
  2. STDOUT – flujo de salida estándar. Es el que hemos usado en nuestro ejemplo.
  3. STDERR – flujo para la salida de errores. Se obtiene mediante Process.getErrorStream().

Segunda parte — como hacer lo mismo pero sin bloquear el Thread.

Copias de seguridad de MySQL con mysqldump

Sábado, 15 de Agosto de 2009

Hace tiempo leí que cada buen administrador de sistemas tiene que recordar tres reglas de oro: copias de seguridad, copias de seguridad y copias de seguridad. De eso voy a hablar en este post, ¿cómo de una forma sencilla podemos organizar el tema de las copias de seguridad usando las herramientas estándar que cualquier sistema Linux ofrece?

Casi siempre que instalamos el servidor de las bases de datos MySQL junto a él viene la utilidad llamada mysqldump. Se trata precisamente de eso, es un programa para sacar las copias de seguridad de la base de datos. Yo lo hago de la forma más sencilla que se me ha ocurrido, en mi crontab (como root) añado la siguiente entrada:


59 23 * * * /usr/bin/mysqldump -u root xand_prod | /bin/gzip -9 -c > /www/xand/backup/xand_prod-`/bin/date +\%Y\%m\%d`.gz

¿Qué hace este código? Cada día a las 23 59 se lanza el comando mysqldump (accede a la base de datos como root) y saca una copia de la BBDD xand_prod por la salida estándar. Como podeis ver, la salida se redirecciona mediante un pipe al gzip. Este a su vez lo comprime y lo guarda en el fichero que hemos indicado. Con el /bin/date pongo la fecha de cada fichero en el nombre del mismo.

Conectarse desde Java a MySQL mediante JDBC

Viernes, 14 de Agosto de 2009

Este post es una microchuleta sobre como uno puede conectarse a MySQL desde Java mediante JDBC. Existe infinidad de métodos y librerías para realizar esta tarea, pero aquí vamos a ver el más directo.

Supongamos que tenemos una tabla llamada test en la base de datos llamada prueba. Lo primero que tenemos que hacer es cargar el controlador (también llamado Driver).

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

Ahora componemos la URL de conexión a la BBDD y conectamos:


String urlDDBB = "jdbc:mysql://localhost:3306/prueba?user=root&password=contrasenya";
Connection conn = DriverManager.getConnection(urlDDBB);

Con esto le indicamos al controlador que nuestro servidor de MySQL se encuentra en localhost, escucha en el puerto 3306 (estándar), la base de datos se llama prueba, el usuario con el que conectamos es root y su contraseña es contrasenya. Una vez realizado podemos empezar a lanzar consultas a la tabla. Vamos a ver como podemos obtener los distintos valores que pueden guardarse en la tabla a asignarlos a las variables nuestras.


String consulta = "SELECT * FROM test";
String s;
int i;
Date d;
long l;
double dd;
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(consulta);
while(rs.next()){
s = rs.getString("columna_string");
i = rs.getInt("columna_int");
d = rs.getDate("columna_date");
l = rs.getLong("columna_long");
dd = rs.getDouble("columna_double");
}

Una cosa muy importante que debemos de hacer una vez hemos terminado de trabajar con los datos de la tabla es cerrar todos los componentes:


rs.close();
stmt.close();
conn.close();

Espero que sirva de ayuda.

Cambiar una palabra por otra en Java

Viernes, 14 de Agosto de 2009

Mucha gente pregunta sobre cómo se puede cambiar una palabra por otra en Java. Es decir, lo que pretende el usuario normalmente es sustituir una cadena de caracteres por otra.

Por suerte, Java es bastante flexible y nos da dos opciones para hacerlo. La primera opción va a resulta más sencilla para toda aquella gente que no conoce expresiones regulares (RegExp) y consiste básicamente en separar la cadena en cadenas más pequeñas usando como separador la palabra buscada. Luego se concatena todo y se mete entre medias la cadena que queramos poner. Este es el algoritmo:

static String replace(String str, String pattern, String replace) {
int s = 0;
int e = 0;
StringBuffer result = new StringBuffer();
while ((e = str.indexOf(pattern, s)) >= 0) {
result.append(str.substring(s, e));
result.append(replace);
s = e+pattern.length();
}
result.append(str.substring(s));
return result.toString()
}

Otra forma mucho más sencilla de hacerlo es mediante las expresiones regulares:

String s = "Cadena1 larga muy muy larga";
s = s.replaceAll("\\{Cadena1\\}", "Cadena2")

En este ejemplo la cadena Cadena1 va a ser sustituida por Cadena2.

TusEuros.com — un sistema de afiliados hecho por mí

Viernes, 7 de Agosto de 2009

Hoy es un gran día, para mí, ya que acabo de lanzar otro proyecto — TusEuros.com. Se trata de un sistema de afiliados para comprar tráfico web.

Sinceramente, a día de hoy es el sistema que más recompensa a los webmasters de España. Pagamos 1 euro por cada persona que se registre a través de nuestra página.

The little book of Flow @ FictionBook

Martes, 23 de Junio de 2009

If you don’t understand spanish, just scroll down to the bottom.

Hace poco he encontrado un libro que en pocas palabras describe toda la filosofía que llevo siguiendo desde hace ya algún tiempo. Tanto me ha gustado que hasta me he tomado la molestía de pasarlo de HTML a FictionBook. Para los que no lo sepan FictionBook (ficheros con extensión fb2) es un formato para poder leer los libros sobre distintos dispositivos: iPhone, Papyre, casi todas las PDA’s, etc. Es un formato abierto y libre.

Este “libro” en realidad es un post que apareció en el blog de Nick Smith (allá en el 2006) y es una recopilación de las ideas que ha ido exponiendo a lo largo de un tiempo. Se titula The Little Book of Flow. Merece la pena leerlo, de verdad, el único inconveniente que os podeis encontrar es que esta en inglés.

Sin más preámbulos aquí os pongo el enlace.

Más sobre el tema: Formato FictionBook.

Now, for my english-spoken readers. I just converted Nick Smith’s The Little Book of Flow from HTML to FB2. I hope you know what this format is, if you don’t, RTFM.

Yo en twitter

Lunes, 22 de Junio de 2009

Por fin tengo configurado mi Twitter. Me gustaría conocer a gente interesante. Podeis encontrarme aquí.

Función replace() en mysql

Domingo, 14 de Junio de 2009

Un día de estos me encontré con un fallo en el código. El fallo era debido a que un famoso editor de texto hecho en Javascript rodeaba ciertas expresiones con comillas simples. Para que nos entendamos aparecía algo así como cadena 1, cadena 2, cadena 3 en vez de cadena 1, cadena 2, cadena 3. Estos valores luego se insertaban en una base de datos MySQL para ser mostrados en la página. Claro está, estas comillas simples generadas rompían toda la lógica y no se mostraba el texto.

La aplicación se corregió, pero quedó por corregir lo que ya estaba en la tabla. Teniendo en cuenta que el texto era bastante largo no se podía hacer SELECT – corregir – UPDATE. Había que corregirlo directamente en base de datos.

La solución ha sido la siguiente:

CREATE TEMPORARY TABLE guion_tmp LIKE guion;

INSERT INTO guion_tmp SELECT * FROM guion where guion_id=67;

UPDATE guion SET guion_contenido=(SELECT replace(guion_contenido, ‘\’Times New Roman\”,’Times’) FROM guion_tmp WHERE guion_id=67) WHERE guion_id=67;

La tabla temporal se eliminará nada más cerrar la conexión.

Despliegue de aplicaciones en Tomcat

Domingo, 7 de Junio de 2009

En esta entrada voy a hablar sobre el despliegue de aplicaciones web en Apache Tomcat. Partimos de la situación cuando tenemos que desplegar una aplicación web en Tomcat que está detrás de un front-end que puede ser Apache, nginx o cualquier otro. Se supone que en el caso de Apache se usa mod_proxy.

Objetivos:

  1. Queremos usar la aplicación Deployer que viene junto a Tomcat y que nos permite desplegar/replegar/parar/arrancar/reiniciar aplicaciones directamente desde un navegador web.
  2. La gestión de hosts virtuales lo dejamos para el front-end en vez de poner múltiples Alias en server.xml.

Existen muchos inconvenientes si dejamos a Tomcat tratar todas las peticiones HTTP. En primer lugar cada petición HTTP va a ocupar bastante más memoria y gastar más tiempo CPU si se trata con Tomcat en vez de con un servidor apropiado, como puede ser Apache o nginx. En segundo lugar, si la petición se procesa mediante Tomcat en primer lugar no se podrá hacer uso de todos los módulos disponibles para Apache. Por último, cada aplicación está hecha para su cosa y para atender peticiónes HTTP están los Apache y nginx y para hacer uso de servlets está el Tomcat.

Primero vamos a configurar el servidor de aplicaciones. Para la explicación de todas las directrices para la configuración de Tomcat se puede echar un ojo a la documentación oficial. Tenemos que conseguir que haya solamente un Engine y un sólo Host que va a ser el que trate las peticiones por defecto. Dentro del Host definiremos un Context que contendrá la aplicación Manager.

La configuración que tengo yo en producción es la siguiente:

<Service name="Catalina">
  <Connector protocol="org.apache.coyote.http11.Http11NioProtocol" URIEncoding="UTF-8" maxThreads="100" port="8080" emptySessionPath="true" />
  <Engine name="Catalina" defaultHost="localhost">
  <Realm className="org.apache.catalina.realm.UserDatabaseRealm" resourceName="UserDatabase"/>
  <Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true" xmlValidation="false" xmlNamespaceAware="false">
    <Context path="/manager" debug="0" privileged="true" docBase="/path/webapps/manager">
    </Context>
  </Host>
  </Engine>
</Service>

Ahora tenemos que conseguir que el front-end redireccione las peticiones del navegador a http://localhost:8080/manager. Yo lo hago con un dominio de tercer nivel y mod_rewrite. Con mod_rewrite vamos a redireccionar todos los dominios, pero de momento resolvemos el tema de Manager.

En primer lugar he creado un nuevo dominio de tercer nivel apuntando a la máquina. Pongamos por ejemplo http://manager.dominio.com. Luego lo he configurado en Apache para que mediante mod_rewrite se transforme en http://localhost:8080/manager. Las directrices son las siguientes:

<VirtualHost *:80>
ServerName    manager.dominio.com
RewriteRule      (.*)                                /manager/$1 [NS,PT,L]
ProxyPreserveHost       On
ProxyPass               /manager   http://localhost:8080/manager
ProxyPassReverse        /manager   http://localhost:8080/manager
</VirtualHost>

Con esto lo que conseguimos es que si apuntamos el navegador web a http://manager.dominio.com Apache va a pasar la petición a http://localhost:8080/manager, el Tomcat la ejecutará y devolverá la respuesta a Apache y este a su vez la devolverá al navegador. Realmente es un mal ejemplo, porque lo que tenemos que conseguir es que Tomcat procese únicamente los servlets, sin embargo aquí le decimos (con (.*)) que procese todo. Es decir, que si el navegador pide una imagen, esta también será devuelta por Tomcat. Es un mal ejemplo, pero por algo tenemos que empezar.

Una vez que tengamos acceso a la aplicación manager ya podemos desplegar otras. Tan sólo tenemos que generar un fichero war y hacer uso del cuadro WAR file to deploy, el resto lo hará el Tomcat, descomprimirá el fichero war dentro del directorio webapp y hará disponible la aplicación en el contexto http://localhost:8080/NombreFicheroWAR.

Ahora tenemos que configurar Apache para que trate las peticiones. Defineremos lo siguiente:

  1. Que todas las peticiones de servlets las procese el Tomcat.
  2. Que todas las demás peticiones las procese el front-end.

En cuanto al primer punto podemos hacerlo de dos formas. O bien definir un RewriteRule para cada servlet, o bien definir un patrón para los servlets y seguir este patrón a la hora de poner los url-pattern en el fichero web.xml. En cuanto al segundo es suficiente con poner como DocumentRoot la dirección donde va a descomprimir Tomcat el fichero. Hay una cosa superimportante que tenemos que recordar siempre, y es que tenemos que prohibir el acceso a todo el mundo a los directorios WEB-INF y META-INF. En Apache es sencillo:

<Location "/WEB-INF/*">
AllowOverride None
Deny from all
</Location>
<Location "/META-INF/*">
AllowOverride None
Deny from all
</Location>

Si no hacemos esto pondremos en peligro la seguridad de la aplicación entera ya que cualquiera podría descargar las clases compiladas, descompilarlas, etc.

Recursos relacionados: