mirror of
https://github.com/fnproject/fn.git
synced 2022-10-28 21:29:17 +03:00
First iteration of support for Java
This commit is contained in:
committed by
Matthew Gilliard
parent
82d612a754
commit
cd0b68dfb7
1
examples/tutorial/hello/java/.gitignore
vendored
Normal file
1
examples/tutorial/hello/java/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
func.yaml
|
||||||
13
examples/tutorial/hello/java/Func.java
Normal file
13
examples/tutorial/hello/java/Func.java
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import java.io.*;
|
||||||
|
|
||||||
|
public class Func {
|
||||||
|
public static void main(String[] args) throws IOException {
|
||||||
|
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
|
||||||
|
|
||||||
|
String name = bufferedReader.readLine();
|
||||||
|
name = (name == null) ? "world" : name;
|
||||||
|
|
||||||
|
System.out.println("Hello, " + name + "!");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
43
examples/tutorial/hello/java/README.md
Normal file
43
examples/tutorial/hello/java/README.md
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
# Oracle Functions: Java
|
||||||
|
This is a hello world example of a Oracle Function using the Java runtime.
|
||||||
|
|
||||||
|
Firstly, we initialize our function by creating a `func.yaml` using `fn init`. This command can optionally take a `--runtime` flag to explicitly specify the target function runtime. In this example, the target runtime is implied to be Java because there is a `Func.java` file in the working directory.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
$ fn init <YOUR_DOCKERHUB_USERNAME>/hello-java
|
||||||
|
```
|
||||||
|
|
||||||
|
This is what our `func.yaml` looks like now.
|
||||||
|
|
||||||
|
|
||||||
|
```
|
||||||
|
name: mhaji/hello-java
|
||||||
|
version: 0.0.1
|
||||||
|
runtime: java
|
||||||
|
entrypoint: java Func
|
||||||
|
path: /hello-java
|
||||||
|
max_concurrency: 1
|
||||||
|
```
|
||||||
|
|
||||||
|
Next, we build and run our function using `fn run`.
|
||||||
|
|
||||||
|
|
||||||
|
```sh
|
||||||
|
$ fn run
|
||||||
|
Hello, world!
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also pipe input via `stdin` into to the function as follows:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
$ echo "Michael FassBender" | fn run
|
||||||
|
Hello Michael FassBender!
|
||||||
|
```
|
||||||
|
|
||||||
|
To execute your function via a HTTP trigger:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
fn apps create myapp
|
||||||
|
fn routes create myapp /hello
|
||||||
|
curl -H "Content-Type: text/plain" -X POST -d "Michael FassBender" http://localhost:8080/r/myapp/hello
|
||||||
|
```
|
||||||
35
fn/init.go
35
fn/init.go
@@ -31,6 +31,7 @@ var (
|
|||||||
".rs": "rust",
|
".rs": "rust",
|
||||||
".cs": "dotnet",
|
".cs": "dotnet",
|
||||||
".fs": "dotnet",
|
".fs": "dotnet",
|
||||||
|
".java": "java",
|
||||||
}
|
}
|
||||||
|
|
||||||
fnInitRuntimes []string
|
fnInitRuntimes []string
|
||||||
@@ -104,11 +105,20 @@ func (a *initFnCmd) init(c *cli.Context) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
runtimeSpecified := a.runtime != ""
|
||||||
|
|
||||||
err := a.buildFuncFile(c)
|
err := a.buildFuncFile(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if runtimeSpecified {
|
||||||
|
err := a.generateBoilerplate()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var ffmt *string
|
var ffmt *string
|
||||||
if a.format != "" {
|
if a.format != "" {
|
||||||
ffmt = &a.format
|
ffmt = &a.format
|
||||||
@@ -135,6 +145,20 @@ func (a *initFnCmd) init(c *cli.Context) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *initFnCmd) generateBoilerplate() error {
|
||||||
|
helper := langs.GetLangHelper(a.runtime)
|
||||||
|
if helper != nil && helper.HasBoilerplate() {
|
||||||
|
if err := helper.GenerateBoilerplate(); err != nil {
|
||||||
|
if err == langs.ErrBoilerplateExists {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Println("function boilerplate generated.")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (a *initFnCmd) buildFuncFile(c *cli.Context) error {
|
func (a *initFnCmd) buildFuncFile(c *cli.Context) error {
|
||||||
pwd, err := os.Getwd()
|
pwd, err := os.Getwd()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -190,9 +214,14 @@ func (a *initFnCmd) buildFuncFile(c *cli.Context) error {
|
|||||||
|
|
||||||
func detectRuntime(path string) (runtime string, err error) {
|
func detectRuntime(path string) (runtime string, err error) {
|
||||||
for ext, runtime := range fileExtToRuntime {
|
for ext, runtime := range fileExtToRuntime {
|
||||||
fn := filepath.Join(path, fmt.Sprintf("func%s", ext))
|
filenames := []string {
|
||||||
if exists(fn) {
|
filepath.Join(path, fmt.Sprintf("func%s", ext)),
|
||||||
return runtime, nil
|
filepath.Join(path, fmt.Sprintf("Func%s", ext)),
|
||||||
|
}
|
||||||
|
for _, filename := range filenames {
|
||||||
|
if exists(filename) {
|
||||||
|
return runtime, nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return "", fmt.Errorf("no supported files found to guess runtime, please set runtime explicitly with --runtime flag.")
|
return "", fmt.Errorf("no supported files found to guess runtime, please set runtime explicitly with --runtime flag.")
|
||||||
|
|||||||
@@ -1,5 +1,15 @@
|
|||||||
package langs
|
package langs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"os"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrBoilerplateExists = errors.New("Function boilerplate already exists")
|
||||||
|
)
|
||||||
|
|
||||||
// GetLangHelper returns a LangHelper for the passed in language
|
// GetLangHelper returns a LangHelper for the passed in language
|
||||||
func GetLangHelper(lang string) LangHelper {
|
func GetLangHelper(lang string) LangHelper {
|
||||||
switch lang {
|
switch lang {
|
||||||
@@ -19,6 +29,8 @@ func GetLangHelper(lang string) LangHelper {
|
|||||||
return &DotNetLangHelper{}
|
return &DotNetLangHelper{}
|
||||||
case "lambda-nodejs4.3":
|
case "lambda-nodejs4.3":
|
||||||
return &LambdaNodeHelper{}
|
return &LambdaNodeHelper{}
|
||||||
|
case "java":
|
||||||
|
return &JavaLangHelper{}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -31,6 +43,11 @@ type LangHelper interface {
|
|||||||
HasPreBuild() bool
|
HasPreBuild() bool
|
||||||
PreBuild() error
|
PreBuild() error
|
||||||
AfterBuild() error
|
AfterBuild() error
|
||||||
|
// HasBoilerplate indicates whether a language has support for generating function boilerplate.
|
||||||
|
HasBoilerplate() bool
|
||||||
|
// GenerateBoilerplate generates basic function boilerplate. Returns ErrBoilerplateExists if the function file
|
||||||
|
// already exists.
|
||||||
|
GenerateBoilerplate() error
|
||||||
}
|
}
|
||||||
|
|
||||||
// BaseHelper is empty implementation of LangHelper for embedding in implementations.
|
// BaseHelper is empty implementation of LangHelper for embedding in implementations.
|
||||||
@@ -38,3 +55,18 @@ type BaseHelper struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (h *BaseHelper) Cmd() string { return "" }
|
func (h *BaseHelper) Cmd() string { return "" }
|
||||||
|
func (h *BaseHelper) HasBoilerplate() bool { return false }
|
||||||
|
func (h *BaseHelper) GenerateBoilerplate() error { return nil }
|
||||||
|
|
||||||
|
func exists(name string) bool {
|
||||||
|
if _, err := os.Stat(name); err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func dockerBuildError(err error) error {
|
||||||
|
return fmt.Errorf("error running docker build: %v", err)
|
||||||
|
}
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
package langs
|
package langs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
)
|
)
|
||||||
@@ -34,7 +33,7 @@ func (lh *DotNetLangHelper) PreBuild() error {
|
|||||||
cmd.Stderr = os.Stderr
|
cmd.Stderr = os.Stderr
|
||||||
cmd.Stdout = os.Stdout
|
cmd.Stdout = os.Stdout
|
||||||
if err := cmd.Run(); err != nil {
|
if err := cmd.Run(); err != nil {
|
||||||
return fmt.Errorf("error running docker build: %v", err)
|
return dockerBuildError(err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ func (lh *GoLangHelper) PreBuild() error {
|
|||||||
cmd.Stderr = os.Stderr
|
cmd.Stderr = os.Stderr
|
||||||
cmd.Stdout = os.Stdout
|
cmd.Stdout = os.Stdout
|
||||||
if err := cmd.Run(); err != nil {
|
if err := cmd.Run(); err != nil {
|
||||||
return fmt.Errorf("error running docker build: %v", err)
|
return dockerBuildError(err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
118
fn/langs/java.go
Normal file
118
fn/langs/java.go
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
package langs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
// JavaLangHelper provides a set of helper methods for the build lifecycle of the Java runtime
|
||||||
|
type JavaLangHelper struct {
|
||||||
|
BaseHelper
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
mainClass = "Func"
|
||||||
|
mainClassFile = mainClass + ".java"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Entrypoint returns the Java runtime Docker entrypoint that will be executed when the function is run
|
||||||
|
func (lh *JavaLangHelper) Entrypoint() string {
|
||||||
|
return fmt.Sprintf("java %s", mainClass)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasPreBuild returns whether the Java runtime has a pre-build step
|
||||||
|
func (lh *JavaLangHelper) HasPreBuild() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// PreBuild executes the pre-build step for the Java runtime which involves compiling the relevant classes. It expects
|
||||||
|
// the entrypoint to the function, in other words or the class with the main method (not to be confused with the Docker
|
||||||
|
// entrypoint from Entrypoint()) to be Function.java
|
||||||
|
func (lh *JavaLangHelper) PreBuild() error {
|
||||||
|
wd, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !exists(filepath.Join(wd, mainClassFile)) {
|
||||||
|
return fmt.Errorf("could not find function: for Java, class with main method must be "+
|
||||||
|
"called %s", mainClassFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := exec.Command(
|
||||||
|
"docker", "run",
|
||||||
|
"--rm", "-v", wd+":/java", "-w", "/java",
|
||||||
|
"funcy/java:dev",
|
||||||
|
"/bin/sh", "-c", "javac "+mainClassFile,
|
||||||
|
)
|
||||||
|
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
if err := cmd.Run(); err != nil {
|
||||||
|
return dockerBuildError(err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AfterBuild removes all compiled class files from the host machine
|
||||||
|
func (lh *JavaLangHelper) AfterBuild() error {
|
||||||
|
wd, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
files, err := filepath.Glob(filepath.Join(wd, "*.class"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, file := range files {
|
||||||
|
err = os.Remove(file)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasPreBuild returns whether the Java runtime has boilerplate that can be generated.
|
||||||
|
func (lh *JavaLangHelper) HasBoilerplate() bool { return true }
|
||||||
|
|
||||||
|
const javaFunctionBoilerplate = `import java.io.*;
|
||||||
|
|
||||||
|
public class Func {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is the entrypoint to your function. Input will be via STDIN.
|
||||||
|
* Any output sent to STDOUT will be sent back as the function result.
|
||||||
|
*/
|
||||||
|
public static void main(String[] args) throws IOException {
|
||||||
|
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
|
||||||
|
|
||||||
|
String name = bufferedReader.readLine();
|
||||||
|
name = (name == null) ? "world" : name;
|
||||||
|
|
||||||
|
System.out.println("Hello, " + name + "!");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
// GenerateBoilerplate will generate function boilerplate (Function.java) for java if it does not exist.
|
||||||
|
// Returns ErrBoilerplateExists if the function file already exists
|
||||||
|
func (lh *JavaLangHelper) GenerateBoilerplate() error {
|
||||||
|
wd, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
pathToFunctionFile := filepath.Join(wd, mainClassFile)
|
||||||
|
if exists(filepath.Join(wd, mainClassFile)) {
|
||||||
|
return ErrBoilerplateExists
|
||||||
|
}
|
||||||
|
return ioutil.WriteFile(pathToFunctionFile, []byte(javaFunctionBoilerplate), os.FileMode(0644))
|
||||||
|
}
|
||||||
@@ -39,7 +39,7 @@ func (lh *PhpLangHelper) PreBuild() error {
|
|||||||
cmd.Stderr = os.Stderr
|
cmd.Stderr = os.Stderr
|
||||||
cmd.Stdout = os.Stdout
|
cmd.Stdout = os.Stdout
|
||||||
if err := cmd.Run(); err != nil {
|
if err := cmd.Run(); err != nil {
|
||||||
return fmt.Errorf("error running docker build: %v", err)
|
return dockerBuildError(err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ func (lh *PythonHelper) PreBuild() error {
|
|||||||
cmd.Stderr = os.Stderr
|
cmd.Stderr = os.Stderr
|
||||||
cmd.Stdout = os.Stdout
|
cmd.Stdout = os.Stdout
|
||||||
if err := cmd.Run(); err != nil {
|
if err := cmd.Run(); err != nil {
|
||||||
return fmt.Errorf("error running docker build: %v", err)
|
return dockerBuildError(err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,20 +39,11 @@ func (lh *RubyLangHelper) PreBuild() error {
|
|||||||
cmd.Stderr = os.Stderr
|
cmd.Stderr = os.Stderr
|
||||||
cmd.Stdout = os.Stdout
|
cmd.Stdout = os.Stdout
|
||||||
if err := cmd.Run(); err != nil {
|
if err := cmd.Run(); err != nil {
|
||||||
return fmt.Errorf("error running docker build: %v", err)
|
return dockerBuildError(err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (lh *RubyLangHelper) AfterBuild() error {
|
func (lh *RubyLangHelper) AfterBuild() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func exists(name string) bool {
|
|
||||||
if _, err := os.Stat(name); err != nil {
|
|
||||||
if os.IsNotExist(err) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
package langs
|
package langs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
)
|
)
|
||||||
@@ -34,7 +33,7 @@ func (lh *RustLangHelper) PreBuild() error {
|
|||||||
cmd.Stderr = os.Stderr
|
cmd.Stderr = os.Stderr
|
||||||
cmd.Stdout = os.Stdout
|
cmd.Stdout = os.Stdout
|
||||||
if err := cmd.Run(); err != nil {
|
if err := cmd.Run(); err != nil {
|
||||||
return fmt.Errorf("error running docker build: %v", err)
|
return dockerBuildError(err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user