It has been a long time since I updated an app which we have developed with SmartGWT. This time we needed to upload a file to update some catalog.

The SmartGWT documentation explains different ways to achieve this, we’re interested in this part

Background upload without the Smart GWT Server

Achieving background file upload without using the Smart GWT server is also possible although considerably more advanced. In addition to the steps above, create a hidden ** element in the page, and **use DynamicForm.target to target the form submission at this IFRAME. In order receive a callback notification when the upload completes, after processing the file upload, your server should output HTML content for the IFRAME that includes a block which will navigate out of the IFRAME (generally via the JavaScript global “top”) and call a global method you have declared as a callback.

So, the first thing we are going to need is the server side service to receive such file. This app had REST web services with Apache CXF already working, so we should use this same framework (credit goes to javatips for this).

import org.apache.cxf.jaxrs.ext.multipart.Attachment;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.activation.DataHandler;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.util.List;

@Path("catalogos")
public class CatalogosREST {
    private final Logger log = LoggerFactory.getLogger(CatalogosREST.class);

    private String getFileName(MultivaluedMap<String, String> header) {
        String[] contentDisposition = header.getFirst("Content-Disposition").split(";");
        for (String filename : contentDisposition) {
            if ((filename.trim().startsWith("filename"))) {
                String[] name = filename.split("=");
                String exactFileName = name[1].trim().replaceAll("\"", "");
                return exactFileName;
            }
        }
        return "unknown";
    }

    @POST
    @Path("/upload")
    @Consumes("multipart/form-data")
    public Response uploadFile(List<Attachment> attachments, @Context HttpServletRequest request) {
        log.debug("se ha recibido una peticion para subir un archivo de proveedores [attachments: {}, request: {}]", attachments, request);
        Response response = null;
        for (Attachment attachment : attachments) {
            DataHandler handler = attachment.getDataHandler();
            try {
                InputStream stream = handler.getInputStream();
                MultivaluedMap<String, String> map = attachment.getHeaders();
                log.debug("headers: {}", map);
                log.debug("fileName: {}", getFileName(map));
                OutputStream out = new FileOutputStream(new File("/tmp/" + getFileName(map)));

                int read = 0;
                byte[] bytes = new byte[1024];
                while ((read = stream.read(bytes)) != -1) {
                    out.write(bytes, 0, read);
                }
                stream.close();
                out.flush();
                out.close();
                StringBuilder scriptOnFileUploadFinished = new StringBuilder(
                        "<script type=\"application/javascript\">\n" +
                                "      window.top.onFileUploadFinished();\n" +
                                "    </script>"
                );
                response = Response.ok(scriptOnFileUploadFinished.toString()).build();
            } catch (Exception e) {
                String error = "Ocurrio un error al subir el archivo de proveedores";
                log.error(error, e);
                response = Response.serverError().entity(error).build();
            }
        }
        return response;
    }
}

You can test this REST web service with something like postman.

It’s worth noting that we are returning a block of script tag from this web service. This is necessary to notify our client code that the file upload has finished.

Now, on the client side let’s make our code as per the docs.

final DynamicForm frmUpload = new DynamicForm();
        NamedFrame iframeUpload = new NamedFrame("iframeUpload");
        iframeUpload.setVisible(false);
        frmUpload.setTarget(iframeUpload.getName());
        frmUpload.setEncoding(Encoding.MULTIPART);
        frmUpload.setMethod(FormMethod.POST);
        UploadItem itmUpload = new UploadItem("itmProveedores", "Archivo Proveedores");
        frmUpload.setItems(itmUpload);
        frmUpload.setAction(GWT.getHostPageBaseURL() + "api/catalogos/upload");
        IButton btnUpload = new IButton("Subir archivo proveedorees");
        btnUpload.addClickHandler(new ClickHandler() {
            @Override
            public void onClick(ClickEvent clickEvent) {
                frmUpload.submitForm();
            }
        });

for this to work, we need to register the callback method using JSNI, let’s do that

private static void onFileUploadFinished(){
        SC.say("upload finished");
    }

    private native void registerOnFileUploadFinished()/*-{
        $wnd.onFileUploadFinished = @com.sample.CatalogosForm::onFileUploadFinished();
    }-*/;

...
/*on the constructor, load event or main entry point -it depends on your app-*/
public CatalogosForm(){
    /*initial setup*/
    registerOnFileUploadFinished();
}

and that’s it, your File should upload successfully and display a message when the upload is finished.

sources:
https://www.smartclient.com/smartgwt/javadoc/com/smartgwt/client/docs/Upload.html
http://www.javatips.net/blog/cxf-rest-file-upload?page=2
http://stackoverflow.com/questions/8488078/get-call-back-from-servlet-in-gwt-when-upload-file-in-smartgwts-dynamic-form-wi
https://webtoolkit.googleblog.com/2008/07/getting-to-really-know-gwt-part-1-jsni.html
http://stackoverflow.com/questions/2161388/calling-a-parent-window-function-from-an-iframe

Dokku is like Heroku running on your own servers. This is pretty cool.

Normally, you should push your changes to the dokku repository and watch your code compile and run smoothly. But sometimes, the process of gathering dependencies and compiling your code takes to much time and processor, that sometimes gets rejected or it simply won’t compile on dokku server.
Probably, you already have a valid, tested, runnning jar in your development machine that you want to run on dokku, so how could you push this jar to the dokku server? well, let’s see…

In heroku, you have, among other options, the command

heroku deploy:jar

I don’t know of such a command for dokku[1]. but nevertheless you can deploy any jar(Many Java and JVM applications are packaged into a self-contained executable JAR file that includes application code, configuration and dependencies. These artifacts are often called “uberjars” or “fat jars”) with the following trick.

This article explains how to get a metabase runnable jar onto dokku. I’ve tried and it almost worked, the only really difference is that you have to create a java application skeleton with a maven archetype, and put your jar into version control.

so, first let’s create the skeleton for our java app deployment

mvn archetype:generate -DgroupId=com.sample -DartifactId=sample-jar-deploy -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false

then run the following commands to configure and deploy to dokku

cd sample-jar-deploy
cp ~/Apps/sample-app/target/sample-app.jar .
echo "web: java -jar sample-app.jar" > Procfile
git init
git add .
git commit -m 'sample-app.jar version 0.1'
ssh dokku@where.your.dokku.is.com apps:create sample-app
git remote add dokku dokku@where.your.dokku.is.com:sample-app
git push dokku master

to upgrade your app

cp ~/Apps/sample-app/target/sample-app.jar .
git commit -am 'sample-app.jar version 0.2'
git push dokku master

Beware that this is definitively going to increase the size of the repository. This is only a temporary repository to have the jar deployed, your source and main repository should be in ~/Apps/sample-app/

to clean up and start over

cd sample-jar-deploy
rm -rf .git/
cp ~/Apps/sample-app/target/sample-app.jar .
git init
git add .
git commit -m 'sample-app.jar version 0.3'
ssh dokku@where.your.dokku.is.com apps:destroy sample-app
ssh dokku@where.your.dokku.is.com apps:create sample-app
git remote add dokku dokku@where.your.dokku.is.com:sample-app
git push dokku master

Of course you can deploy using buildpacker, dockerfile, docker image, and tarball based deploys which are documented.

Fuentes:
View story at Medium.com
http://dokku.viewdocs.io/dokku/
https://devcenter.heroku.com/articles/deploying-executable-jar-files
http://www.mkyong.com/maven/how-to-create-a-java-project-with-maven/

[1]:

 Hi, do anyone know if there's command like 'heroku deploy:jar' available for dokku
[16:09]  https://devcenter.heroku.com/articles/deploying-executable-jar-files#using-the-heroku-deploy-cli-plugin
[16:13]   no
[16:13]   we support buildpacker, dockerfile, docker image, and tarball based deploys
[16:14]   http://dokku.viewdocs.io/dokku/getting-started/installation/
[16:14]   sidebar on that page shows our deployment methods

Dokku es tu propio Heroku, gracias a Docker. Es un servicio de PaaS (Platform as a Service) para que puedas desplegar (deployear) tus aplicaciones de la misma manera que lo haces en Heroku.

Instalar Dokku es muy sencillo, yo lo hice en una máquina virtual con Ubuntu 16.04 siguiendo la documentación

wget https://raw.githubusercontent.com/dokku/dokku/v0.8.2/bootstrap.sh
sudo DOKKU_TAG=v0.8.2 bash bootstrap.sh

Una vez instalado, abres un navegador con la dirección http://localhost para completar la instalación. Puedes dejar los valores por default o modificarlos de acuerdo a tu configuración. Por ejemplo, si no configuras la IP, el valor por default es la IP publica del servidor. Decidí configurar el servidor con una IP estática y ese fue el valor que puse en el instalador. En el campo Public Key debes poner la llave publica de la computadora desde la cual vas a realizar los deployments (tu máquina de desarrollo). Más adelante puedes agregar otras llaves.
Para obtener tu llave pública, si no tienes una, ejecuta el siguiente comando en tu máquina de desarrollo

ssh-keygen -t rsa

y guarda bien el passphrase que escojas.Luego copia el contenido del archivo ~/.ssh/id_rsa al campo Public Key

Una vez terminado ya puedes crear y deployear aplicaciones.

Para crear una nueva aplicación (vamos a utilizar el ejemplo ruby-rails-sample), primero creala en el servidor dokku mediante el comando

dokku apps:create ruby-rails-sample

luego en tu maquina de desarrollo, clona la aplicación (primero debes agregar tu llave publica a tu cuenta de github)

git clone git@github.com:heroku/ruby-rails-sample.git

ingresa en la carpeta y agrega el repositorio remoto

cd ruby-rails-sample
git remote add dokku dokku@{dokku server ip}

sube la aplicación y mira como se inicia con el comando
gzip: stdin: unexpected end of file
! tar: Unexpected EOF in archive

git push dokku master

la primera vez no pude ejecutar la aplicación de manera exitosa, me aparecía un error

gzip: stdin: unexpected end of file
tar: Unexpected EOF in archive

este error esta documentado en la guía de errores y se debe a una conexión a internet lenta. Para solucionarlo, simplemente ejecuta los siguientes comandos en el servidor de dokku

dokku config:set --global CURL_TIMEOUT=600
dokku config:set --global CURL_CONNECT_TIMEOUT=30

y vuelve a publicar tu aplicación

git push dokku master

Para instalar nodejs hay muchas opciones, cada una con sus pros y contras. Si haces una búsqueda en Google los 2 primeros resultados son:

https://www.digitalocean.com/community/tutorials/como-instalar-node-js-en-ubuntu-16-04-es
https://nodejs.org/en/download/package-manager/

En la primera opción te explican diferentes maneras de instalar nodejs

1.- Desde los repositorios
ejecuta

sudo apt install -y nodejs nodejs-legacy

Ventaja: Fácil
Desventaja: No es la versión más actual (instala la versión 4.2.6) y no instala npm por default (tienes que ejecutar sudo apt install npm)
Resumen: Si no puedes vivir sin una versión reciente, esta opción no es para ti.

2.- Ejecutando un script remoto

curl -sL https://deb.nodesource.com/setup_6.x | sudo -E bash –
sudo apt-get install -y nodejs
sudo apt-get install -y build-essential

Ventaja: Fácil e incluye la instalación de npm por default. La última línea permite la compilación de paquetes de npm.
Desventaja: Tienes que confiar en que el script remoto no es malicioso
Resumen: Esta es la opción que utilicé porque es la misma que se menciona en la documentación oficial.

3.- Utilizar NVM (Node Version Manager)

… (demasiado largo, referirse al enlace)

Ventaja: Puedes cambiar entre versiones de node.
Desventaja: Es más complejo y en algunos casos marca errores.
Resumen: Apto para usuarios avanzados con requerimientos específicos.

Fuentes:
https://www.google.com.mx/search?hl=es-419&output=search&sclient=psy-ab&q=ubuntu+16.04+install+node&btnG=&oq=&aq=&aqi=&aql=&gs_l=&pbx=1
https://www.digitalocean.com/community/tutorials/como-instalar-node-js-en-ubuntu-16-04-es
https://nodejs.org/en/download/package-manager/#debian-and-ubuntu-based-linux-distributions

Tenemos un servidor al que necesitabamos otorgar acceso por TeamViewer a personal externo a nuestra oficina.

Lo primero que hice fue ingresar por conexion remota de windows (RDP, en realidad con remmina desde una máquina con Ubuntu) -debido a que el servidor se encuentra en una locación remota- para instalar TeamViewer.
Una vez instalado, tomé nota del id y el password y lo envíe al personal externo para que pudieran ingresar, no sin antes cerrar la sesión remota por RDP. Inmediatamente nos reportaron que no podían iniciar sesión por TeamViewer, que se quedaba congelado intentando hacer la conexión.
El problema se debe a que TeamViewer crea un ID distinto por cada usuario del host, y cuando la sesión del usuario se bloquea o se cierra (es el caso cuando cierras la conexion por RDP) no puede acceder al sistema. Lo que necesitas es utilizar el ID del Sistema que genera TeamViewer y haberlo instalado como un servicio de windows (debe estar de esta manera si seleccionaste la opción de acceso desatendido durante la instalación). Lo puedes obtener dando clic en el icono de un foco que aparece a un lado del ID de usuario en TeamViewer.

Lo otro que hay que considerar es cerrar la sesión remota por RDP, si la tuvieras abierta. Luego iniciar sesión desde TeamViewer con el ID del sistema. Nos va a aparecer bloqueada la PC y debemos presionar CTRL-ALT-SUPR (puedes enviar esta combinación de teclas en el menú de TeamViewer) para iniciar sesión con el usuario que pretendemos realizar la sesión remota. Una vez iniciada la sesión podemos tomar nota de las credenciales de TeamViewer y enviarlas al personal externo. Cuando terminemos la sesión de TeamViewer, esta no cierra la sesión ni bloquea la PC, lo que permite el acceso sin problemas al sistema.

1b4bc9ea-1add-4f77-acf5-5e5eb2ab94fc

fuentes:
http://teamviewerforums.com/index.php?topic=1332.0

EDIT: wordpress removed html sample code and I don’t know how to fix it, in the meantime here’s a link to the source of the article.

I was trying to use a simple checkbox on a form, so I just copied the sample from bootstrap. But I forgot that I was using FuelUX on that form, so it never showed the checkbox. Apparently, there’s a problem mixing checkbox controls -at least in this case-.

So, my form was like this

<form name="editForm" role="form" novalidate>
    <div class="fuelux">
        ...
            <div class="modal-body">
            <div class="wizard" id="myWizard">
            ...

I’m using a wizard control inside a modal dialog. Well, it didn’t show up but the label. So instead, of using the bootstrap checkbox sample code I opted for using the FuelUX checkbox.

<div class="checkbox" id="myCheckbox">


    <span class="checkbox-label">Custom checkbox unchecked on page load</span>

</div>

now the checkbox was displaying just fine. It’s time to bind that the model, so I just added an ng-model attribute like so

<div class="checkbox" id="myCheckbox">


    <span class="checkbox-label">Custom checkbox unchecked on page load</span>

</div>

and it worked great, but only one way. I mean, when you tick the checkbox it updates the model, but if you update the model -anyhow- it won’t change it’s displayed value (won’t check or uncheck). So, you have to update the checkbox state by adding a ng-class attribute, which will add or remove a ‘checked’ class according to the model.

Finally, we have this working like a charm

<div class="checkbox" id="myCheckbox">


    <span class="checkbox-label">Privada</span>

</div>

Sources:
https://docs.angularjs.org/api/ng/directive/ngChecked
https://docs.angularjs.org/api/ng/directive/ngClass

HandBrakeCLI es una excelente herramienta para transcodificar (convertir) vídeos de un formato a otro.

Para instalarlo en Linux RedHat Enterprise Linux puedes agregar un repositorio que ya tenga el paquete compilado, por ejemplo nux-desktop o linuxtech, y luego hacer un simple `yum install HandBrake-cli’.

Fue lo que hice primero, desafortunadamente estos repositorios contienen una versión anterior de HanBrakeCLI (0.9.5). Lo que ocasionó un error (HandBrakeCLI: unrecognized option '--audio-copy-mask') al tratar de usar un parámetro que esta disponible en la versión 0.10.2

Para instalar la última versión en Linux RHEL 7, debes compilar el paquete mediante los siguientes comandos (mediante sudo o como usuario root).

Activamos el repositorio opcional para poder encontrar las dependencias necesarias (este comando es para Oracle Linux).

yum-config-manager --enable ol7_optional_latest

Para RHEL, puedes buscar el nombre del repositorio opcional mediante el comando yum repolist all y verificar que este activado.

Instalamos los requisitos para poder compilar

yum groupinstall "Development Tools" "Development Libraries" "X Software Development" "GNOME Software Development" --setopt=group_package_types=mandatory,default,optional
yum install yasm zlib-devel bzip2-devel libogg-devel libtheora-devel libvorbis-devel libsamplerate-devel libxml2-devel fribidi-devel freetype-devel fontconfig-devel libass-devel dbus-glib-devel libgudev1-devel webkitgtk-devel libnotify-devel gstreamer-devel gstreamer-plugins-base-devel opus-devel jansson-devel

Obtenemos e instalamos la librería libmp3lame antes de compilar HandBrakeCLI, de otra forma nos marcará el error ERROR: libmp3lame &gt;= 3.98.3 not found durante la compilación

cd /usr/src
wget http://downloads.sourceforge.net/project/lame/lame/3.99/lame-3.99.5.tar.gz -O lame-3.99.5.tar.gz
tar xzvf lame-3.99.5.tar.gz
cd lame-3.99.5.tar.gz
./configure
make
make install

Obtenemos e instalamos la librería x264

cd /usr/src
git clone git://git.videolan.org/x264.git
cd x264
./configure --enable-static --enable-shared
make
make install
echo '/usr/local/lib' >> /etc/ld.so.conf
ldconfig

Obtenemos el código fuente y compilamos únicamente la versión de línea de comandos (HandBrake CLI)

cd /usr/src
git clone https://github.com/HandBrake/HandBrake.git handbrake
cd handbrake
./configure --launch --disable-gtk
cd build
make install

Antes, podías verificar la versión instalada ejecutando el comando HanBrakeCLI -u, pero al parecer esta opción ya no esta disponible, así que para verificar que se haya instalado correctamente puedes intentar realizar la conversión de algún vídeo.

Fuentes:
http://xmodulo.com/how-to-install-handbrake-on-linux.html
http://xmodulo.com/how-to-install-handbrake-on-linux.html
https://access.redhat.com/solutions/1310043
https://access.redhat.com/solutions/265523
http://stackoverflow.com/questions/35937403/error-libmp3lame-3-98-3-not-found
http://ask.xmodulo.com/fatal-error-x264-h-no-such-file-or-directory.html