We have a bunch of tables per office, each on its own schema in an Oracle Database.

For simplicity, we have created views on this tables and synonyms to access this views from one single schema. Hibernate supports mapping entities to synonyms, so there’s nothing more we need to do on our entities. But we still want to be able to add and modify underlying data.

To achieve this we had to create views and triggers like so

SET DEFINE OFF;
CREATE OR REPLACE FORCE VIEW CENTRAL.VPARTES
(
   SID_PARTE,
   ...
)
AS
   SELECT 'OF001' SID_OFICINA,
          T.SID_PARTE,
          ...
     FROM OFICINA001.TPARTES T
    WHERE 1 = 1 AND T.NESTATUS IN (1, 2)
   UNION ALL
   SELECT 'OF002' SID_OFICINA,
          T.SID_PARTE,
          ...
     FROM OFICINA002.TPARTES T
    WHERE 1 = 1 AND T.NESTATUS IN (1, 2);


CREATE OR REPLACE TRIGGER CENTRAL.TG_VPARTES_UPD
INSTEAD OF UPDATE
ON CENTRAL.VPARTES
FOR EACH ROW
BEGIN
IF :OLD.SID_OFICINA = 'OF001' THEN
UPDATE OFICINA001.TPARTES
SET    
       SESTATUS       = :NEW.SESTATUS,
       ...
WHERE  1 = 1
AND SID_PARTE        = :NEW.SID_PARTE ;
ELSIF :OLD.SID_OFICINA = 'OF002' THEN 
UPDATE OFICINA002.TPARTES
SET    
       SESTATUS       = :NEW.SESTATUS,
       ...
WHERE  1 = 1
AND SID_PARTE        = :NEW.SID_PARTE ;
END IF;
END;
/

for the INSERT it would be kind of the same.

If you happen to use JHipster, to allow the usage of synonyms while passing the liquibase validation we need to add a hibernate property on our corresponding application-{profile}.yml or disable liquibase altogether.

spring:
    profiles:
        include: no-liquibase
...
    jpa:
        database-platform: org.hibernate.dialect.Oracle12cDialect
        database: ORACLE
        show-sql: false
        properties:
            ...
            hibernate.synonyms: true

sources:
http://stackoverflow.com/questions/6531729/updatable-view-oracle
http://stackoverflow.com/questions/8817253/jpa-entiy-on-synonym-instead-of-table

Anuncios

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

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 >= 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

Supongamos que tenemos una entidad como la siguiente

@Entity
public class Book {

    @Id
    private String id;

    @ManyToMany
    private Collection<Author> authors;

...

}

y su respectivo autor

@Entity
public class Author{
    @Id
    private Long id;

    @Column(name = "NAME")
    private String name;
...    
}

y necesitamos realizar una consulta para listar los libros cuyo nombre del autor contiene un nombre ingresado por el usuario.
Con SQL la consulta sería algo como

SELECT book.id,
       book.title,
       book.isbn,
       author.name AS author
  FROM books book
       JOIN book_authors ba ON ba.book_id = book.id
       JOIN authors author ON ba.author_id = author.id
 WHERE author.name LIKE '%' || :name || '%'

Para realizar esta consulta en JPA la podemos realizar utilizando el Criteria API de la siguiente manera

public List<Book> search(String author){
    CriteriaBuilder cb = em.getCriteriaBuilder();
    CriteriaQuery<Book> cq = cb.createQuery(Book.class);
    Root<Book> from = cq.from(Book.class);
    ParameterExpression<String> authorParameter = cb.parameter(String.class);
    cq.where(cb.like(from.join("author").get("name"), authorParameter));
    TypedQuery<Book> q = em.createQuery(cq);
    q.setParameter(authorParameter, "%" + author + "%");
    return q.getResultList();
}

La parte importante es la línea

cq.where(cb.like(from.join(“author”).get(“name”), authorParameter));

particularmente el método join de la clase Root

Fuentes:
http://www.objectdb.com/java/jpa/query/criteria#Parameters_in_Criteria_Queries_
http://stackoverflow.com/questions/2883887/in-jpa-2-using-a-criteriaquery-how-to-count-results
http://stackoverflow.com/questions/10708809/how-to-query-for-entities-by-their-collection-value

Supongamos que tenemos una entidad simple como la siguiente

@Entity
public class VideoTag {

    @Id
    String id;

    @Column(name = &quot;TITULO&quot;)
    String titulo;

    @Column(name = &quot;TIMESTAMP&quot;)
    LocalTime timestamp;

    @Column(name = &quot;PRIVADA&quot;)
    Boolean privada;

... constructor, getters, setters, etc

Si guardamos un objeto de esta clase en la base de datos, los campos Boolean y LocalTime serán persistidos como [‘true, ‘false’] o ‘{hour: 10, minute:0, seconds: 0}’, respectivamente.

Esto puede ser aceptable, pero hay ocasiones que necesitamos representar esos tipos de datos de forma diferente en la base de datos, por ejemplo, si hay un sistema existente que guarda ‘S’ o ‘N’ para los valores booleanos o una cadena como ’10:20:00′ para representar un objeto LocalTime.

En estos casos es necesario utilizar un ‘Convertidor’ para traducir entre la base de datos y nuestra aplicación. Por ejemplo,

@Converter
public class BooleanToStringConverter implements AttributeConverter&lt;Boolean, String&gt;{

    @Override
    public String convertToDatabaseColumn(Boolean value) {
        return (value != null &amp;&amp; value) ? &quot;S&quot; : &quot;N&quot;;
    }

    @Override
    public Boolean convertToEntityAttribute(String value) {
        return &quot;S&quot;.equals(value) ? true : false;
    }

}

y para la clase LocalTime

@Converter
public class LocalTimeToStringConverter implements AttributeConverter&lt;LocalTime, String&gt;{

    @Override
    public String convertToDatabaseColumn(LocalTime value) {
        return value.toString();
    }

    @Override
    public LocalTime convertToEntityAttribute(String value) {
        return LocalTime.parse(value);
    }

}

Con ello ya podemos guardar y leer de la base de datos en el formato esperado, solo basta especificar en nuestra entidad que vamos a utilizar estos Convertidores

    @Column(name = &quot;TIMESTAMP&quot;)
    @Convert(converter = LocalTimeToStringConverter.class)
    LocalTime timestamp;

    @Column(name = &quot;PRIVADA&quot;)
    @Convert(converter = BooleanToStringConverter.class)
    Boolean privada;

Si utilizaste un archivo persistence.xml para escpecificar las entidades, también debes agregar los convertidores

...
&lt;!--    Convertidores--&gt;
    &lt;class&gt;mx.gob.queretaro.brujula.tsj.domain.jpa.BooleanToStringConverter&lt;/class&gt;
    &lt;class&gt;mx.gob.queretaro.brujula.tsj.domain.jpa.LocalTimeToStringConverter&lt;/class&gt;
...

Genial! pero cuando intentamos enviar uno de estos objetos a través de un servicio REST como JSON -utilizando Jackson-, sigue utilizando el formato anterior, crap!

Bueno, pues también tenemos que especificar la forma en que vamos a serializar los datos con Jackson. Para ello, necesitamos implementar 2 interfaces JsonSerializer y JsonDeserializer

/**
 * Convierte un objeto LocalTime y lo convierte a una representacion serializable de tipo cadena
 * p. ej. &quot;10:15:30&quot; o &quot;13:45.30.123456789&quot;
 */
public class LocalTimeSerializer extends JsonSerializer&lt;LocalTime&gt;{

    @Override
    public void serialize(LocalTime t, JsonGenerator jg, SerializerProvider sp) throws IOException, JsonProcessingException {
        jg.writeString(t.toString());
    }

}
/**
 * Convierte una cadena a un objeto LocalTime
 */
public class LocalTimeDeserializer extends JsonDeserializer&lt;LocalTime&gt;{

    @Override
    public LocalTime deserialize(JsonParser jp, DeserializationContext dc) throws IOException, JsonProcessingException {
        return LocalTime.parse(jp.getText());
    }

}

Y finalmente las agregamos a la entidad

@Column(name = &quot;TIMESTAMP&quot;)
    @Convert(converter = LocalTimeToStringConverter.class)
    @JsonSerialize(using = LocalTimeSerializer.class)
    @JsonDeserialize(using = LocalTimeDeserializer.class)
    LocalTime timestamp;

Fuentes:
http://stackoverflow.com/questions/1154833/configure-hibernate-using-jpa-to-store-y-n-for-type-boolean-instead-of-0-1
http://stackoverflow.com/questions/27952472/serialize-deserialize-java-8-java-time-with-jackson-json-mapper
http://stackoverflow.com/questions/27574855/registering-converters-in-jpa-2-1-with-eclipselink

If you need to convert a video there’s a great tool called HandBrake. There’s also a command line utility called HandBrakeCLI which we can call from our java program as any other executable program.

Executing an external program from Java should be trivial, but it is not. There are several articles on the web and questions on stackoverflow about this. And remember that its platform specific.

Our environment is Ubuntu 16.04 with HanbrakeCLI 0.10.2 and JDK 8.

We needed to convert some video file and replace it with the new converted file. From CLI it would be

mv {fullPath} {fullPath}.converting
HandBrakeCLI -i {fullPath}.converting -o {fullPath} --preset 'Normal'
rm {fullPath}.converting

from Java we changed the command because it was not taking the ‘preset’ parameter, so instead of using the Normal preset we explicitly set the same parameters used by the preset

HandBrakeCLI -i {fullPath}.converting -o {fullPath} -e x264 -q 20.0 -a 1 -E faac -B 160 -6 dpl2 -R Auto -D 0.0 –audio-copy-mask aac,ac3,dtshd,dts,mp3 –audio-fallback ffac3 -f mp4 –loose-anamorphic –modulus 2 -m –x264-preset veryfast –h264-profile main –h264-level 4.0

We ended up with this code

public void convertVideo(String fullPath) throws IOException, InterruptedException {
    log.debug(&quot;Converting video: &quot; + fullPath);
    Runtime.getRuntime().exec(String.format(&quot;mv %s %s.converting&quot;, fullPath, fullPath)).waitFor();
    /**
     * Invoke HandBrakeCLI
     * epxlicitly set parameters from --preset 'Normal'
     * https://trac.handbrake.fr/wiki/BuiltInPresets#normal
     */
    String cmd = new StringBuilder(String.format(&quot;HandBrakeCLI -i %s.converting -o %s -e x264  -q 20.0 -a 1 -E faac -B 160 -6 dpl2 -R Auto -D 0.0 --audio-copy-mask aac,ac3,dtshd,dts,mp3 --audio-fallback ffac3 -f mp4 --loose-anamorphic --modulus 2 -m --x264-preset veryfast --h264-profile main --h264-level 4.0&quot;, fullPath, fullPath)).append(&quot; 2&gt;&amp;1&quot;).toString();
    log.debug(String.format(&quot;Command used: %s&quot;, cmd));
    Process p = Runtime.getRuntime().exec(cmd);
    /**
     * Cleanup async the streams to not block the main thread
     * http://stackoverflow.com/a/8596996/961652
     */
    new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                BufferedReader rdr = new BufferedReader(new InputStreamReader(p.getInputStream()));
                String rdrLine;
                while (((rdrLine = rdr.readLine()) != null)) {
//                    System.out.println(&quot;out: &quot; + rdrLine);
                }
                /**
                 * After converting the file, delete the created temporal file
                 */
                Runtime.getRuntime().exec(String.format(&quot;rm %s.converting&quot;, fullPath));
            } catch (IOException e) {
                log.error(String.format(&quot;Tehre was a problem converting the video: %s&quot;, fullPath), e);
            }
        }
    }).start();
    log.debug(&quot;Video conversion process invoked&quot;);
}

notice that we didn’t take care of other things that could possibly go wrong, like spaces in filenames or paths. This was sufficient for our needs.

sources:
http://stackoverflow.com/a/3403259/961652
http://stackoverflow.com/a/25735681/961652
https://trac.handbrake.fr/wiki/BuiltInPresets#normal

fuelux

The FuelUX library contains a great set of components to use on your next project. If you’d like to use it alongside jhipster you just need to add it with bower:

bower install –save fuelux

beware that fuelux uses requirejs and the order of injection of wiredep grunt task causes the following error

Error: Mismatched anonymous define() module: function (require)

I had to move the requirejs inclusion at the end

<a href="http://bower_components/fuelux/dist/js/fuelux.js">http://bower_components/fuelux/dist/js/fuelux.js</a>
        <a href="http://bower_components/underscore/underscore.js">http://bower_components/underscore/underscore.js</a>
        <a href="http://bower_components/requirejs/require.js">http://bower_components/requirejs/require.js</a>

The problem is that each time the wiredep task runs it changes back the order and causes the problem again. If anyone know how may I fix this it would be great.

Fuentes:
http://stackoverflow.com/questions/15371918/mismatched-anonymous-define-module