Java calls Golang generated dynamic library (DLL,SO)

Source: Internet
Author: User
0x01. Environmental preparation
A. GCC
Enter in the console

Gcc -v
If the prompt command is not found, then there is no gcc in your computer, go to install one, gcc official website: https://gcc.gnu.org/ If you have never installed gcc friends can install win-build directly, Can help you quickly install the official website: http://mingw-w64.org/doku.php/download/win-builds

0x02. Write the go program
We are just writing a simple calculation addition program that takes two integers, then calculates their sum and returns. Here we name the file libhello.go

Package main
Package main

Import "C"

//export Sum
Func Sum(a int, b int) int {
    Return a + b
}

Func main() {
}
}
Note that even if you want to compile into a dynamic library, you must have a main function. The above import "C" must be present and must be annotated.

//export Sum
After testing, if there is no corresponding function found in the exported DLL library

0x03. Compile go program
First, switch the directory where the console is located to the directory where the go program is located, that is, the directory where libhello.go is located.

A. Windows dynamic library
Execute the following command to generate a DLL dynamic link library:

Go build -buildmode=c-shared -o libhello.dll .\libhello.go
If the console does not report an error, the libhello.dll file will be generated in the current path.

B. Linux/Unix/macOS dynamic library
Execute the following command to generate the SO dynamic library:

Go build -buildmode=c-shared -o libhello.so .\libhello.go
0x04. Called in java
A. Reference to JNA
Java calls Native's dynamic library in two ways. JNI and JNA, JNA is Oracle's latest way to interact with Native. I will not mention more about it. I quote Baidu Encyclopedia's connection: https://baike.baidu. Com/item/JNA/8637274?fr=aladdin, friends in need can go and see. Here, we use the JNA approach, JNI's approach is basically abandoned, unless there are special needs, not to mention here, you can contact me for discussion. New Java project, I am using Maven for package management, so I directly reference JNA's dependencies:

      <artifactId>jna</artifactId></dependency>
If you don't use the package management tool, you can directly download the Jar file and download it. The download address is also posted in version 4.5.2: http://central.maven.org/maven2/net/java/dev/jna/jna /4.5.2/jna-4.5.2.jar
<dependency>
      <groupId>net.java.dev.jna</groupId>
      <artifactId>jna</artifactId>
      <version>4.5.2</version>
</dependency>

B. Create an interface
We need to create an interface to map the functions in the DLL, then we can access the functions in the DLL through the instance of the interface.

Import com.sun.jna.Native;}
Note that Sum is the name of the function, it must be consistent with the name of the function written in Go. The first parameter of Native.loadLibrary() is a string, the name of the dynamic library to be loaded or the full path, which does not need to be added later. The .dll or .so suffix. The second parameter is the class name of the interface.

Package cn.lemonit.robot.runner.executor;

Import com.sun.jna.Library;
Import com.sun.jna.Native;

Public interface LibHello extends Library {
    LibHello INSTANCE = (LibHello) Native.loadLibrary("E:/workspace/libhello", LibHello.class);

    Int Sum(int a, int b);
}
C. Call
We create a new App class, as the entry class of the main method, do not need extra operations in the main method, just need to call, here we call the Sum method, at the same time pass 222, 333, you can see the console output: 555

    Public static void main(String[] args) {}
I'm done, I finally played Java to call the Go program! ! ! ! ? ? ? Something is too gloating, go on >>>>

0x05. The parameter contains a string
Package cn.lemonit.robot.runner.executor;

Public class App {

    Public static void main(String[] args) {
        System.out.println(LibHello.INSTANCE.Sum(222, 333));
    }
}

A. Am I really done?
Our program can not only pass numerical parameters, we change the GO program, replace it with a string as a parameter function, accept a string parameter, and then output from the console: hello: xxx, as follows:

Package main

Import "fmt"

//export Hello
Func Hello(msg string) {
    fmt.Print("hello: " + msg)
}

Func main() {
}

According to the above 0x02.B step, we change the Java LibHello interface to look like this:

Public interface LibHello extends Library {}
Next, we call this interface, change the startup entry class App code in 0x02.C to this:

Package cn.lemonit.robot.runner.executor;

Package cn.lemonit.robot.runner.executor;

Import com.sun.jna.Library;
Import com.sun.jna.Native;

Public interface LibHello extends Library {
    LibHello INSTANCE = (LibHello) Native.loadLibrary("E:/workspace/libhello", LibHello.class);

    Void Hello(String msg);
}
}
Run it up, what? Wrong? ? ?

Fatal error: string concatenation too long

Goroutine 17 [running, locked to thread]:
Public class App {

    Public static void main(String[] args) {
        LibHello.INSTANCE.Hello("LemonIT.CN");
    }
}
Runtime.throw(0x644c1d4f, 0x1d)
  Xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx (runtime error, no meaning, not posted)
What is going on here, I am passing a very standard String, how can I report an error? In a clumsy, I found that just after calling the go build -buildmode=c-shared -o libhello.dll .\libhello.go command, in addition to libhello.dll is generated in the folder, a libhello is generated. h file! ! ! Is this not the header file of C? Out of curiosity, I opened up to see what was tall, and this opening really scared me:
#include <stddef.h> /* for ptrdiff_t below */#endif
With such a big one, I flipped it down and found the definition of our Hello function:

Extern void Hello(GoString p0);
Found the problem, the human parameters are GoString, and we pass the Java String, which is definitely inconsistent. What is GoString? What should I pass to him? Going up, I found two lines of code:

/* Created by "go tool cgo" - DO NOT EDIT. */

/* package command-line-arguments */

#line 1 "cgo-builtin-prolog"

#include <stddef.h> /* for ptrdiff_t below */

#ifndef GO_CGO_EXPORT_PROLOGUE_H
#define GO_CGO_EXPORT_PROLOGUE_H

Typedef struct { const char *p; ptrdiff_t n; } _GoString_;

#endif

/* Start of preamble from import "C" comments. */
/* End of preamble from import "C" comments. */

/* Start of boilerplate cgo prologue. */
#line 1 "cgo-gcc-export-header-prolog"

#ifndef GO_CGO_PROLOGUE_H
#define GO_CGO_PROLOGUE_H

Typedef signed char GoInt8;
Typedef unsigned char GoUint8;
Typedef short GoInt16;
Typedef unsigned short GoUint16;
Typedef int GoInt32;
Typedef unsigned int GoUint32;
Typedef long long GoInt64;
Typedef unsigned long long GoUint64;
Typedef GoInt64 GoInt;
Typedef GoUint64 GoUint;
Typedef __SIZE_TYPE__ GoUintptr;
Typedef float GoFloat32;
Typedef double GoFloat64;
Typedef float _Complex GoComplex64;
Typedef double _Complex GoComplex128;

/*
  Static assertion to make sure the file is being used on architecture
  At least with matching size of GoInt.
*/
Typedef char _check_for_64_bit_pointer_matching_GoInt[sizeof(void*)==64/8 ? 1:-1];

Typedef _GoString_ GoString;
Typedef void *GoMap;
Typedef void *GoChan;
Typedef struct { void *t; void *v; } GoInterface;
Typedef struct { void *data; GoInt len; GoInt cap; } GoSlice;

#endif

/* End of boilerplate cgo prologue. */

#ifdef __cplusplus
Extern "C" {
#endif

Extern void Hello(GoString p0);

#ifdef __cplusplus
}
#endif
Typedef struct { const char *p; ptrdiff_t n; } _GoString_;
// .....
Typedef _GoString_ GoString;

Uh huh, it seems that this GoString is just a structure inside C, a char in the structure * a ptrdiff_
t, it seems that when we use java to call the program, constructing such a structure for him to pass in should be fine, well, there are ideas, began toss.

B. Create a GoString!
We first use JNA to build a C structure type, then the problem comes, JNA char can be directly replaced by java String, then ptrdiff_t this stuff ... a little speechless, this is what? After a bit of operation Baidu and Google, I finally realized that this type is actually the value of the distance between two memory addresses. The data type is actually the long int in C. Here he represents the length of the string char. That is, the length of the string 呗~, knowing this is easy, we use the long type instead of it in Java. We create a new GoString class to correspond to the GoString structure in C, which is the string in the Go program. This piece has to be said, some people may not have used JNA. If you want to define a structure in JNA, you need to create a class. Inherited from com.sun.jna.Structure, people who are familiar with C should know (do not know it does not matter), there are usually two kinds of values to pass to C, one is to pass the reference (that is, the type of pointer), one is fax Values, if done in JNA, we usually create two static inner classes in this struct class. These two inner classes inherit from this struct class and implement the Structure.ByValue and Structure.ByReference interfaces, where ByValue is passed. When the real value is used, ByReference is used when passing the reference. In summary, our GoString class should grow like this:

Package cn.lemonit.robot.runner.executor;

Import com.sun.jna.Structure;

Import java.util.ArrayList;
Import java.util.List;

Public class GoString extends Structure {

    Public String str;
    Public long length;

    Public goString() {
    }

    Public GoString(String str) {
        This.str = str;
        This.length = str.length();
    }

    @Override
    Protected List<String> getFieldOrder() {
        List<String> fields = new ArrayList<>();
        Fields.add("str");
        Fields.add("length");
        Return fields;
    }

    Public static class ByValue extends GoString implements Structure.ByValue {
        Public ByValue() {
        }

        Public ByValue(String str) {
            Super(str);
        }
    }

    Public static class ByReference extends GoString implements Structure.ByReference {
        Public ByReference() {
        }

        Public ByReference(String str) {
            Super(str);
        }
    }
}

We can see that we have rewritten a getFieldOrder method, create a new list inside, and then put the two property names as strings, and then return as the return value. This operation is actually to tell JNA how the two variables and the variables in the C structure correspond to each other. Let's review the GoString structure defined in libhello.h (in fact, it saves you from going up). Look over, it’s hard, just stick it out for your convenience):

Typedef struct { const char *p; ptrdiff_t n; } _GoString_;
Our string is called str, and the name of char * is p. Our string length is called length, and the structure is called n. JNA is not artificial intelligence framework. You can't guess it. You want to match str to p, length. I want to correspond to n, so we put the field names in the list in the order of the list, telling JNA, my str wants to correspond to the first attribute of the structure, and length wants to correspond to the second attribute of the structure. (You can try, change the order of fields.add, there will definitely be problems).

C. With GoString! I can gloat again! ? ? ?
Ok, GoString has it, everything is ready, just owe it! With one hand, we changed the LibHello class in 0x05.A to something like this:

Package cn.lemonit.robot.runner.executor;

Import com.sun.jna.Library;
Import com.sun.jna.Native;

Public interface LibHello extends Library {
    LibHello INSTANCE = (LibHello) Native.loadLibrary("E:/workspace/libhello", LibHello.class);

    Void Hello(GoString.ByValue msg);
}

Change the App entry class code to this:

Package cn.lemonit.robot.runner.executor;

Public class App {
    Public static void main(String[] args) {
        LibHello.INSTANCE.Hello(new GoString.ByValue("LemonIT.CN"));
    }
}

run! The console successfully outputs:

Hello: LemonIT.CN
Hahaha! Successful, a little excited! Send the code to your friends! ! ! A friend asked me, can the result of your Hello function not be printed on the console in Go, but printed to the console in Java? Amount... I hesitated a bit, it should be able to... Open it!

0x06. The return value contains a string
A. Do a little experiment~
We change the Go function in 0x05 to Hello, and let the result be returned by the return value instead of printing directly in the console.

Package main

Import "C"

//export Hello
Func Hello(msg string) string{
    Return "hello:" + msg
}

Func main() {
}

Since the return value is also string, then the JNA side has to be changed a little, change the LibHello class in 0x05.C to this:

Package cn.lemonit.robot.runner.executor;

Import com.sun.jna.Library;
Import com.sun.jna.Native;

Public interface LibHello extends Library {
    LibHello INSTANCE = (LibHello) Native.loadLibrary("E:/workspace/libhello", LibHello.class);

    GoString.ByValue Hello(GoString.ByValue msg);
}

Run the entry class App and modify it accordingly:
Package cn.lemonit.robot.runner.executor;

Public class App {
    Public static void main(String[] args) {
        System.out.println(LibHello.INSTANCE.Hello(new GoString.ByValue("LemonIT.CN")).str);
    }
}

You're done, run it!

Panic: runtime error: cgo result has Go pointer

Goroutine 17 [running, locked to thread]:
Main._cgoexpwrap_b02601c1465e_Hello.func1(0xc04203deb8)
    _cgo_gotypes.go:59 +0x6c
main._cgoexpwrap_b02601c1465e_Hello(0xbe3ce0, 0xa, 0xc042008050, 0x10)
    _cgo_gotypes.go:61 +0xa1

Ok? What? What about my LemonIT.CN? Not found in the console! A bit maddening, how to step by step kan ~ but think about Bill Gates, I decided to be a good-tempered programmer, slowly study it.

B. There is always a solution! (There must be a road to the front of the mountain, there must be a Toyota in the road)
Although there is no LemonIT.CN, but looking at the error in the console, cgo result has Go pointer, still found a clue. Start another operation of Baidu and Google. It turns out that Go has its own GC (garbage collection, no explanation), and the popular point is that I don't want to use other pointers in Go language! Amount, that's it! I am in a hurry, even the class notes in college have been turned out. Inadvertently saw the communication with JNA and C at the time, C returned char to Java, and then Java can receive it using String. Well, huh? Have you forgotten this? Hahaha, isn't it possible for me to convert the string in Go to C and return it to C? Ok, let's try it and change the Hello function in Go just once again:

Package main

Import "C"

//export Hello
Func Hello(msg string) *C.char{
    Return C.CString("hello : " + msg)
}

Func main() {
}

Similarly, our JNA side has to be changed, and the LibHello class is modified like this:

Package cn.lemonit.robot.runner.executor;

Import com.sun.jna.Library;
Import com.sun.jna.Native;

Public interface LibHello extends Library {
    LibHello INSTANCE = (LibHello) Native.loadLibrary("E:/workspace/libhello", LibHello.class);

    String Hello(GoString.ByValue msg);
}

Since LibHello has changed, the entry class App has to be modified accordingly:

Package cn.lemonit.robot.runner.executor;

Public class App {
    Public static void main(String[] args) {
        System.out.println(LibHello.INSTANCE.Hello(new GoString.ByValue("LemonIT.CN")));
    }
}

Ok, well, run:

Hello : LemonIT.CN
Finally output it!

0x07. Summary
This Go and Java interaction has just taken this small step and it’s a step in the way. It seems that you can’t just gloat! ! Still have to be modest, the road can go further and further. Although it took such a big effort to solve such a small thing, but the advantage of Go language is very big, it is still worthy of my toss, I believe that friends who can read here are also very fond of Go language, big.Let's cheer up with the family. Welcome everyone to correct me.
Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.