Refactor tests
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..b19d1c0
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,31 @@
+# Auto detect text files and perform LF normalization.
+# Resources:
+#       - https://www.kernel.org/pub/software/scm/git/docs/gitattributes.html
+#       - http://davidlaing.com/2012/09/19/customise-your-gitattributes-to-become-a-git-ninja/
+#       - https://help.github.com/articles/dealing-with-line-endings/
+* text=auto
+
+*.go            text eol=lf
+*.java          text
+*.js            text
+*.md            text
+*.py            text eol=lf
+*.scala         text
+*.sh            text eol=lf
+*.gradle        text
+*.xml           text
+*.bat           text eol=crlf
+
+*.jar           binary
+*.png           binary
+
+# python files not having the .py extension
+tools/cli/wsk      text eol=lf
+tools/cli/wskadmin text eol=lf
+
+# bash files not having the .sh extension
+tools/vagrant/simple/wsk        text eol=lf
+gradlew                         text eol=lf
+core/javaAction/proxy/gradlew   text eol=lf
+tools/vagrant/hello             text eol=lf
+sdk/docker/client/action        text eol=lf
diff --git a/.gitignore b/.gitignore
index 6e7acee..b81c91e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,7 +1,18 @@
 .gradle/
 .gogradle/
+*.log
 vendor/
 core/proxy
+core/actionloop-golang/proxy
+core/actionloop-golang/gobuild
+core/actionloop/proxy
 openwhisk/_test/exec
-openwhisk/_test/exec.zip
+openwhisk/_test/hi
+openwhisk/_test/hello_greeting
+openwhisk/_test/hello_message
+openwhisk/_test/*.zip
+openwhisk/_test/compile/
+openwhisk/_test/output/
 openwhisk/action/
+openwhisk/compile/
+openwhisk/debug.test
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..bf65ff1
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,33 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more contributor
+# license agreements.  See the NOTICE file distributed with this work for additional
+# information regarding copyright ownership.  The ASF licenses this file to you
+# under the Apache License, Version 2.0 (the # "License"); you may not use this
+# file except in compliance with the License.  You may obtain a copy of the License
+# at:
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software distributed
+# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+# CONDITIONS OF ANY KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations under the License.
+#
+
+sudo: required
+language: go
+go: 
+  - "1.9"
+services:
+- docker
+
+notifications:
+  email: true
+
+before_install:
+- "./tools/travis/setup.sh"
+install: true
+script:
+- "./tools/travis/build.sh &&  ./tools/travis/test.sh"
+after_failure: "cat /home/travis/gopath/src/github.com/sciabarracom/incubator-openwhisk-runtime-go/.gogradle/reports/test/index.html"
+
diff --git a/CREDITS.txt b/CREDITS.txt
new file mode 100644
index 0000000..0b41824
--- /dev/null
+++ b/CREDITS.txt
@@ -0,0 +1,3 @@
+Michele Sciabarra <michele@sciabarra.com>
+
+James Thomas <jthomas.uk@gmail.com>
\ No newline at end of file
diff --git a/NOTICE.md b/NOTICE.txt
similarity index 82%
rename from NOTICE.md
rename to NOTICE.txt
index 59c6d70..a5ae01e 100644
--- a/NOTICE.md
+++ b/NOTICE.txt
@@ -1,4 +1,5 @@
-Apache OpenWhisk Runtime Docker
+Apache OpenWhisk Runtime Go
+
 Copyright 2016-2018 The Apache Software Foundation
 
 This product includes software developed at
diff --git a/README.md b/README.md
index 8eacd6c..7afddd4 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,133 @@
-# Apache OpenWhisk Runtime for Go
+<!--
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more contributor 
+# license agreements.  See the NOTICE file distributed with this work for additional 
+# information regarding copyright ownership.  The ASF licenses this file to you
+# under the Apache License, Version 2.0 (the # "License"); you may not use this 
+# file except in compliance with the License.  You may obtain a copy of the License 
+# at:
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software distributed 
+# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 
+# CONDITIONS OF ANY KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations under the License.
+#
+-->
+# Apache OpenWhisk Runtime for Go (and Generic executables)
 
-This is an  OpenWhisk runtime for  Golang.
+This is an OpenWhisk runtime for Golang and Generic executables.
 
-Check [test documentation](./test/README.md) for testing.
+- [Building it](#building)
+- [Using it with Go Sources](#gosources)
+- [Precompiling Go Sources](#precompile)
+- [Using it with Generic executables](#generic)
 
+<a name="building"/>
+
+# How to Build
+
+You need a linux environment, with Java and Docker installed to build the sources.
+
+To build and run the test suite, use: 
+
+`./gradlew distDocker`
+
+This will build the images:
+
+* `actionloop-golang-v1.9`: an image supporting  Go sources
+* `actionloop`: the base image, supporting generic executables
+
+The `actionloop` image is used as a basis also for the `actionloop-swift` image. It can be used for supporting other compiled programming languages as long as they implement a `compile` script and the *action loop* protocol described below.
+
+<a name="gosources"/>
+
+# Using it with Go Sources
+
+The image can execute, compiling them on the fly, Golang OpenWhisk actions in source format. An action must be a Go source file, placed in the `action` package, implementing the `Main` function (or the function specified as `main`).  
+
+The expected signature is:
+
+`func Main(event json.RawMessage) (json.RawMessage, error)`
+
+Note the name of the function must be capitalised, because it needs to be exported from the `action` package. You can say the name of the function also in lower case, it will be capitalised anyway.
+
+For example:
+
+```
+package action
+
+import (
+  "encoding/json"
+  "log"
+)
+
+// Main is the function implementing the action
+func Main(event json.RawMessage) (json.RawMessage, error) {
+  // decode the json
+  var obj map[string]interface{}
+  json.Unmarshal(event, &obj)
+  // do your work
+  name, ok := obj["name"].(string)
+  if !ok {
+    name = "Stranger"
+  }
+  msg := map[string]string{"message": ("Hello, " + name + "!")}
+  // log in stdout or in stderr 
+  log.Printf("name=%s\n", name)
+  // encode the result back in json
+  return json.Marshal(msg)
+}
+```
+
+You can also have multiple source files in an action. In this case you need to collect all the sources  in a zip file for posting.
+
+<a name="precompile"/>
+
+## Precompiling Go Sources Offline
+
+Compiling sources on the image can take some time when the images is initialised. You can speed up precompiling the sources using the image as an offline compiler. You need `docker` for doing that.
+
+The images accepts a `compile` command expecting sources in `/src`. It will then compile them and place the resut in `/out`.
+
+If you have docker, you can do it this way:
+
+- place your sources under `src` folder in current directory
+- create an `out` folder to receive the binary
+- run: `docker run -v $PWD/src:/src -v $PWD/out openwhisk/actionloop-golang-v1.9 compile`
+- you can now use `wsk` to publish the `out/main` executable
+
+If you have a function named in a different way, for example `Hello`, specify `compile hello`. It will produce a binary named `out/hello`
+
+<a name="generic"/>
+
+# Using it with generic Binaries
+
+The `actionloop` image is designed to support generic linux executable in an efficient way. 
+
+As such it works with any executable that supports the following simple protocol:
+
+Repeat forever:
+- read one line from stadard input (file descriptor 0)
+- parse the line as a json object
+- execute the action, logging in standard output and in standardar error (file descriptor 1 and 2)
+- write an anwser in json format as a single line (without embedding newlines - newlines in strings must be quoted)
+
+The `actionloop` image works actually with executable in unix sense, so also scripts are acceptable. In the actionloop image there is `bash` and the `jq` command, so you can for example implement the actionloop with a shell script:
+
+```
+#!/bin/bash
+# read input forever line by line
+while read line
+do
+   # parse the in input with `jq`
+   name="$(echo $line | jq -r .name)"
+   # log in stdout
+   echo msg="hello $name"
+   # produce the result - note the fd3
+   echo '{"hello": "'$name'"}' >&3
+done
+```
+
+Note the `actionloop` image will accept any source and will try to run it (if it is possible), while the `actionloop-golang` and `actionloop-swift` images will try to compile the sources instead.
diff --git a/core/actionloop-golang/Dockerfile b/core/actionloop-golang/Dockerfile
new file mode 100644
index 0000000..c419a7a
--- /dev/null
+++ b/core/actionloop-golang/Dockerfile
@@ -0,0 +1,13 @@
+FROM ibmcom/ubuntu:14.04
+RUN apt-get -y update && apt-get -y upgrade && apt-get -y install curl jq git
+RUN mkdir /action /home/go
+WORKDIR /action
+ADD proxy /proxy
+ADD gobuild /bin/compile
+RUN curl https://dl.google.com/go/go1.9.4.linux-amd64.tar.gz  | tar xzvf - -C /usr/local
+ENV GOROOT /usr/local/go
+ENV GOPATH /home/go
+ENV COMPILER /bin/compile
+ENV PATH /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/go/bin:/home/go/bin
+RUN go get github.com/apache/incubator-openwhisk-client-go/whisk
+CMD /proxy
diff --git a/core/actionloop-golang/build.gradle b/core/actionloop-golang/build.gradle
new file mode 100644
index 0000000..e2935bd
--- /dev/null
+++ b/core/actionloop-golang/build.gradle
@@ -0,0 +1,17 @@
+ext.dockerImageName = 'actionloop-golang-v1.9'
+apply from: '../../gradle/docker.gradle'
+
+distDocker.dependsOn ':build'
+distDocker.dependsOn 'copyProxy'
+distDocker.dependsOn 'copyCompiler'
+
+
+task copyProxy(type: Copy) {
+    from '../proxy'
+    into '.'
+}
+
+task copyCompiler(type: Copy) {
+    from '../gobuild'
+    into '.'
+}
diff --git a/core/actionloop/Dockerfile b/core/actionloop/Dockerfile
new file mode 100644
index 0000000..ee0f1bd
--- /dev/null
+++ b/core/actionloop/Dockerfile
@@ -0,0 +1,6 @@
+FROM ibmcom/ubuntu:14.04
+RUN apt-get -y update && apt-get -y upgrade && apt-get -y install curl jq
+RUN mkdir /action
+WORKDIR /action
+ADD proxy /proxy
+CMD /proxy
diff --git a/core/actionloop/build.gradle b/core/actionloop/build.gradle
new file mode 100644
index 0000000..da826b0
--- /dev/null
+++ b/core/actionloop/build.gradle
@@ -0,0 +1,10 @@
+ext.dockerImageName = 'actionloop'
+apply from: '../../gradle/docker.gradle'
+
+distDocker.dependsOn ':build'
+distDocker.dependsOn 'copyProxy'
+
+task copyProxy(type: Copy) {
+    from '../proxy'
+    into '.'
+}
diff --git a/core/gobuild b/core/gobuild
new file mode 100755
index 0000000..4aecf7b
--- /dev/null
+++ b/core/gobuild
@@ -0,0 +1,48 @@
+#!/bin/bash
+# executable, defaults to main
+exec="${1:-main}"
+# absolute path of taget dir or file
+source="${2:-/src}"
+source="$(readlink -f $source)"
+dest="${3:-/out}"
+dest="$(readlink -f $dest)"
+# prepare a compilation dir
+compiledir="$(mktemp -d)"
+compilefile="$(mktemp)"
+mkdir -p "$compiledir/src/action" "$compiledir/src/main" 
+# capitalized main function name
+main="$(tr '[:lower:]' '[:upper:]' <<< ${exec:0:1})${exec:1}"
+# preparing for compilation
+if test -d "$source"
+# copy all the files unzipped
+then cp -rf "$source"/* "$compiledir/src/"
+     mkdir "$compiledir/src/action" 2>/dev/null
+     cp "$source"/* "$compiledir/src/action/"
+# if we have a single file action, copy it
+else cp "$source" "$compiledir/src/action/action.go"
+fi 
+# prepare the main
+cat <<EOF >$compiledir/src/main/main.go
+package main
+
+import (
+	"os"
+	"action"
+
+	"github.com/apache/incubator-openwhisk-client-go/whisk"
+)
+
+func main() {
+	whisk.StartWithArgs(action.$main, os.Args[1:])
+}
+EOF
+# build it
+cd "$compiledir"
+GOPATH="$GOPATH:$compiledir" go build -i action
+GOPATH="$GOPATH:$compiledir" go build -o "$compilefile" main
+# if output is a directory use executable name 
+if test -d "$dest"
+then dest="$dest/$exec"
+fi
+cp "$compilefile" "$dest"
+chmod +x "$dest"
diff --git a/main/proxy.go b/main/proxy.go
index fe8c0bc..ccf36b4 100644
--- a/main/proxy.go
+++ b/main/proxy.go
@@ -20,17 +20,31 @@
 	"flag"
 	"io/ioutil"
 	"log"
+	"os"
 
 	"github.com/sciabarracom/incubator-openwhisk-runtime-go/openwhisk"
 )
 
-// disable stderr except when debugging
+// flag to enable tracing
+var trace = flag.Bool("trace", false, "enable detailed tracing output")
+
+// flag to enable debug
 var debug = flag.Bool("debug", false, "enable debug output")
 
+// flag for the compiler
+var compiler = flag.String("compiler", os.Getenv("COMPILER"), "define the compiler on the command line")
+
 func main() {
 	flag.Parse()
-	if !*debug {
+
+	if !(*debug || *trace) {
+		// hide log unless you are debugging
 		log.SetOutput(ioutil.Discard)
 	}
-	openwhisk.Start()
+
+	// start the balls rolling
+	ap := openwhisk.NewActionProxy("./action", *compiler, os.Stdout)
+	ap.Debug = *debug || *trace
+	ap.Trace = *trace
+	ap.Start(8080)
 }
diff --git a/openwhisk/_test/action/.gitignore b/openwhisk/_test/action/.gitignore
new file mode 100644
index 0000000..2d71be7
--- /dev/null
+++ b/openwhisk/_test/action/.gitignore
@@ -0,0 +1,8 @@
+.DS_Store
+/.build
+/Packages
+/*.xcodeproj
+Package.resolved
+*.json
+.*_history
+.viminfo
diff --git a/openwhisk/_test/action/hello.go b/openwhisk/_test/action/hello.go
new file mode 100644
index 0000000..0369d68
--- /dev/null
+++ b/openwhisk/_test/action/hello.go
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package action
+
+import (
+	"encoding/json"
+	"fmt"
+	//"log"
+)
+
+// Hello receive an event in format
+// { "name": "Mike"}
+// and returns a greeting in format
+// { "greetings": "Hello, Mike"}
+func Hello(event json.RawMessage) (json.RawMessage, error) {
+	// input and output
+	var input struct {
+		Name string
+	}
+	var output struct {
+		Greetings string `json:"greetings"`
+	}
+	// read the input event
+	json.Unmarshal(event, &input)
+	if input.Name != "" {
+		// handle the event
+		output.Greetings = "Hello, " + input.Name
+		fmt.Println(output.Greetings)
+		return json.Marshal(output)
+	}
+	return nil, fmt.Errorf("no name specified")
+}
diff --git a/openwhisk/_test/action/hello_test.go b/openwhisk/_test/action/hello_test.go
new file mode 100644
index 0000000..d084454
--- /dev/null
+++ b/openwhisk/_test/action/hello_test.go
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package action
+
+import (
+	"fmt"
+)
+
+func ExampleHello() {
+	name := []byte(`{ "name": "Mike"}`)
+	data, _ := Hello(name)
+	fmt.Printf("%s", data)
+	// Output:
+	// Hello, Mike
+	// {"greetings":"Hello, Mike"}
+}
+
+func ExampleHello_noName() {
+	name := []byte(`{ "noname": "Mike"}`)
+	_, err := Hello(name)
+	fmt.Print(err)
+	// Output:
+	// no name specified
+}
+func ExampleHello_badJson() {
+	name := []byte(`{{`)
+	_, err := Hello(name)
+	fmt.Print(err)
+	// Output:
+	// no name specified
+}
diff --git a/openwhisk/_test/action/main.go b/openwhisk/_test/action/main.go
new file mode 100644
index 0000000..ecc0ff4
--- /dev/null
+++ b/openwhisk/_test/action/main.go
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package action
+
+import (
+	"encoding/json"
+	"fmt"
+)
+
+// Main forwading to Hello
+func Main(event json.RawMessage) (json.RawMessage, error) {
+	fmt.Println("Main:")
+	return Hello(event)
+}
diff --git a/openwhisk/_test/build.sh b/openwhisk/_test/build.sh
index 82f3378..1b40bd9 100755
--- a/openwhisk/_test/build.sh
+++ b/openwhisk/_test/build.sh
@@ -1,5 +1,49 @@
 #!/bin/bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
 cd "$(dirname $0)"
-test -e exec || GOARCH=amd64 GOOS=linux go build -o exec exec.go
+
+function build {
+  if ! test -e $1
+  then cp $1.src $1.go
+       GOARCH=amd64 GOOS=linux go build -a -o $1 $1.go
+       rm $1.go
+  fi
+}
+
+function zipit {
+  if ! test -e $1
+  then
+    mkdir $$
+    cp $2 $$/$3
+    zip -q -j $1 $$/$3
+    rm -rf $$
+  fi 
+}
+
+build exec
 test -e exec.zip || zip -q -r exec.zip exec etc dir
-cd -
+build hi
+zipit hi.zip hi main
+build hello_message
+zipit hello_message.zip hello_message main
+zipit hello_message1.zip hello_message message
+build hello_greeting
+zipit hello_greeting.zip hello_greeting main
+zipit hello_greeting1.zip hello_greeting greeting
+
diff --git a/openwhisk/_test/dir/etc b/openwhisk/_test/dir/etc
index e69de29..d00491f 100644
--- a/openwhisk/_test/dir/etc
+++ b/openwhisk/_test/dir/etc
@@ -0,0 +1 @@
+1
diff --git a/openwhisk/_test/etc b/openwhisk/_test/etc
index e69de29..d00491f 100644
--- a/openwhisk/_test/etc
+++ b/openwhisk/_test/etc
@@ -0,0 +1 @@
+1
diff --git a/openwhisk/_test/exec.go b/openwhisk/_test/exec.go
index 792a517..35a2611 100644
--- a/openwhisk/_test/exec.go
+++ b/openwhisk/_test/exec.go
@@ -1,2 +1,20 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package main
-func main() { }
+
+func main() {}
diff --git a/openwhisk/_test/exec.src b/openwhisk/_test/exec.src
new file mode 100644
index 0000000..35a2611
--- /dev/null
+++ b/openwhisk/_test/exec.src
@@ -0,0 +1,20 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package main
+
+func main() {}
diff --git a/openwhisk/_test/hello.src b/openwhisk/_test/hello.src
new file mode 100644
index 0000000..2bfd8c1
--- /dev/null
+++ b/openwhisk/_test/hello.src
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package action
+
+import (
+	"encoding/json"
+	"fmt"
+)
+
+func Main(event json.RawMessage) (json.RawMessage, error) {
+	var obj map[string]interface{}
+	json.Unmarshal(event, &obj)
+	name, ok := obj["name"].(string)
+	if !ok {
+		name = "Stranger"
+	}
+	fmt.Printf("name=%s\n", name)
+	msg := map[string]string{"message": ("Hello, " + name + "!")}
+	return json.Marshal(msg)
+}
diff --git a/openwhisk/_test/hello1.src b/openwhisk/_test/hello1.src
new file mode 100644
index 0000000..95e1586
--- /dev/null
+++ b/openwhisk/_test/hello1.src
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package action
+
+import (
+	"encoding/json"
+	"fmt"
+)
+
+func Hello(event json.RawMessage) (json.RawMessage, error) {
+	var obj struct {
+		Name string `json:",omitempty"`
+	}
+	err := json.Unmarshal(event, &obj)
+	if err != nil {
+		return nil, err
+	}
+	name := obj.Name
+	if name == "" {
+		name = "Stranger"
+	}
+	fmt.Printf("name=%s\n", name)
+	msg := map[string]string{"hello": ("Hello, " + name + "!")}
+	return json.Marshal(msg)
+}
diff --git a/openwhisk/_test/hello_greeting.src b/openwhisk/_test/hello_greeting.src
new file mode 100644
index 0000000..24d9f2f
--- /dev/null
+++ b/openwhisk/_test/hello_greeting.src
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package main
+
+import (
+	//"log"
+	"os"
+
+	"github.com/apache/incubator-openwhisk-client-go/whisk"
+	"github.com/sciabarracom/incubator-openwhisk-runtime-go/openwhisk/_test/action"
+)
+
+func main() {
+	//log.SetPrefix("hello_greeting: ")
+	whisk.StartWithArgs(action.Hello, os.Args[1:])
+}
diff --git a/openwhisk/_test/hello_message.src b/openwhisk/_test/hello_message.src
new file mode 100644
index 0000000..3c89484
--- /dev/null
+++ b/openwhisk/_test/hello_message.src
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package main
+
+import (
+	"encoding/json"
+	"fmt"
+	"os"
+
+	"github.com/apache/incubator-openwhisk-client-go/whisk"
+)
+
+func sayHello(event json.RawMessage) (json.RawMessage, error) {
+	var obj map[string]interface{}
+	json.Unmarshal(event, &obj)
+	name, ok := obj["name"].(string)
+	if !ok {
+		name = "Stranger"
+	}
+	fmt.Printf("name=%s\n", name)
+	msg := map[string]string{"message": ("Hello, " + name + "!")}
+	return json.Marshal(msg)
+}
+
+func main() {
+	//log.SetPrefix("hello_message: ")
+	whisk.StartWithArgs(sayHello, os.Args[1:])
+}
diff --git a/openwhisk/_test/hi.src b/openwhisk/_test/hi.src
new file mode 100644
index 0000000..6a07be8
--- /dev/null
+++ b/openwhisk/_test/hi.src
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package main
+
+import "fmt"
+
+func main() {
+	fmt.Println("hi")
+}
diff --git a/openwhisk/_test/postcompile.sh b/openwhisk/_test/postcompile.sh
new file mode 100755
index 0000000..00415d0
--- /dev/null
+++ b/openwhisk/_test/postcompile.sh
@@ -0,0 +1,6 @@
+#!/bin/bash
+FILE=${1:?compiled file}
+file -i "$FILE"
+echo '{"name":"Mike"}' | $FILE 3>/tmp/$$
+cat /tmp/$$
+rm /tmp/$$
diff --git a/openwhisk/_test/precompile.sh b/openwhisk/_test/precompile.sh
new file mode 100755
index 0000000..6e9c293
--- /dev/null
+++ b/openwhisk/_test/precompile.sh
@@ -0,0 +1,14 @@
+#!/bin/bash
+cd "$(dirname $0)"
+SRC=${1:?source}
+ID=${2:?numbe}
+go get github.com/apache/incubator-openwhisk-client-go/whisk
+rm -Rvf compile/$ID >/dev/null
+rm -Rvf output/$ID >/dev/null
+mkdir -p compile/$ID output/$ID
+if test -d "$SRC"
+then cp -r "$SRC" compile/$ID
+else cp $SRC compile/$ID/exec
+fi
+
+
diff --git a/openwhisk/_test/zips.sh b/openwhisk/_test/zips.sh
new file mode 100755
index 0000000..d212614
--- /dev/null
+++ b/openwhisk/_test/zips.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+cd "$(dirname $0)"
+rm action.zip 2>/dev/null
+zip -r -q action.zip action
diff --git a/openwhisk/actionProxy.go b/openwhisk/actionProxy.go
index 5889b01..50299e9 100644
--- a/openwhisk/actionProxy.go
+++ b/openwhisk/actionProxy.go
@@ -24,36 +24,67 @@
 	"os"
 )
 
-// theServer is the current server
-var theServer http.Server
+// ActionProxy is the container of the data specific to a server
+type ActionProxy struct {
+	// current directory
+	baseDir string
 
-// theChannel is the channel communicating with the action
-var theExecutor *Executor
+	// Compiler is the script to use to compile your code when action are source code
+	compiler string
+
+	// index current dir
+	currentDir int
+
+	// theChannel is the channel communicating with the action
+	theExecutor *Executor
+
+	// log file
+	logFile *os.File
+
+	// debug
+	Debug bool
+	Trace bool
+}
+
+// NewActionProxy creates a new action proxy that can handle http requests
+func NewActionProxy(baseDir string, compiler string, logFile *os.File) *ActionProxy {
+	os.Mkdir(baseDir, 0755)
+	return &ActionProxy{
+		baseDir,
+		compiler,
+		highestDir(baseDir),
+		nil,
+		logFile,
+		false,
+		false,
+	}
+}
 
 // StartLatestAction tries to start
 // the more recently uplodaded
 // action if valid, otherwise remove it
 // and fallback to the previous, if any
-func StartLatestAction() error {
+func (ap *ActionProxy) StartLatestAction(main string) error {
 
 	// find the action if any
-	highestDir := highestDir("./action")
+	highestDir := highestDir(ap.baseDir)
 	if highestDir == 0 {
 		log.Println("no action found")
-		theExecutor = nil
+		ap.theExecutor = nil
 		return fmt.Errorf("no valid actions available")
 	}
 
 	// save the current executor
-	curExecutor := theExecutor
+	curExecutor := ap.theExecutor
 
 	// try to launch the action
-	executable := fmt.Sprintf("./action/%d/exec", highestDir)
-	newExecutor := NewExecutor(executable)
+	executable := fmt.Sprintf("%s/%d/%s", ap.baseDir, highestDir, main)
+	os.Chmod(executable, 0755)
+	newExecutor := NewExecutor(ap.logFile, executable)
 	log.Printf("starting %s", executable)
 	err := newExecutor.Start()
 	if err == nil {
-		theExecutor = newExecutor
+		ap.theExecutor = newExecutor
 		if curExecutor != nil {
 			log.Println("stopping old executor")
 			curExecutor.Stop()
@@ -63,22 +94,30 @@
 
 	// cannot start, removing the action
 	// and leaving the current executor running
+	if !ap.Debug {
+		exeDir := fmt.Sprintf("./action/%d/", highestDir)
+		log.Printf("removing the failed action in %s", exeDir)
+		os.RemoveAll(exeDir)
+	}
 
-	exeDir := fmt.Sprintf("./action/%d/", highestDir)
-	log.Printf("removing the failed action in %s", exeDir)
-	os.RemoveAll(exeDir)
 	return err
 }
 
-// Start creates a proxy to execute actions
-func Start() {
-	// handle initialization
-	http.HandleFunc("/init", initHandler)
-	// handle execution
-	http.HandleFunc("/run", runHandler)
+func (ap *ActionProxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+	//fmt.Println(r.URL.Path)
+	switch r.URL.Path {
+	case "/init":
+		ap.initHandler(w, r)
+	case "/run":
+		ap.runHandler(w, r)
+	}
+}
 
-	// start
-	log.Println("Started!")
-	theServer.Addr = ":8080"
-	log.Fatal(theServer.ListenAndServe())
+// Start creates a proxy to execute actions
+func (ap *ActionProxy) Start(port int) {
+
+	// listen and start
+	//http.HandleFunc("/init", func(w http.ResponseWriter, r *http.Request) { ap.initHandler(w, r) })
+	//http.HandleFunc("/run", func(w http.ResponseWriter, r *http.Request) { ap.runHandler(w, r) })
+	log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", port), ap))
 }
diff --git a/openwhisk/actionProxy_test.go b/openwhisk/actionProxy_test.go
index b46ca3d..047cced 100644
--- a/openwhisk/actionProxy_test.go
+++ b/openwhisk/actionProxy_test.go
@@ -1,44 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package openwhisk
 
 import (
+	"fmt"
+	"io/ioutil"
 	"os"
 	"testing"
 
 	"github.com/stretchr/testify/assert"
 )
 
+func Example_startTestServer() {
+	ts, cur, log := startTestServer("")
+	res, _, _ := doPost(ts.URL+"/init", "{}")
+	fmt.Print(res)
+	res, _, _ = doPost(ts.URL+"/init", "XXX")
+	fmt.Print(res)
+	res, _, _ = doPost(ts.URL+"/run", "{}")
+	fmt.Print(res)
+	res, _, _ = doPost(ts.URL+"/run", "XXX")
+	fmt.Print(res)
+	stopTestServer(ts, cur, log)
+	// Output:
+	// {"ok":true}
+	// {"error":"Error unmarshaling request: invalid character 'X' looking for beginning of value"}
+	// {"error":"no action defined yet"}
+	// {"error":"Error unmarshaling request: invalid character 'X' looking for beginning of value"}
+}
+
 func TestStartLatestAction(t *testing.T) {
 
 	// cleanup
 	os.RemoveAll("./action")
-	theExecutor = nil
+	log, _ := ioutil.TempFile("", "log")
+	ap := NewActionProxy("./action", "", log)
 
 	// start an action that terminate immediately
 	buf := []byte("#!/bin/sh\ntrue\n")
-	extractAction(&buf, true)
-	StartLatestAction()
-	assert.Nil(t, theExecutor)
+	ap.ExtractAction(&buf, "main")
+	ap.StartLatestAction("main")
+	assert.Nil(t, ap.theExecutor)
 
 	// start the action that emits 1
 	buf = []byte("#!/bin/sh\nwhile read a; do echo 1 >&3 ; done\n")
-	extractAction(&buf, true)
-	StartLatestAction()
-	theExecutor.io <- "x"
-	assert.Equal(t, <-theExecutor.io, "1")
+	ap.ExtractAction(&buf, "main")
+	ap.StartLatestAction("main")
+	ap.theExecutor.io <- "x"
+	assert.Equal(t, <-ap.theExecutor.io, "1")
 
 	// now start an action that terminate immediately
 	buf = []byte("#!/bin/sh\ntrue\n")
-	extractAction(&buf, true)
-	StartLatestAction()
-	theExecutor.io <- "y"
-	assert.Equal(t, <-theExecutor.io, "1")
+	ap.ExtractAction(&buf, "main")
+	ap.StartLatestAction("main")
+	ap.theExecutor.io <- "y"
+	assert.Equal(t, <-ap.theExecutor.io, "1")
 
 	// start the action that emits 2
 	buf = []byte("#!/bin/sh\nwhile read a; do echo 2 >&3 ; done\n")
-	extractAction(&buf, true)
-	StartLatestAction()
-	theExecutor.io <- "z"
-	assert.Equal(t, <-theExecutor.io, "2")
+	ap.ExtractAction(&buf, "main")
+	ap.StartLatestAction("main")
+	ap.theExecutor.io <- "z"
+	assert.Equal(t, <-ap.theExecutor.io, "2")
 	/**/
-	theExecutor.Stop()
+	ap.theExecutor.Stop()
 }
diff --git a/openwhisk/compiler.go b/openwhisk/compiler.go
new file mode 100644
index 0000000..49e30de
--- /dev/null
+++ b/openwhisk/compiler.go
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package openwhisk
+
+import (
+	"fmt"
+	"io/ioutil"
+	"log"
+	"os"
+	"os/exec"
+
+	"github.com/h2non/filetype"
+)
+
+// check if the file is already compiled
+// if the file is a directoy look for a file with the given name
+
+func isCompiled(fileOrDir string, name string) bool {
+	fi, err := os.Stat(fileOrDir)
+	if err != nil {
+		log.Print(err)
+		return false
+	}
+	file := fileOrDir
+	if fi.IsDir() {
+		file = fmt.Sprintf("%s/%s", fileOrDir, name)
+	}
+
+	log.Printf("isCompiled: %s", file)
+	buf, err := ioutil.ReadFile(file)
+	if err != nil {
+		log.Println(err)
+		return false
+	}
+	kind, err := filetype.Match(buf)
+	if err != nil {
+		log.Println(err)
+		return false
+	}
+	if kind.Extension == "elf" {
+		return true
+	}
+	return false
+}
+
+// CompileAction will compile an anction in source format invoking a compiler
+func (ap *ActionProxy) CompileAction(main string, src string, out string) error {
+	if ap.compiler == "" {
+		return fmt.Errorf("No compiler defined")
+	}
+	log.Printf("compiling: compiler=%s main=%s src=%s out=%s", ap.compiler, main, src, out)
+	var cmd *exec.Cmd
+	if out == "" {
+		cmd = exec.Command(ap.compiler, main, src, src)
+	} else {
+		cmd = exec.Command(ap.compiler, main, src, out)
+	}
+	res, err := cmd.CombinedOutput()
+	log.Print(string(res))
+	return err
+}
diff --git a/openwhisk/compiler_test.go b/openwhisk/compiler_test.go
new file mode 100644
index 0000000..4d2b5b7
--- /dev/null
+++ b/openwhisk/compiler_test.go
@@ -0,0 +1,165 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package openwhisk
+
+import (
+	"fmt"
+	"io/ioutil"
+)
+
+/* this test confuses gogradle
+func Example_compileAction_wrong() {
+	sys("_test/precompile.sh", "hello.sh", "0")
+	log, _ := ioutil.TempFile("", "log")
+	ap := NewActionProxy("./action/0", "../core/gobuild", log)
+	fmt.Println(ap.CompileAction("_test/compile/0/exec", "exec", ""))
+	// Output:
+	// exit status 1
+}*/
+
+func Example_isCompiled() {
+	sys("_test/precompile.sh", "hello.src", "c")
+	file := abs("./_test/compile/c/exec")
+	dir := abs("./_test/compile/c")
+	fmt.Println(isCompiled(file, "main"))
+	fmt.Println(isCompiled(dir, "exec"))
+
+	log, _ := ioutil.TempFile("", "log")
+	ap := NewActionProxy("./action/c", "../core/gobuild", log)
+	ap.CompileAction("main", abs("_test/compile/c/exec"), "")
+
+	fmt.Println(isCompiled(file, "main"))
+	fmt.Println(isCompiled(dir, "exec"))
+	// errors
+	fmt.Println(isCompiled(dir, "main"))
+	fmt.Println(isCompiled(file+"1", "main"))
+
+	// Output:
+	// false
+	// false
+	// true
+	// true
+	// false
+	// false
+}
+
+func Example_compileAction_singlefile_main() {
+	sys("_test/precompile.sh", "hello.src", "1")
+	log, _ := ioutil.TempFile("", "log")
+	ap := NewActionProxy("./action/1", "../core/gobuild", log)
+	fmt.Println(ap.CompileAction("main", abs("_test/compile/1/exec"), ""))
+	sys("_test/postcompile.sh", "_test/compile/1/exec")
+	// Output:
+	// <nil>
+	// _test/compile/1/exec: application/x-executable; charset=binary
+	// name=Mike
+	// {"message":"Hello, Mike!"}
+}
+
+func Example_compileAction_singlefile_main_out() {
+	sys("_test/precompile.sh", "hello.src", "1a")
+	log, _ := ioutil.TempFile("", "log")
+	ap := NewActionProxy("./action/1a", "../core/gobuild", log)
+	fmt.Println(ap.CompileAction("main", abs("_test/compile/1a/exec"), abs("_test/output/1a")))
+	sys("_test/postcompile.sh", "_test/output/1a/main")
+	// Output:
+	// <nil>
+	// _test/output/1a/main: application/x-executable; charset=binary
+	// name=Mike
+	// {"message":"Hello, Mike!"}
+}
+
+func Example_compileAction_singlefile_hello() {
+	sys("_test/precompile.sh", "hello1.src", "2")
+	log, _ := ioutil.TempFile("", "log")
+	ap := NewActionProxy("./action/2", "../core/gobuild", log)
+	fmt.Println(ap.CompileAction("hello", "_test/compile/2/exec", ""))
+	sys("_test/postcompile.sh", "_test/compile/2/exec")
+	// Output:
+	// <nil>
+	// _test/compile/2/exec: application/x-executable; charset=binary
+	// name=Mike
+	// {"hello":"Hello, Mike!"}
+}
+
+func Example_compileAction_singlefile_hello_out() {
+	sys("_test/precompile.sh", "hello1.src", "2a")
+	log, _ := ioutil.TempFile("", "log")
+	ap := NewActionProxy("./action/2a", "../core/gobuild", log)
+	fmt.Println(ap.CompileAction("hello", "_test/compile/2a/exec", abs("_test/output/2a")))
+	sys("_test/postcompile.sh", "_test/output/2a/hello")
+	// Output:
+	// <nil>
+	// _test/output/2a/hello: application/x-executable; charset=binary
+	// name=Mike
+	// {"hello":"Hello, Mike!"}
+}
+
+func Example_compileAction_multifile_main() {
+	sys("_test/precompile.sh", "action", "3")
+	log, _ := ioutil.TempFile("", "log")
+	ap := NewActionProxy("./action/3", "../core/gobuild", log)
+	fmt.Println(ap.CompileAction("main", "_test/compile/3/", ""))
+	sys("_test/postcompile.sh", "_test/compile/3/main")
+	// Output:
+	// <nil>
+	// _test/compile/3/main: application/x-executable; charset=binary
+	// Main:
+	// Hello, Mike
+	// {"greetings":"Hello, Mike"}
+}
+
+func Example_compileAction_multifile_main_out() {
+	sys("_test/precompile.sh", "action", "3a")
+	log, _ := ioutil.TempFile("", "log")
+	ap := NewActionProxy("./action/3a", "../core/gobuild", log)
+	fmt.Println(ap.CompileAction("main", "_test/compile/3a/", abs("_test/output/3a")))
+	sys("_test/postcompile.sh", "_test/output/3a/main")
+	// Output:
+	// <nil>
+	// _test/output/3a/main: application/x-executable; charset=binary
+	// Main:
+	// Hello, Mike
+	// {"greetings":"Hello, Mike"}
+}
+
+func Example_compileAction_multifile_hello() {
+	sys("_test/precompile.sh", "action", "4")
+	log, _ := ioutil.TempFile("", "log")
+	ap := NewActionProxy("./action/4", "../core/gobuild", log)
+	fmt.Println(ap.CompileAction("hello", "_test/compile/4/", ""))
+	sys("_test/postcompile.sh", "_test/compile/4/hello")
+	// Output:
+	// <nil>
+	// _test/compile/4/hello: application/x-executable; charset=binary
+	// Hello, Mike
+	// {"greetings":"Hello, Mike"}
+}
+
+func Example_compileAction_multifile_hello_out() {
+	sys("_test/precompile.sh", "action", "4a")
+	log, _ := ioutil.TempFile("", "log")
+	ap := NewActionProxy("./action/4a", "../core/gobuild", log)
+	fmt.Println(ap.CompileAction("hello", "_test/compile/4/", abs("_test/output/4a")))
+	sys("_test/postcompile.sh", "_test/output/4a/hello")
+	// Output:
+	// <nil>
+	// _test/output/4a/hello: application/x-executable; charset=binary
+	// Hello, Mike
+	// {"greetings":"Hello, Mike"}
+}
diff --git a/openwhisk/executor.go b/openwhisk/executor.go
index 6a48f4b..160aea3 100644
--- a/openwhisk/executor.go
+++ b/openwhisk/executor.go
@@ -42,11 +42,13 @@
 	_output *bufio.Scanner
 	_logout *bufio.Scanner
 	_logerr *bufio.Scanner
+	_logbuf *os.File
 }
 
-// NewExecutor creates a child subprocess using the provided command line.
+// NewExecutor creates a child subprocess using the provided command line,
+// writing the logs in the given file.
 // You can then start it getting a communication channel
-func NewExecutor(command string, args ...string) (proc *Executor) {
+func NewExecutor(logbuf *os.File, command string, args ...string) (proc *Executor) {
 	cmd := exec.Command(command, args...)
 
 	stdin, err := cmd.StdinPipe()
@@ -79,6 +81,7 @@
 		bufio.NewScanner(pipeOut),
 		bufio.NewScanner(stdout),
 		bufio.NewScanner(stderr),
+		logbuf,
 	}
 }
 
@@ -106,6 +109,19 @@
 	log.Println("run: end")
 }
 
+func (proc *Executor) drain(ch chan string) {
+	runtime.Gosched()
+	for loop := true; loop; {
+		select {
+		case buf := <-ch:
+			fmt.Fprintln(proc._logbuf, buf)
+		case <-time.After(TIMEOUT):
+			loop = false
+		}
+	}
+	fmt.Fprintln(proc._logbuf, "XXX_THE_END_OF_A_WHISK_ACTIVATION_XXX")
+}
+
 // manage copying stdout and stder in output
 // with log guards
 func (proc *Executor) logger() {
@@ -119,28 +135,9 @@
 	// wait for the signal
 	for <-proc.log {
 		// flush stdout
-		runtime.Gosched()
-		for loop := true; loop; {
-			select {
-			case buf := <-chOut:
-				fmt.Println(buf)
-			case <-time.After(TIMEOUT):
-				loop = false
-			}
-		}
-		fmt.Println("XXX_THE_END_OF_A_WHISK_ACTIVATION_XXX")
-
+		proc.drain(chOut)
 		// flush stderr
-		runtime.Gosched()
-		for loop := true; loop; {
-			select {
-			case buf := <-chErr:
-				fmt.Println(buf)
-			case <-time.After(TIMEOUT):
-				loop = false
-			}
-		}
-		fmt.Println("XXX_THE_END_OF_A_WHISK_ACTIVATION_XXX")
+		proc.drain(chErr)
 	}
 	log.Printf("logger: end")
 }
diff --git a/openwhisk/executor_test.go b/openwhisk/executor_test.go
index b904cb3..db8247b 100644
--- a/openwhisk/executor_test.go
+++ b/openwhisk/executor_test.go
@@ -18,23 +18,25 @@
 
 import (
 	"fmt"
+	"io/ioutil"
 	"time"
 )
 
 func ExampleNewExecutor_failed() {
-	proc := NewExecutor("true")
+	log, _ := ioutil.TempFile("", "log")
+	proc := NewExecutor(log, "true")
 	err := proc.Start()
 	fmt.Println(err)
 	proc.Stop()
-	proc = NewExecutor("/bin/pwd")
+	proc = NewExecutor(log, "/bin/pwd")
 	err = proc.Start()
 	fmt.Println(err)
 	proc.Stop()
-	proc = NewExecutor("donotexist")
+	proc = NewExecutor(log, "donotexist")
 	err = proc.Start()
 	fmt.Println(err)
 	proc.Stop()
-	proc = NewExecutor("/etc/passwd")
+	proc = NewExecutor(log, "/etc/passwd")
 	err = proc.Start()
 	fmt.Println(err)
 	proc.Stop()
@@ -46,7 +48,8 @@
 }
 
 func ExampleNewExecutor_bc() {
-	proc := NewExecutor("_test/bc.sh")
+	log, _ := ioutil.TempFile("", "log")
+	proc := NewExecutor(log, "_test/bc.sh")
 	err := proc.Start()
 	fmt.Println(err)
 	//proc.log <- true
@@ -63,6 +66,7 @@
 	}
 	time.Sleep(100 * time.Millisecond)
 	proc.Stop()
+	dump(log)
 	// Output:
 	// <nil>
 	// 4
@@ -72,7 +76,8 @@
 }
 
 func ExampleNewExecutor_hello() {
-	proc := NewExecutor("_test/hello.sh")
+	log, _ := ioutil.TempFile("", "log")
+	proc := NewExecutor(log, "_test/hello.sh")
 	err := proc.Start()
 	fmt.Println(err)
 	proc.io <- `{"name":"Mike"}`
@@ -83,6 +88,7 @@
 	time.Sleep(100 * time.Millisecond)
 	_, ok := <-proc.io
 	fmt.Printf("io %v\n", ok)
+	dump(log)
 	// Unordered output:
 	// <nil>
 	// {"hello": "Mike"}
@@ -93,7 +99,8 @@
 }
 
 func ExampleNewExecutor_term() {
-	proc := NewExecutor("_test/hello.sh")
+	log, _ := ioutil.TempFile("", "log")
+	proc := NewExecutor(log, "_test/hello.sh")
 	err := proc.Start()
 	fmt.Println(err)
 	proc.io <- `{"name":"*"}`
@@ -111,6 +118,7 @@
 	time.Sleep(100 * time.Millisecond)
 	_, ok := <-proc.io
 	fmt.Printf("io %v\n", ok)
+	dump(log)
 	// Unordered output:
 	// <nil>
 	// exit true
diff --git a/openwhisk/extractor.go b/openwhisk/extractor.go
index 17965bb..e496b07 100644
--- a/openwhisk/extractor.go
+++ b/openwhisk/extractor.go
@@ -100,32 +100,25 @@
 	return max
 }
 
-var currentDir = highestDir("./action")
-
-// extractAction accept a byte array write it to a file
-func extractAction(buf *[]byte, isScript bool) error {
+// ExtractAction accept a byte array and write it to a file
+// it handles zip files extracting the content
+// it stores in a new directory under ./action/XXX whee x is incremented every time
+// it returns the file if a file or the directory if it was a zip file
+func (ap *ActionProxy) ExtractAction(buf *[]byte, main string) (string, error) {
 	if buf == nil || len(*buf) == 0 {
-		return fmt.Errorf("no file")
+		return "", fmt.Errorf("no file")
 	}
-	currentDir++
-	newDir := fmt.Sprintf("./action/%d", currentDir)
+	ap.currentDir++
+	newDir := fmt.Sprintf("%s/%d", ap.baseDir, ap.currentDir)
 	os.MkdirAll(newDir, 0755)
 	kind, err := filetype.Match(*buf)
 	if err != nil {
-		return err
+		return "", err
 	}
 	if kind.Extension == "zip" {
 		log.Println("Extract Action, assuming a zip")
-		return unzip(*buf, newDir)
+		return newDir, unzip(*buf, newDir)
 	}
-	if kind.Extension == "elf" || isScript {
-		if isScript {
-			log.Println("Extract Action, assuming a script")
-		} else {
-			log.Println("Extract Action, assuming a binary")
-		}
-		return ioutil.WriteFile(newDir+"/exec", *buf, 0755)
-	}
-	log.Println("No valid action found")
-	return fmt.Errorf("unknown filetype %s", kind)
+	file := newDir + "/" + main
+	return file, ioutil.WriteFile(file, *buf, 0755)
 }
diff --git a/openwhisk/extractor_test.go b/openwhisk/extractor_test.go
index ddc6522..6414120 100644
--- a/openwhisk/extractor_test.go
+++ b/openwhisk/extractor_test.go
@@ -29,8 +29,9 @@
 	"github.com/stretchr/testify/assert"
 )
 
-func sys(cli string) {
-	cmd := exec.Command(cli)
+func sys(cli string, args ...string) {
+	os.Chmod(cli, 0755)
+	cmd := exec.Command(cli, args...)
 	out, err := cmd.CombinedOutput()
 	if err != nil {
 		log.Print(err)
@@ -39,15 +40,6 @@
 	}
 }
 
-func TestExtractActionTest_exec(t *testing.T) {
-	sys("_test/build.sh")
-	// cleanup
-	assert.Nil(t, os.RemoveAll("./action"))
-	file, _ := ioutil.ReadFile("_test/exec")
-	extractAction(&file, false)
-	assert.Nil(t, exists("./action", "exec"))
-}
-
 func exists(dir, filename string) error {
 	path := fmt.Sprintf("%s/%d/%s", dir, highestDir(dir), filename)
 	_, err := os.Stat(path)
@@ -60,32 +52,51 @@
 	kind, _ := filetype.Match(file)
 	return kind.Extension
 }
-func TestExtractActionTest_exe(t *testing.T) {
+
+func TestExtractActionTest_exec(t *testing.T) {
+	log, _ := ioutil.TempFile("", "log")
+	ap := NewActionProxy("./action/x1", "", log)
 	sys("_test/build.sh")
 	// cleanup
-	assert.Nil(t, os.RemoveAll("./action"))
+	assert.Nil(t, os.RemoveAll("./action/x1"))
+	file, _ := ioutil.ReadFile("_test/exec")
+	ap.ExtractAction(&file, "exec")
+	assert.Nil(t, exists("./action/x1", "exec"))
+}
+
+func TestExtractActionTest_exe(t *testing.T) {
+	log, _ := ioutil.TempFile("", "log")
+	ap := NewActionProxy("./action/x2", "", log)
+	sys("_test/build.sh")
+	// cleanup
+	assert.Nil(t, os.RemoveAll("./action/x2"))
 	// match  exe
 	file, _ := ioutil.ReadFile("_test/exec")
-	extractAction(&file, false)
-	assert.Equal(t, detect("./action", "exec"), "elf")
+	ap.ExtractAction(&file, "exec")
+	assert.Equal(t, detect("./action/x2", "exec"), "elf")
 }
 
 func TestExtractActionTest_zip(t *testing.T) {
+	log, _ := ioutil.TempFile("", "log")
 	sys("_test/build.sh")
+	ap := NewActionProxy("./action/x3", "", log)
 	// cleanup
-	assert.Nil(t, os.RemoveAll("./action"))
+	assert.Nil(t, os.RemoveAll("./action/x3"))
 	// match  exe
 	file, _ := ioutil.ReadFile("_test/exec.zip")
-	extractAction(&file, false)
-	assert.Equal(t, detect("./action", "exec"), "elf")
-	assert.Nil(t, exists("./action", "etc"))
-	assert.Nil(t, exists("./action", "dir/etc"))
+	ap.ExtractAction(&file, "exec")
+	assert.Equal(t, detect("./action/x3", "exec"), "elf")
+	assert.Nil(t, exists("./action/x3", "etc"))
+	assert.Nil(t, exists("./action/x3", "dir/etc"))
 }
 
 func TestExtractAction_script(t *testing.T) {
+	log, _ := ioutil.TempFile("", "log")
+	ap := NewActionProxy("./action/x4", "", log)
 	buf := []byte("#!/bin/sh\necho ok")
-	assert.NotNil(t, extractAction(&buf, false))
-	assert.Nil(t, extractAction(&buf, true))
+	_, err := ap.ExtractAction(&buf, "exec")
+	//fmt.Print(err)
+	assert.Nil(t, err)
 }
 
 func TestHighestDir(t *testing.T) {
diff --git a/openwhisk/initHandler.go b/openwhisk/initHandler.go
index 59dce64..41dec5e 100644
--- a/openwhisk/initHandler.go
+++ b/openwhisk/initHandler.go
@@ -22,30 +22,37 @@
 	"encoding/json"
 	"fmt"
 	"io/ioutil"
+	"log"
 	"net/http"
 )
 
+type initBodyRequest struct {
+	Code   string `json:"code,omitempty"`
+	Binary bool   `json:"binary,omitempty"`
+	Main   string `json:"main,omitempty"`
+}
 type initRequest struct {
-	Value struct {
-		Code   string `json:",omitempty"`
-		Binary bool   `json:",omitempty"`
-	} `json:",omitempty"`
+	Value initBodyRequest `json:"value,omitempty"`
 }
 
 func sendOK(w http.ResponseWriter) {
 	// answer OK
 	w.Header().Set("Content-Type", "application/json")
-	w.Header().Set("Content-Length", "12")
-	w.Write([]byte("{\"ok\":true}\n"))
+	buf := []byte("{\"ok\":true}\n")
+	w.Header().Set("Content-Length", fmt.Sprintf("%d", len(buf)))
+	w.Write(buf)
 	if f, ok := w.(http.Flusher); ok {
 		f.Flush()
 	}
 }
 
-func initHandler(w http.ResponseWriter, r *http.Request) {
+func (ap *ActionProxy) initHandler(w http.ResponseWriter, r *http.Request) {
 
 	// read body of the request
-	// log.Println("init: reading")
+	if ap.compiler != "" {
+		log.Println("compiler: " + ap.compiler)
+	}
+
 	body, err := ioutil.ReadAll(r.Body)
 	defer r.Body.Close()
 	if err != nil {
@@ -53,7 +60,9 @@
 	}
 
 	// decode request parameters
-	//log.Println("init: decoding")
+	if ap.Trace {
+		log.Printf("init: decoding %s\n", string(body))
+	}
 	var request initRequest
 	err = json.Unmarshal(body, &request)
 
@@ -62,32 +71,58 @@
 		return
 	}
 
+	// request with empty code - stop any executor but return ok
+	if ap.Trace {
+		log.Printf("request: %v\n", request)
+	}
 	if request.Value.Code == "" {
+		if ap.theExecutor != nil {
+			log.Printf("stop running action")
+			ap.theExecutor.Stop()
+			ap.theExecutor = nil
+		}
 		sendOK(w)
 		return
 	}
 
-	// check if it is a binary
+	main := request.Value.Main
+	if main == "" {
+		main = "main"
+	}
+
+	// extract code eventually decoding it
+	var buf []byte
 	if request.Value.Binary {
-		var decoded []byte
-		decoded, err = base64.StdEncoding.DecodeString(request.Value.Code)
+		log.Printf("binary")
+		buf, err = base64.StdEncoding.DecodeString(request.Value.Code)
 		if err != nil {
 			sendError(w, http.StatusBadRequest, "cannot decode the request: "+err.Error())
 			return
 		}
-		// extract the replacement, stopping and then starting the action
-		err = extractAction(&decoded, false)
 	} else {
-		buf := []byte(request.Value.Code)
-		err = extractAction(&buf, true)
+		log.Printf("plain text")
+		buf = []byte(request.Value.Code)
 	}
-	if err != nil {
+
+	// extract the action,
+	file, err := ap.ExtractAction(&buf, main)
+	if err != nil || file == "" {
 		sendError(w, http.StatusBadRequest, "invalid action: "+err.Error())
 		return
 	}
 
+	// compile it if a compiler is available
+	if ap.compiler != "" && !isCompiled(file, main) {
+		log.Printf("compiling: %s main: %s", file, main)
+		err = ap.CompileAction(main, file, file)
+		if err != nil {
+			sendError(w, http.StatusBadRequest, "cannot compile action: "+err.Error())
+			return
+		}
+	}
+
 	// stop and start
-	err = StartLatestAction()
+	err = ap.StartLatestAction(main)
 	if err != nil {
 		sendError(w, http.StatusBadRequest, "cannot start action: "+err.Error())
 		return
diff --git a/openwhisk/initHandler_test.go b/openwhisk/initHandler_test.go
new file mode 100644
index 0000000..aa76f77
--- /dev/null
+++ b/openwhisk/initHandler_test.go
@@ -0,0 +1,238 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package openwhisk
+
+import "path/filepath"
+
+func Example_badinit_nocompiler() {
+	ts, cur, log := startTestServer("")
+	sys("_test/build.sh")
+	doRun(ts, "")
+	doInit(ts, "{}")
+	//sys("ls", "_test/exec")
+	doInit(ts, initBinary("_test/exec", ""))      // empty
+	doInit(ts, initBinary("_test/hi", ""))        // say hi
+	doInit(ts, initBinary("_test/hello.src", "")) // source not excutable
+	doRun(ts, "")
+	stopTestServer(ts, cur, log)
+	// Output:
+	// 400 {"error":"no action defined yet"}
+	// 200 {"ok":true}
+	// 400 {"error":"cannot start action: command exited"}
+	// 400 {"error":"cannot start action: command exited"}
+	// 400 {"error":"cannot start action: command exited"}
+	// 400 {"error":"no action defined yet"}
+
+}
+
+func Example_bininit_nocompiler() {
+	ts, cur, log := startTestServer("")
+	sys("_test/build.sh")
+	doRun(ts, "")
+	doInit(ts, initBinary("_test/hello_message", ""))
+	doRun(ts, "")
+	doInit(ts, initBinary("_test/hello_greeting", ""))
+	doRun(ts, "")
+	stopTestServer(ts, cur, log)
+	// Output:
+	// 400 {"error":"no action defined yet"}
+	// 200 {"ok":true}
+	// 200 {"message":"Hello, Mike!"}
+	// 200 {"ok":true}
+	// 200 {"greetings":"Hello, Mike"}
+	// name=Mike
+	// XXX_THE_END_OF_A_WHISK_ACTIVATION_XXX
+	// XXX_THE_END_OF_A_WHISK_ACTIVATION_XXX
+	// Hello, Mike
+	// XXX_THE_END_OF_A_WHISK_ACTIVATION_XXX
+	// XXX_THE_END_OF_A_WHISK_ACTIVATION_XXX
+}
+
+func Example_zipinit_nocompiler() {
+	ts, cur, log := startTestServer("")
+	sys("_test/build.sh")
+	doRun(ts, "")
+	doInit(ts, initBinary("_test/hello_greeting.zip", ""))
+	doRun(ts, "")
+	doInit(ts, initBinary("_test/hello_message.zip", ""))
+	doRun(ts, "")
+	stopTestServer(ts, cur, log)
+	// Output:
+	// 400 {"error":"no action defined yet"}
+	// 200 {"ok":true}
+	// 200 {"greetings":"Hello, Mike"}
+	// 200 {"ok":true}
+	// 200 {"message":"Hello, Mike!"}
+	// Hello, Mike
+	// XXX_THE_END_OF_A_WHISK_ACTIVATION_XXX
+	// XXX_THE_END_OF_A_WHISK_ACTIVATION_XXX
+	// name=Mike
+	// XXX_THE_END_OF_A_WHISK_ACTIVATION_XXX
+	// XXX_THE_END_OF_A_WHISK_ACTIVATION_XXX
+}
+
+/* commented out test for timing problems
+func Example_shell_nocompiler() {
+	ts, cur, log := startTestServer("")
+	doRun(ts, "")
+	doInit(ts, initBinary("_test/hello.sh", ""))
+	doRun(ts, "")
+	doRun(ts, `{"name":"*"}`)
+	doRun(ts, "")
+	stopTestServer(ts, cur, log)
+	// Output:
+	// 400 {"error":"no action defined yet"}
+	// 200 {"ok":true}
+	// 200 {"hello": "Mike"}
+	// 400 {"error":"command exited"}
+	// 400 {"error":"no action defined yet"}
+	// msg=hello Mike
+	// XXX_THE_END_OF_A_WHISK_ACTIVATION_XXX
+	// Goodbye!
+	// XXX_THE_END_OF_A_WHISK_ACTIVATION_XXX
+}*/
+
+func Example_main_nocompiler() {
+	ts, cur, log := startTestServer("")
+	sys("_test/build.sh")
+	doRun(ts, "")
+	doInit(ts, initBinary("_test/hello_message", "message"))
+	doRun(ts, "")
+	doInit(ts, initBinary("_test/hello_greeting", "greeting"))
+	doRun(ts, "")
+	stopTestServer(ts, cur, log)
+	// Output:
+	// 400 {"error":"no action defined yet"}
+	// 200 {"ok":true}
+	// 200 {"message":"Hello, Mike!"}
+	// 200 {"ok":true}
+	// 200 {"greetings":"Hello, Mike"}
+	// name=Mike
+	// XXX_THE_END_OF_A_WHISK_ACTIVATION_XXX
+	// XXX_THE_END_OF_A_WHISK_ACTIVATION_XXX
+	// Hello, Mike
+	// XXX_THE_END_OF_A_WHISK_ACTIVATION_XXX
+	// XXX_THE_END_OF_A_WHISK_ACTIVATION_XXX
+}
+
+func Example_main_zipinit_nocompiler() {
+	ts, cur, log := startTestServer("")
+	sys("_test/build.sh")
+	doRun(ts, "")
+	doInit(ts, initBinary("_test/hello_greeting.zip", "greeting"))
+	doInit(ts, initBinary("_test/hello_greeting1.zip", "greeting"))
+	doRun(ts, "")
+	doInit(ts, initBinary("_test/hello_message.zip", "message"))
+	doInit(ts, initBinary("_test/hello_message1.zip", "message"))
+	doRun(ts, "")
+	stopTestServer(ts, cur, log)
+	// Output:
+	// 400 {"error":"no action defined yet"}
+	// 400 {"error":"cannot start action: command exited"}
+	// 200 {"ok":true}
+	// 200 {"greetings":"Hello, Mike"}
+	// 400 {"error":"cannot start action: command exited"}
+	// 200 {"ok":true}
+	// 200 {"message":"Hello, Mike!"}
+	// Hello, Mike
+	// XXX_THE_END_OF_A_WHISK_ACTIVATION_XXX
+	// XXX_THE_END_OF_A_WHISK_ACTIVATION_XXX
+	// name=Mike
+	// XXX_THE_END_OF_A_WHISK_ACTIVATION_XXX
+	// XXX_THE_END_OF_A_WHISK_ACTIVATION_XXX
+}
+
+func Example_compile_simple() {
+	comp, _ := filepath.Abs("../core/gobuild")
+	ts, cur, log := startTestServer(comp)
+	sys("_test/build.sh")
+	doRun(ts, "")
+	doInit(ts, initCode("_test/hello.src", ""))
+	doRun(ts, "")
+	stopTestServer(ts, cur, log)
+	// Output:
+	// 400 {"error":"no action defined yet"}
+	// 200 {"ok":true}
+	// 200 {"message":"Hello, Mike!"}
+	// name=Mike
+	// XXX_THE_END_OF_A_WHISK_ACTIVATION_XXX
+	// XXX_THE_END_OF_A_WHISK_ACTIVATION_XXX
+}
+
+func Example_compile_withMain() {
+	comp, _ := filepath.Abs("../core/gobuild")
+	ts, cur, log := startTestServer(comp)
+	sys("_test/build.sh")
+	doRun(ts, "")
+	doInit(ts, initCode("_test/hello1.src", ""))
+	doInit(ts, initCode("_test/hello1.src", "hello"))
+	doRun(ts, "")
+	stopTestServer(ts, cur, log)
+	// Output:
+	// 400 {"error":"no action defined yet"}
+	// 400 {"error":"cannot start action: command exited"}
+	// 200 {"ok":true}
+	// 200 {"hello":"Hello, Mike!"}
+	// name=Mike
+	// XXX_THE_END_OF_A_WHISK_ACTIVATION_XXX
+	// XXX_THE_END_OF_A_WHISK_ACTIVATION_XXX
+}
+
+func Example_compile_withZipSrc() {
+	sys("_test/zips.sh")
+	comp, _ := filepath.Abs("../core/gobuild")
+	ts, cur, log := startTestServer(comp)
+	doRun(ts, "")
+	doInit(ts, initBinary("_test/action.zip", ""))
+	doRun(ts, "")
+	doInit(ts, initBinary("_test/action.zip", "hello"))
+	doRun(ts, "")
+	stopTestServer(ts, cur, log)
+	// Output:
+	// 400 {"error":"no action defined yet"}
+	// 200 {"ok":true}
+	// 200 {"greetings":"Hello, Mike"}
+	// 200 {"ok":true}
+	// 200 {"greetings":"Hello, Mike"}
+	// Main:
+	// Hello, Mike
+	// XXX_THE_END_OF_A_WHISK_ACTIVATION_XXX
+	// XXX_THE_END_OF_A_WHISK_ACTIVATION_XXX
+	// Hello, Mike
+	// XXX_THE_END_OF_A_WHISK_ACTIVATION_XXX
+	// XXX_THE_END_OF_A_WHISK_ACTIVATION_XXX
+}
+
+/*
+func Example_compile_withZipSrcDefault() {
+	sys("_test/zips.sh")
+	comp, _ := filepath.Abs("../core/gobuild")
+	ts, cur := startTestServer(comp)
+	doRun(ts, "")
+	doInit(ts, initBinary("_test/action.zip", ""))
+	doRun(ts, "")
+	stopTestServer(ts, cur)
+	// Output:
+	// 400 {"error":"no action defined yet"}
+	// 200 {"ok":true}
+	// name=Mike
+	// 200 {"hello":"Hello, Mike!"}
+	// XXX_THE_END_OF_A_WHISK_ACTIVATION_XXX
+	// XXX_THE_END_OF_A_WHISK_ACTIVATION_XXX
+}
+/**/
diff --git a/openwhisk/runHandler.go b/openwhisk/runHandler.go
index 027a9d8..14cf614 100644
--- a/openwhisk/runHandler.go
+++ b/openwhisk/runHandler.go
@@ -49,7 +49,7 @@
 	w.Write([]byte("\n"))
 }
 
-func runHandler(w http.ResponseWriter, r *http.Request) {
+func (ap *ActionProxy) runHandler(w http.ResponseWriter, r *http.Request) {
 
 	// parse the request
 	params := Params{}
@@ -68,32 +68,32 @@
 	}
 
 	// check if you have an action
-	if theExecutor == nil {
+	if ap.theExecutor == nil {
 		sendError(w, http.StatusBadRequest, fmt.Sprintf("no action defined yet"))
 		return
 	}
 
 	// execute the action
 	// and check for early termination
-	theExecutor.io <- string(params.Value)
+	ap.theExecutor.io <- string(params.Value)
 	var response string
 	var exited bool
 	select {
-	case response = <-theExecutor.io:
+	case response = <-ap.theExecutor.io:
 		exited = false
-	case err = <-theExecutor.exit:
+	case err = <-ap.theExecutor.exit:
 		exited = true
 	}
 
 	// check for early termination
 	if exited {
-		theExecutor = nil
+		ap.theExecutor = nil
 		sendError(w, http.StatusBadRequest, fmt.Sprintf("command exited"))
 		return
 	}
 
 	// flush the logs sending the activation message at the end
-	theExecutor.log <- true
+	ap.theExecutor.log <- true
 
 	// check response
 	if response == "" {
@@ -107,6 +107,7 @@
 	}
 	log.Print(response)
 	w.Header().Set("Content-Type", "application/json")
+	w.Header().Set("Content-Length", fmt.Sprintf("%d", len(response)))
 	numBytesWritten, err := w.Write([]byte(response))
 
 	// flush output
diff --git a/openwhisk/util_test.go b/openwhisk/util_test.go
new file mode 100644
index 0000000..6eea7af
--- /dev/null
+++ b/openwhisk/util_test.go
@@ -0,0 +1,141 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package openwhisk
+
+import (
+	"bytes"
+	"encoding/base64"
+	"encoding/json"
+	"fmt"
+	"io/ioutil"
+	"log"
+	"net/http"
+	"net/http/httptest"
+	"os"
+	"path/filepath"
+	"runtime"
+	"time"
+)
+
+func startTestServer(compiler string) (*httptest.Server, string, *os.File) {
+	// temporary workdir
+	cur, _ := os.Getwd()
+	dir, _ := ioutil.TempDir("", "action")
+	file, _ := filepath.Abs("_test")
+	os.Symlink(file, dir+"/_test")
+	os.Chdir(dir)
+	log.Printf(dir)
+	// setup the server
+	buf, _ := ioutil.TempFile("", "log")
+	ap := NewActionProxy(dir, compiler, buf)
+	ts := httptest.NewServer(ap)
+	log.Printf(ts.URL)
+	doPost(ts.URL+"/init", `{value: {code: ""}}`)
+	return ts, cur, buf
+}
+
+func stopTestServer(ts *httptest.Server, cur string, buf *os.File) {
+	runtime.Gosched()
+	time.Sleep(1 * time.Second)
+	os.Chdir(cur)
+	ts.Close()
+	dump(buf)
+}
+
+func doPost(url string, message string) (string, int, error) {
+	buf := bytes.NewBufferString(message)
+	res, err := http.Post(url, "application/json", buf)
+	if err != nil {
+		return "", -1, err
+	}
+	defer res.Body.Close()
+	resp, err := ioutil.ReadAll(res.Body)
+	if err != nil {
+		return "", -1, err
+	}
+	return string(resp), res.StatusCode, nil
+}
+
+func doRun(ts *httptest.Server, message string) {
+	if message == "" {
+		message = `{"name":"Mike"}`
+	}
+	resp, status, err := doPost(ts.URL+"/run", `{ "value": `+message+`}`)
+	if err != nil {
+		fmt.Println(err)
+	} else {
+		fmt.Printf("%d %s", status, resp)
+	}
+}
+
+func doInit(ts *httptest.Server, message string) {
+	resp, status, err := doPost(ts.URL+"/init", message)
+	if err != nil {
+		fmt.Println(err)
+	} else {
+		fmt.Printf("%d %s", status, resp)
+	}
+}
+
+func initCode(file string, main string) string {
+	dat, _ := ioutil.ReadFile(file)
+	body := initBodyRequest{Code: string(dat)}
+	if main != "" {
+		body.Main = main
+	}
+	j, _ := json.Marshal(initRequest{Value: body})
+	return string(j)
+}
+
+func initBinary(file string, main string) string {
+	dat, _ := ioutil.ReadFile(file)
+	enc := base64.StdEncoding.EncodeToString(dat)
+	body := initBodyRequest{Binary: true, Code: enc}
+	if main != "" {
+		body.Main = main
+	}
+	j, _ := json.Marshal(initRequest{Value: body})
+	return string(j)
+}
+
+func abs(in string) string {
+	out, _ := filepath.Abs(in)
+	return out
+}
+
+func Example_json_init() {
+	fmt.Println(initCode("", ""))
+	fmt.Println(initCode("_test/etc", ""))
+	fmt.Println(initCode("_test/etc", "world"))
+	fmt.Println(initBinary("_test/etc", ""))
+	fmt.Println(initBinary("_test/etc", "hello"))
+	// Output:
+	// {"value":{}}
+	// {"value":{"code":"1\n"}}
+	// {"value":{"code":"1\n","main":"world"}}
+	// {"value":{"code":"MQo=","binary":true}}
+	// {"value":{"code":"MQo=","binary":true,"main":"hello"}}
+}
+
+func dump(file *os.File) {
+	//file.Read()
+	buf, _ := ioutil.ReadFile(file.Name())
+	fmt.Print(string(buf))
+	//fmt.Print(file.ReadAll())
+	os.Remove(file.Name())
+}
diff --git a/settings.gradle b/settings.gradle
index d161c89..4972eb7 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1,3 +1,6 @@
+include 'core:actionloop'
+include 'core:actionloop-golang'
+
 rootProject.name = 'runtime-golang'
 
 gradle.ext.openwhisk = [
diff --git a/test/NOTES.md b/test/NOTES.md
new file mode 100644
index 0000000..1546b09
--- /dev/null
+++ b/test/NOTES.md
@@ -0,0 +1,38 @@
+
+<!--
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more contributor 
+# license agreements.  See the NOTICE file distributed with this work for additional 
+# information regarding copyright ownership.  The ASF licenses this file to you
+# under the Apache License, Version 2.0 (the # "License"); you may not use this 
+# file except in compliance with the License.  You may obtain a copy of the License 
+# at:
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software distributed 
+# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 
+# CONDITIONS OF ANY KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations under the License.
+#
+-->
+This is an optional command line test kit. 
+
+It has been superseded by a go test suite and scalatests, 
+but it is still around for debugging.
+
+
+TO use it:
+- you need to install cram to use it and build images
+- you can run them with `cram test_actionloop.t`
+- and `cram test_actionloop-golang`
+
+also you can start directy the binary without the images with
+
+- `./start.sh` 
+- or `COMPILER=../core/goproxy ./start.sh`
+
+If you start them,  images won't be started by the test.
+
+
+
diff --git a/test/README.md b/test/README.md
deleted file mode 100644
index 3a601c9..0000000
--- a/test/README.md
+++ /dev/null
@@ -1,9 +0,0 @@
-# Test
-
-How to run tests:
-
-- ensure you are running them under linux !
-- install `cram`: `sudo pip install cram`
-- start the tester: `cd test ; ./start.sh`
-- in another terminal `cd test ; cram test.t`
-
diff --git a/test/bin/build.sh b/test/bin/build.sh
index 4ed5b0a..a000403 100755
--- a/test/bin/build.sh
+++ b/test/bin/build.sh
@@ -1,10 +1,27 @@
 #!/bin/sh
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
 FILE=${1:?go file}
+EXEC=${2:-main}
 OUT=$(basename $FILE)
 BIN=${OUT%%.go}
 ZIP=${BIN}.zip
 go build -i -o bin/$BIN $FILE
-GOOS=linux GOARCH=amd64 go build -o exec $FILE
-zip zip/$ZIP exec
-rm exec
-echo "built bin/$BIN zip/$ZIP"
+GOOS=linux GOARCH=amd64 go build -o $EXEC $FILE
+zip zip/$ZIP $EXEC
+rm $EXEC
+echo "built $EXEC bin/$BIN zip/$ZIP"
diff --git a/test/bin/init.sh b/test/bin/init.sh
index e700bb4..6efc105 100755
--- a/test/bin/init.sh
+++ b/test/bin/init.sh
@@ -1,10 +1,31 @@
 #!/bin/bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
 FILE=${1:?file}
-JSON=${2:-/tmp/json$$}
-if file -i $FILE | grep text/ >/dev/null
-then echo '{"value":{"main":"main","code":'$(cat $FILE | jq -R -s .)'}}' >/tmp/json$$
-else echo '{"value":{"binary":true,"code":"'$(base64 -w 0 $FILE)'"}}' >/tmp/json$$
+MAIN=${2:-}
+JSON=/tmp/json$$
+if test -n "$MAIN"
+then MAIN=",\"main\":\"$MAIN\""
 fi
-#cat $JSON | jq .
-curl -H "Content-Type: application/json" -XPOST -w "%{http_code}\n" http://localhost:${PORT:-8080}/init -d @$JSON 2>/dev/null
-rm /tmp/json$$ 2>/dev/null
+if file -i $FILE | grep text/ >/dev/null
+then echo '{"value":{"code":' $(cat $FILE | jq -R -s .) $MAIN '}}' >$JSON
+else echo '{"value":{"binary":true,"code":"'$(base64 -w 0 $FILE)'"}}' >$JSON
+fi
+curl -H "Content-Type: application/json" -XPOST -w "%{http_code}\n" http://${HOST:-localhost}:${PORT:-8080}/init -d @"$JSON" 2>/dev/null
+#echo $JSON
+#echo $MAIN
+rm $JSON 2>/dev/null
diff --git a/test/bin/post.sh b/test/bin/post.sh
index 6255f39..fbe853e 100755
--- a/test/bin/post.sh
+++ b/test/bin/post.sh
@@ -1,3 +1,19 @@
 #!/bin/bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
 FILE=${1:?file}
 curl -H "Content-Type: application/json" -XPOST -w "%{http_code}\n" http://localhost:${PORT:-8080}/init -d @$FILE 2>/dev/null
diff --git a/test/bin/run.sh b/test/bin/run.sh
index 0df821e..637c4cf 100755
--- a/test/bin/run.sh
+++ b/test/bin/run.sh
@@ -1,6 +1,22 @@
 #!/bin/bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
 DEFAULT='{"name": "Mike"}'
 JSON=${1:-$DEFAULT}
 DATA='{"value":'$JSON'}'
-curl -H "Content-Type: application/json" -w "%{http_code}\n" -XPOST http://localhost:${PORT:-8080}/run -d "$DATA" 2>/dev/null
+curl -H "Content-Type: application/json" -w "%{http_code}\n" -XPOST http://${HOST:-localhost}:${PORT:-8080}/run -d "$DATA" 2>/dev/null
 
diff --git a/test/crammer.sh b/test/crammer.sh
new file mode 100644
index 0000000..b968fe4
--- /dev/null
+++ b/test/crammer.sh
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+export T=$PWD
+while read -p "> " line
+do eval ${line#$}
+done
diff --git a/test/etc/hello.go b/test/etc/hello.go
new file mode 100644
index 0000000..990f919
--- /dev/null
+++ b/test/etc/hello.go
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package action
+
+import (
+	"encoding/json"
+	"log"
+)
+
+func Main(event json.RawMessage) (json.RawMessage, error) {
+	var obj map[string]interface{}
+	json.Unmarshal(event, &obj)
+	name, ok := obj["name"].(string)
+	if !ok {
+		name = "Stranger"
+	}
+	log.Printf("name=%s\n", name)
+	msg := map[string]string{"hello": ("Hello, " + name + "!")}
+	return json.Marshal(msg)
+}
diff --git a/test/etc/hello.js b/test/etc/hello.js
index a1bf768..90bc08c 100644
--- a/test/etc/hello.js
+++ b/test/etc/hello.js
@@ -1,5 +1,23 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 function main(args) {
   return {
      "result": "Hello, "+args.name
   }
 }
+exports.main = main
diff --git a/test/etc/hello.sh b/test/etc/hello.sh
index 9b7f318..13771b8 100755
--- a/test/etc/hello.sh
+++ b/test/etc/hello.sh
@@ -1,4 +1,20 @@
 #!/bin/bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
 while read line
 do
    name="$(echo $line | jq -r .name)"
diff --git a/test/golang/hello/hello.go b/test/golang/hello/hello.go
new file mode 100644
index 0000000..95e1586
--- /dev/null
+++ b/test/golang/hello/hello.go
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package action
+
+import (
+	"encoding/json"
+	"fmt"
+)
+
+func Hello(event json.RawMessage) (json.RawMessage, error) {
+	var obj struct {
+		Name string `json:",omitempty"`
+	}
+	err := json.Unmarshal(event, &obj)
+	if err != nil {
+		return nil, err
+	}
+	name := obj.Name
+	if name == "" {
+		name = "Stranger"
+	}
+	fmt.Printf("name=%s\n", name)
+	msg := map[string]string{"hello": ("Hello, " + name + "!")}
+	return json.Marshal(msg)
+}
diff --git a/test/golang/main/main.go b/test/golang/main/main.go
new file mode 100644
index 0000000..2bfd8c1
--- /dev/null
+++ b/test/golang/main/main.go
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package action
+
+import (
+	"encoding/json"
+	"fmt"
+)
+
+func Main(event json.RawMessage) (json.RawMessage, error) {
+	var obj map[string]interface{}
+	json.Unmarshal(event, &obj)
+	name, ok := obj["name"].(string)
+	if !ok {
+		name = "Stranger"
+	}
+	fmt.Printf("name=%s\n", name)
+	msg := map[string]string{"message": ("Hello, " + name + "!")}
+	return json.Marshal(msg)
+}
diff --git a/test/golang/out/hello b/test/golang/out/hello
new file mode 100755
index 0000000..a252d9d
--- /dev/null
+++ b/test/golang/out/hello
Binary files differ
diff --git a/test/golang/out/main b/test/golang/out/main
new file mode 100755
index 0000000..5bbd213
--- /dev/null
+++ b/test/golang/out/main
Binary files differ
diff --git a/test/src/hello/hello.go b/test/src/hello/hello.go
index b0592d0..2baaa01 100644
--- a/test/src/hello/hello.go
+++ b/test/src/hello/hello.go
@@ -1,3 +1,19 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 package hello
 
 import (
diff --git a/test/src/hello/hello_test.go b/test/src/hello/hello_test.go
index 0624cad..94de222 100644
--- a/test/src/hello/hello_test.go
+++ b/test/src/hello/hello_test.go
@@ -1,3 +1,19 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 package hello
 
 import (
diff --git a/test/src/hello_greeting.go b/test/src/hello_greeting.go
index f70bd2c..139abe3 100644
--- a/test/src/hello_greeting.go
+++ b/test/src/hello_greeting.go
@@ -21,10 +21,10 @@
 	"os"
 
 	"github.com/apache/incubator-openwhisk-client-go/whisk"
-	"github.com/sciabarracom/incubator-openwhisk-runtime-go/test/src/hello"
+	"github.com/sciabarracom/incubator-openwhisk-runtime-go/test/src/action"
 )
 
 func main() {
 	log.SetPrefix("hello_greeting: ")
-	whisk.StartWithArgs(hello.Hello, os.Args[1:])
+	whisk.StartWithArgs(action.Hello, os.Args[1:])
 }
diff --git a/test/start.sh b/test/start.sh
index 5310d7f..6f574ce 100755
--- a/test/start.sh
+++ b/test/start.sh
@@ -1,8 +1,32 @@
 #!/bin/bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+if netstat -tlnp 2>/dev/null | grep :8080 >/dev/null
+then exit 0
+fi
 cd "$(dirname $0)"
 bin/build.sh src/hello_greeting.go 
 bin/build.sh src/hello_message.go 
 bin/build.sh src/empty.go
 bin/build.sh src/hi.go
+zip -j zip/hello_message1.zip bin/hello_message
 rm -Rvf action
-go run ../main/proxy.go -debug
+mkdir action
+if test -n "$1"
+then docker run --name=goproxy -p 8080:8080 -d "$@" 
+else go run ../main/proxy.go -debug
+fi
diff --git a/test/stop.sh b/test/stop.sh
new file mode 100755
index 0000000..961fd6d
--- /dev/null
+++ b/test/stop.sh
@@ -0,0 +1,26 @@
+#!/bin/bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+IMG=$(docker ps -q -f name=goproxy)
+if test -n "$IMG"
+then docker kill $IMG  >/dev/null 2>/dev/null
+     docker rm $IMG >/dev/null 2>/dev/null
+fi
+PID=$(lsof -t -i:8080)
+if test -n "$PID"
+then kill -9 $PID 2>&1 >/dev/null
+fi
diff --git a/test/swift/hello/hello.swift b/test/swift/hello/hello.swift
new file mode 100644
index 0000000..7120aa7
--- /dev/null
+++ b/test/swift/hello/hello.swift
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+func hello(args: [String:Any]) -> [String:Any] {
+    if let name = args["name"] as? String {
+        print(name)
+        return [ "hello" : "Hello \(name)!" ]
+    } else {
+        return [ "hello" : "Hello swif4!" ]
+    }
+}
diff --git a/test/swift/main/main.swift b/test/swift/main/main.swift
new file mode 100644
index 0000000..059a1a0
--- /dev/null
+++ b/test/swift/main/main.swift
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+func main(args: [String:Any]) -> [String:Any] {
+    if let name = args["name"] as? String {
+        print(name)
+        return [ "main" : "Hello \(name)!" ]
+    } else {
+        return [ "main" : "Hello swif4!" ]
+    }
+}
diff --git a/test/swift/out/hello b/test/swift/out/hello
new file mode 100755
index 0000000..0c6aca6
--- /dev/null
+++ b/test/swift/out/hello
Binary files differ
diff --git a/test/swift/out/main b/test/swift/out/main
new file mode 100755
index 0000000..2174175
--- /dev/null
+++ b/test/swift/out/main
Binary files differ
diff --git a/test/test.t b/test/test.t
deleted file mode 100644
index a85b86c..0000000
--- a/test/test.t
+++ /dev/null
@@ -1,93 +0,0 @@
-  $ export T=$TESTDIR
-
-  $ $T/bin/run.sh 
-  {"error":"no action defined yet"}
-  400
-
-  $ $T/bin/post.sh $T/etc/empty.json
-  {"ok":true}
-  200
-
-  $ $T/bin/init.sh $T/test.t
-  {"error":"cannot start action: command exited"}
-  400
-
-  $ $T/bin/init.sh $T/bin/empty
-  {"error":"cannot start action: command exited"}
-  400
-
-  $ $T/bin/init.sh $T/bin/hi
-  {"error":"cannot start action: command exited"}
-  400
-
-  $ $T/bin/run.sh 
-  {"error":"no action defined yet"}
-  400
-
-  $ $T/bin/init.sh $T/bin/hello_message
-  {"ok":true}
-  200
-
-  $ $T/bin/run.sh 
-  {"message":"Hello, Mike!"}
-  200
-
-  $ $T/bin/init.sh $T/bin/hello_greeting
-  {"ok":true}
-  200
-
-  $ $T/bin/run.sh 
-  {"greetings":"Hello, Mike"}
-  200
-
-  $ $T/bin/init.sh $T/zip/hello_message.zip
-  {"ok":true}
-  200
-
-  $ $T/bin/run.sh 
-  {"message":"Hello, Mike!"}
-  200
-
-  $ $T/bin/init.sh $T/zip/hello_greeting.zip
-  {"ok":true}
-  200
-
-  $ $T/bin/run.sh 
-  {"greetings":"Hello, Mike"}
-  200
-
-  $ $T/bin/init.sh $T/test.t
-  {"error":"cannot start action: command exited"}
-  400
-
-  $ $T/bin/run.sh 
-  {"greetings":"Hello, Mike"}
-  200
-
-  $ $T/bin/init.sh $T/bin/empty
-  {"error":"cannot start action: command exited"}
-  400
-
-  $ $T/bin/run.sh 
-  {"greetings":"Hello, Mike"}
-  200
-
-  $ $T/bin/init.sh $T/bin/hi
-  {"error":"cannot start action: command exited"}
-  400
-
-  $ $T/bin/init.sh $T/etc/hello.sh
-  {"ok":true}
-  200
-
-  $ $T/bin/run.sh
-  {"hello":"Hello, Mike"}
-  200
-
-  $ $T/bin/run.sh '{"name": ""}'
-  {"error":"command exited"}
-  400
-
-  $ $T/bin/run.sh
-  {"error":"no action defined yet"}
-  400
\ No newline at end of file
diff --git a/test/test_actionloop-golang.t b/test/test_actionloop-golang.t
new file mode 100644
index 0000000..91d8eef
--- /dev/null
+++ b/test/test_actionloop-golang.t
@@ -0,0 +1,66 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+  $ export T=$TESTDIR
+  $ $T/start.sh actionloop-golang-v1.9 2>$T/err.log >$T/out.log 
+
+  $ $T/bin/run.sh
+  {"error":"no action defined yet"}
+  400
+
+  $ $T/bin/post.sh $T/etc/empty.json
+  {"ok":true}
+  200
+
+  $ $T/bin/init.sh $T/bin/empty
+  {"error":"cannot start action: command exited"}
+  400
+
+
+  $ $T/bin/run.sh 
+  {"error":"no action defined yet"}
+  400
+
+  $ $T/bin/init.sh $T/etc/hello.go
+  {"ok":true}
+  200
+ 
+  $ $T/bin/run.sh 
+  {"hello":"Hello, Mike!"}
+  200
+
+  $ $T/bin/run.sh '{"name":"world"}'
+  {"hello":"Hello, world!"}
+  200
+
+  $ $T/bin/init.sh $T/bin/hello_message
+  {"ok":true}
+  200
+ 
+  $ $T/bin/run.sh 
+  {"message":"Hello, Mike!"}
+  200
+
+  $ $T/bin/init.sh $T/zip/hello_greeting.zip
+  {"ok":true}
+  200
+
+  $ $T/bin/run.sh 
+  {"greetings":"Hello, Mike"}
+  200
+
+  $ $T/stop.sh
diff --git a/test/test_actionloop.t b/test/test_actionloop.t
new file mode 100644
index 0000000..4435404
--- /dev/null
+++ b/test/test_actionloop.t
@@ -0,0 +1,133 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+  $ export T=$TESTDIR
+  $ $T/start.sh actionloop 2>$T/err.log >$T/out.log 
+
+Default: no action defined 
+
+  $ $T/bin/post.sh $T/etc/empty.json
+  {"ok":true}
+  200
+
+  $ $T/bin/run.sh 
+  {"error":"no action defined yet"}
+  400
+
+  $ $T/bin/init.sh $T/etc/hello.go
+  {"error":"cannot start action: command exited"}
+  400
+
+  $ $T/bin/init.sh $T/bin/empty
+  {"error":"cannot start action: command exited"}
+  400
+
+  $ $T/bin/init.sh $T/bin/hi
+  {"error":"cannot start action: command exited"}
+  400
+
+  $ $T/bin/run.sh 
+  {"error":"no action defined yet"}
+  400
+
+Sending some messages 
+
+  $ $T/bin/init.sh $T/bin/hello_message
+  {"ok":true}
+  200
+
+  $ $T/bin/run.sh 
+  {"message":"Hello, Mike!"}
+  200
+
+  $ $T/bin/init.sh $T/bin/hello_greeting
+  {"ok":true}
+  200
+
+  $ $T/bin/run.sh 
+  {"greetings":"Hello, Mike"}
+  200
+
+  $ $T/bin/init.sh $T/zip/hello_message.zip
+  {"ok":true}
+  200
+
+  $ $T/bin/run.sh 
+  {"message":"Hello, Mike!"}
+  200
+
+  $ $T/bin/init.sh $T/zip/hello_greeting.zip
+  {"ok":true}
+  200
+
+  $ $T/bin/run.sh 
+  {"greetings":"Hello, Mike"}
+  200
+
+Invalid  script
+
+  $ $T/bin/init.sh $T/test_actionloop.t
+  {"error":"cannot start action: command exited"}
+  400
+
+  $ $T/bin/run.sh 
+  {"greetings":"Hello, Mike"}
+  200
+
+  $ $T/bin/init.sh $T/bin/empty
+  {"error":"cannot start action: command exited"}
+  400
+
+  $ $T/bin/run.sh 
+  {"greetings":"Hello, Mike"}
+  200
+
+  $ $T/bin/init.sh $T/bin/hi
+  {"error":"cannot start action: command exited"}
+  400
+
+A shell script
+
+  $ $T/bin/init.sh $T/etc/hello.sh
+  {"ok":true}
+  200
+
+  $ $T/bin/run.sh
+  {"hello":"Hello, Mike"}
+  200
+
+  $ $T/bin/run.sh '{"name": ""}'
+  {"error":"command exited"}
+  400
+
+  $ $T/bin/run.sh
+  {"error":"no action defined yet"}
+  400
+
+
+Test with a non-main executable
+
+  $ $T/bin/init.sh $T/zip/hello_message1.zip
+  {"error":"cannot start action: command exited"}
+  400
+
+  $ $T/bin/init.sh $T/zip/hello_message1.zip hello_message
+  {"error":"cannot start action: command exited"}
+  400
+
+
+  $ $T/stop.sh 
diff --git a/tools/travis/build.sh b/tools/travis/build.sh
new file mode 100755
index 0000000..d6a0f0e
--- /dev/null
+++ b/tools/travis/build.sh
@@ -0,0 +1,40 @@
+#!/bin/bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+set -ex
+
+# Build script for Travis-CI.
+
+SCRIPTDIR=$(cd $(dirname "$0") && pwd)
+ROOTDIR="$SCRIPTDIR/../.."
+WHISKDIR="$ROOTDIR/../openwhisk"
+UTILDIR="$ROOTDIR/../incubator-openwhisk-utilities"
+
+export OPENWHISK_HOME=$WHISKDIR
+
+IMAGE_PREFIX="testing"
+
+# run scancode using the ASF Release configuration
+cd $UTILDIR
+scancode/scanCode.py --config scancode/ASF-Release.cfg $ROOTDIR
+
+# Build runtime
+cd $ROOTDIR
+TERM=dumb ./gradlew \
+distDocker \
+-PdockerImagePrefix=${IMAGE_PREFIX}
diff --git a/tools/travis/setup.sh b/tools/travis/setup.sh
new file mode 100755
index 0000000..b039213
--- /dev/null
+++ b/tools/travis/setup.sh
@@ -0,0 +1,30 @@
+#!/bin/bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+set -e
+
+# Build script for Travis-CI.
+
+SCRIPTDIR=$(cd $(dirname "$0") && pwd)
+ROOTDIR="$SCRIPTDIR/../.."
+HOMEDIR="$SCRIPTDIR/../../../"
+
+# clone OpenWhisk utilities repo. in order to run scanCode
+cd $HOMEDIR
+git clone https://github.com/apache/incubator-openwhisk-utilities.git
+
diff --git a/tools/travis/test.sh b/tools/travis/test.sh
new file mode 100755
index 0000000..5ff94f1
--- /dev/null
+++ b/tools/travis/test.sh
@@ -0,0 +1,33 @@
+#!/bin/bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+set -ex
+
+# Build script for Travis-CI.
+
+SCRIPTDIR=$(cd $(dirname "$0") && pwd)
+ROOTDIR="$SCRIPTDIR/../.."
+WHISKDIR="$ROOTDIR/../openwhisk"
+
+export OPENWHISK_HOME=$WHISKDIR
+
+cd ${ROOTDIR}
+TERM=dumb ./gradlew test
+
+
+