Compare commits
9 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
1f518ab27f | ||
![]() |
e0caf138a0 | ||
![]() |
61f30dd25c | ||
![]() |
174c183f31 | ||
![]() |
3d0c812790 | ||
![]() |
7e3907d19d | ||
![]() |
35f49e85ec | ||
![]() |
5541c5a726 | ||
![]() |
f4fd2050fa |
12
Makefile
12
Makefile
@@ -2,8 +2,16 @@
|
||||
export CLASSPATH="app.jar:$(HOME)/i2p/lib/*:$()/java/net/i2p/ExampleClientApp/*"
|
||||
export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64
|
||||
|
||||
GOPATH:=$(HOME)/go
|
||||
|
||||
all: package jar check
|
||||
|
||||
fixvendor:
|
||||
mkdir -p $(GOPATH)/pkg/mod/github.com/sridharv/gojava/
|
||||
cp $(GOPATH)/src/github.com/sridharv/gojava/LoadJNI.java $(GOPATH)/pkg/mod/github.com/sridharv/gojava/LoadJNI.java
|
||||
|
||||
package: fmt
|
||||
gojava -v -o app.jar build . #i2pgit.org/idk/clientapp
|
||||
gojava -v -o app.jar build i2pgit.org/idk/clientapp
|
||||
|
||||
fmt:
|
||||
gofmt -w -s *.go
|
||||
@@ -15,5 +23,3 @@ check:
|
||||
jar:
|
||||
rm -rf java && mkdir java
|
||||
javac -classpath "app.jar:$(HOME)/i2p/lib/*" -d java *.java
|
||||
|
||||
|
68
README.md
Normal file
68
README.md
Normal file
@@ -0,0 +1,68 @@
|
||||
# clientapp
|
||||
|
||||
Code generation tools for creating for-real ClientApp's using Go/JNI. Currently
|
||||
just a usable POC, if you're doing everything right it works perfectly and
|
||||
it's reasonably simple. In the future, the goal is foolproof and automatic.
|
||||
|
||||
Credit: https://github.com/sridharv/gojava for figuring out how to hack
|
||||
`gomobile bind` and enable this technique.
|
||||
|
||||
Usage: First, adapt your application so that it can be controlled by the I2P
|
||||
ClientAppManager by implementing this interface:
|
||||
|
||||
``` Go
|
||||
type ClientApp interface {
|
||||
GetDisplayName() string
|
||||
GetName() string
|
||||
GetState() ClientAppState
|
||||
GetClientState() int
|
||||
StartupClient()
|
||||
Shutdown()
|
||||
}
|
||||
```
|
||||
|
||||
in your Go application. Once you have done that, import the library:
|
||||
|
||||
``` Go
|
||||
import "i2pgit.org/idk/clientapp"
|
||||
```
|
||||
|
||||
and call `clientapp.GenerateJava(&ExampleClientApp)` during your code generation
|
||||
phase to generate the `.java` wrapper. This small script will work:
|
||||
|
||||
``` Go
|
||||
//go:build generate
|
||||
// +build generate
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
|
||||
. "i2pgit.org/idk/clientapp"
|
||||
)
|
||||
|
||||
func main() {
|
||||
ioutil.WriteFile(ClientAppName()+".java", []byte(GenerateJava(&ExampleClientApp{})), 0644)
|
||||
}
|
||||
```
|
||||
|
||||
Which you can call by adding the a `go generate` to your source file:
|
||||
|
||||
``` Go
|
||||
//go:generate go run --tags=generate i2pgit.org/idk/clientapp/cmd
|
||||
```
|
||||
|
||||
Build your application and generate any `.jar` files for your Go library using
|
||||
`go build` or `gojava build` first. Then, run `go generate`. You should end up
|
||||
with a file named `ExampleClientApp.java` in the build directory. The hard part's
|
||||
over.
|
||||
|
||||
Copy the `.jar` file containing your JNI bindings and your Go library to your
|
||||
classpath. Then, copy the generated `.java` file to the desired location in your
|
||||
codebase. Now you can use your Go ClientApp from Java with real state tracking,
|
||||
you can call Go functions from Java and with a little more effort, call Java
|
||||
functions from Go.
|
||||
|
||||
Likely application: Yggdrasil plugin. IPFS plugin. Syndie plugin. VPN plugin?
|
||||
Syncthing plugin? Really a ton of stuff.
|
42
clientapp.go
42
clientapp.go
@@ -2,12 +2,18 @@ package clientapp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"reflect"
|
||||
"strings"
|
||||
// "path/filepath"
|
||||
)
|
||||
|
||||
//go:generate go run --tags=generate i2pgit.org/idk/clientapp/cmd
|
||||
//go:generate go test
|
||||
|
||||
func Generator() error {
|
||||
return ioutil.WriteFile(basicClientApp.GetName()+".java", []byte(GenerateJava(basicClientApp)), 0644)
|
||||
}
|
||||
|
||||
// ClientAppState shadows ClientAppState from Java. In go there are no enums,
|
||||
// only enum types instead, so this requires a function to convert it, which
|
||||
@@ -53,11 +59,17 @@ type ExampleClientApp struct {
|
||||
|
||||
// GetDisplayName() implements ClientApp in Java and in Go
|
||||
func (e *ExampleClientApp) GetDisplayName() string {
|
||||
if e.DisplayName == "" {
|
||||
return e.GetName()
|
||||
}
|
||||
return e.DisplayName
|
||||
}
|
||||
|
||||
// GetName() implements ClientApp in Java and in Go
|
||||
func (e *ExampleClientApp) GetName() string {
|
||||
if e.Name == "" {
|
||||
return "ExampleClientApp"
|
||||
}
|
||||
return e.Name
|
||||
}
|
||||
|
||||
@@ -87,18 +99,22 @@ var basicClientApp ClientApp = &ExampleClientApp{}
|
||||
// clientAppImpl is used by libraries that implement the ClientApp interface.
|
||||
// Set ClientAppImpl = an instance of your struct in order to emit a Java file
|
||||
// with a wrapper class to accompany your generated `.jar` file.
|
||||
var ClientAppImpl interface{} = &ExampleClientApp{}
|
||||
//var ClientAppImpl interface{} = &ExampleClientApp{}
|
||||
|
||||
func GenerateJava() string {
|
||||
func GenerateJava(ClientAppImpl interface{}) string {
|
||||
var ECA string = strings.Split(reflect.TypeOf(ClientAppImpl).String(), ".")[len(strings.Split(reflect.TypeOf(ClientAppImpl).String(), "."))-1]
|
||||
// var CECA string = reflect.TypeOf(ExampleClientApp{}).String()
|
||||
|
||||
var pkgdeclaration string = fmt.Sprintf("\npackage net.i2p.%s;\n", ECA)
|
||||
var importdeclaration string = "import net.i2p.app.ClientApp;\nimport net.i2p.app.ClientAppState;\n"
|
||||
var declaration string = fmt.Sprintf("public class %s implements ClientApp {\n", ECA)
|
||||
var innerdeclaration string = fmt.Sprintf("\tgo.%s.%s.%s _clientApp;\n", ClientPkgName(), ClientPkgJavaName(), ClientAppName())
|
||||
elem1 := ClientPkgName(ClientAppImpl)
|
||||
elem2 := ClientPkgJavaName(ClientAppImpl)
|
||||
elem3 := ClientAppName(ClientAppImpl)
|
||||
log.Printf("%s %s %s", elem1, elem2, elem3)
|
||||
var innerdeclaration string = fmt.Sprintf("\tgo.%s.%s.%s _clientApp;\n", elem1, elem2, elem3)
|
||||
var overrideDisplayName string = "\t@Override\n\tpublic String getDisplayName() {\n\t\treturn _clientApp.getDisplayName();\n\t}\n"
|
||||
var overrideName string = "\t@Override\n\tpublic String getName() {\n\t\treturn _clientApp.getName();\n\t}\n"
|
||||
var overrideName string = "\t@Override\n\tpublic String GetName() {\n\t\treturn _clientApp.getName();\n\t}\n"
|
||||
var convertClientState string = ` private ClientAppState convertClientState(long cas){
|
||||
switch((int)cas){
|
||||
case 0:
|
||||
@@ -140,15 +156,17 @@ func GenerateJava() string {
|
||||
closerdeclaration
|
||||
}
|
||||
|
||||
func ClientAppName() string {
|
||||
var CA string = strings.Split(reflect.TypeOf(ExampleClientApp{}).String(), ".")[len(strings.Split(reflect.TypeOf(ExampleClientApp{}).String(), "."))-1]
|
||||
return CA
|
||||
func ClientAppName(clientAppImpl interface{}) string {
|
||||
var CA string = strings.Split(reflect.TypeOf(clientAppImpl).String(), ".")[len(strings.Split(reflect.TypeOf(clientAppImpl).String(), "."))-1]
|
||||
return strings.Replace(CA, "*", "", -1)
|
||||
}
|
||||
|
||||
func ClientPkgJavaName() string {
|
||||
return strings.Title(strings.ToLower(strings.Split(reflect.TypeOf(ExampleClientApp{}).String(), ".")[0]))
|
||||
func ClientPkgJavaName(clientAppImpl interface{}) string {
|
||||
TC := strings.Title(strings.ToLower(strings.Split(reflect.TypeOf(clientAppImpl).String(), ".")[0]))
|
||||
return strings.Replace(TC, "*", "", -1)
|
||||
}
|
||||
|
||||
func ClientPkgName() string {
|
||||
return strings.Split(reflect.TypeOf(ExampleClientApp{}).String(), ".")[0]
|
||||
func ClientPkgName(clientAppImpl interface{}) string {
|
||||
PN := strings.Split(reflect.TypeOf(clientAppImpl).String(), ".")[0]
|
||||
return strings.Replace(PN, "*", "", -1)
|
||||
}
|
||||
|
@@ -3,5 +3,5 @@ package clientapp
|
||||
import "testing"
|
||||
|
||||
func TestJavaOutput(t *testing.T) {
|
||||
t.Log(GenerateJava())
|
||||
t.Log(Generator())
|
||||
}
|
||||
|
@@ -1,11 +0,0 @@
|
||||
//go:build generate
|
||||
// +build generate
|
||||
|
||||
package main
|
||||
|
||||
import "io/ioutil"
|
||||
import . "i2pgit.org/idk/clientapp"
|
||||
|
||||
func main() {
|
||||
ioutil.WriteFile(ClientAppName()+".java",[]byte(GenerateJava()), 0644)
|
||||
}
|
5
go.mod
Normal file
5
go.mod
Normal file
@@ -0,0 +1,5 @@
|
||||
module i2pgit.org/idk/clientapp
|
||||
|
||||
go 1.17
|
||||
|
||||
require github.com/sridharv/gomobile-java v0.0.0-20160328180427-34d2814361d9 // indirect
|
2
go.sum
Normal file
2
go.sum
Normal file
@@ -0,0 +1,2 @@
|
||||
github.com/sridharv/gomobile-java v0.0.0-20160328180427-34d2814361d9 h1:CxtEo+hway5QMC0/orvahh/wI4PLxpqSfa3LcPUzTHc=
|
||||
github.com/sridharv/gomobile-java v0.0.0-20160328180427-34d2814361d9/go.mod h1:AS6xm8lC0q4pJwq3IkCYnQFj1MKCEEdJc5Qa/CVTmn8=
|
BIN
go/LoadJNI.class
BIN
go/LoadJNI.class
Binary file not shown.
Binary file not shown.
BIN
go/Seq$Ref.class
BIN
go/Seq$Ref.class
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
go/Seq.class
BIN
go/Seq.class
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
go/libgojava
BIN
go/libgojava
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user