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