[go: up one dir, main page]

Skip to content

Commit

Permalink
Complete code with tests.
Browse files Browse the repository at this point in the history
  • Loading branch information
rwxrob committed Aug 1, 2022
1 parent d78e324 commit 1e85a8e
Show file tree
Hide file tree
Showing 8 changed files with 122 additions and 45 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
go.work
8 changes: 2 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
# Encapsulate Java in Go Packages

Using Go `embed` this module contains functions for running Java JAR and
class files that have been embedded into the package with the default
`java` executable on the host system.

# Embed Java Jar, Class, source into Go Packages

See the package documentation.
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
module github.com/rwxrob/java

go 1.18

require github.com/rwxrob/fs v0.6.0
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
github.com/rwxrob/fs v0.6.0 h1:q8U4n0E8CkzkZ7+l8uX7vD6yU0tO2X9jam0vojjqwcA=
github.com/rwxrob/fs v0.6.0/go.mod h1:vO8AeluD7rnrO7zC54745xTEBFgHPUpHL0hbp1NnsVo=
100 changes: 66 additions & 34 deletions java.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
/*
Package java uses Go embed.FS so that packages can be created to encapsulate
Java JAR, class, and raw source files that have been embedded into the
package with the default java executable on the host system. This
package includes a caching mechanism implemented in the Extract and
Cached functions so that the files need not be extracted with every run.
The java command invocation depends entirely on the version of java
installed on the host system and depends on properties and CLASSPATH
being maintained outside of this package itself.
*/
package java

import (
Expand All @@ -11,37 +21,38 @@ import (
"github.com/rwxrob/java/internal"
)

// FS, when assigned, will first be used to locate all calls before
// looking at the host file system.
var FS embed.FS
// CacheDir is set to os.UserCacheDir() plus "gojavacache" by default at
// init time.
var CacheDir string

var CacheDir = filepath.Join(os.UserCacheDir, "gojavacache")

var extracted bool

func cacheFS() {
if extracted {
return
}
var zerofs embed.FS
if FS == zerofs {
// TODO needs to be written
// fs.ExtractEmbed(FS, CacheDir)
extracted = true
func init() {
dir, err := os.UserCacheDir()
if err == nil {
CacheDir = filepath.Join(dir, "gojavacache")
}
}

// Extract explicitly extracts all of an embedded file system into the
// CacheDir starting from the root path passed.
func Extract(fsys embed.FS, root string) error {
os.MkdirAll(CacheDir, fs.ExtractDirPerms)
return fs.ExtractEmbed(fsys, root, CacheDir)
}

// Cached returns the full path the extracted cache location of the file
// indicated by it.
func Cached(it string) string {
cacheFS()
path := filepath.Join(CacheDir, it)
if extracted == true && fs.Exists(path) {
func Cached(file string) string {
path := filepath.Join(CacheDir, file)
if fs.Exists(path) {
return path
}
return ""
}

// Run is a convenience function that takes a file path ending with
// ".class", ".jar", ".java" and calls RunClass, RunJar, or RunJava. If
// none of these suffixes match, and the string has a length greater
// than 10, assumes RunString.
func Run(it string) error {
switch {
case strings.HasSuffix(it, ".class"):
Expand All @@ -58,35 +69,56 @@ func Run(it string) error {
return nil
}

func RunString(it string) error {
// RunString writes the Java source passed to a temporary file and then
// calls RunJava on it.
func RunString(src string) error {
tmpfile := filepath.Join(os.TempDir(), internal.Isonan()+`.java`)
if err := os.WriteFile(tmpfile, []byte(it), 0600); err != nil {
if err := os.WriteFile(tmpfile, []byte(src), 0600); err != nil {
return err
}
defer os.Remove(tmpfile)
return RunJava(tmpfile)
}

func RunJava(it string) error {
cached := Cached(it)
// RunJava dynamically compiles and runs the java file passed but Java
// 11 or higher to be installed in host system. The file must end with
// ".java". If the java.FS is defined it will first be checked before
// checking the local file system.
func RunJava(file string) error {
cached := Cached(file)
if cached != "" {
it = cached
file = cached
}
return internal.Exec("java", it)
return internal.Exec("java", file)
}

func RunJar(it string) error {
cached := Cached(it)
// RunJar calls "java -jar <file>". The file must end with ".jar". If
// the file is found to be Cached, the cached copy will be used.
// Otherwise, the current file system is assumed. The Java CLASSPATH and
// other properties must be set using other methods when wanted.
func RunJar(file string) error {
cached := Cached(file)
if cached != "" {
it = cached
file = cached
}
return internal.Exec("java", "-jar", it)
return internal.Exec("java", "-jar", file)
}

func RunClass(it string) error {
cached := Cached(it)
// RunClass calls "java <name>". The name must be a file ending with
// ".class" in the CLASSPATH. If a <name>.class file is found in the
// cache (see Cached) the directory of the path returned from Cached
// will be added to the front of the current CLASSPATH. Otherwise, the
// regular Java rules for finding class files apply.
func RunClass(name string) error {
cached := Cached(name + ".class")
if cached != "" {
it = cached
cp := os.Getenv("CLASSPATH")
dir := filepath.Dir(cached)
if len(cp) > 0 {
os.Setenv("CLASSPATH", dir+string(os.PathListSeparator)+cp)
} else {
os.Setenv("CLASSPATH", dir)
}
}
return internal.Exec("java", it)
return internal.Exec("java", name)
}
54 changes: 49 additions & 5 deletions java_test.go
Original file line number Diff line number Diff line change
@@ -1,21 +1,46 @@
package java_test

import (
"embed"
_ "embed"
"fmt"
"os"

"github.com/rwxrob/fs/file"
"github.com/rwxrob/java"
)

//go:embed testdata/hello.java
//go:embed testdata/javafiles/hello.java
var helloJava string

// fooJava from go:embed testdata/hello.java
//go:embed testdata/javafiles
var javafiles embed.FS

func ExampleExtract() {

java.CacheDir = "testdata/tmpcache"
defer os.RemoveAll("testdata/tmpcache")

if err := java.Extract(javafiles, "testdata/javafiles"); err != nil {
fmt.Println(err)
}

fmt.Println(java.Cached("hello.java"))
fmt.Println(java.Cached("HelloWorld.class"))
fmt.Println(file.Exists("testdata/tmpcache/hello.java"))
fmt.Println(file.Exists("testdata/tmpcache/HelloWorld.class"))

// Output:
// testdata/tmpcache/hello.java
// testdata/tmpcache/HelloWorld.class
// true
// true

}

func ExampleRunJava() {

err := java.RunJava("testdata/hello.java")
err := java.RunJava("testdata/javafiles/hello.java")
if err != nil {
fmt.Println(err)
}
Expand Down Expand Up @@ -43,9 +68,28 @@ class HelloWorld {
// Hello, World!
}

func ExampleRunClass() {
func ExampleRunClass_nocache() {

defer os.Setenv("CLASSPATH", os.Getenv("CLASSPATH"))
os.Setenv("CLASSPATH", "testdata/javafiles")

err := java.RunClass("HelloWorld")
if err != nil {
fmt.Println(err)
}

// Output:
// Hello, World!
}

func ExampleRunClass_cached() {

java.CacheDir = "testdata/tmpcache"
defer os.RemoveAll("testdata/tmpcache")
if err := java.Extract(javafiles, "testdata/javafiles"); err != nil {
fmt.Println(err)
}

os.Setenv("CLASSPATH", "testdata")
err := java.RunClass("HelloWorld")
if err != nil {
fmt.Println(err)
Expand Down
File renamed without changes.
File renamed without changes.

0 comments on commit 1e85a8e

Please sign in to comment.