Flisol Goiânia Introdução ao JNI: Misturando Java com Código

Propaganda
Flisol Goiânia
Introdução ao JNI: Misturando
Java com Código Nativo (C/C++)
Marcos Paulino Roriz Junior
Motivação

Java é uniforme nas diversas plataformas.


Abstrai diversas funcionalidades para permitir isto.
Problema: acessar recursos específicos.

Ex: Área de Notificação; Rede (ICMP e ARP)
Motivação

Reaproveitamento de código.


Existe diversos programas que podem ser
aproveitados e/ou oferecer serviços para/em Java.
Ex: Octave ↔ Jopas (Front-End em Java)
Motivação

Código crítico


Placa de vídeo, codecs, requisições de um
servidor.
Ex: JCuda
JNI – Básico

Java Native Interface (Interface Nativa da Java)

É uma interface que permite a interação de código
Java com código nativo.
Aplicativo/Biblioteca
em Java
JVM
JNI
Aplicativo/Biblioteca
Nativa
Sistema Operacional
JNI – Básico

Interface é bidirecional.

Java chamar uma aplicação nativa


AWT, Driver MySQL
Uma app nativa chamar código em Java

Plugin Java do navegador
JNI – Básico

Implicações de usar JNI:

Perca de portabilidade:


Parte em Java – OK
Recompilar parte nativa para cada ambiente NOK
JNI – Básico

Implicações de usar JNI:

Segurança:


Java perdoa, C/C++ Não :'(
Erro qualquer pode derrubar a aplicação.
JNI – Básico

Implicações de usar JNI:

Memória:

Esteja preparado para o bom e velho free()
JNI – Básico


JNI foi lançado com o JDK 1.1
Qualquer linguagem que consiga gerar uma
biblioteca dinâmica (.so/.dll) pode interagir com
JNI.

Geralmente C/C++

Mas tem outros bindings
JNI – Básico

Como funciona
1.Cria uma classe Java que utiliza um método nativo;
2.Compile a classe com javac;
3.Usa o javah para gerar um header (cabeçalho);
4.Escreve uma implementação do método em nativo
em C/C++;
5.Compilar a implementação em uma biblioteca
compartilhada;
6.Executa o programa
JNI – Básico

Cria uma classe Java que utiliza um método
nativo;
Carga da biblioteca
Método Nativo
JNI – Básico

Compile a classe com javac;

$ cd workspace/JNITest/src/flisol/go

$ javac JNISample.java
JNI – Básico

Usa o javah para gerar um header (cabeçalho);



Programas nativos precisam implementar o header
javah atua sobre bytecode e não sobre código
fonte.
$ javah flisol.go.JNISample
Ao contrário de:

$ javah flisol.go.JNISample.java
JNI – Básico

Usa o javah para gerar um header (cabeçalho);

$ cd ../../

$ javah flisol.go.JNISample

Irar gerar um arquivo de cabeçalho contendo
métodos a ser implementado.
JNI – Básico

Usa o javah para gerar um header (cabeçalho);
JNI – Básico

Usa o javah para gerar um header (cabeçalho);
JNI – Básico

Escreve uma implementação do método em
nativo em C/C++;
JNI – Básico

Compilar a implementação em uma biblioteca
compartilhada;

JAVA_HOME='diretório de instalação do JDK'




Ubuntu: /usr/lib/jvm/java-6-openjdk
Arch: /opt/jdk
export JAVA_HOME='/usr/lib/jvm/java-6-openjdk'
Precisamos do diretório do jdk porque ele contém o
include para o jni.h e linux.h
JNI – Básico

Compilar a implementação em uma biblioteca
compartilhada;
Diretório para
incluir/procura o jni.h...

$ g++ -I$JAVA_HOME/include -I$JAVA_HOME/include/linux
-c -fPIC -Wall flisolImpl.cpp
Gerar Código em Posição
Independente de Memória

Arquivo fonte
$ g++ -shared -o libflisol.so flisolImpl.o
Cria uma biblioteca
Dinâmica
Nome para a biblioteca
Arquivos objetos
JNI – Básico

Compilar a implementação em uma biblioteca
compartilhada;
JNI – Básico

Compilar a implementação em uma biblioteca
compartilhada;

A biblioteca dinâmica deve estar em um diretório
especificado pela variável LD_LIBRARY_PATH


export LD_LIBRARY_PATH=dir1:dir2
A JVM anexa a LD_LIBRARY_PATH o diretório
apontado pela propriedade java.library.path


Sobrescrever essa propriedade:
java -Djava.library.path=dir
JNI – Básico

Executa o programa

$ java -Djava.library.path=dir
flisol.go.JNISample
JNI – Detalhes

System.loadLibrary(“Nome”);

Nome é mapeado conforme a plataforma, para o
seguinte nome:

Linux:


libNome.so
Windows:

Nome.dll
JNI – Detalhes


System.loadLibrary é invocado em um
ambiente estático porque assim só é executado
uma vez, quando a classe é carregada.
Ex:
JNI – Detalhes

Todos os métodos vão para o mesmo arquivo
de cabeçalho.



Classe aninhada → Arquivos .h diferente
Pode se implementar os métodos em arquivos
distintos.
Todos os métodos DEVEM ser implementados.
JNI – Detalhes

Erros comuns:

Compilação:

jni.h não foi encontrado


Verificar JAVA_HOME
Execução:

UnsatisfiedLinkError:


Biblioteca não encontrada
Todos os métodos não foram implementados
JNI – Arquitetura

O método print() não tinha argumentos, mas na
implementação nativa havia:
JNI – Arquitetura


O primeiro argumento, JNIEnv, é um ponteiro
para acessar as funções JNI.
Todas as funções são acessadas através deste
ponteiro.
JNI – Arquitetura

O segundo argumento varia:

Método de instância:



Uma referência ao objeto que invocou o método.
Neste caso: jobject
Se o método é estático:


Uma referência a classe em que o método foi
invocado.
Neste caso: jclass
JNI – Arquitetura

Acesso as funções:

env → NomeDaFunção (Lista Param)
ou


(*env).NomeDaFunção (Lista Param)
Mapeamento de Tipos:

JNI define vários tipos de java para código nativo.
JNI – Arquitetura

Acesso as funções:

env → NomeDaFunção (Lista Param)
ou


(*env).NomeDaFunção (Lista Param)
Mapeamento de Tipos:

JNI define vários tipos de java para código nativo.
JNI – Arquitetura

Tipos:
JNI – Arquitetura

Podemos dividir os tipos de dados em duas
categorias:

Primitivo (jint, jfloat...)

Objeto (jobject, jclass, jstring)
JNI – Arquitetura

Tipo primitivo

Mapeamento 1 ↔ 1

Simples :)
JNI – Arquitetura

Exemplo Simples:
JNI – Arquitetura

Exemplo Simples:
JNI – Arquitetura

Exemplo Simples:
JNI – Arquitetura

Objetos:


JNI passa objetos para o código nativo como
referência opaca.
Você manipula objetos SOMENTE através de
funções.


Vamos usar o JNIEnv =]
Como String e Exception é bastante comum, eles
possuem tipo de dados pré-definido.
JNI – Arquitetura

Objetos:

Herança
JNI – Arquitetura

String:

Podemos manipular dois tipos:



UTF-8 (Mais comum)
Unicode
Funções importantes:

jint GetStringUTFLength (jstring str)


Retorna o tamanho do String str
const jbyte* GetStringUTFChars (jstring str,
jboolean (isCopy)


Retorna um ponteiro para o vetor de chars (ascii)
do String str.
Esta função aloca recursos que devem ser
liberados manualmente
JNI – Arquitetura

String:

jstring NewStringUTF (char* c)


Cria um novo String com os caracteres contidos
no array c
ReleaseStringUTFChars (jstring str, jbyte* chars)

Libera os recursos alocados por uma chamada
prévia a GetStringUTFChars
JNI – Arquitetura

Exemplo Simples:
JNI – Arquitetura

Exemplo Simples:
JNI – Arquitetura

Exemplo Simples:
JNI – Arquitetura

Array:

JNI possui os tipos jarray e seus descendentes

j<tipo>Array, como:



jintArray – vetor de inteiros
jbooleanArray – vetor de boolean
jobjectArray – vetor de objetos
JNI – Arquitetura

Array:

As principais funções para manipulação de arrays
são:

jint Get<tipo>ArrayLengh (jarray arr)


void Get<tipo>ArrayRegion (jarray arr, jint start,
jint len, j<tipo>*buff)


Retorna o tamanho do array
Põe em buff todos len elementos do array arr a
partir da posição start
void Set<tipo>ArrayRegion (jarray arr, jint start,
jint len, const j<tipo> *buff)

Copia para arr os len elementos em buff a partir da
posição start
JNI – Sentindo Contrário

Interação sentido código nativo → Java


Ocorre quando código nativo pretende fazer uso de
algum método de classes ou objetos Java
Tipicamente isso se dá em casos em que o código
nativo precisa:



Instanciar uma classe java (e não há uma função
pronta para tal, como NewStringUTF e
NewIntArray)
Invocar um método de um objeto ou de uma
classes (método estático)
Acessar campos de um objeto ou classes
(campo estático)
JNI – Sentindo Contrário

Atenção:



Código nativo tem acesso irrestrito a métodos e
campos, sejam eles públicos ou não
A modificação destes pode comprometer a
confiabilidade dos resultados gerados por um
sistema
Assinatura de métodos na visão de JNI

Cada método possui além de um nome, uma
assinatura que em JNI é determinada por:

“(tipos-de-argumentos)tipo-de-retorno”
JNI – Sentindo Contrário

Onde tipo pode ser:
JNI – Sentindo Contrário

Exemplo:

“()Ljava/lang/String” → String metodo()

“()V” → void metodo ()

“([CII)V” → void metodo (char[] c, int i1,
int i2)
JNI – Sentindo Contrário

Dúvida:

Como saber a assinatura de um método.
javap
javap -s -p NomeDaClasse


JNI – Sentindo Contrário

Métodos e Campos:
1. Pega se o objeto que representa a classe
2. Pega o identificador (ID) do membro;
3. Invoque o método ou pegue o dado de um campo.
JNI – Sentindo Contrário

Pega se o objeto que representa a classe

jclass GetObjectClass (jobject obj)
Se você têm uma referência para um objeto da
classe que deseja
jclass FindClass (char* className)



Para achar a classe a partir do seu nome
completo
JNI – Sentindo Contrário

Pega se o objeto que representa a classe

Exemplos:

jclass clazz = env → GetObjectClass(obj)

jclass clazz = env → FindClass(“java/lang/String”)
Note que apesar de se utilizar
Ljava/lang/String para
identificador em métodos etc,
na classe é retirado o L.
JNI – Sentindo Contrário

Pega o identificador (ID) do membro;

Método



jmethodID GetMethodID(jclass clazz, const char *name,
const char *sig);
Caso estático == GetStaticMethodID
Campo


jfieldID GetFieldID(jclass clazz, const char *name,
const char *sig);
Caso estático == GetStaticFieldID
JNI – Sentindo Contrário

Pega o identificador (ID) do membro;

Exemplo:

Método

jmethodID mID = env → GetMethodID(jclass clazz,
“toString”, “()Ljava/lang/String”);
JNI – Sentindo Contrário

Invoque o método ou pegue o dado de um
campo.

Método:

São invocados através das funções:



Call<tipo>Method (class obj, jmethodID id, …)
CallStatic<tipo>Method (jclass cl, jmethodID id, …)
Ex:

jboolean b = env → CallBooleanMethod(obj, mid);
JNI – Sentindo Contrário

Invoque o método ou pegue o dado de um
campo.

Campo:

São invocados através das funções:



Get<tipo>Field (class obj, jfieldID id, …)
GetStatic<tipo>Field (jclass cl, jfieldID id, …)
Ex:

jboolean live = env → GetBooleanField(obj, fid);
JNI – Sentindo Contrário

Exemplo:
JNI – Sentindo Contrário

Exemplo:
JNI – Sentindo Contrário

Exemplo:
JNI – Sentindo Contrário

OnLoad e UnLoad
Permite nos fazer carregamento quando a
biblioteca vai ser carregada e quando a
classe que a carregou está sendo coletada
da vm.
Invocation API



Permite subir uma JVM a partir de código
nativo.
JNI – Caso de Uso

Ginga-J (IXC):
JNI – Caso de Uso

Ginga-J (IXC):
JNI – Caso de Uso

Ginga-J (IXC):
JNI – Dúvidas?

Dúvidas?
JNI – Caso de Uso

Referências:


The Java Native Interface Programmer's
Guide and Specification
Java Native Interface - Ginga
Download