“Revolution OS”, the movie.

És estiu i és moment de relaxar-se.  Avui toca cine: El documental “Revolution OS”.

Vet aquí la ressenya de la pel·lícula que es pot trobar al YouTube:

“Revolution OS is a 2001 documentary which traces the history of GNU, Linux, and the open source and free software movements.

It features several interviews with prominent hackers and entrepreneurs (and hackers-cum-entrepreneurs), including Richard Stallman, Michael Tiemann, Linus Torvalds, Larry Augustin, Eric S. Raymond, Bruce Perens, Frank Hecker and Brian Behlendorf.

The film begins in medias res with an IPO, and then sets the historical stage by showing the beginnings of software development back in the day when software was shared on paper tape for the price of the paper itself.

It then segues to Bill Gates’s Open Letter to Hobbyists in which he asks Computer Hobbyists to not share, but to buy software. (This letter was written by Gates when Microsoft was still based in Arizona and spelled “Micro-Soft”.)

Richard Stallman then explains how and why he left the MIT Lab for Artificial Intelligence in order to devote his life to the development of free software, as well as how he started with the GNU project.

Linus Torvalds is interviewed on his development of the Linux kernel as well as on the GNU/Linux naming controversy and Linux’s further evolution, including its commercialization.

Richard Stallman remarks on some of the ideological aspects of open source vis-á-vis Communism and capitalism and well as on several aspects of the development of GNU/Linux.

Michael Tiemann (interviewed in a desert) tells how he met Stallman and got an early version of Stallman’s GCC and founded Cygnus Solutions.

Larry Augustin tells how he combined the resulting GNU software and a normal PC to create a UNIX-like Workstation which cost one third the price of a workstation by Sun Microsystems even though it was three times as powerful. His narrative includes his early dealings with venture capitalists, the eventual capitalization and commodification of Linux for his own company, VA Linux, and ends with its IPO.

Frank Hecker of Netscape tells how Netscape executives released the source code for Netscape’s browser, one of the signal events which made Open Source a force to be reckoned with by business executives, the mainstream media, and the public at large.

(this text is available under the terms of the GNU Free Documentation License)

I sense més dil·lació, amb tots vosaltres els gurús i hackers que són a l’origen del Linux, del Programari Lliure (el “Free Software”, amb “free as in freedom”) , del sistema GNU i del Free and Open Source Software (FOSS).

Apaguem els llums. “Revolution OS”, the movie:

Any Nou, Sistema Operatiu Nou.

Any nou, Sistema Operatiu nou. He aprofitat uns dies lliures entre compromisos nadalencs per a reinstal·lar els sistemes operatius a l’ordinador de sobretaula i al netbook.

Els meus ordinadors són màquines antiquades, però que serveixen de sobres per a les tasques que faig amb ells. Es tracta de màquines de capacitat limitada on l’utilització d’una distribució Linux és gairebé obligatòria. Fa força temps que als ordinadors de casa hi tinc instal·lat Lubuntu, la versió d’Ubuntu lleugera basada en LXDE i OpenBox. Les coses han anat bé fins aquest any passat en que m’han anat apareixent errors de sistema esporàdics. Què estava passant?

Doncs resulta que des de la versió Ubuntu 10.10 (estem a la versió 12.10) no hi ha suport per a màquines i586.

Una mala notícia per als meus vells AMD. Val a dir que els errors que es produïen no eren, en cap cas, catastròfics; però acostumat a que les coses sempre m’anessin bé amb Linux, els errors em feien pujar la mosca al nas.

La solució ha estat la següent: al sobretaula he instal·lat Debian 6.06; i al netbook la Lubuntu 10.04 (la darrera que suporta AMD geode, el micro del meu Hercules eCAFÉ 800 20G).

Primer de tot: còpies de seguretat de tot allò digne de ser resguardat.

Al netbook, el procés d’instal·lació ha estat el mateix que he descrit en posts anteriors, només que amb la versió 10.04. No té truc: es tracta de preparar un pendrive autoarrencable amb Unetbootin i la iso del Lubuntu 10.04; engegar i anar responent les preguntes de les pantalles d’instal·lació. Un monitor extern serà útil. la connexió a Internet per cable de xarxa necessària. Un cop finalitzada la instal·lació, es pot corregir el problema de visualització reemplaçant el fitxer /etc/X11/xorg.conf pel de l’enllaç.

Pel sobretaula he descarregat la iso de la versió Debian 6.06 netinst (altres opcions aquí) i n’he cremat un CD. Engegar i anar responent les pantalles d’instal·lació. Carrego les opcions de desktop i utilitats bàsiques del sistema. Suficient. Després de la descàrrega i instal·lació dels paquets i instal·lar el GRUB, el sistema demana retirar el CD i reiniciar. Fet. Debian instal·lat. Així m’agrada. Amb seguretat.

Coses interessants a fer tot just completada la instal·lació: he afegit el meu usuari a /etc/sudoers (un costum pres de l’Ubuntu). Per això faig su, i visudo -s. Tot just a sota de la línia amb

root ALL=(ALL) ALL

afegeixo

albert ALL=(ALL) ALL

I ja tinc el meu usuari amb superpoders. Ara instal·lo XAMPP. El procés dja el vaig explicar en aquest post. De totes formes, he vist que és de força ajuda fer un sudo chmod -R 777 lampp/ i asudo chmod -R 755 lampp/etc/my.cnfabans de fer el sudo lampp/lampp start inicial.

Per a poder fer servir el phpMyAdmin també cal corregir lampp/etc/extra/httpd-xampp.conf

#
# New XAMPP security concept
#
#<LocationMatch "^/(?i:(?:xampp|security|licenses|phpmyadmin|webalizer|server-status|server-info))">
#	Order deny,allow
#	Deny from all
#	Allow from ::1 127.0.0.0/8 \
#		fc00::/7 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16 \
#		fe80::/10 169.254.0.0/16
#
#	ErrorDocument 403 /error/XAMPP_FORBIDDEN.html.var
#</LocationMatch>

<LocationMatch "^/(?i:(?:xampp|security|licenses|phpmyadmin|webalizer|server-status|server-info))">
	Order deny,allow
	Allow from all
        Require all granted
</LocationMatch>

El que he fet és permetre l’accés des de qualsevol ubicació

Finalment, també m’ha calgut establir contrasenyes per a tot amb sudo lampp/lampp security

Amb aquests canvis fets, el XAMPP ja és útil. Penso instal·lar-hi Drupal, WordPress i Symfony, a més de Magento i Prestashop. El que sigui per aprendre. Potser acabaré tenint la meva pròpia botiga virtual. Qui sap. En tot cas,la informàtica domèstica de casa torna a estar en condicions, que d’això es tractava.

Espero que aquest any sigui profitós. Amb feina, èxit als projectes que s’encetin i posts interessants al bloc.

Feliç 2013!

LinuxCon Europe 2012. Del 5 al 9 de novembre a l’Hotel Fira Palace de Barcelona.

Durant la setmana del 5 al 9 de novembre es celebrarà a Barcelona, a l’hotel Fira Palace del carrer Lleida, la LinuxCon Europe 2012.

Per a més informació, podeu vistar la pàgina oficial de la LinuxCon.

La LinuxCon és una conferència en la que acostumen a participar alguns noms importants o populars del món Linux. Per exemple, en aquesta ocasió es comptarà amb Mark Shuttleworth, el fundador de Canonical, l’empresa que impulsa les distribucions Ubuntu. Però no només amb ell. També hi seran altres actors importants, com Red Hat, SuSE, Intel, o EverNote. A la conferència es presentaran els plans dels diferents actors pel que fa al futur de Linux.

Principals oradors:

Dave Engberg Dave Engberg
Evernote
Ralf Flaxa Ralf Flaxa
SUSE
Catarina Mota Catarina Mota
Open Materials
Marten Mickos Marten Mickos
Eucalyptus
Mark Shuttleworth Mark Shuttleworth
Canonical
Imad Sousou Imad Sousou
Intel
Brian Stevens Brian Stevens
Red Hat

De la pàgina oficial:

“LinuxCon Europe 2012

November 5 – 9, 2012 · Hotel Fira Palace · Barcelona, Spain

LinuxCon Europe will bring together the best and brightest that the Linux community has to offer, including community developers, system administrators, business executives and operations experts. LinuxCon Europe will deliver attendees top notch speaking talent (from across Europe and around the globe), innovative and abundant program content, and a wide variety of opportunities to connect with peers.

Who Should Attend:
Software Developers, Linux IT Professionals, Corporate End Users, Senior Business Executives, IT Operations Experts, System Administrators, Students, Media and anyone else with an interest in the Linux ecosystem.
Schedule of Events:

Monday, November 5th
LinuxCon Europe, ELCE, EFL Developer Day, Cloud Summit

Tuesday, November 6th
LinuxCon Europe, ELCE, Cloud Summit

Wednesday, November 7th
LinuxCon Europe, ELCE, Cloud Summit, KVM Forum, oVirt Workshop

Thursday, November 8th
Gluster Workshop, Yocto Developer Day, KVM Forum, oVirt Workshop, OpenSAF Symposium

Friday, November 9th
KVM Forum, oVirt Workshop, Wireless Summit”

Com preparar un entorn de desenvolupament per a Python for Android.

En un post anterior vaig presentar el Python for Android (Py4A), que és un python per a dispositius Android que funciona sobre l’Scripting Layer For Android (SL4A).

En aquell post presentava diversos scripts que es podien executar sobre el dispositiu Android. O sobre l’emulador d’Android que es troba disponible a l’Android SDK.

Tanmateix, en eaquell post no parlava dels entorns disponibles per a desenvolupar els scripts. Com puc desenvolupar amb Py4A de forma còmoda?

Prerequisits

Evidentment, és prerequisit tenir instal·lat a la tablet l’SL4A i el Py4A.
A l’ordinador que farem servir per desenvolupar cal tenir el JDK 1.6 de Java, com a mínim. El JDK permetrà instal·lar, a continuació l’Android SDK, el qual ens proporciona les eines que ens interessen: l’adb i el ddms. El DDMS és, en realitat, una aplicació Java.

L’Android SDK proporciona un parell d’eines que seran útils per a desenvolupar scripts Python: l’Android Debug Bridge (adb) i el Dalvik Debug Monitor Server (ddms).

Per a poder aprofitar les dues eines esmentades caldrà posar l’SL4A en “mode servidor”. Amb la combinació del “mode servidor”, l’adb, el ddms, i un cable usb serem capaços de desenvolupar scripts python al nostre ordinador i executar-los de forma immediata al dispositiu Android o a l’emulador de l’Android SDK.

Connectar el dispositiu per USB.

El que ve a continuació està basat en https://help.ubuntu.com/community/AndroidSDK

El primer pas ha estat aconseguir que l’adb i el ddms reconeixessin la meva tablet. Es tracta d’un model barat de fabricació xinesa: una tab-playtabpro de Point-of-View, que munta un sistema operatiu Android 4.0.3.

Activar depuració USB
Per a fer-ho, he connectat la tablet a l’ordinador amb el cable USB. NO he activat l’emmagatzematge USB. Quan s’activa l’emmagatzematge USB, per seguretat, hi han tot un seguit d’opcions que es desactiven a la tablet. En particular, es perd la visibilitat de sdcard. Repeteixo: NO he activat l’emmagatzematge USB. Aleshores he posat la tablet en mode de “depuració USB”. Per a fer això he anat a la configuració de la tablet, a les “opcions de desenvolupador” dins del submenú “Sistema” i allà he marcat “Depuració d’USB”, com es veu a la imatge:

ID Vendor
(de http://developer.android.com/tools/device.html)
En un terminal he executat l’ordre lsusb (estic fent les meves proves amb un Linux Lubuntu 12.04).

albert@athena:~$ lsusb
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 002 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 003 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 004 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 005 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 006 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 007 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 008 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 001 Device 003: ID 058f:6254 Alcor Micro Corp. USB Hub
Bus 003 Device 002: ID 1631:5400 Good Way Technology 
Bus 003 Device 003: ID 1631:5002 Good Way Technology 
Bus 003 Device 004: ID 045e:00f6 Microsoft Corp. Comfort Optical Mouse 1000
Bus 001 Device 008: ID 0781:5535 SanDisk Corp. 
Bus 002 Device 006: ID 18d1:0003 Google Inc. 

Observo el dispositiu Bus 002 Device 006: ID 18d1:0003 Google Inc. És la meva tablet. M’interessa l’ID 18dl. Aquest paràmetre el faré servir per a crear un fitxer de filtre amb

sudo leafpad /etc/udev/rules.d/51-android.rules

Si el fitxer no existeix, aleshores el crea. Cal afegir la línia següent. En negreta, el ID Vendor que he trobat amb lsusb

SUBSYSTEM=="usb", ATTR{idVendor}=="18d1", MODE="0666", GROUP="plugdev"

i li donem els permisos adequats amb

sudo chmod a+r /etc/udev/rules.d/51-android.rules

Finalment, el fitxer que he creat:

albert@athena:~$ ls -al /etc/udev/rules.d/51-android.rules
-rw-r--r-- 1 root root 70 ago 17 19:25 /etc/udev/rules.d/51-android.rules
albert@athena:~$ 

Amb l’anterior ja hauria de tenir prou com per a poder activar el DDMS i l’ADB

Si tot ha anat bé, Engegant el DDMS (l’script ddms el trobaré a HOME_ANDROID_SDK/platform/tools/) el dispositiu apareixerà “online”, com a la imatge de sota.

En cas de no haver-se pogut establir la connexió, apareixeran ?????????.???. Caldrà revisar què és el que no hem fet bé.

En aquest moment, amb el DDMS ja podem fer coses com:
– còpies de la pantalla del dispositiu Android.
– enviar i rebre fitxers a i del dispositiu.
– analitzar el consum de memòria de les aplicacions del dispositiu
– analitzar el tràfic de xarxa de les aplicacions.
– emular l’entrada i sortida de trucades telefòniques, SMS, simular ubicació per a testejar el posicionament.
– …

Python en Server Mode

A continuació he posat l’SL4A en Server Mode. Primer de tot, obro SL4A

Per comoditat, preestableixo el port del Server Mode a 50000 (ha de ser un port lliure, i els port “alts”, per sobre de 32768 acostumen a estar-ho). Com alternativa, si poso 0 al valor del port, aleshores el SL4A és el que tria el port. En aquest últim cas, però, com em caldrà després, hauré de revisar quin port ha establert.

Per a fer-ho, primer de tot, al SL4A obro el menú de preferències:

Trio l’opció Server Port:

I l’estableixo a 50000.

A continuació, obro la vista d'”Interpreters”

Activo el mode Servidor (Start Server)

I l’inicio en mode “Privat”

En aquest moment, el SL4A – Py4A ja està iniciat en mode servidor, com podem veure a la notificació:

Vull confirmar que el port del servidor és el 50000. Si faig click a l’icona de l’SL4A s’obre la notificació que em mostra els scripts de SL4A que estan en funcionament:

I obtinc el port. Efectivament és el 50000.

Ja gairebé ho tinc tot a punt.

android.py

Finalment, he d’obtenir de la carpeta d’android el fitxer android.py i l’he de copiar a l’ordinador. Aquest android.py s’ha d’importar en els scripts de Py4A i, per tant, haurà de ser “visible” des de l’interpret de python de l’ordinador. Android.py actua com a proxy entre l’interpret de Python i l’SL4A. Aquesta comunicació quan el SL4A està en server mode es fa per RPC, de forma que realment no cal que l’interpret i el SL4A resideixin a la mateixa màquina. Aquest fet és el que permet desenvolupar a l’ordinador i executar a la tablet.

Per a obtenir el fitxer android.py de la tableta faré servir el File Explorer del ddms.

El fitxer es troba a /sdcard/com.googlecode.pythonforandroid/extras/python

Aleshores fent servir el botó “Pull File From Device”, El botó amb l’icona del disquet del File Explorer, puc copiar android.py a la carpeta de fonts python amb la que treballaré. En el meu cas l’he copiat a /home/albert/workspace/wk-python/prova-py4a

adb forwardind
Finalment, obro un terminal i em poso a la carpeta de fonts, i faig el següent:

albert@athena:~/workspace/wk-python/prova-py4a$ /home/albert/android-sdk-linux/platform-tools/adb start-server
albert@athena:~/workspace/wk-python/prova-py4a$ /home/albert/android-sdk-linux/platform-tools/adb forward tcp:9999 tcp:50000
albert@athena:~/workspace/wk-python/prova-py4a$ export AP_PORT=9999

El port 9999 és troba a la major part de la literatura sobre SL4A, però és per convenció.

Fixem-nos que les quatre línies anteriors sempre seran les mateixes, o sigui que les puc posar en un script.

Dit i fet, el meu script per engegar l’entorn d’sl4a és aquest.

#!/bin/bash

/home/albert/android-sdk-linux/tools/ddms &
/home/albert/android-sdk-linux/platform-tools/adb start-server
/home/albert/android-sdk-linux/platform-tools/adb forward tcp:9999 tcp:50000
export AP_PORT=9999
idle &

Li en dono els permisos d’execució (chmod a+x sl4a)

A L’script primer engego el ddms per si em cal passar scripts de l’ordinador a la tablet, o per si cal controlar l’us de memòria, comunicacions, o per si vull fer còpies de pantalla… Després engeo l’adb i li passo els ports 9999, i 50000 (el que he preestablert per al SL4A Server Mode). Finalment, exporto la variable AP_PORT i, finalment, engego IDLE, l’IDE de pyhton construit amb TkInter.

En cas que no el tingueu instal·lat, IDLE es pot instal·lar amb Synaptic, o des del Centre de Programari de l’Ubuntu

Finalment, des de l’IDLE faig la prova canònica:

I, com no podria ser d’una altre forma, funciona 😉

Hem construït un entorn de desenvolupament per a SL4A i Py4A amb:
Un cable USB
Un ordinador amb
Lubuntu 12.01
JDK1.6
Android SDK
IDE de Python IDLE
Una tablet barata.

Albert Baranguer – 26.08.2012

HTML5. WebSockets amb Jetty

Continuant amb l’exploració de l’HTML5, avui parlo dels WebSockets. I per a il·lustrar-lo, faré un petit exercici amb websockets fent servir el servidor java Jetty.

La idea de l’exemple l’he tret del projecte http://code.google.com/p/phpwebsocket/. El client html és pràcticament idèntic. La part del servidor, en el meu exemple, l’he implementada amb Java; estant en PHP en l’original.

Els WebSockets permeten realitzar la comunicació bidireccional i full-duplex sobre un canal TCP, entre la pàgina web i el servidor de forma molt més eficient que amb l’esquema de petició-resposta de l’HTML, o que la comunicació amb AJAX o, més explí­citament, mitjançant Comet.

El protocol WebSocket es descriu a la RFC6455.

A dia d’avui, aquest protocol es troba implementat per les darreres versions dels principals navegadors:  Chrome, Internet Explorer, Firefox i Safari.

A més, el servidor ha de soportar també els WebSockets de servidor.

En si mateix el protocol recorda una crida HTTP, però no ho és:

Exemple de petició des del client:

GET /mychat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat
Sec-WebSocket-Version: 13
Origin: http://example.com

Exemple de resposta del servidor:

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
Sec-WebSocket-Protocol: chat

Des del punt de vista del desenvolupament parlem, doncs, de clients i servidors de WebSockets.
HTML5 ofereix una API (normalitzada pel W3C) per a desenvolupar clients d’aplicacions basades en WebSocket.

Per la banda del servidor, els llenguatges més utlitzats, com Java o php aporten classes, mètodes i funcions per a crear i utilitzar websockets de servidor.

Sobre servidors Java: jWebSocket, Apache Tomcat i Jetty també tenen implementacions de sockets de servidor.
Sobre servidors PHP: phpWebSockets

Podem trobar implementacions de servidor WebSocket amb altres llenguatges. Per exemple amb Python una cerca ens proporciona: pywebsocket, o WebSocket for Python (ws4py).

Per a fer un experiment senzill utilitzaré el servidor de java servlets Jetty (versió 8) que proporciona una implementació java de WebSockets de servidor. Podeu revisar aquest article: Jetty WebSocket Server, i aquest altre: Jetty/Feature/WebSockets.
Per descomptat, el javadoc: http://download.eclipse.org/jetty/stable-8/apidocs/

Per a provar els WebSockets he creat un petit projecte amb Eclipse, amb la següent estructura de carpetes:

A la carpeta src hi creo el package java amb la següent classe: com.stsoftlliure.proves.html5.websockets.ProvaWebSocket

Fitxer ProvaWebSocket.java:

package com.stsoftlliure.proves.html5.websockets;

import java.io.IOException;
import java.util.Date;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.eclipse.jetty.websocket.WebSocket;
import org.eclipse.jetty.websocket.WebSocketServlet;
import org.eclipse.jetty.util.log.Log;
public class ProvaWebSocket extends WebSocketServlet  {

   private static final long serialVersionUID = 8442112605629206192L;


    protected void doGet(HttpServletRequest request,
                         HttpServletResponse response)
    throws ServletException ,IOException  {
      response.getOutputStream().println("aquesta és la resposta");
      Log.info("[doGet] No fa res");
    }

    public WebSocket doWebSocketConnect(HttpServletRequest request,
                                        String protocol) {
      Log.info("[doWebSocketConnect] entra a doWebSocketConnect");
      return new ProvaServerWebSocket();
    }

    // inner class que implementa la interface WebSocket
    class ProvaServerWebSocket implements WebSocket.OnTextMessage {
      // la connexió
      Connection wsConn;
      Date dataInici;

      public ProvaServerWebSocket() {
         dataInici = new Date();
      }

      @Override
      public void onClose(int arg0, String arg1) {
         // TODO Auto-generated method stub
         Log.info("[onClose] arg0:" + arg0 + "; arg1: " + arg1);
         wsConn.close(arg0, arg1);
      }

      @Override
      public void onOpen(Connection wsConn) {
         this.wsConn = wsConn;
         try {
            Log.info("[onOpen] wsConn:" + wsConn);
            wsConn.sendMessage("Estic connectat!");
         } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            Log.debug("Error: " + e.toString());
         }
      }

      @Override
      public void onMessage(String sMessage) {
         // TODO Auto-generated method stub
         Log.info("[onMessage] Missatge rebut: " + sMessage);
            // fa el tractament:
         // to do
         // help, hola, nom, edat, data, adeu
 
         try {
            if (sMessage.trim().equalsIgnoreCase("help")) {
               wsConn.sendMessage("ordres acceptades: help, hola, nom, edat, data, adeu");
            }

            if (sMessage.trim().equalsIgnoreCase("hola")) {
               wsConn.sendMessage("Hola! benvingut al servidor WebSocket!");
            }

            if (sMessage.trim().equalsIgnoreCase("nom")) {
               wsConn.sendMessage("Jo em dic ProvaWebSocket.class");
            }

            if (sMessage.trim().equalsIgnoreCase("edat")) {
               wsConn.sendMessage("En funcionament des de: " + dataInici);
            }

            if (sMessage.trim().equalsIgnoreCase("data")) {
               wsConn.sendMessage("ara són les: " + (new Date()));
            }

            if (sMessage.trim().equalsIgnoreCase("adeu")) {
               wsConn.sendMessage("Adéu siau!");
               wsConn.close();
            }
         } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            Log.debug("Error: " + e.toString());
         }
      }
   }
}

Algunes remarques a la classe anterior:

– La classe és un servlet del tipus nou WebSocketServlet.
– Els WebSocketServlet implementen el doGet. El puc invocar directament i obtinc una resposta HTML. Però la funcionalitat apareix amb el nou mètode doWebSocketConnect. A doWebSocketConnect és on treballa el socket de servidor.
-Quan es rep una nova connexió, el mètode doWebSocketConnect el que fa és instanciar la inner class ProvaServerWebSocket, que implementa la interface WebSocket.OnTextMessage.
– La interface WebSocket té altres subinterfaces, a més de la OnTextMessage, per a tractar altres tipus de connexions. Les subinterfaces de WebSocket són: WebSocket.OnBinaryMessage, WebSocket.OnControl, WebSocket.OnFrame, WebSocket.OnTextMessage.
– en la connexió onOpen, obtinc l’objecte Connection wsConn. Que serà propiament el canal full duplex entre servidor i client.
– El tractament d’un missatge rebut el faig en implementar el mètode public void onMessage(String sMessage). sMessage és el missatge rebut des del client. Les respostes al client s’envien mitjançant l’objecte Connection wsConn obtingut en l’event onOpen. En aquest exemple, simplement analitzo quin missatge rebo i, depenent del missatge, envio una o altre resposta al client.
– A la sortida onClose, tanco la connexió

Senzill i net, oi?

L’altre part és la pàgina del client. Una pàgina HTML5 que fa servir websockets ha d’implementar els mètodes que responen als esdeveniments de connexió, alliberament, recepció i enviament de missatges al servidor. Vet aquí la pàgina HTML que dialoga amb el servidor:

Fitxer prova.html

<html>
<head>
<title>WebSocket</title>

<style>
 html,body{font:normal 0.9em arial,helvetica;}
 #log {width:440px; height:200px; border:1px solid #7F9DB9; overflow:auto;}
 #msg {width:330px;}
</style>

<script>
var socket;

function init(){
  var host = "ws://localhost:8080/websockets2/servlet/WebSocket";
  try{
    socket = new WebSocket(host);
    log('WebSocket - status '+socket.readyState);
    socket.onopen    = function(msg){ log("Welcome - status "+this.readyState); };
    socket.onmessage = function(msg){ log("Received: "+msg.data); };
    socket.onclose   = function(msg){ log("Disconnected - status "+this.readyState); };
  }
  catch(ex){ log(ex); }
  $("msg").focus();
}

function send(){
  var txt,msg;
  txt = $("msg");
  msg = txt.value;
  if(!msg){ alert("El missatge no pot ser nul"); return; }
  txt.value="";
  txt.focus();
  try{ socket.send(msg); log('Enviat: '+msg); } catch(ex){ log(ex); }
}

function quit(){
  log("Adéu!");
  socket.close();
  socket=null;
}

// Utilities
function $(id){ return document.getElementById(id); }
function log(msg){ $("log").innerHTML+="<br>"+msg; }
function onkey(event){ if(event.keyCode==13){ send(); } }
</script>

</head>
<body onload="init()">
 <h3>WebSocket</h3>
 <div id="log"></div>
 <input id="msg" type="textbox" onkeypress="onkey(event)"/>
 <button onclick="send()">Send</button>
 <button onclick="quit()">Quit</button>
 <div>Commands: help, hola, nom, edat, data, adeu</div>
</body>
</html>

Alguns comentaris: com es veu, la pàgina és senzilla, la clau del funcionament està al mètode init

function init(){
  var host = "ws://localhost:8080/websockets2/servlet/WebSocket";
  try{
    socket = new WebSocket(host);
    log('WebSocket - status '+socket.readyState);
    socket.onopen    = function(msg){ log("Welcome - status "+this.readyState); };
    socket.onmessage = function(msg){ log("Received: "+msg.data); };
    socket.onclose   = function(msg){ log("Disconnected - status "+this.readyState); };
  }
  catch(ex){ log(ex); }
  $("msg").focus();
}

El qual crea l’objecte socket, el connecta al host, adonem-nos del protocol “ws”; i enllaça els esdeveniments del socket: onopen, onmessage, onclose amb les funcions que els tracten.

L’enviament de missatges al servidor es realitza amb la funció send, que invoca al mètode send de l’objecte socket.

function send(){
  var txt,msg;
  txt = $("msg");
  msg = txt.value;
  if(!msg){ alert("El missatge no pot ser nul"); return; }
  txt.value="";
  txt.focus();
  try{ socket.send(msg); log('Enviat: '+msg); } catch(ex){ log(ex); }
}

Amb l’anterior ja tinc tot el codi. Poso la pàgina prova.html a la carpeta webcontent del projecte Eclipse. A més, en la carpeta webcontent també hi posaré la carpeta WEB-INF, amb una subcarpeta classes buida, la carpeta lib amb les llibreries que em calen i el web.xml.

En resum, a webcontent hi trobo:

webcontent/
    prova.html
    WEB-INF/
        classes/
        lib/
            jetty-util-8.1.2.v20120308.jar
            jetty-websocket-8.1.2.v20120308.jar
        web.xml

Les llibreries a la carpeta lib corresponen a la versió 8 del Jetty i es poden trobar a la seva carpeta lib. He vist que cal incloure-les al war. Em pensava que es referenciarien automàticament al fer deploy al Jetty, però no ha estat així.

El web.xml és força estàndar:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
	http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
	<display-name>tailor</display-name>
	<servlet>
		<servlet-name>WebSocket</servlet-name>
		<servlet-class>com.stsoftlliure.proves.html5.websockets.ProvaWebSocket</servlet-class>
	</servlet>
	<servlet-mapping>
		<servlet-name>WebSocket</servlet-name>
		<url-pattern>/servlet/*</url-pattern>
	</servlet-mapping>

	<welcome-file-list>
		<welcome-file>prova.html</welcome-file>
	</welcome-file-list>
</web-app>

Si de cas, remarcar el mapeig de /servlet/* a WebSocket. Aquesta és l’explicació de
var host = “ws://localhost:8080/websockets2/servlet/WebSocket”;
a la funció init.

Amb Jetty, a més, m’ha calgut establir el “context.” Al projecte, el fitxer de context l’he guardat a la carpeta homònima. És el següent fitxer.

websockets2.xml:

<?xml version="1.0"  encoding="ISO-8859-1"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure.dtd">

<Configure class="org.eclipse.jetty.webapp.WebAppContext">
  <Set name="contextPath">/websockets2</Set>
  <Set name="war"><SystemProperty name="jetty.home" default="."/>/webapps/websockets2.war</Set>
</Configure>

Compte amb els salts de línia. El fitxer XML de context potser no queda “bonic” tal com està, però us podeu trobar amb problemes estranys si afegiu salts de línia.

Amb el fitxer anterior li estem dient quin és el war que ha de desplegar i quin nom tindrà el context. Amb aquest fitxer de context acabem de completar l’explicació del

var host = “ws://localhost:8080/websockets2/servlet/WebSocket”;

Ara és evident:

ws:// – Protocol websocket. Amb websockets segurs, serà wss://
localhost:8080 – Host i port del servidor websocket
websockets2 – El context, o l’aplicació web.
servlet/WebSocket – el Servlet servidor de WebSocket

Finalment, per automatitzar el muntatge i ddesplegament al servidor jetty local de l’apliació, faig servir el següent build.xml d’ant:

<project name="websocket" default="deploy" basedir=".">
  <description>
  proves amb websockets 2
  </description>

  <!-- estableix propietats globals -->
  <property name="src" location="src"/>
  <property name="bin" location="bin"/>
  <property name="war" location="war"/>
  <property name="webcontent" location="webcontent" />
  <property name="context" location="context" />
  <property name="build" location="build"/>
  <property name="lib" location="/home/albert/jetty/lib" />
  <property name="deploy" location="/home/albert/jetty/webapps" />
  <property name="deploy-context" location="/home/albert/jetty/contexts" />


  <target name="init">
    <!-- time stap -->
    <tstamp/>
    <!-- Crea el directory bin utilitzat en la compilació -->
    <mkdir dir="${bin}"/>
  </target>


  <target name="compile" depends="init" description="compila els fitxers font" >
    <!-- Compila les fonts java de ${src} en ${build} -->
    <javac srcdir="${src}" destdir="${bin}">
      <classpath>
       <pathelement location="${lib}/jetty-websocket-8.1.2.v20120308.jar"/>
       <pathelement location="${lib}/servlet-api-3.0.jar"/>
       <pathelement location="${lib}/jetty-util-8.1.2.v20120308.jar"/>
      </classpath>
   </javac>
  </target>

  <target name="build" depends="compile" description="genera el build">
    <!-- Crea el directori de distribució -->
    <delete dir="${build}" />
    <mkdir dir="${build}"/>

    <!--  copia webcontent a build -->
    <copy todir="${build}" >
        <fileset dir="${webcontent}" />
    </copy>
    
    <!--  copia bin a classes -->
    <copy todir="${build}/WEB-INF/classes">
        <fileset dir="${bin}" />
    </copy>
  </target>
     
  <target name="war" depends="build" description="genera el war">   
    <delete file="${war}/websockets2.war" />
    <jar jarfile="${war}/websockets2.war" basedir="${build}"> 
      <include name="**/*"/>
    </jar>
  </target>
  	
  <target name="deploy" depends="war" description="deploy del war i del context">
    <delete file="${deploy}/websockets2.war" />
    <delete file="${deploy-context}/websockets2.xml" />
  	<copy file="${war}/websockets2.war" todir="${deploy}" />
  	<copy file="${context}/websockets2.xml" todir="${deploy-context}" /> 
  </target>

</project>

Aleshores, ses d’Eclipse puc executar el build.xml que compila la classe, construeix el war i el copia a la carpeta webapps del meu Jetty local.

Per provar l’aplicació només en cal engegar el Jetty i obrir el navegador amb suport de websockets (en el meu cas, el Chrome) i apuntar-lo a l’adreça http://localhost:8080/websockets2

I el resultat és el següent.

I, efectivament, puc mantindre el diàleg amb el servidor:

Lubuntu a un Hercules eCafe 800

En un post anterior vam instal·lar Ubuntu de la darrera versió Netbook Remix en un petit Hercules eCafe 800.La maquineta ha funcionat prou bé però ha estat evident, des del primer dia, que Gnome li anava gran.

La solució ha estat posar una distribució més lleugera. En aquest cas la Lubuntu. Lubuntu és una distribució que fa servir LXDE com a entorn d’escriptori i OpenBox com a gestor de finestres, amb la intenció de minimitzar el consum de recursos que calen. Vegeu també http://ca.wikipedia.org/wiki/Lubuntu. Per cert, està disponible en català.
La instal·lació no ha tingut complicacions: he descarregat la iso de http://cdimages.ubuntu.com/lubuntu/releases/11.10/release/ i he creat un pendrive d’instal·lació.
Per a fer la instal·lació m’ha estat útil connectar un monitor extern, degut a que la petita pantalla de l’eCafe no era suficient per als continguts que s’estaven mostrant. Aquest problema ja el vaig trobar en l’anterior instal·lació de Ubuntu (Gnome), i es resol igual: un cop feta la instal·lació cal afegir el fitxer xorg.conf a /etc/X11.
Un cop feta la instal·lació i fet l’ajust de pantalla, ja es pot treballar amb el netbook. Ens adonarem d’alguns canvis, per exemple en el programari instal·lat. Per mi el més cridaner ha estat trobar Chromium, el navegador open-source que ha servit de base per a Chrome, en comptes de Firefox. I també trobar Synaptic, en comptes del “Centre de Programari de l’Ubuntu”.
La millora en la reproducció de vídeos i música és evident. També en la reproducció de vídeos online.
Pegues, de moment, poques. Li he instal·lat Thunderbird, LibreOffice, Skype… i el netbook els mou de forma que són útils. No està gens malament per a aquesta petita maquina.
Potser encara posaré Lubuntu a l’ordinador de sobretaula.

Groovy i HSQLDB

Groovy aporta classes per al treball amb bases de dades que permeten un increment de la productivitat en el desenvolupament de codi d’aplicacions que en facin us.

Dit d’una altre forma: Groovy permet simplificar el codi de les aplicacions de bases de dades.  Res millor que un exemple.

En l’exercici que es proposa faré us de la base de dades HSQLDB, que és el motor de base de dades que es fa servir, per exemple,  a les suites ofimàtiques OpenOffice.org i LibreOffice.

Per a poder utilitzar HSQLDB des de Groovy cal afegir el hsqldb.jar a les llibreries que es carreguen a l’inici. En el cas que m’ocupa, un Groovy sobre Linux Ubuntu, cal revisar el fitxer $GROOVY_HOME/conf/groovy-starter.conf en el que es pot trobar el següent:

    # load user specific libraries
    load !{user.home}/.groovy/lib/*.jar

Tinc l’HSQLDB instal·lat a una carpeta apart. Per tant, per evitar duplicitats, en comptes de posar l’hsqldb.jar a $HOME/.groovy/lib, el que faig és posar en aquesta carpeta un enllaç simbòlic al jar que hi ha a la carpeta on està desplegat el motor de la base de dades.

A continuació, creo una base de dades del tipus fitxer i standalone. Cal dir que HSQLDB pot funcionar en “mode servidor” o  bé en “mode standalone”. A més, també pot funcionar o bé mantenint les dades en un fitxer, o bé “mantenint” les dades en memòria (evidentment, només es “mantenen” mentre el procés és viu).

Per a crear la taula es pot fer servir, per exemple, l’OpenOffice.org/LibreOffice Base. En aquest cas caldria afegir hsqldb.jar al classpath de l’OOo/LO (eines – opcions – java)

i camí a les classes:

i, a continuació, crearia una base de dades del tipus Jdbc.

En el meu cas, la base de dades la fico a Documentes/databases/hsqldb/prova.db, per tant la URL JDBC que he de fer servir és:


jdbc:hsqldb:/home/albert/Documents/databases/hsqldb/prova.db

i el driver:

org.hsqldb.jdbc.JDBCDriver

(canviar aquesta imatge)

Això em permetrà fer servir l’assistent de l’OOo/LO per a definir la taula i els tipus de dades. Creo una taula diccionari amb tres columnes: id (enter), nom (varchar de 100) i valor (varchar de 100).

I  un cop creada la taula, la informo amb algunes dades de prova:

Probablement la classe més útil de Groovy per al treball amb bases de dades sigui groovy.sql.Sql.  Aquesta classe és una mena de navalla suïssa que proporciona una varietat de mètodes per a fer gairebé totes les tasques comuns amb taules.

La pàgina de la documentació de groovy.sql.Sql ens explica les possibilitats d’aquesta navalla suïssa.

Per a utilitzar-la  cal importar-la

import groovy.sql.Sql

A continuació puc configurar la connexió a la base de dades que he creat fa un moment:

def ds = [url:’jdbc:hsqldb:/home/albert/Documents/databases/hsqldb/prova.db’, 
          user:’sa’, 
          password:”, 
          driver:’org.hsqldb.jdbc.JDBCDriver’]
def sql = Sql.newInstance(ds.url, ds.user, ds.password, ds.driver)

Consulto la taula que he creat amb el OOo/LO amb el potent mètode eachRow, adonem-nos com la fila en curs és accessible amb ‘it’:

sql.eachRow(“select * from public.\”diccionari\” where \”id\” >= 3″) {
    println “id = ${it.id}; nom = ${it.nom}; valor = ${it.valor}”
}

i el resultat és

albert@athena:~/Workspace/wk-groovy/prova-jdbc$ groovy provabd.groovy 
id = 3; nom = nom3; valor = valor3
id = 4; nom = nom4; valor = valor3
id = 5; nom = nom5; valor = valor5

Creo una nova taula i la informo amb dades. Cal tenir en compte algunes particularitats de l’SQL de  l’HSQLDB: els noms de les taules han d’anar precedides de l’esquema; els noms de taules, columnes i  d’altres objectes van entre cometes dobles; els literals van entre comentes simples.

sql.execute ”’
create table public.\”traductor\” (
   \”id\” integer not null,
   \”catala\” varchar(100),
   \”castella\” varchar(100),
)
”’


Faig insert de dades amb diferents mètodes:

– fent servir la sintaxi amb ”’
sql.execute ”’
insert into public.\”traductor\”(\”id\”,\”catala\”,\”castella\”) values (1, ‘catala 1′,’castella 1’) 
”’
println “inserta fila 1”

–  fent servir la sintaxi de preparedStatement de JDBC
def params = [2, ‘catala 2’, ‘castella 2’]
sql.execute(“insert into public.\”traductor\” (\”id\”, \”catala\”, \”castella\”) values (?, ?, ?)”, params)
println “inserta fila 2”


params = [3, ‘catala 3’, ‘castella 3’]
sql.execute(“insert into public.\”traductor\” (\”id\”, \”catala\”, \”castella\”) values (?, ?, ?)”, params)
println “inserta fila 3”

– fent servir la sintaxi GString
def map = [id:4, catala:’català 4′, castella:’castellà 4′]
sql.execute(“insert into public.\”traductor\” (\”id\”, \”catala\”, \”castella\”) values ($map.id, $map.catala, $map.castella)”)
println “inserta fila 4”

Visualitzo els resultats amb eachRow, ara fent servir el paràmetre fila a la closure, en comptes d’it.

sql.eachRow(“select * from public.\”traductor\””) {fila ->
    println “id = $fila.id; català = $fila.catala; castellà = $fila.castella”
}

Després d’aquestes accions, tinc les dues taules amb dades, com puc veure amb l’OOo/LO

He fet inserts, evidentment també es poden fer update, delete… fent servir execute, o millor  executeUpdate, que em retorna una llista de les files afectades.

eachRow admet paginació. He afegit algunes dades més a la taula traductor i la visualitzo, primer tota i després mostrant només quatre files a partir de la fila tres:

println “mostra la taula \”traductor\””
sql.eachRow(“select * from public.\”traductor\””) {fila ->
    println “id = $fila.id; català = $fila.catala; castellà = $fila.castella”
}


println “mostra 4 files de la taula \”traductor\” a partir de la fila 3″
sql.eachRow(“select * from public.\”traductor\””, 3, 4 ) {fila ->
    println “id = $fila.id; català = $fila.catala; castellà = $fila.castella”
}

El resultat és:
mostra la taula “traductor”

id = 1; català = catala 1; castellà = castella 1
id = 2; català = catala 2; castellà = castella 2
id = 3; català = catala 3; castellà = castella 3
id = 4; català = català 4; castellà = castellà 4
id = 5; català = catala 5; castellà = castella 5
id = 6; català = catala 6; castellà = castella 6
id = 7; català = catala 7; castellà = castella 7
id = 8; català = català 8; castellà = castellà 8
mostra 4 files de la taula “traductor” a partir de la fila 3
id = 3; català = catala 3; castellà = castella 3
id = 4; català = català 4; castellà = castellà 4
id = 5; català = catala 5; castellà = castella 5
id = 6; català = catala 6; castellà = castella 6

La classe groovy.sql.Sql té altres mètodes que també permeten augmentar la productivitat del desenvolupament, que s’afegeixen al fet que els scripts en Groovy no requereixen compilació. 
groovy.sql.Sql també té mètodes que retornen ResultSet de jdbc, permetent el tractament típic de Java.
El package groovy.sql també inclou la classe  DataSet que deriva de groovy.sql.Sql i que permet fer queries a la base de dades fent servir operadors i noms de camps de Groovy en comptes de crides a API JDBC i noms de taules i columnes. Un petit exemple:
Primer de tot, cal importar DataSet
import groovy.sql.DataSet
i ja el puc fer servir:
primer mostro el contingut de la taula amb eachRow d’Sql i després fa la query amb DataSet
println “mostra la taula \”traductor\””
sql.eachRow(“select * from public.\”traductor\””) {fila ->
    println “id = $fila.id; català = $fila.catala; castellà = $fila.castella”
}
println “mostra valors amb id entre 3 i 7 fent servir la classe DataSet”
def traduccions = sql.dataSet(“public.\”traductor\””)
def filtrat = traduccions.findAll {it.”\”id\”” >= 3 && it.”\”id\”” <= 7 }
filtrat.each { fila ->
    println “id = $fila.id; català = $fila.catala; castellà = $fila.castella”
}
El resultat és el següent
mostra la taula “traductor”
id = 1; català = catala 1; castellà = castella 1
id = 2; català = catala 2; castellà = castella 2
id = 3; català = catala 3; castellà = castella 3
id = 4; català = català 4; castellà = castellà 4
id = 5; català = catala 5; castellà = castella 5
id = 6; català = catala 6; castellà = castella 6
id = 7; català = catala 7; castellà = castella 7
id = 8; català = català 8; castellà = castellà 8

mostra valors amb id entre 3 i 7 fent servir la classe DataSet
id = 3; català = catala 3; castellà = castella 3
id = 4; català = català 4; castellà = castellà 4
id = 5; català = catala 5; castellà = castella 5
id = 6; català = catala 6; castellà = castella 6
id = 7; català = catala 7; castellà = castella 7
En aquest exemple les particularitats de HSQLDB pel que fa als noms de taules i columnes entre cometes  no permeten aprofitar la simplificació de fer servir els noms de les columnes com si fossin noms de camps Groovy de l’objecte it. Però, si més no, es permet veure l’ús de l’operador && per a construir el “where” del Dataset
I finalment, per acabar…
// tanca la connexió
sql.close()

Com instal·lar XAMPP a Ubuntu.

A l’hora de fer presentacions i demos d’aplicacions web, pot ser molt útil disposar d’una versió de la demo en un servidor web en localhost, potser en una clau usb.

També pot ser molt útil un servidor web d’aquest tipus a l’hora de preparar sessions de formació.

És possible trobar per Internet diverses distribucions que d’aquest tipus que combinen Apache amb  MySQL i PHP, tant sobre Linux (LAMP), com sobre Windows (WAMP), per exemple XAMPP o MoWeS Portable.

MoWeS Portable empaqueta, a més de la plataforma AMP, altres aplicacions que utilitzen aquesta infraestructura, com Joomla! o Drupal. Ara bé, MoWeS Portable només està disponible per a Windows.

XAMPP, en canvi, s’ofereix per a Linux i Windows.

En el post d’avuí el que faig és instal·lar el XAMPP a un netbook Linux.

No cal dir que la plataforma AMP es pot instal·lar des del Centre de Programari de l’Ubuntu. Però la instal·lació de XAMPP és encara més senzilla.

Dit i fet. El primer pas és descarregar-se el paquet d’instal·lació. El  puc trobar en aquesta adreça:  http://sourceforge.net/projects/xampp/files/BETAS/xampp-linux-1.7.7.tar.gz/download. Són uns 77MB.

Aquesta versió incorpora:   Apache, MySQL, PHP amb PEAR, Perl, ProFTPD, phpMyAdmin, OpenSSL, GD, Freetype2, libjpeg, libpng, gdbm, zlib, expat, Sablotron, libxml, Ming, Webalizer, pdf class, ncurses, mod_perl, FreeTDS, gettext, mcrypt, mhash, eAccelerator, SQLite i IMAP C-Client.

En particular Apache 2.2.21, MySQL 5.5.16, PHP 5.3.8, i phpMyAdmin 3.4.5.

Per instal·lar  n’hi ha prou amb desempaquetar el tar.gz. Cal tenir en compte que als fitxers d’instal·lació s’assumeix que la home del XAMPP serà /opt/lampp. Això NO vol dir que només es pugui instal·lar en aquesta carpeta. Això l’únic que vol dir és que caldrà que /opt/lampp sigui la “home” del XAMPP i això es podrà aconseguir amb un enllaç simbòlic des de /opt/lampp al directori d’instal·lació que triï.

Si la carpeta de destinació del XAMPP es troba en una clau usb caldrà que aquesta clau estigui formatada amb un sistema de fitxers que permeti els enllaços simbòlics, per exemple  ext2,ext3 o ext4.

A l’hora de fer l’enllaç simbòlic caldrà tenir en compte el punt de muntatge del dispositiu. Per exemple, al meu netbook amb Ubuntu 11.04, el punt de muntatge és /media.

Per a poder instal·lar correctament el XAMPP caldran, a més, permisos de root. I també per a posar en marxa i aturar els diversos components de la distribució.

Tenint en compte tot l’anterior, instal·lo el XAMPP a la meva carpeta home, on he deixat el tar.gz:

sudo tar xvfz xampp-linux-1.7.7.tar.gz -C .

Amb la ordre anterior es crea una carpeta lampp amb els dversos components del XAMPP.

Ara creo l’enllaç simbòlic:

sudo ln -s /home/albert/lampp /opt/lampp

I engego per primer cop el servidor. Aquesta primera arrencada acaba d’instal·lar i configurar els components.

albert@athena:~$ sudo /opt/lampp/lampp start
Starting XAMPP for Linux 1.7.7…
XAMPP: Starting Apache with SSL (and PHP5)…
XAMPP: Starting MySQL…
XAMPP: Starting ProFTPD…
XAMPP for Linux started.
albert@athena:~$ 

I per revisar la nova instal·lació en tinc prou amb obrir un navegador i apuntar-lo al localhost:

El directori arrel del servidor web es troba a /opt/lampp/htdocs (en el meu cas, mercès a l’enllaç simbòlic, és /home/albert/lampp/htdocs).

Per aturar el XAMPP puc fer:

albert@athena:~$ sudo /opt/lampp/lampp stop
Stopping XAMPP for Linux 1.7.7…
XAMPP: Stopping Apache with SSL…
XAMPP: Stopping MySQL…
XAMPP: Stopping ProFTPD…
XAMPP stopped.
albert@athena:~$ 

I per a eliminar la instal·lació sense deixar rastres, amb el XAMPP aturat, n’hi ha prou amb fer:
sudo rm -r lampp
És possible engegar i aturar individualment els diversos components del XAMPP. Examinant el fitxer $HOME/lampp/lampp es poden  trobar les diverses opcions disponibles. La forma d’invocar-les, en general, és: 
sudo /opt/lampp/lampp opció

Retardador de subtítols SubRip (again). BeanShell version.

El mateix retardador de subtítols del post anterior, ara fet amb BeanShell.

Suposant que guardo l’script al fitxer delayer.bsh i que tinc el fitxer prova.srt del post anterior, aleshores puc retardar l’aparició dels subtítols amb la següent línia de comandes:

./delayer.bsh prova.srt output.srt 10000

Vet aquí el codi:

#!/usr/bin/bsh
 

import java.text.DateFormat;
import java.text.SimpleDateFormat;

// obté els arguments
args = this.interpreter.get(“bsh.args”);

// verifica que té 3 arguments
if (args.length != 3 ) {
    sUsage = “Usage:\tdelayer filein.srt fileout.srt delay” +
             “\n\texample 1: delayer filein.srt fileout.srt 10000 –> delays 10 seconds” +
             “\n\texample 2: delayer filein.srt fileout.srt -10000 –> advances 10 seconds”;

    print(“incorrect number of arguments”);
    print(sUsage);
    System.exit(1);
}

// calcula el delta
lDelta = Long.parseLong(args[2]);

// prepara el SimpleDateFormat
sdf = new SimpleDateFormat(“HH:mm:ss,SSS”);

// obre els fitxers args[0] per a lectura i args[1] per a escriptura
fFileIn = new FileReader(args[0]);
fFileOut = new FileWriter(args[1]);
brFileIn = new BufferedReader(fFileIn);
brFileOut = new BufferedWriter(fFileOut);

while((sLine = brFileIn.readLine()) != null) {
    sTokens= sLine.split(“–>”);   // mètode split() milor que StringTokenizer des de JDK1.4.
    if (sTokens.length == 2) {
    // si té dos elements, és la línia de temps

    // els “pela”
    sTempsInici = sTokens[0].trim();
    sTempsFi = sTokens[1].trim();

    // els converteix a Date
    dTempsInici = sdf.parse(sTempsInici);
    dTempsFi = sdf.parse(sTempsFi);

    // Els suma el retard
    dTempsIniciDelayed = new Date(dTempsInici.getTime() + lDelta);
    dTempsFiDelayed = new Date(dTempsFi.getTime() + lDelta);

    // converteix els Date a String amb el mateix SimpleDateFormat
    sTempsIniciDelayed = sdf.format(dTempsIniciDelayed);
    sTempsFiDelayed = sdf.format(dTempsFiDelayed);

    // Munta la línia
    sLine = sTempsIniciDelayed + ” –> ” + sTempsFiDelayed;
    }

    // escriu la línia
    brFileOut.write(sLine);
    // i salta de línia
    brFileOut.newLine();
}

// tanca fitxers i buffered readers / writers
brFileOut.close();
brFileIn.close();
fFileIn.close();
fFileOut.close();

// i acaba aquí

Molt poques coses a dir. Si de cas destacar que:

No he declarat els tipus de cap variable.

No ha calgut importar les classes de java.io ni java.util.En canvi sí les de java.text

Us de

// obté els arguments
args = this.interpreter.get(“bsh.args”);

per obtenir els arguments de la línia de comandes.

Evidentment, la versió amb Python i optparse era més potent funcionalment pel que fa al tractament dels arguments d’entrada. Ara bé, a la simplicitat en aquest cas juga a favor i fa que l’argument de retard negatiu es tracti de forma més natural que amb la solució Python.

Us del format HH:mm:ss,MMM (hores de  0 a 23:minuts:segons,milisegons) al SimpleDateFormat, tant per a parsejar strings com per a formatar dates.

I finalment, l’us d’Split, en comptes del clàssic StringTokenizer per a obtenir els temps d’inici i final de presentació dels subtítols.

Què és més eficient? Python o BeanShell? Per a aquest cas, ho deixo en empat. Per mi ha estat més fàcil BeanShell que Python, però crec que això respon al fet que estic molt més familiaritzat amb Java.

El proper pas podria ser escriure el mateix script en Groovy. Sospito que només caldrà tocar el pas d’arguments i poca cosa més.

Un retardador de subtítols SubRip amb Python

EL cap de setmana passat em vaig dedicar a buscar subtítols en anglès per a pel·lícules en anglès. Va molt bé per a l’aprenentatge i la pràctica de l’idioma el poder veure la pel·lícula en versió original i, a l’hora, poder llegir el que estan dient en el mateix idioma. I després, tornar a veure la pel·lícula sense subtítols. Són tècniques d’aprenentatge.

El cas és que vaig trobar alguns subtítols però als posar-los al VLC amb la pel·lícula corresponent resultava que no estaven sincronitzats amb la imatge. El VLC permet adelantar o retardar el temps d’aparició dels subtítols, però és poc pràctic, ja que cal sincronitzar cada cop que obres la pel·lícula. O, almenys, no vaig veure com guardar la sincronització; i amb el reproductor de DVD del menjador és pitjor perquè no tinc la  possibilitat de sincronitzar.

Per tant, vaig decidir que la solució era fer un retardador per als subtítols i així generar fitxers de subtítols per a les pel·lícules que tinc i que estiguin correctament sincronitzats amb la imatge.

En el post d’avui, doncs,programo una petita aplicació amb python (versió 2.6.5, un pel antiga, però és la que tinc instal·lada a l’Ubuntu) que servirà per retardar o avançar el moment d’aparició de subtítols en un fitxer de subtítols del tipus SubRip (extensió .srt).

La WikiPedia diu del format SubRip qu és “probablement, el més senzill dels formats de subtítols”. Els fitxers SubRip amb extensió .srt, són de text pla. El format de temps emprat és “hores:minuts:segons,milisegons”. El separador decimal és una coma, en comptes d’un punt perquè l’especificació original és francesa. El salt de línia és, tot sovint,CR+LF. Els subtítols es numeren en seqüència, començant per 1. Els subtítols se separen per una línia en blanc.

En resum:

Número del subtítol
temps d’inici –> temps de fi
text del subtítol (una o més línies) 

Línia en blanc.

un exemple de fixer SubRip (prova.srt)

1
00:00:20,000 –> 00:00:24,400
Altocumulus clouds occur between six thousand

2
00:00:24,600 –> 00:00:27,800
and twenty thousand feet above ground level.

 Doncs bé, amb el següent script Python podré retardar o avançar l’aparició dels subtítols.

#!/usr/bin/python
# coding: latin-1

# L’objectiu del programa és retardar o adelantar el temps d’aparició del subtítol
# el programa pren tres arguments: fitxer original, fitxer de sortida i
# temps expressat en mili segons a avançar o retardar (un negatiu, vol dir avançar)
# El fitxer de sortida serà un fitxer .srt igual a l’original, però amb tots els
# temps retardats en la quantitat de mili segons indicada

import optparse 
from datetime import datetime
from datetime import timedelta

def main():
    # analitza els arguments rebuts
    usage = “\t%prog filein.srt fileout.srt delay”
    usage = usage + “\n\texample 1: %prog filein.srt fileout.srt 10000 –> delays 10 seconds”
    usage = usage + “\n\texample 2: %prog filein.srt fileout.srt — -10000 –> advances 10 seconds”
    parser = optparse.OptionParser(usage)
    (options, args) = parser.parse_args()

 
    # if args != 3 (fitxer d’entrada, fitxer de sortida, delay) aleshores error
    if len(args) != 3:
        parser.error(“incorrect number of arguments”)

    # calcula el delta
        delta =  timedelta(milliseconds=int(args[2]))

    # obre el fitxer per llegir-lo
    f1 = open(args[0])
    f2 = open(args[1], “w”)
    for line in f1:
            tokens = line.split(” –> “)
            if len(tokens) == 2:
            
        # strip() –> trim spaces
           
        TempsInici = datetime.strptime(tokens[0].strip(), “%H:%M:%S,%f”) + delta
           
        TempsFi = datetime.strptime(tokens[1].strip(), “%H:%M:%S,%f”) + delta

           
        # multilínia amb “\”
           
        line = \
           
        str(TempsInici.hour).zfill(2) + “:” + \
           
        str(TempsInici.minute).zfill(2) + “:” + \
           
        str(TempsInici.second).zfill(2) + “,” + \
           
        str(int(TempsInici.microsecond / 1000)).zfill(3) + ” –> ” + \
           
        str(TempsFi.hour).zfill(2) + “:” + \
           
        str(TempsFi.minute).zfill(2) + “:” + \
           
        str(TempsFi.second).zfill(2) + “,” + \
           
        str(int(TempsFi.microsecond/ 1000)).zfill(3) + “\r\n”
   
        
            f2.write(line)
    

    f1.close()
    f2.close()

#main
if __name__ == “__main__”:
    main()

El programa és trivial, però en faré alguns comentaris.

Us d’optparse per a fer el tractament dels arguments d’entrada.  A partir de la versió 2.7 aquest mòdul es considera obsolet i cal fer servir argparse. Ara bé, la forma com funcionen tots dos mòduls és similar. A destacar com es fa el missatge d’us, i com es tornen en un diccionari les opcions (que en aquest cas, no n’hi han) i en un array els arguments posicionals (fitxer-d’entrada, fitxer de sortida, retard).

A destacar com passar arguments negatius (en aquest cas, això vol dir avançar l’aparició dels subtítols) fent servir  “–“. El exemple que acompanya l’explicació de l’ús és aclaridor:

delayer.py entrada.srt sortida.srt — -10000

Avança l’aparició dels subtítols 10 segons

En cas de no disposar dels arguments suficient, finalitza l’execució de l’script amb parser.error

Immediatament que ha tractat els arguments d’entrada, procedeix a calcular el retard amb:

delta =  timedelta(milliseconds=int(args[2]))

A continuació obre el fitxer original per a lectura i el fitxer de sortida.

La iteració línia a línia pel fitxer original es senzilla amb la potent sintaxi del for:

for line in f1:

Per a cada línia, verifica si es pot dividir amb –>. Les línies que tinguin –> seran les de temps i les que cal tractar.

El càlcul dels nous temps es fa amb el parell de línies

TempsInici = datetime.strptime(tokens[0].strip(), “%H:%M:%S,%f”) + delta

TempsFi = datetime.strptime(tokens[1].strip(), “%H:%M:%S,%f”) + delta

A destacar l’us de strptime  que converteix una cadena en un objecte datetime fent servir una cadena de format. El format “%H:%M:%S,%f” correspon a  hora:minut:segon,milisegons. El mètode strip(), per la seva part, elimina espais en blanc per davant i pel darrera de la cadena, com el trim de java.

Finalment, els camps útils dels datetime TempsInici i TempsFi es reordenen i s’ajunten en una cadena de text en la següent línia

line = \
str(TempsInici.hour).zfill(2) + “:” + \
str(TempsInici.minute).zfill(2) + “:” + \
str(TempsInici.second).zfill(2) + “,” + \
str(int(TempsInici.microsecond / 1000)).zfill(3) + ” –> ” + \
str(TempsFi.hour).zfill(2) + “:” + \
str(TempsFi.minute).zfill(2) + “:” + \
str(TempsFi.second).zfill(2) + “,” + \
str(int(TempsFi.microsecond/ 1000)).zfill(3) + “\r\n”

Aquesta instrucció llarga es divideix en vàries línies fent servir \.
Els camps numèrics es converteixen a cadena amb str().
El mètode de cadena zfill(longitud) zero fill posa zeros per l’esquerra fins a que la cadena té la longitud indicada.
Els datetime no tenen un camp millisecond, però sí que el tenen microsecond. aleshores divideixo aquest camp per 1000 i el converteixo a enter amb int(). Finalment afegeixo un CR+LF

Per acabar, fora de l’if, s’escriu al fitxer de sortida la línia. Si era una línia amb –> haurà estat processada, si no, es manté com a l’original.

Es tanquen els fitxers i s’invoca el main. Aquest if __name__ == “__main__”:  és un “truc” de python que permet que aquest script pugui ser utilitzat com a llibreria des d’un altre script: en aquest cas, la variable __name__ prendria un valor diferent de __main__.