Category: java

Language detection using pdf documents

This is a set of test for detect tika extracted documents.. It uses Language Detection Library for Java (langdetect.jar). Add more files to /docs/{lang}, the test will load them automatically.

Current support

  • Portuguese
  • English
  • French
  • German
  • Dutch
  • Italian
  • Spanish
  • Korean
  • Simplified Chinese
  • Traditional Chinese
  • Japanese

Install

Install Language Detection Library for Java manually

mvn install:install-file -Dfile=lib/langdetect.jar -DgroupId=com.cybozu.labs.langdetect -DartifactId=langdetect -Dversion=1.0 -Dpackaging=jar

The others dependencies will be installed by Maven.

Two steps to add your language

Create a directory under docs/{langX}, add some document to it and then create a new test method.

@Test
public void LanguageX() throws Exception {
  genericTest("Language X", "lang-x");
}

Running

export JAVA_OPTS=-Djava.awt.headless=true
mvn test

Log Output

-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running TestSuite
[INFO ][LangMatcherTest] Setup
[INFO ][LangMatcherTest] Loading lang detector
[INFO ][LangMatcherTest] Loading tika
[INFO ][LangMatcherTest] Language Dutch(nl): loading
[INFO ][LangMatcherTest] Language Dutch(nl): 9 files
[INFO ][LangMatcherTest] Language Dutch(nl): docs/nl/bereikbaarkaart-nl_print.pdf
[INFO ][LangMatcherTest] Language Dutch(nl): docs/nl/de_bodem_onder_amsterdam.pdf
[INFO ][LangMatcherTest] Language Dutch(nl): docs/nl/Economische verkenningen MRA2011_tcm14-228419.pdf
[INFO ][LangMatcherTest] Language Dutch(nl): docs/nl/How To Reach Amsterdam RAI.pdf
[INFO ][LangMatcherTest] Language Dutch(nl): docs/nl/IBM_Amsterdam_HDK.pdf
[INFO ][LangMatcherTest] Language Dutch(nl): docs/nl/kwaliteitswijzer_291111_web_def.pdf
[INFO ][LangMatcherTest] Language Dutch(nl): docs/nl/mas0.pdf
[INFO ][LangMatcherTest] Language Dutch(nl): docs/nl/route-oracle-amsterdam1-157087-nl.pdf
[INFO ][LangMatcherTest] Language Dutch(nl): docs/nl/{00C1E905-406C-490B-8079-5B9DCA9927BA}_C_NED.pdf
[INFO ][LangMatcherTest] Language English(en): loading
[INFO ][LangMatcherTest] Language English(en): 8 files
[INFO ][LangMatcherTest] Language English(en): docs/en/188741.pdf
[INFO ][LangMatcherTest] Language English(en): docs/en/Article33.pdf
[INFO ][LangMatcherTest] Language English(en): docs/en/BOSTmap.pdf
[INFO ][LangMatcherTest] Language English(en): docs/en/ff-boston_tcm7-4572.pdf
[INFO ][LangMatcherTest] Language English(en): docs/en/file84471.pdf
[INFO ][LangMatcherTest] Language English(en): docs/en/InformingTheDebate_Final.pdf
[INFO ][LangMatcherTest] Language English(en): docs/en/preven_code_tcm3-4039.pdf
[INFO ][LangMatcherTest] Language English(en): docs/en/STATEOFBLACKBOSTON_000.pdf
[INFO ][LangMatcherTest] Language French(fr): loading

Dump files

When the test passes to the language you can get extracted text in dump directory in the root of the project.

dump/nl
dump/nl/bereikbaarkaart-nl_print.pdf.txt
dump/nl/de_bodem_onder_amsterdam.pdf.txt
dump/nl/Economische verkenningen MRA2011_tcm14-228419.pdf.txt
dump/nl/How To Reach Amsterdam RAI.pdf.txt
dump/nl/IBM_Amsterdam_HDK.pdf.txt
dump/nl/kwaliteitswijzer_291111_web_def.pdf.txt
dump/nl/mas0.pdf.txt
dump/nl/route-oracle-amsterdam1-157087-nl.pdf.txt
dump/nl/{00C1E905-406C-490B-8079-5B9DCA9927BA}_C_NED.pdf.txt
dump/en
dump/en/188741.pdf.txt
dump/en/Article33.pdf.txt
dump/en/BOSTmap.pdf.txt
dump/en/ff-boston_tcm7-4572.pdf.txt
dump/en/file84471.pdf.txt
dump/en/InformingTheDebate_Final.pdf.txt
dump/en/preven_code_tcm3-4039.pdf.txt
dump/en/STATEOFBLACKBOSTON_000.pdf.txt

Scala in a Mavenized Netbeans project

I like Netbeans development environment, with it I can handle Java, C++, Ruby and now Scala. I found Scala NetBeans Plugin, in my research about how to bring netbeans working with Scala I came across some outdated documentation, in some of those the plugin working only with Scala 2.8.x + Netbeans 6.9, issues with OSX Lion and so on.
Another problem(no problem for you but for me) is the Scala NetBeans Plugin is based on Apache Ant build project, I prefer to use Apache Maven instead. I’ve got some karma by this liking. To save your time, I’ve written some steps to put Java, Scala, Maven and Netbeans working together.

Scala 2.9.1

To install Scala just download it, uncompress it to a directory, in this example, I’m going to use /Application/CustomApps so my SCALA_HOME is

/Applications/CustomApps/scala-2.9.1.final

Download Netbeans 7.1

Download Netbeans 7.1

Install it, probably, the installer will place it to /Applications/NetBeans/NetBeans7.1.app, let’s bind SCALA_HOME to be accessible in Netbeans. Edit Netbeans start up script

/Applications/NetBeans/NetBeans\ 7.1.app/Contents/Resources/NetBeans/bin/netbeans

After commented lines export SCALA_HOME environment variable, this way

export SCALA_HOME=/Applications/CustomApps/scala-2.9.1.final

Ps. There was a way to bind environment variables by adding a .plist in ~/.MacOSX/environment.plist. it’s deprecated in OSX Lion then you can edit Netbeans start up script that will work for all OSX versions.

Scala NetBeans Plugin(nbscala)

Download Scala plugin for Netbeans 7.1 and Scala 2.9.x. Uncompress it anywhere, but don’t forget the path :)

Installing the plugin

Open Netbeans, menu Tools -> Plugins -> Downloaded, button Add Plugins, select *.nbm files of nbscala plugin. Say yes/agree/allow for every dialogs.

Maven vs Ant

The Netbeans Scala plugin uses ant as builder if you don’t care about using ant, this tutorial ends up here. If you want to Maven(yes! Maven!) go ahead in the next topic.

Maven based Scala project

Let’s create a new ‘mavenized’ project, File -> New Project -> Maven -> Java Application go there and place the project in where do you want to. There is the maven-scala-plugin we’re going to use it, it is not needed download anything, Maven takes care about that, do you need to add some lines to your pom.xml.

The Maven repository for Scala

<repositories>
  <repository>
      <id>scala-tools.org</id>
      <name>Scala-tools Maven2 Repository</name>
      <url>http://scala-tools.org/repo-releases</url>
    </repository>
</repositories>

The build entry

<build>
    <plugins>
      <plugin>
        <groupId>org.scala-tools</groupId>
        <artifactId>maven-scala-plugin</artifactId>
        <version>2.15.2</version>
        <executions>
          <execution>
            <phase>process-resources</phase>
            <goals>
              <goal>add-source</goal>
              <goal>compile</goal>
            </goals>
          </execution>
          <execution>
            <id>scala-test-compile</id>
            <phase>process-test-resources</phase>
            <goals>
              <goal>testCompile</goal>
            </goals>
          </execution>
        </executions>
        <configuration>
          <scalaVersion>${scala.version}</scalaVersion>
          <sendJavaToScalac>true</sendJavaToScalac>
          <args>
            <arg>-target:jvm-1.5</arg>
            <arg>-g:vars</arg>
            <arg>-deprecation</arg>
            <arg>-dependencyfile</arg>
            <arg>${project.build.directory}/.scala_dependencies</arg>
          </args>
        </configuration>
      </plugin>
    </plugins>
</build>

And scala-lang dependency

<dependency>
     <groupId>org.scala-lang</groupId>
     <artifactId>scala-library</artifactId>
     <version>2.9.0</version>
 </dependency>

Before run the project, you may create scala source directory src/main/scala and also the test src/test/scala.

A peace of scala code

Under src/main/scala create a directory mytest it will be our package. Now theMain.scala object in src/main/scala/mytest/Main.scala with content:

package mytest
import java.io.File

object Main {

  def main(args: Array[String]):Unit = {
    println("Hello this is my /tmp")
    for(file <- new File("/tmp").listFiles){
      println(file)
    }
  }
}

By default Netbeans continues point to App.java, you may change the main class in right-click in Project -> Properties -> Run in the Main Class type mytest.Main.

F6 and be happy.

Shared Object Híbrido, Java e Ruby

Pense em dois softwares, ambos monitoram um dispositivo via driver, aparentemente iguais diferindo apenas pela linguagem em que foram concebidos, um em Java e outro em Ruby. Conseqüentemente, duas bibliotecas fazendo interface entre o driver e o ambiente da linguagem de auto nível.
Resolvi me aventurar nessa idéia e criei uma solução híbrida, uma biblioteca compatível com Ruby e Java.
jni_ruby.png
Testei com um SmartCard, como é algo específico(e você não vai comprar um só pra testar isso :) ) usarei a função getenv definida em stdlib.h .

Vamos dividir em três estágios:
– Criar um teste jni(java)
– Criar um teste em ruby(Extending)
– Juntar Java e Ruby com C

Ingredientes(debian packages)
– sun-java6-jdk
– ruby1.8
– ruby1.8-dev
– gcc-4.1

Java

Vamos seguir este fluxo

jni_java_flow.png

Crie um diretório “java” para o teste. Entre no diretório.

1. Criar o arquivo fonte java
Primeiramente crie o arquivo EnviromentVariable.java que depois de compilado será o protótipo para implementação em C.

 class EnviromentVariable {  

  public native String get(String varname);  

 }

2. Gerar o bytecode
Compile

 javac EnviromentVariable.java

3. Gerar os headers

 javah -jni EnviromentVariable

O javah criará um arquivo .h com o conteúdo

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class EnviromentVariable */
#ifndef _Included_EnviromentVariable
#define _Included_EnviromentVariable

#ifdef __cplusplus
extern "C" {
#endif
/*

 * Class:     EnviromentVariable
 * Method:    get
 * Signature: (Ljava/lang/String;)Ljava/lang/String;
 */

JNIEXPORT jstring JNICALL Java_EnviromentVariable_get(JNIEnv *, jobject, jstring);

#ifdef __cplusplus
}
#endif
#endif

4. Criar o arquivo fonte C
Agora faremos a lógica da biblioteca, portando é necessário a criação do arquivo enviroment_variable_java.c com o conteúdo


#include  <stdio.h>
#include  <stdlib.h> //getenv vem daqui
#include "EnviromentVariable.h"

JNIEXPORT jstring JNICALL Java_EnviromentVariable_get(JNIEnv *jvm, jobject obj, jstring varname){
  //Convertendo uma String Java em C String
  char * c_varname=(char *) (*jvm)->GetStringUTFChars(jvm, varname,NULL);

  //Obtendo o valor da variável e criando uma string para retorno
  return (*jvm)->NewStringUTF(jvm, getenv(c_varname) );
}

5. Gerar o shared object
O home da JVM na minha máquina está em /usr/lib/jvm/java-6-sun, verifique na sua distro onde foi instalada. Pra facilitar export a variável JAVA_HOME

export JAVA_HOME=/usr/lib/jvm/java-6-sun

Agora vamos gerar o libEnviromentVariable.so

gcc enviroment_variable_java.c \\
-shared -o libEnviromentVariable.so\\
-I. -I$JAVA_HOME/include -I$JAVA_HOME/include/linux

6. Testando
Para testarmos, crie um arquivo Main.java com o conteúdo.

class Main{
 static{
   System.loadLibrary("EnviromentVariable");
 }
 static public void main(String args[]){
  System.out.println(new EnviromentVariable().get("PATH"));
 }
}

Observe a cima vamos pegar o valor da variável PATH do seu sistema. Lembrando que a variável tem que existir.
Compile Main.java

javac Main.java

Execute o programa Main com a opção -Djava.library.path=”O diretório onde está o .so” no nosso caso em ‘.’

java -Djava.library.path=. Main

O output semelhante a

/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/local/shairon/bin:/usr/lib/jvm/java-6-sun/bin

Depois teste mudando a variável PATH para uma outra variável válida.

Ruby

Vamos seguir o fluxo
jni_ruby_flowx.png
Crie outro diretório no mesmo nível do java mas com o nome de “ruby”. Entre do diretório.

1. Criar o arquivo fonte ruby

Crie o arquivo enviroment_variable_ruby.c conforme abaixo.

#include  <ruby.h>
#include  <stdlib.h>

static VALUE t_get(VALUE self, VALUE varname){
    //Converte String Ruby em C String
   char *c_varname=STR2CSTR(varname);

   //Obtendo o valor da variável e criando uma string para retorno
   return rb_str_new2( getenv(c_varname)); 

}

VALUE cEnviromentVariable;

//Função de inicialização do arquivo
void Init_libEnviromentVariable() {

  //Cria a classe EnviromentVariable
  cEnviromentVariable = rb_define_class("EnviromentVariable", rb_cObject);

  //Associa o método t_get com EnviromentVariable#get
  rb_define_method(cEnviromentVariable, "get", t_get, 1);
}

2. Criar o arquivo para geração do makefile

Vamos criar o arquivo extconfig.rb para geração do makefile. Abaixo

require "mkmf"
create_makefile("libEnviromentVariable")

Execute

ruby extconfig.rb

3. Compile e instale

make

depois

make install

Abrao irb e digite:

require 'libEnviromentVariable'
puts EnviromentVariable.new.get("PATH")

Java e Ruby estão chamando diretamente o getenv, algo fácil pois é só uma chamada de função. Imagine se fosse algo mais complexo? O desenvolvedor iria reutilizar o código em C via copy-paste, nada elegante.
Daqui em diante vamos fazer Ruby e Java usar uma função comum e gerar apenas um Shared Object.

Solução Híbrida

Antes de mais nada observe que o módulo “mkfm” identifica onde estão os headers necessários para a compilação do .so em ruby, durante a compilação aparece o caminho detectado, no meu caso em /usr/lib/ruby/1.8/i486-linux, portanto declarare um variável de ambiente RUBY_HOME para facilitar o entendimento.

export RUBY_HOME=/usr/lib/ruby/1.8/i486-linux

Agora o arquivo que compartilha a mesma função entre Java e Ruby, o arquivo enviroment_variable.c. Verifique a disposição dos seus arquivos e diretório. A estrutura usada é:
1p.png

O conteúdo de enviroment_variable.c, acompanhe pelos comentários.

#include 
#include "java/EnviromentVariable.h"
#include 
#include 

//Função que será compartilhada

char *var(char *varname){
  return getenv(varname);
}

//JAVA SIDE
JNIEXPORT jstring JNICALL Java_EnviromentVariable_get(JNIEnv *jvm, jobject obj, jstring varname){

  char * c_varname=(char *)(*jvm)->GetStringUTFChars(jvm, varname,NULL);

 //Chama a função var e retorna uma String para JVM
  return (*jvm)->NewStringUTF(jvm, var(c_varname) );
}

//RUBY SIDE

static VALUE t_get(VALUE self, VALUE varname){
  char *c_varname=STR2CSTR(varname);
  //Chama a função var e retorna uma String para JVM
  return rb_str_new2( var(c_varname));
}

VALUE cEnviromentVariable;

void Init_libEnviromentVariable() {
  cEnviromentVariable = rb_define_class("EnviromentVariable", rb_cObject);
  rb_define_method(cEnviromentVariable, "get", t_get, 1);
}

Linkando e Compilando

Lembrando que já definimos as variáveis de ambiente $JAVA_HOME e $RUBY_HOME.

Gerando o objeto

gcc -c enviroment_variable.c \
-fPIC -Wall -g -fno-strict-aliasing -O2 \
-I$RUBY_HOME \
-I$JAVA_HOME/include \
-I$JAVA_HOME/include/linux

depois o Shared Object

gcc -L"/usr/lib" \
-shared -o libEnviromentVariable.so enviroment_variable.o \\
-I$RUBY_HOME \\
-I$JAVA_HOME/include \\
-I$JAVA_HOME/include/linux \\
-lruby1.8 -lpthread -ldl -lcrypt -lm -lc

Pronto! Geramos o bacalhau :) o arquivo libEnviromentVariable.so está pronto para ser usado.
Testando em java

java -Djava.library.path=. -cp ./java Main

em Ruby pelo irb

require 'libEnviromentVariable'
puts  EnviromentVariable.new.get("PATH")

Alguns outputs do irb

rb(main):001:0> require 'libEnviromentVariable'
=> true
irb(main):002:0> envs=EnviromentVariable.new
=> #
irb(main):003:0> envs.get("USERNAME")
=> "shairon"
irb(main):004:0> envs.get("TERM")
=> "xterm"
irb(main):005:0> envs.get("SHELL")
=> "/bin/bash"

obs.: depois troque enviroment por environment. :)

Referências

Extending Ruby
Java Native Interface Specification

Ruby Consumindo Objetos Java via RMI

Demonstrarei uma solução de um programa Ruby consumindo um objeto Java via RMI. Usarei o gem Rjb – Ruby Java Bridge.

Ingredientes
+ debian packages
– sun-java6-jdk
– ruby1.8
– ruby1.8-dev
– gcc-4.1
+ ruby gem
– rjb

Instalação
Instale os pacotes, antes de instalar o gem RJB verifique as variáveis de ambiente JAVA_HOME e LD_LIBRARY_PATH, pois durante instalação do gem será compilado um objeto.

JAVA_HOME apontando para o home da JVM(os paths a seguir são relativos ao Ubuntu, verifique onde está a JVM na sua distro)

export JAVA_HOME=/usr/lib/jvm/java-6-sun

LD_LIBRARY_PATH apontando para os shared objects da JVM

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$JAVA_HOME/jre/lib/i386/client

Agora o gem
Instalação normale

gem install rjb

O gem perguntará qual versão você gostaria de instalar, estou usando a 1.0.6.

Bulk updating Gem source index for: http://gems.rubyforge.org
Select which gem to install for your platform (i486-linux)
 1. rjb 1.0.6 (ruby)
 2. rjb 1.0.6 (mswin32)
 3. rjb 1.0.4 (ruby)
 4. rjb 1.0.4 (mswin32)
 5. Skip this gem
 6. Cancel installation
> 1
Building native extensions.  This could take a while...
Successfully installed rjb-1.0.6

Montando a estrutura do RMI
No nosso teste vamos ler um arquivo em Java passando o caminho do arquivo. Vamos aos passos.

Criando a interface

import java.rmi.RemoteException;
import java.rmi.Remote;

public interface AbstractAnyFile extends Remote {
    String get(String filePath) throws RemoteException;
}

Depois a implementação da interface com a lógica de leitura do arquivo.

import java.rmi.RemoteException;
import java.rmi.Naming;
import java.rmi.server.UnicastRemoteObject;
import java.io.*;
public class AnyFile extends UnicastRemoteObject implements AbstractAnyFile {

  public AnyFile() throws RemoteException { }

  public String get(String filePath) {

    try{
      InputStream in = new FileInputStream(new File(filePath));
      StringBuilder ret = new StringBuilder();
      int lidos=0;
      byte b[] = new byte[2048];
      String temp = null;
      while ( (lidos=in.read(b)) != -1){
         temp = new String(b,0,lidos);
         ret.append(temp);
      }
      return ret.toString();
    }catch(Exception e){
       return "Deu pau "+e;
    }
  }

  static void bind(){

    try { 

      Naming.rebind("RemoteFile", new AnyFile() );

    }catch(Exception e){
      System.out.println("Problema ao inicializar " + e.getMessage());
    }
   }

  static public void main(String args[]) {  AnyFile.bind();  }
}

lembranças do passado …
Observe que registramos um objeto AnyFile com o nome de RemoteFile.

Levantando o servidor(Binding)
Copile os .java

javac *.java

Gere o stub

rmic AnyFile

Inicie o rmiregistry(em background)

rmiregistry &

Inicie o servidor

java AnyFile

Do lado Ruby(cliente)
Acompanhe pelos comentários

require 'rubygems'
require 'rjb'

#garantindo a localização da JVM
ENV['JAVA_HOME']='/usr/lib/jvm/java-6-sun'

#Iniciando a JVM
Rjb::load

#Atribuindo a classe Naming a variável naming
naming = Rjb::import('java.rmi.Naming')

#Obtendo o objeto AnyFile pelo nome de RemoteFile(já associado)
myfile = naming.lookup('//127.0.0.1/RemoteFile')

#Obtendo o arquivo remoto '/tmp/teste.txt' através do objeto myfile e imprimindo
puts myfile.get("/tmp/teste.txt")

“Se der tudo certo vai funcionar :)

Possíveis problemas
Quando for instalar o gem

Building native extensions.  This could take a while...
ERROR:  While executing gem ... (Gem::Installer::ExtensionBuildError)
    ERROR: Failed to build gem native extension.

ruby extconf.rb install rjb
checking for jni.h... no
*** extconf.rb failed ***

Falta de exportar as variáveis JAVA_HOME e LD_LIBRARY_PATH ou não encontrou a JVM.

Em ruby

-:7:in `load': can't create Java VM (RuntimeError)
	from -:7

Também não achou a JVM