1 % Using Panama "foreign" JDK 2 3 <?xml version="1.0" encoding="utf-8"?> 4 5 # Using Panama "foreign-jextract" JDK 6 7 You can build "foreign-jextract" branch of panama repo [https://github.com/openjdk/panama-foreign](https://github.com/openjdk/panama-foreign) 8 9 Using foreign function call in Java involves the following two steps: 10 11 1. Use **jextract** tool to generate java interface for your C header file(s) 12 2. Invoke C functions via the jextracted Java interface 13 14 ## Hello World 15 16 ### Hello World C Header (helloworld.h) 17 18 ```C 19 20 #ifndef helloworld_h 21 #define helloworld_h 22 23 extern void helloworld(void); 24 25 #endif /* helloworld_h */ 26 27 28 ``` 29 30 ### Hello World C Source (helloworld.c) 31 32 ```C 33 34 #include <stdio.h> 35 36 #include "helloworld.h" 37 38 void helloworld(void) { 39 printf("Hello World!\n"); 40 } 41 42 ``` 43 44 ### Building Hello World 45 46 ```sh 47 48 cc -shared -o libhelloworld.dylib helloworld.c 49 50 ``` 51 52 53 ### jextract a Jar file for helloworld.h 54 55 ```sh 56 57 jextract -t org.hello -lhelloworld helloworld.h 58 59 ``` 60 61 ### Java program that uses extracted helloworld interface 62 63 ```java 64 65 import static org.hello.helloworld_h.*; 66 67 public class HelloWorld { 68 public static void main(String[] args) { 69 helloworld(); 70 } 71 } 72 73 ``` 74 75 ### Running the Java code that invokes helloworld 76 77 ```sh 78 79 java -Dforeign.restricted=permit --add-modules jdk.incubator.foreign HelloWorld.java 80 81 ``` 82 83 ## Embedding Python interpreter in your Java program (Mac OS) 84 85 ### jextract Python.h 86 87 ```sh 88 89 jextract -l python2.7 \ 90 -I /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include \ 91 -I /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/python2.7/ \ 92 -t org.python \ 93 /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/python2.7/Python.h 94 95 ``` 96 97 ### Java program that uses extracted Python interface 98 99 ```java 100 101 import org.python.Cstring; 102 import static jdk.incubator.foreign.MemoryAddress.NULL; 103 // import jextracted python 'header' class 104 import static org.python.RuntimeHelper.*; 105 import static org.python.Python_h.*; 106 107 public class PythonMain { 108 public static void main(String[] args) { 109 String script = "print(sum([33, 55, 66])); print('Hello from Python!')\n"; 110 111 Py_Initialize(); 112 try (var s = Cstring.toCString(script)) { 113 var str = s.baseAddress(); 114 PyRun_SimpleStringFlags(str, NULL); 115 Py_Finalize(); 116 } 117 } 118 } 119 120 ``` 121 122 ### Running the Java code that calls Python interpreter 123 124 ```sh 125 126 java -Dforeign.restricted=permit --add-modules jdk.incubator.foreign \ 127 -Djava.library.path=/System/Library/Frameworks/Python.framework/Versions/2.7/lib \ 128 PythonMain.java 129 130 ``` 131 132 ## Using readline library from Java code (Mac OS) 133 134 ### jextract readline.h 135 136 ```sh 137 138 jextract -l readline -t org.unix \ 139 -I /Library/Developer/CommandLineTools/SDKs/MacOSX10.14.sdk/usr/include \ 140 /Library/Developer/CommandLineTools/SDKs/MacOSX10.14.sdk/usr/include/readline/readline.h 141 142 ``` 143 144 ### Java code that uses readline 145 146 ```java 147 148 import org.unix.Cstring; 149 import static org.unix.RuntimeHelper.*; 150 import static org.unix.readline_h.*; 151 152 public class Readline { 153 public static void main(String[] args) { 154 try (var s = Cstring.toCString("name? ")) { 155 var pstr = s.baseAddress(); 156 // call "readline" API 157 var p = readline(pstr); 158 159 // print char* as is 160 System.out.println(p); 161 // convert char* ptr from readline as Java String & print it 162 System.out.println("Hello, " + Cstring.toJavaString(p)); 163 } 164 } 165 } 166 167 ``` 168 169 ### Running the java code that uses readline 170 171 ``` 172 java -Dforeign.restricted=permit --add-modules jdk.incubator.foreign \ 173 -Djava.library.path=/usr/local/opt/readline/lib/ Readline.java 174 175 ``` 176 177 ## Using libcurl from Java (Mac OS) 178 179 ### jextract curl.h 180 181 ```sh 182 183 jextract -t org.unix -lcurl \ 184 -I /Library/Developer/CommandLineTools/SDKs/MacOSX10.14.sdk/usr/include/ \ 185 -I /Library/Developer/CommandLineTools/SDKs/MacOSX10.14.sdk/usr/include/curl/ \ 186 /Library/Developer/CommandLineTools/SDKs/MacOSX10.14.sdk/usr/include/curl/curl.h 187 188 ``` 189 190 ### Java code that uses libcurl 191 192 ```java 193 194 import org.unix.Cstring; 195 import static jdk.incubator.foreign.MemoryAddress.NULL; 196 import static org.unix.RuntimeHelper.*; 197 import static org.unix.curl_h.*; 198 199 public class CurlMain { 200 public static void main(String[] args) { 201 var urlStr = args[0]; 202 curl_global_init(CURL_GLOBAL_DEFAULT()); 203 var curl = curl_easy_init(); 204 if(!curl.equals(NULL)) { 205 try (var s = Cstring.toCString(urlStr)) { 206 var url = s.baseAddress(); 207 curl_easy_setopt(curl, CURLOPT_URL(), url); 208 int res = curl_easy_perform(curl); 209 if (res != CURLE_OK()) { 210 curl_easy_cleanup(curl); 211 } 212 } 213 } 214 curl_global_cleanup(); 215 } 216 } 217 218 ``` 219 220 ### Running the java code that uses libcurl 221 222 ```sh 223 224 # run this shell script by passing a URL as first argument 225 java -Dforeign.restricted=permit --add-modules jdk.incubator.foreign \ 226 -Djava.library.path=/usr/lib CurlMain.java $* 227 228 ``` 229 230 ## Using BLAS library 231 232 BLAS is a popular library that allows fast matrix and vector computation: [http://www.netlib.org/blas/](http://www.netlib.org/blas/). 233 234 ### Installing OpenBLAS (Mac OS) 235 236 On Mac, blas is available as part of the OpenBLAS library: [https://github.com/xianyi/OpenBLAS/wiki](https://github.com/xianyi/OpenBLAS/wiki) 237 238 OpenBLAS is an optimized BLAS library based on GotoBLAS2 1.13 BSD version. 239 240 You can install openblas using HomeBrew 241 242 ```sh 243 244 brew install openblas 245 246 ``` 247 248 It installs include and lib directories under /usr/local/opt/openblas 249 250 ### jextracting cblas.h (MacOS) 251 252 The following command can be used to extract cblas.h on MacOs 253 254 ```sh 255 256 jextract -C "-D FORCE_OPENBLAS_COMPLEX_STRUCT" \ 257 -I /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include \ 258 -l openblas -t blas /usr/local/opt/openblas/include/cblas.h 259 260 ``` 261 262 ### Java sample code that uses cblas library 263 264 ```java 265 266 import jdk.incubator.foreign.NativeAllocationScope; 267 import blas.*; 268 import static blas.RuntimeHelper.*; 269 import static blas.cblas_h.*; 270 271 public class TestBlas { 272 public static void main(String[] args) { 273 int Layout; 274 int transa; 275 276 double alpha, beta; 277 int m, n, lda, incx, incy, i; 278 279 Layout = CblasColMajor(); 280 transa = CblasNoTrans(); 281 282 m = 4; /* Size of Column ( the number of rows ) */ 283 n = 4; /* Size of Row ( the number of columns ) */ 284 lda = 4; /* Leading dimension of 5 * 4 matrix is 5 */ 285 incx = 1; 286 incy = 1; 287 alpha = 1; 288 beta = 0; 289 try (NativeAllocationScope scope = NativeAllocationScope.unboundedScope()) { 290 var a = Cdouble.allocateArray(m*n, scope); 291 var x = Cdouble.allocateArray(n, scope); 292 var y = Cdouble.allocateArray(n, scope); 293 294 /* The elements of the first column */ 295 Cdouble.set(a, 0, 1.0); 296 Cdouble.set(a, 1, 2.0); 297 Cdouble.set(a, 2, 3.0); 298 Cdouble.set(a, 3, 4.0); 299 /* The elements of the second column */ 300 Cdouble.set(a, m, 1.0); 301 Cdouble.set(a, m + 1, 1.0); 302 Cdouble.set(a, m + 2, 1.0); 303 Cdouble.set(a, m + 3, 1.0); 304 /* The elements of the third column */ 305 Cdouble.set(a, m*2, 3.0); 306 Cdouble.set(a, m*2 + 1, 4.0); 307 Cdouble.set(a, m*2 + 2, 5.0); 308 Cdouble.set(a, m*2 + 3, 6.0); 309 /* The elements of the fourth column */ 310 Cdouble.set(a, m*3, 5.0); 311 Cdouble.set(a, m*3 + 1, 6.0); 312 Cdouble.set(a, m*3 + 2, 7.0); 313 Cdouble.set(a, m*3 + 3, 8.0); 314 /* The elemetns of x and y */ 315 Cdouble.set(x, 0, 1.0); 316 Cdouble.set(x, 1, 2.0); 317 Cdouble.set(x, 2, 1.0); 318 Cdouble.set(x, 3, 1.0); 319 Cdouble.set(y, 0, 0.0); 320 Cdouble.set(y, 1, 0.0); 321 Cdouble.set(y, 2, 0.0); 322 Cdouble.set(y, 3, 0.0); 323 cblas_dgemv(Layout, transa, m, n, alpha, a, lda, x, incx, beta, y, incy); 324 /* Print y */ 325 for (i = 0; i < n; i++) { 326 System.out.print(String.format(" y%d = %f\n", i, Cdouble.get(y, (long)i))); 327 } 328 } 329 } 330 } 331 332 ``` 333 334 ### Compiling and running the above BLAS sample 335 336 ```sh 337 338 java -Dforeign.restricted=permit --add-modules jdk.incubator.foreign \ 339 -Djava.library.path=/usr/local/opt/openblas/lib \ 340 TestBlas.java 341 342 ``` 343 344 ## Using LAPACK library (Mac OS) 345 346 On Mac OS, lapack is installed under /usr/local/opt/lapack directory. 347 348 ### jextracting lapacke.h 349 350 ```sh 351 352 jextract \ 353 -I /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include \ 354 -l lapacke -t lapack \ 355 --filter lapacke.h \ 356 /usr/local/opt/lapack/include/lapacke.h 357 358 ``` 359 360 ### Java sample code that uses LAPACK library 361 362 ```java 363 364 import jdk.incubator.foreign.NativeAllocationScope; 365 import lapack.*; 366 import static lapack.lapacke_h.*; 367 368 public class TestLapack { 369 public static void main(String[] args) { 370 371 /* Locals */ 372 try (var scope = NativeAllocationScope.unboundedScope()) { 373 var A = Cdouble.allocateArray(new double[]{ 374 1, 2, 3, 4, 5, 1, 3, 5, 2, 4, 1, 4, 2, 5, 3 375 }, scope); 376 var b = Cdouble.allocateArray(new double[]{ 377 -10, 12, 14, 16, 18, -3, 14, 12, 16, 16 378 }, scope); 379 int info, m, n, lda, ldb, nrhs; 380 381 /* Initialization */ 382 m = 5; 383 n = 3; 384 nrhs = 2; 385 lda = 5; 386 ldb = 5; 387 388 /* Print Entry Matrix */ 389 print_matrix_colmajor("Entry Matrix A", m, n, A, lda ); 390 /* Print Right Rand Side */ 391 print_matrix_colmajor("Right Hand Side b", n, nrhs, b, ldb ); 392 System.out.println(); 393 394 /* Executable statements */ 395 // printf( "LAPACKE_dgels (col-major, high-level) Example Program Results\n" ); 396 /* Solve least squares problem*/ 397 info = LAPACKE_dgels(LAPACK_COL_MAJOR(), (byte)'N', m, n, nrhs, A, lda, b, ldb); 398 399 /* Print Solution */ 400 print_matrix_colmajor("Solution", n, nrhs, b, ldb ); 401 System.out.println(); 402 System.exit(info); 403 } 404 } 405 406 static void print_matrix_colmajor(String msg, int m, int n, MemoryAddress mat, int ldm) { 407 int i, j; 408 System.out.printf("\n %s\n", msg); 409 410 for( i = 0; i < m; i++ ) { 411 for( j = 0; j < n; j++ ) System.out.printf(" %6.2f", Cdouble.get(mat, i+j*ldm)); 412 System.out.printf( "\n" ); 413 } 414 } 415 } 416 417 ``` 418 419 ### Compiling and running the above LAPACK sample 420 421 ```sh 422 423 java -Dforeign.restricted=permit \ 424 --add-modules jdk.incubator.foreign \ 425 -Djava.library.path=/usr/local/opt/lapack/lib \ 426 TestLapack.java 427 428 ``` 429 ## Using libproc library to list processes from Java (Mac OS) 430 431 ### jextract libproc.h 432 433 ```sh 434 435 jextract -t org.unix \ 436 -I /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include \ 437 --filter libproc.h \ 438 /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/libproc.h 439 440 ``` 441 442 ### Java program that uses libproc to list processes 443 444 ```java 445 446 import jdk.incubator.foreign.NativeAllocationScope; 447 import org.unix.*; 448 import static jdk.incubator.foreign.MemoryAddress.NULL; 449 import static org.unix.libproc_h.*; 450 451 public class LibprocMain { 452 private static final int NAME_BUF_MAX = 256; 453 454 public static void main(String[] args) { 455 try (var scope = NativeAllocationScope.unboundedScope()) { 456 // get the number of processes 457 int numPids = proc_listallpids(NULL, 0); 458 // allocate an array 459 var pids = Cint.allocateArray(numPids, scope); 460 // list all the pids into the native array 461 proc_listallpids(pids, numPids); 462 // convert native array to java array 463 int[] jpids = Cint.toJavaArray(pids.segment()); 464 // buffer for process name 465 var nameBuf = Cchar.allocateArray(NAME_BUF_MAX,scope); 466 for (int i = 0; i < jpids.length; i++) { 467 int pid = jpids[i]; 468 // get the process name 469 proc_name(pid, nameBuf, NAME_BUF_MAX); 470 String procName = Cstring.toJavaString(nameBuf); 471 // print pid and process name 472 System.out.printf("%d %s\n", pid, procName); 473 } 474 } 475 } 476 } 477 478 ``` 479 480 ### Compiling and running the libproc sample 481 482 ```sh 483 484 java -Dforeign.restricted=permit \ 485 --add-modules jdk.incubator.foreign \ 486 -Djava.library.path=/usr/lib LibprocMain.java 487 488 ``` 489 490 ## Using libgit2 from Java (Mac OS) 491 492 ### Getting and building libgit2 493 494 * Download libgit2 v1.0.0 source from https://github.com/libgit2/libgit2/releases 495 * Use cmake to build from libgit2 496 * Let ${LIBGIT2_HOME} be the directory where you expanded libgit2 sources. 497 * Let ${LIBGIT2_HOME}/build be the build directory where libgit2.dylib is built. 498 499 ### jextract git2.h 500 501 ```sh 502 503 jextract -t com.github -lgit2 \ 504 -I /Library/Developer/CommandLineTools/SDKs/MacOSX10.14.sdk/usr/include/ \ 505 -I ${LIBGIT2_HOME}/include/ \ 506 -I ${LIBGIT2_HOME}/include/git2 \ 507 ${LIBGIT2_HOME}/include/git2.h 508 509 ``` 510 511 ### Java program that uses libgit2 to clone github repo 512 513 ```java 514 515 import static com.github.git2_h.*; 516 import static jdk.incubator.foreign.CSupport.*; 517 import static jdk.incubator.foreign.MemoryAddress.NULL; 518 import static jdk.incubator.foreign.NativeAllocationScope.*; 519 import static com.github.Cstring.*; 520 521 public class GitClone { 522 public static void main(String[] args) { 523 if (args.length != 2) { 524 System.err.println("java GitClone <url> <path>"); 525 System.exit(1); 526 } 527 git_libgit2_init(); 528 try (var scope = unboundedScope()) { 529 var repo = scope.allocate(C_POINTER, NULL); 530 var url = toCString(args[0], scope); 531 var path = toCString(args[1], scope); 532 System.out.println(git_clone(repo, url, path, NULL)); 533 } 534 git_libgit2_shutdown(); 535 } 536 } 537 538 ``` 539 540 ### Compiling and running the libgit2 sample 541 542 ```sh 543 544 # file run.sh 545 546 java -Dforeign.restricted=permit --add-modules jdk.incubator.foreign \ 547 -Djava.library.path=${LIBGIT2_HOME}/build/ \ 548 GitClone.java $* 549 ``` 550 551 ### Cloning a github repo using the above run.sh command 552 553 ```sh 554 555 sh run.sh https://github.com/libgit2/libgit2.git libgit2 556 557 ``` 558 559 ## Using sqlite3 library from Java (Mac OS) 560 561 562 ### jextract sqlite3.h 563 564 ```sh 565 566 jextract \ 567 -I /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include \ 568 /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/sqlite3.h \ 569 -t org.sqlite -lsqlite3 570 571 ``` 572 ### Java program that uses sqlite3 573 574 ```java 575 576 import org.sqlite.Cpointer; 577 import org.sqlite.Cstring; 578 import org.sqlite.RuntimeHelper.CScope; 579 import static jdk.incubator.foreign.MemoryAddress.NULL; 580 import static org.sqlite.sqlite3_h.*; 581 582 public class SqliteMain { 583 public static void main(String[] args) throws Exception { 584 try (var scope = new CScope()) { 585 // char** errMsgPtrPtr; 586 var errMsgPtrPtr = Cpointer.allocate(NULL, scope); 587 588 // sqlite3** dbPtrPtr; 589 var dbPtrPtr = Cpointer.allocate(NULL, scope); 590 591 int rc = sqlite3_open(Cstring.toCString("employee.db",scope), dbPtrPtr); 592 if (rc != 0) { 593 System.err.println("sqlite3_open failed: " + rc); 594 return; 595 } else { 596 System.out.println("employee db opened"); 597 } 598 599 // sqlite3* dbPtr; 600 var dbPtr = Cpointer.get(dbPtrPtr); 601 602 // create a new table 603 var sql = Cstring.toCString( 604 "CREATE TABLE EMPLOYEE (" + 605 " ID INT PRIMARY KEY NOT NULL," + 606 " NAME TEXT NOT NULL," + 607 " SALARY REAL NOT NULL )", scope); 608 609 rc = sqlite3_exec(dbPtr, sql, NULL, NULL, errMsgPtrPtr); 610 611 if (rc != 0) { 612 System.err.println("sqlite3_exec failed: " + rc); 613 System.err.println("SQL error: " + Cstring.toJavaString(Cpointer.get(errMsgPtrPtr))); 614 sqlite3_free(Cpointer.get(errMsgPtrPtr)); 615 } else { 616 System.out.println("employee table created"); 617 } 618 619 // insert two rows 620 sql = Cstring.toCString( 621 "INSERT INTO EMPLOYEE (ID,NAME,SALARY) " + 622 "VALUES (134, 'Xyz', 200000.0); " + 623 "INSERT INTO EMPLOYEE (ID,NAME,SALARY) " + 624 "VALUES (333, 'Abc', 100000.0);", scope 625 ); 626 rc = sqlite3_exec(dbPtr, sql, NULL, NULL, errMsgPtrPtr); 627 628 if (rc != 0) { 629 System.err.println("sqlite3_exec failed: " + rc); 630 System.err.println("SQL error: " + Cstring.toJavaString(Cpointer.get(errMsgPtrPtr))); 631 sqlite3_free(Cpointer.get(errMsgPtrPtr)); 632 } else { 633 System.out.println("rows inserted"); 634 } 635 636 int[] rowNum = new int[1]; 637 // callback to print rows from SELECT query 638 var callback = sqlite3_exec$callback.allocate((a, argc, argv, columnNames) -> { 639 System.out.println("Row num: " + rowNum[0]++); 640 System.out.println("numColumns = " + argc); 641 argv = Cpointer.asArray(argv, argc); 642 columnNames = Cpointer.asArray(columnNames, argc); 643 for (int i = 0; i < argc; i++) { 644 String name = Cstring.toJavaString(Cpointer.get(columnNames, i)); 645 String value = Cstring.toJavaString(Cpointer.get(argv, i)); 646 System.out.printf("%s = %s\n", name, value); 647 } 648 return 0; 649 }); 650 scope.register(callback); 651 652 // select query 653 sql = Cstring.toCString("SELECT * FROM EMPLOYEE", scope); 654 rc = sqlite3_exec(dbPtr, sql, callback.baseAddress(), NULL, errMsgPtrPtr); 655 656 if (rc != 0) { 657 System.err.println("sqlite3_exec failed: " + rc); 658 System.err.println("SQL error: " + Cstring.toJavaString(Cpointer.get(errMsgPtrPtr))); 659 sqlite3_free(Cpointer.get(errMsgPtrPtr)); 660 } else { 661 System.out.println("done"); 662 } 663 664 sqlite3_close(dbPtr); 665 } 666 } 667 } 668 669 ``` 670 671 ### Compiling and running the sqlite3 sample 672 673 ```sh 674 675 java -Dforeign.restricted=permit \ 676 --add-modules jdk.incubator.foreign \ 677 -Djava.library.path=/usr/lib SqliteMain.java 678 679 ```