27 Nisan 2017 Perşembe

JNI

İsimlendirme
Java kodu şöyle olsun.
public class JNItest2 {

public native void helloworld(String[] args);

  static{
    System.loadLibrary("Dense");        
  }
  public static void main(String[] args)
  {        
    new JNItest2().helloworld(args);
  }
}
C metod ismi Java ile başlar, Java sınıfı ismi eklenir, metod ismi eklenir. 
İlk parametre JNIEnv, 
İkinci parametre metodu ihtiva eden nesnedir. Eğer metod static ise bu parametreye gerek yok. 
Diğer parametreler metoda geçilen değerler. 

Şöyle yaparız.
JNIEXPORT void JNICALL
Java_JNItest2_helloworld(JNIEnv *env,jobject object, jobjectArray args)
{
...
}   
Java'dan CPP'yi çağırmak
Şöyle yaparız.
header dosyasında şu include yapılır
#include <jni.h>
cpp şuna benzer.
JNIEXPORT jstring JNICALL Java_six_pack_Neatifier_neatify(JNIEnv* env, 
                                                          jclass clazz, 
                                                          jlong val, 
                                                          jchar pad, 
                                                          jint span)
{
   ... 
   jstring ret = env->NewStringUTF(out);
   return ret;
}
Java'dan DLL'i yükleriz.
System.load(System.getProperty("user.dir") + File.separator +
                        "src" + File.separator + "mylib.jnilib");
Java içinde Cpp ile aynı imzayı taşıyan bir metod tanımlarız.
public static native String neatify(final long val,
                                    final char pad,
                                    final int span);
Metodu çağırırız.
neatify(l, '_', 3);
JNI Tipleri
Şöyle
jboolean , jchar, jint,  jlong , jobjectArrayi jIntArray, jclass
Çeşitli Örnekler
Şöyle yaparız.
JNIEXPORT jlong JNICALL Java_com_mypackage_ClipboardHelper_initialize
    (JNIEnv *env, jclass obj, jstring arg) {
C kodundan Java'yı çağırmak
JNI - C Kodundan Java Çağırmak yazısına taşıdım.

JNIEnv Sınıfı

Giriş
Bu sınıf JNI'da kullanılan belki de en önemli sınıf.
FindClass metodu
C kodundan Java kodunu çağırırken, Java sınıfını bulmak için kullanılır.

GetArrayLength metodu
Java kodu şöyle olsun.
String[] args = ...;
new JNItest2().helloworld(args);
C kodunda şöyle yaparız.
JNIEXPORT void JNICALL
Java_JNItest2_helloworld(JNIEnv *env,jobject object, jobjectArray args)
{
  int argc = env->GetArrayLength(args);   
}
GetObjectArrayElement metodu
Java kodu şöyle olsun.
String[] args = ...;
new JNItest2().helloworld(args);
C kodunda şöyle yaparız.
JNIEXPORT void JNICALL
Java_JNItest2_helloworld(JNIEnv *env,jobject object, jobjectArray args)
{
 
  int i = ...
  jstring string = (jstring) env->GetObjectArrayElement(args, i);
  ...
}
GetStaticMethodID metoud
C kodundan Java kodunu çağırırken, FindClass ile bulunan Java nesnesinin static metodunu bulmak için kullanılır.

GetStringChars metodu
İmzası şöyle
// Returns a pointer to the array of Unicode characters of the string. 
// This pointer is valid until ReleaseStringchars() is called.
const jchar * GetStringChars(JNIEnv *env, jstring string, jboolean *isCopy);
GetStringLength metodu
jstring'in uzunluğunu verir. C kodunda şöyle yaparız.
jstring str =...;
int slength = env->GetStringLength(str);
GetStringUTFChars metodu
jstring'in karakterlerini döner. İmzası şöyle
// Returns a pointer to an array of bytes representing the string 
// in modified UTF-8 encoding. This array is valid until it is released 
// by ReleaseStringUTFChars().
const char * GetStringUTFChars(JNIEnv *env, jstring string, jboolean *isCopy);
C kodunda şöyle yaparız.
jstring str = ...;
char *jchars = (char *)env->GetStringUTFChars(str,NULL);
Şöyle yaparız.
std::string jstring2string(JNIEnv *env, jstring jStr) {
  if (!jStr)
    return "";

  std::vector<char> charsCode;
  const jchar *chars = env->GetStringChars(jStr, NULL);
  jsize len = env->GetStringLength(jStr);
  jsize i;

  for( i=0;i<len; i++){
    int code = (int)chars[i];
    charsCode.push_back( code );
  }
  env->ReleaseStringChars(jStr, chars);

  return  std::string(charsCode.begin(), charsCode.end());
}
NewIntArray metodu
Şöyle yaparız.
jint count = ...;
jintArray arr = env->NewIntArray (count);
ReleaseStringChars metodu
Şöyle yaparız.
std::string jstring2string(JNIEnv *env, jstring jStr) {
  if (!jStr)
    return "";

  std::vector<char> charsCode;
  const jchar *chars = env->GetStringChars(jStr, NULL);
  ...
  env->ReleaseStringChars(jStr, chars);

  return  std::string(charsCode.begin(), charsCode.end());
}


26 Nisan 2017 Çarşamba

ProcessBuilder Sınıfı

Giriş
Java kullanarak bir başka uygulamayı çalıştırmak için Runtime.exec() veya ProcessBuilder sınıfını kullanmak lazım. Her iki sınıf ta Process nesnesi ürettiği için geri kalan işlemler aslında hemen hemen aynı.

Redirect işlemleri için şu satırı dahil ederiz.
import java.lang.ProcessBuilder.Redirect;
Bu sınıfın Runtime.exec()'ten en önemli farkı çalıştırılacak uygulama ve parametrelerini teker teker alması. Runtime.exec() şöyle kullanılır.
String command = "cmd.exe /c \"dir -d\"";
File workingDir = new File("C:\\");
String[] env = {};
Runtime.getRuntime().exec(command, env, workingDir);
Bu sınıf ise aşağıdaki constructor metodlarında görüldüğü gibi tüm parametreleri ayrı ayrı alır. Ayrıca sınıfı fluent kullanılmak üzere tasarlanmış.
Process process = new ProcessBuilder(...)
            .redirectOutput(...)
            .redirectError(...)
            .start();

constructor - array
Uygulamayı çalıştırmak için gereken tüm parametreleri bir array olarak alır. Komut satırından çalıştırılan bir uygulamamız olsun.
/usr/local/bin/ticketer/ticketer_robot "test" "testoce" "2 - Medium" 
"/data/mars/logs/tesst.log" "test@test.com"
Aynısını kodla yapmak için şöyle yaparız.
String[] command = {"/bin/bash",
                    "/usr/local/bin/ticketer/ticketer_robot", 
                    "test",
                    "testoce",
                    "2 - Medium",
                    "/data/mars/logs/tesst.log",
                    "test@test.com"
};

ProcessBuilder pb = new ProcessBuilder (command);
constructor - vararg for 
Eğer parametreleri array olarak geçmek istemiyorsak var arg olarak ta geçebiliriz.
Örnek
Linux'ta kabuğu kullanmak için şöyle yaparız. Kabuk ile echo programı sudo uygulamasına şifreyi geçer.
new ProcessBuilder("/bin/sh", "-c", "echo <password> | sudo -S /opt/script.sh");
b.start();
Örnek
Windows'ta kabuğu kullanmak için şöyle yaparız.
new ProcessBuilder ("cmd.exe", "/c",  "dir -d")
directory metodu
Komutun "working directory" dizinini belirtir.
File workingDir = new File("C:\\");
ProcessBuilder pb = new ProcessBuilder(...);
pb.directory(workingDir);
redirectError metodu
Şöyle yaparız.
pb.redirectError (Redirect.INHERIT)
redirectErrorStream metodu
stderr akımını stdout akımına yönlendirir. Şöyle yaparız.
pb.redirectErrorStream (true)
redirectInput metodu
start metodu ile uygulamayı başlatmakdan önce şöyle yaparız.
ProcessBuilder builder = new ProcessBuilder(...);
pb.redirectInput(Redirect.PIPE);
Daha sonra start() metodu ile uygulamayı başlatıp girdi sağlarız.
Process process = pb.start();
BufferedWriter stdin = new BufferedWriter(
  new OutputStreamWriter(process.getOutputStream(), StandardCharsets.US_ASCII));
stdin.write("help\n");
stdin.flush();
process.waitFor();
redirectOutput metodu
Şöyle yaparız.
pb.redirectOutput(Redirect.INHERIT);
start metodu
Process nesnesi döndürür. Şöyle yaparız.
ProcessBuilder pb = new ProcessBuilder(...);
Process process = pb.start();