{"id":21428094,"url":"https://github.com/redhat-developer-demos/faas-tutorial","last_synced_at":"2025-07-14T10:31:40.586Z","repository":{"id":91531440,"uuid":"125627767","full_name":"redhat-developer-demos/faas-tutorial","owner":"redhat-developer-demos","description":"Java FaaS demos with OpenWhisk and OpenShift","archived":false,"fork":false,"pushed_at":"2018-09-14T11:18:19.000Z","size":1080,"stargazers_count":43,"open_issues_count":12,"forks_count":28,"subscribers_count":14,"default_branch":"master","last_synced_at":"2025-04-08T06:51:11.595Z","etag":null,"topics":["demos","faas","java","openshift","openwhisk","serverless"],"latest_commit_sha":null,"homepage":null,"language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/redhat-developer-demos.png","metadata":{"files":{"readme":"README.adoc","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-03-17T12:39:06.000Z","updated_at":"2023-09-08T17:38:02.000Z","dependencies_parsed_at":null,"dependency_job_id":"1bd3293c-4e75-4b8d-979d-e43909fc54d8","html_url":"https://github.com/redhat-developer-demos/faas-tutorial","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/redhat-developer-demos/faas-tutorial","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/redhat-developer-demos%2Ffaas-tutorial","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/redhat-developer-demos%2Ffaas-tutorial/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/redhat-developer-demos%2Ffaas-tutorial/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/redhat-developer-demos%2Ffaas-tutorial/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/redhat-developer-demos","download_url":"https://codeload.github.com/redhat-developer-demos/faas-tutorial/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/redhat-developer-demos%2Ffaas-tutorial/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":265280659,"owners_count":23739852,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["demos","faas","java","openshift","openwhisk","serverless"],"created_at":"2024-11-22T22:10:24.721Z","updated_at":"2025-07-14T10:31:40.571Z","avatar_url":"https://github.com/redhat-developer-demos.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"= Function as a Service(FaaS) Tutorial\n// Settings:\n:idprefix:\n:idseparator: -\nifndef::env-github[]\n:icons: font\nendif::[]\nifdef::env-github,env-browser[]\n:toc: preamble\n:toclevels: 5\nendif::[]\nifdef::env-github[]\n:status:\n:outfilesuffix: .adoc\n:!toc-title:\n:caution-caption: :fire:\n:important-caption: :exclamation:\n:note-caption: :paperclip:\n:tip-caption: :bulb:\n:warning-caption: :warning:\nendif::[]\n\n(C) 2018 https://developers.redhat.com[Red Hat Developer Experience Team]\n\n//Aliases\n:conum-guard-sh: #\nifndef::icons[:conum-guard-sh: # #]\n\n:conum-guard-java: //\nifndef::icons[:conum-guard-java: // //]\n\n// URIs:\n:uri-minishift: https://docs.openshift.org/latest/minishift/getting-started/installing.html\n:uri-openwhisk-cli: https://github.com/apache/incubator-openwhisk-cli/releases/\n:uri-openwhisk-openshift: https://github.com/projectodd/openwhisk-openshift\n:uri-openwhisk-repo: https://github.com/apache/incubator-openwhisk\n:uri-repo: https://github.com/redhat-developer-demos/faas-java-tutorial\n:uri-repo-file-prefix: {uri-repo}/blob/master/\n:uri-repo-tree-prefix: {uri-repo}/tree/master/\n:uri-openwhisk-docs-prefix: {uri-openwhisk-repo}/blob/master/docs\nifdef::env-github[]\n:uri-repo-file-prefix: link:\n:uri-repo-tree-prefix: link:\nendif::[]\n== Overview\n\nThis tutorial walks you through on how to build a Java functions on a Function as a Service(FaaS) platform\n https://openwhisk.apache.org/[Apache OpenWhisk].\n\n== Prerequisites\n\nYou will need in this tutorial\n\n=== Tools\n* {uri-minishift}[minishift]\n* https://www.docker.com/docker-mac[docker]\n* https://kubernetes.io/docs/tasks/tools/install-kubectl/#install-kubectl-binary-via-curl[kubectl]\n* oc (eval $(minishift oc-env))\n* https://maven.apache.org[Apache Maven]\n* stern (brew install stern)\n* {uri-openwhisk-cli}[OpenWhisk CLI]\n* curl, gunzip, tar are built-in to MacOS or part of your bash shell\n* git (everybody needs the git CLI)\n* Java 8\n\n=== Setup minishift\nLocal development and testing can be done using https://github.com/minishift/minishift[minishift].  Minishift is a tool that helps you run\n OpenShift locally by running a single-node OpenShift cluster inside a VM.  Details on minishift and installation procedures can be found\n https://docs.openshift.org/latest/minishift/getting-started/index.html[here].\n\n==== Minishift Profile Setup\n\n[source,sh,subs=attributes+]\n----\n\n#!/bin/bash\n\n# Don't foget to add the location of minishift executable to PATH\n\nminishift profile set faas-tutorial\nminishift config set memory 8GB\nminishift config set cpus 3\nminishift config set image-caching true\nminishift addon enable admin-user\nminishift addon enable anyuid {conum-guard-sh} # \u003c1\u003e\n\nminishift start\n\nminishift ssh -- sudo ip link set docker0 promisc on {conum-guard-sh} # \u003c2\u003e\n----\n\n\u003c1\u003e Some images that are in Apache OpenWhisk Docker hub requires __anyuid__ SCC in OpenShift\n\u003c2\u003e This is needed for pods to communicate with each other within the cluster (TODO: need to add more clear details here)\n\n[IMPORTANT]\n====\n`minishift ssh -- sudo ip link set docker0 promisc on` command needs to be execute each and every time minishift restarted\n====\n\nIf your minishift is running OpenShift 3.9.0, you'll need to fix\nanother bug:\n----\n    minishift openshift config set --patch \\\n        '{\"admissionConfig\":\n            {\"pluginConfig\":\n                {\"openshift.io/ImagePolicy\":\n                    {\"configuration\":\n                        {\"apiVersion\": \"v1\",\n                         \"kind\": \"ImagePolicyConfig\",\n                         \"resolveImages\": \"AttemptRewrite\"}}}}}'\n\n----\n\n=== Setup environment\n\n[source,sh,subs=attributes+]\n----\n#!/bin/bash\n\neval $(minishift oc-env) \u0026\u0026 eval $(minishift docker-env)\noc login $(minishift ip):8443 -u admin -p admin\n----\n\n=== Setup OpenWhisk\n\nThe project {uri-openwhisk-openshift}[OpenWhisk on OpenShift] provides the OpenShift templates required to deploy Apache OpenWhisk.\n\n[source,sh,subs=attributes+]\n----\noc new-project faas {conum-guard-sh} # \u003c1\u003e\noc project -q {conum-guard-sh} # \u003c2\u003e\noc process -f https://git.io/openwhisk-template | oc create -f - {conum-guard-sh} # \u003c3\u003e\noc adm policy add-role-to-user admin developer -n faas {conum-guard-sh} # \u003c4\u003e\n----\n\n\u003c1\u003e Its always better to group certain class of applications, create a new OpenShift project called `faas` to deploy all OpenWhisk applications\n\u003c2\u003e Make sure we are in right project\n\u003c3\u003e Deploy OpenWhisk applications to `openwhisk` project\n\u003c4\u003e (**Optional**) Add `developer` user as admin to `faas` project so as to allow you to login with developer user and access `faas` project\n\n[NOTE]\n====\nYou need to wait for sometime to have all the required OpenWhisk pods come up and the FaaS is ready for some load. You can watch the\nstatus using watch -n 5 'oc logs -f controller-0 -n faas | grep \"invoker status changed\"'\n====\n\n==== Verify Deployment\n\nLaunch OpenShift console via `minishift console`.  Navigate to the `faas` project by clicking the name in the upper right corner.  A\nsuccessful deployment will look like:\n\nimage::readme_images/ow_deployed_success_1.png[OpenWhisk Pods]\nimage::readme_images/ow_deployed_success_2.png[OpenWhisk Pods]\n\n[[configure-wsk]]\n==== Configure WSK CLI\n\nDownload {uri-openwhisk-cli}[OpenWhisk CLI] and add it your PATH.  Verify your path using the command\n`wsk --help`\n\nThe {uri-openwhisk-cli}[OpenWhisk CLI] needs to be configured to know where the OpenWhisk is located\nand the authorization that could be used to invoke `wsk` commands.  Run the following command to have that setup:\n\n[source,sh,subs=attributes+]\n----\n#!/bin/bash\n\nAUTH_SECRET=$(oc get secret whisk.auth -o yaml | grep \"system:\" | awk '{print $2}' | base64 --decode)\nwsk property set --auth $AUTH_SECRET --apihost $(oc get route/openwhisk --template=\"{{.spec.host}}\")\n----\n\nSuccessful setup of WSK CLI will show output like:\n\nimage::readme_images/ow_wsk_cli_setup.png[WSK CLI]\n\nIn this case the OpenWhisk API Host is pointing to the local minishift nip.io address\n\nTo verify if wsk CLI is configured properly run `wsk -i action list`.  This will list some actions which are installed as part of the\nOpenWhisk setup.  If you see empty result, please see \u003c\u003cinstall-catalog\u003e\u003e\n\n[TIP]\n====\nThe `nginx` in OpenWhisk deployment uses a self-signed certificate.  To avoid certificate errors when using `wsk`, you need to add `wsk -i`\nto each of your `wsk` commands. For convenience, you can add an alias to your profile with `alias wsk='wsk -i $@'`.\n==== \n\n=== Setup your Development environment\n\nClone the complete project from `git clone {uri-repo}`, we will refer to this location as $PROJECT_HOME through out the document\nfor convenience.\n\n== What is an Action ?\n\n**Actions** are stateless code snippets that run on the OpenWhisk platform. They are analogous to methods in Java idioms.  OpenWhisk\n**Actions** are thread-safe meaning at a given point of time only one invocation happens.\n\nFore more details refer the official documentation {uri-openwhisk-docs-prefix}/actions.md[here].\n\n=== Your first Action\n\nLet's quickly create a simple function in JavaScript to see it all working:\n\n[source,sh,subs=attributes+]\n----\nmkdir -p getstarted\ncd $PROJECT_HOME/getstarted\n----\n\nCreate a file called `$PROJECT_HOME/getstarted/greeter.js` and add the following content to it:\n\n[source,js,subs=attributes+]\n----\nfunction main() {\n    return {payload: 'Welcome to OpenWhisk on OpenShift'};\n}\n----\n\nCreate an action called **greeter**:\n\n[source,sh,subs=attributes+]\n----\nwsk -i action update greeter greeter.js\n----\n\nLets invoke the action using command: \n\n[source,sh,subs=attributes+]\n----\nwsk -i action invoke greeter --result\n----\n\nThe action invoke should respond with the following JSON:\n\n[source,json,subs=attributes+]\n----\n{\n    \"payload\": \"Welcome to OpenWhisk on OpenShift\"\n}\n----\n\n== Java Actions\n\n=== Install Maven Archetype\n\nMaven Archetype can be used to generate the template Java Action project, as of writing this tutorial the archetype is not maven central\nhence it need to install it locally,\n\n[source,sh,subs=attributes+]\n----\ngit clone https://github.com/apache/incubator-openwhisk-devtools\ncd incubator-openwhisk-devtools/java-action-archetype\nmvn -DskipTests clean install\ncd $PROJECT_HOME\n----\n\n=== Your first Java Action\n\nLet's now create the first Java Action a simple \"hello world\" kind of function,  have it deployed to OpenWhisk and finally\ninvoke to see the result.  This section will also details the complete Create-Update-Delete cycle of Java **Actions** on OpenWhisk.\n\n[NOTE]\n====\nFor easier jar names all the examples will be using maven `\u003cfinalName\u003e`.  If you generating new project following the instructions\njust be sure to update the default `\u003cfinalName\u003e` in `pom.xml` to `${artifactId}` to make the command instructions in subsequent section \nwork without any changes.\n====\n\n==== Create Java Action\n\n[source,sh,subs=attributes+]\n----\ncd $PROJECT_HOME\nmvn archetype:generate \\\n  -DarchetypeGroupId=org.apache.openwhisk.java \\\n  -DarchetypeArtifactId=java-action-archetype \\\n  -DarchetypeVersion=1.0-SNAPSHOT \\\n  -DgroupId=com.example \\\n  -DartifactId=hello-openwhisk \\\n  -Dversion=1.0-SNAPSHOT \\\n  -DinteractiveMode=false\n----\n\n==== Build\n[source,sh,subs=attributes+]\n----\ncd hello-openwhisk\nmvn clean package \n----\n\n==== Deploy to OpenWhisk\n\n===== Create\n\n[source,sh,subs=attributes+]\n----\nwsk -i action create hello-openwhisk target/hello-openwhisk.jar --main com.example.FunctionApp\n----\n\n[[action-invocation]]\n==== Invoke and Verify the result\n\n[[sync-invocation]]\n===== Synchronously\n\n[source,sh,subs=attributes+]\n----\nwsk -i action invoke hello-openwhisk --result\n----\n\nAs all the OpenWhisk actions are asynchronous, we need to add `--result` to  get the result shown on the console.\n\nSuccessful execution of the command will show the following output:\n\n\n[[action-response]]\n[source,json,subs=attributes+]\n----\n{\"greetings\":  \"Hello! Welcome to OpenWhisk\" }\n----\n\n[[async-invocation]]\n===== Asynchronously\n\n[source,sh,subs=attributes+]\n----\nwsk -i action invoke hello-openwhisk\n----\n\nA successful action invoke will return an **activation id** :\n\nimage::readme_images/ow_action_with_activation_id.png[Action with Activation ID]\n\nWe can then use the to **activation id** check the response using `wsk` CLI:\n\n[source,sh,subs=attributes+]\n----\nwsk -i activation result \u003cactivation_id\u003e\n----\n\ne.g. \n\n[source,sh,subs=attributes+]\n----\nwsk -i activation result ffb2966350904356b29663509043566e\n----\n\nSuccessful execution of the command will show the same output like \u003c\u003caction-response,Action Response\u003e\u003e.\n\n===== Update\n\nUpdate the FunctionApp class response with the String:\n\n[source,java,subs=attributes+]\n----\n    response.addProperty(\"greetings\", \"Hello! Welcome to OpenWhisk on OpenShift\");\n----\n\nUpdate the FunctionAppTest Test class to match the same String:\n\n[source,java,subs=attributes+]\n----\n    assertEquals(\"Hello! Welcome to OpenWhisk on OpenShift\", greetings);\n----\n\n[source,java,subs=attributes+]\n----\ncd $PROJECT_HOME/hello-openwhisk\nmvn clean package\nwsk -i action update hello-openwhisk target/hello-openwhisk.jar --main com.example.FunctionApp\n----\n\nSuccessful update should show a output like:\n\nimage::readme_images/ow_action_update_result.png[]\n\nRepeating the \u003c\u003caction-invocation,Invocation and Verification\u003e\u003e steps should result in the updated response (\"... on OpenShift\") like:\n[source,json,subs=attributes+]\n----\n{\n    \"greetings\": \"Hello! Welcome to OpenWhisk on OpenShift\"\n}\n----\n\n===== Delete\n\n[source,sh,subs=attributes+]\n----\nwsk -i action delete hello-openwhisk\n----\n\nA successful delete should show output like:\n\nimage::readme_images/ow_action_delete_result.png[]\n\n=== Web Action\n\n**WebActions** allow the OpenWhisk action to be invoked via HTTP verbs like GET, POST, PUT etc.  The **WebActions** can be enabled for\nany **Action** using the parameter `--web=true` during the creation of the action using {uri-openwhisk-cli}[WSK CLI].\n\n[source,sh,subs=attributes+]\n----\ncd $PROJECT_HOME\nmvn archetype:generate \\\n  -DarchetypeGroupId=org.apache.openwhisk.java \\\n  -DarchetypeArtifactId=java-action-archetype \\\n  -DarchetypeVersion=1.0-SNAPSHOT \\\n  -DgroupId=com.example \\\n  -DartifactId=hello-web \\\n  -Dversion=1.0-SNAPSHOT \\\n  -DinteractiveMode=false\n----\n\nUpdate the FunctionApp class response to show application's arguments:\n[source,java,subs=attributes+]\n----\n    response.add(\"response\", args);\n----\n\nUpdate the FunctionAppTest testFunction method with code:\n[source,java,subs=attributes+]\n----\n  @Test\n  public void testFunction() {\n    JsonObject args = new JsonObject();\n    args.addProperty(\"name\", \"test\");\n    JsonObject response = FunctionApp.main(args);\n    assertNotNull(response);\n    String actual = response.get(\"response\").getAsJsonObject().get(\"name\").getAsString();\n    assertEquals(\"test\", actual);\n  }\n----\n\n==== Build\n[source,sh,subs=attributes+]\n----\ncd hello-web\nmvn clean package \n----\n\n==== Deploy to OpenWhisk\n[source,sh,subs=attributes+]\n----\nwsk -i action update --web=true hello-web target/hello-web.jar --main com.example.FunctionApp\n----\n\n==== Invoke and Verify the result\n\n[source,sh,subs=attributes+]\n----\nWEB_URL=`wsk -i action get hello-web --url | awk 'FNR==2{print $1\".json\"}'` {conum-guard-sh} # \u003c1\u003e\nAUTH=`oc get secret whisk.auth -n faas -o yaml | grep \"system:\" | awk '{print $2}'` {conum-guard-sh} # \u003c2\u003e\n----\n\u003c1\u003e Get the HTTP URL for invoking the action, the returned URL will have `.json` suffix to allow the WSK to set the right `Content-Type` and `Accept`\nheaders\n\u003c2\u003e Some resources requires authentication, for those requests its required to add `Authorization` header with value as `$AUTH`\n\n[source,sh,subs=attributes+]\n----\ncurl -k $WEB_URL\n----\n\nYou can also access the url via browser using address held in variable $WEB_URL.\n\n[NOTE]\n=====\n\nThe following section shows some example requests and their expected responses\n\n**Without any request data**\n\n[source,json,subs=attributes+]\n-----\n{\n  \"response\": {\n    \"__ow_method\": \"get\",\n    \"__ow_headers\": {\n      \"x-forwarded-port\": \"443\",\n      \"accept\": \"*/*\",\n      \"forwarded\": \"for=192.168.64.1;host=openwhisk-faas.192.168.64.67.nip.io;proto=https\",\n      \"user-agent\": \"curl/7.54.0\",\n      \"x-forwarded-proto\": \"https\",\n      \"host\": \"controller.faas.svc.cluster.local:8080\",\n      \"x-forwarded-host\": \"openwhisk-faas.192.168.64.67.nip.io\",\n      \"x-forwarded-for\": \"192.168.64.1\"\n    },\n    \"__ow_path\": \"\"\n  }\n}\n-----\n\n**With any JSON request data**\n\n[source,sh,subs=attributes+]\n----\ncurl -k -X POST -H 'Content-Type: application/json' -d '{\"name\": \"test\"}' $WEB_URL.json\n----\n\n[source,json,subs=attributes+]\n----\n{\n  \"response\": {\n    \"__ow_method\": \"post\",\n    \"__ow_headers\": {\n      \"x-forwarded-port\": \"443\",\n      \"accept\": \"*/*\",\n      \"forwarded\": \"for=192.168.64.1;host=openwhisk-faas.192.168.64.67.nip.io;proto=https\",\n      \"user-agent\": \"curl/7.54.0\",\n      \"x-forwarded-proto\": \"https\",\n      \"host\": \"controller.faas.svc.cluster.local:8080\",\n      \"content-type\": \"application/json\",\n      \"x-forwarded-host\": \"openwhisk-faas.192.168.64.67.nip.io\",\n      \"x-forwarded-for\": \"192.168.64.1\"\n    },\n    \"__ow_path\": \"\",\n    \"name\": \"test\"\n  }\n}\n----\n\n**With request data and an invalid content type**\n\n[source,sh,subs=attributes+]\n----\ncurl -k -X POST -H 'Content-Type: application/something' -d '{\"name\": \"test\"}' $WEB_URL.json\n----\n\nInvoke via curl like above , with request data you will see the response like:\n\n[source,json,subs=attributes+]\n----\n{\n  \"response\": {\n    \"__ow_method\": \"post\",\n    \"__ow_headers\": {\n      \"x-forwarded-port\": \"443\",\n      \"accept\": \"*/*\",\n      \"forwarded\": \"for=192.168.64.1;host=openwhisk-faas.192.168.64.67.nip.io;proto=https\",\n      \"user-agent\": \"curl/7.54.0\",\n      \"x-forwarded-proto\": \"https\",\n      \"host\": \"controller.faas.svc.cluster.local:8080\",\n      \"content-type\": \"application/something\",\n      \"x-forwarded-host\": \"openwhisk-faas.192.168.64.67.nip.io\",\n      \"x-forwarded-for\": \"192.168.64.1\"\n    },\n    \"__ow_path\": \"\",\n    \"__ow_body\": \"eyJuYW1lIjogInRlc3QifQ==\" //\u003c1\u003e\n  }\n}\n----\n\u003c1\u003e for unknown content-type the request body will be sent as base64 encoded string\n=====\n\n=== Chaining Actions\n\nApache OpenWhisk allows chaining of actions which are called in the same sequence as they are defined.  We will now create\na simple sequence of actions which will split, convert to uppercase, and sort a comma separated string.\n\nAll the three projects can be co-located in same directory for clarity and easy building:\n\n[source,sh,subs=attributes+]\n-----\ncd ..\nmkdir -p sequence-demo \ncd sequence-demo\nwsk -i package create redhat-developers-demo {conum-guard-sh} \u003c1\u003e\n-----\n\n\u003c1\u003e Create a new package to hold our actions, this gives a better clarity on which actions we add to our sequence.  For more details \nrefer to the {uri-openwhisk-docs-prefix}/packages.md[Packages] documentation.\n\n==== Create Split Action\n\nThis Action will receive a comma separated string as a parameter and return a array of Strings as a response.\n\n[source,sh,subs=attributes+]\n----\ncd $PROJECT_HOME/sequence-demo\nmvn archetype:generate \\\n  -DarchetypeGroupId=org.apache.openwhisk.java \\\n  -DarchetypeArtifactId=java-action-archetype \\\n  -DarchetypeVersion=1.0-SNAPSHOT \\\n  -DgroupId=com.example \\\n  -DartifactId=splitter \\\n  -Dversion=1.0-SNAPSHOT \\\n  -DinteractiveMode=false\n----\n\nUpdate the FunctionApp class with this code:\n[source,java,subs=attributes+]\n----\n  public static JsonObject main(JsonObject args) {\n    JsonObject response = new JsonObject();\n    String text = null;\n    if (args.has(\"text\")) {\n      text = args.getAsJsonPrimitive(\"text\").getAsString();\n    }\n    String[] results = new String[] { text };\n    if (text != null \u0026\u0026 text.indexOf(\",\") != -1) {\n      results = text.split(\",\");\n    }\n    JsonArray splitStrings = new JsonArray();\n    for (String var : results) {\n      splitStrings.add(var);\n    }\n    response.add(\"result\", splitStrings);\n    return response;\n}\n----\n\nUpdate the FunctionAppTest testFunction method with code:\n[source,java,subs=attributes+]\n----\n  @Test\n  public void testFunction() {\n    JsonObject args = new JsonObject();\n    args.addProperty(\"text\", \"apple,orange,banana\");\n    JsonObject response = FunctionApp.main(args);\n    assertNotNull(response);\n    JsonArray results = response.getAsJsonArray(\"result\");\n    assertNotNull(results);\n    assertEquals(3, results.size());\n    List\u003cString\u003e actuals = new ArrayList\u003c\u003e();\n    results.forEach(j -\u003e actuals.add(j.getAsString()));\n    assertTrue(actuals.contains(\"apple\"));\n    assertTrue(actuals.contains(\"orange\"));\n    assertTrue(actuals.contains(\"banana\"));\n  }\n----\n\n===== Build Splitter Action\n[source,sh,subs=attributes+]\n----\ncd splitter\nmvn clean package\nwsk -i action update redhat-developers-demo/splitter target/splitter.jar --main com.example.FunctionApp\n----\n\n==== Create Uppercase Action\n\nThis Action will take the array of Strings from previous step (Splitter Action) and convert the strings to upper case\n\n[source,sh,subs=attributes+]\n----\ncd ..\nmvn archetype:generate \\\n  -DarchetypeGroupId=org.apache.openwhisk.java \\\n  -DarchetypeArtifactId=java-action-archetype \\\n  -DarchetypeVersion=1.0-SNAPSHOT \\\n  -DgroupId=com.example \\\n  -DartifactId=uppercase \\\n  -Dversion=1.0-SNAPSHOT \\\n  -DinteractiveMode=false\n----\n\nUpdate the FunctionApp class with this code:\n[source,java,subs=attributes+]\n----\n  public static JsonObject main(JsonObject args) {\n    JsonObject response = new JsonObject();\n    JsonArray upperArray = new JsonArray();\n    if (args.has(\"result\")) { {conum-guard-java} // \u003c1\u003e\n      args.getAsJsonArray(\"result\").forEach(e -\u003e upperArray.add(e.getAsString().toUpperCase()));\n    }\n    response.add(\"result\", upperArray);\n    return response;\n  }\n----\n\n\u003c1\u003e The function expects the previous action in sequence to send the parameter with JSON attribute called `result`\n\nUpdate the FunctionAppTest testFunction method with code:\n[source,java,subs=attributes+]\n----\n  @Test\n  public void testFunction() {\n    JsonObject args = new JsonObject();\n    JsonArray splitStrings = new JsonArray();\n    splitStrings.add(\"apple\");\n    splitStrings.add(\"orange\");\n    splitStrings.add(\"banana\");\n    args.add(\"result\", splitStrings);\n    JsonObject response = FunctionApp.main(args);\n    assertNotNull(response);\n    JsonArray results = response.getAsJsonArray(\"result\");\n    assertNotNull(results);\n    assertEquals(3, results.size());\n    List\u003cString\u003e actuals = new ArrayList\u003c\u003e();\n    results.forEach(j -\u003e actuals.add(j.getAsString()));\n    assertTrue(actuals.contains(\"APPLE\"));\n    assertTrue(actuals.contains(\"ORANGE\"));\n    assertTrue(actuals.contains(\"BANANA\"));\n  }\n----\n\n===== Build Uppercase Action\n[source,sh,subs=attributes+]\n----\ncd uppercase\nmvn clean package\nwsk -i action update redhat-developers-demo/uppercase target/uppercase.jar --main com.example.FunctionApp\n----\n\n==== Create Sort Action\n\nThis Action will take the array of Strings from previous step (Upppercase Action) and sort them\n\n[source,sh,subs=attributes+]\n----\ncd ..\nmvn archetype:generate \\\n  -DarchetypeGroupId=org.apache.openwhisk.java \\\n  -DarchetypeArtifactId=java-action-archetype \\\n  -DarchetypeVersion=1.0-SNAPSHOT \\\n  -DgroupId=com.example \\\n  -DartifactId=sorter \\\n  -Dversion=1.0-SNAPSHOT \\\n  -DinteractiveMode=false\n----\n\nUpdate the FunctionApp class with this code:\n[source,java,subs=attributes+]\n----\n  public static JsonObject main(JsonObject args) {\n    JsonObject response = new JsonObject();\n    List\u003cString\u003e upperStrings = new ArrayList\u003c\u003e();\n    if (args.has(\"result\")) {\n      args.getAsJsonArray(\"result\").forEach(e -\u003e upperStrings.add(e.getAsString()));\n    }\n\n    JsonArray sortedArray = new JsonArray();\n    upperStrings.stream().sorted(Comparator.naturalOrder()).forEach(s -\u003e sortedArray.add(s));\n\n    response.add(\"result\", sortedArray);\n    return response;\n  }\n----\n\nUpdate the FunctionAppTest testFunction method with code:\n[source,java,subs=attributes+]\n----\n  @Test\n  public void testFunction() {\n    JsonObject args = new JsonObject();\n    JsonArray splitStrings = new JsonArray();\n    splitStrings.add(\"APPLE\");\n    splitStrings.add(\"ORANGE\");\n    splitStrings.add(\"BANANA\");\n    args.add(\"result\", splitStrings);\n    JsonObject response = FunctionApp.main(args);\n    assertNotNull(response);\n    JsonArray results = response.getAsJsonArray(\"result\");\n    assertNotNull(results);\n    assertEquals(3, results.size());\n    List\u003cString\u003e actuals = new ArrayList\u003c\u003e();\n    results.forEach(j -\u003e actuals.add(j.getAsString()));\n    assertTrue(actuals.get(0).equals(\"APPLE\"));\n    assertTrue(actuals.get(1).equals(\"BANANA\"));\n    assertTrue(actuals.get(2).equals(\"ORANGE\"));\n  }\n\n----\n\n===== Build Sorter Action\n[source,sh,subs=attributes+]\n----\ncd sorter\nmvn clean package\nwsk -i action update redhat-developers-demo/sorter target/sorter.jar --main com.example.FunctionApp\n----\n\n==== Create an Action Sequence\n\nHaving created all the three actions, lets now create OpenWhisk that calls all three function split,uppercase and sort in sequence.\n\n[source,sh,subs=attributes+]\n----\ncd ..\nwsk -i action update redhat-developers-demo/splitUpperAndSort --sequence redhat-developers-demo/splitter,redhat-developers-demo/uppercase,redhat-developers-demo/sorter\n----\n\n====== Invoke and Verify\n\n[source,sh,subs=attributes+]\n----\nwsk -i action invoke redhat-developers-demo/splitUpperAndSort --param text \"zebra,cat,antelope\" --result\n----\n\nThe above action invoke should result in response like:\n[source,sh,subs=attributes+]\n----\n{\n    \"result\": [\n        \"ANTELOPE\",\n        \"CAT\",\n        \"ZEBRA\"\n    ]\n}\n----\n\n=== Advanced\n\nCheck \u003chttps://github.com/redhat-developer-demos/adv-faas-tutorial\u003e\n\n== Troubleshooting\n[[install-catalog]]\n=== Reinstall default Catalog\n\nIf you are on a low bandwidth sometimes the default catalog will not be populated, run the following commands to have them installed\n[source,sh,subs=attributes+]\n----\n#!/bin/bash\n\noc delete job install-catalog \u003c1\u003e\n\ncat \u003c\u003cEOF | oc apply -f -\napiVersion: batch/v1\nkind: Job\nmetadata:\n  name: install-catalog\nspec:\n  activeDeadlineSeconds: 600\n  template:\n    metadata:\n      name: install-catalog\n    spec:\n      containers:\n      - name: catalog\n        image: projectodd/whisk_catalog:openshift-latest\n        env:\n          - name: \"WHISK_CLI_VERSION\"\n            valueFrom:\n              configMapKeyRef:\n                name: whisk.config\n                key: whisk_cli_version_tag\n          - name: \"WHISK_AUTH\"\n            valueFrom:\n              secretKeyRef:\n                name: whisk.auth\n                key: system\n          - name: \"WHISK_API_HOST_NAME\"\n            value: \"http://controller:8080\"\n      initContainers:\n      - name: wait-for-controller\n        image: busybox\n        command: ['sh', '-c', 'until wget -T 5 --spider http://controller:8080/ping; do echo waiting for controller; sleep 2; done;']\n      restartPolicy: Never\nEOF {conum-guard-sh} \u003c2\u003e\n----\n\n\u003c1\u003e Delete the old job\n\u003c2\u003e Run the install-catalog job again \n\nNow when you run `wsk -i action list` you should see output like:\n\nimage::readme_images/ow_install_catalog.png[Install Catalog]\n\n[[tips-and-tricks]]\n== Tips and Tricks\n\n[TIP]\n====\n* If you are going to use a lot of `wsk` then its worth aliasing wsk with `alias wsk='wsk -i $@'` to avoid SSL errors and skip adding `-i`\nfor every command.\n* For detailed JSON output form `wsk` commands prefix `-v`.  This is a great command option for troubleshooting.\n* Its safe to use `wsk -i update [resource]` when creating OpenWhisk resources like **Actions**, **Packages** etc., as this command \nwill act like `create` for new resources and `update` for existing resources.\n* `wsk -i [resource command ] --summary`  provides detailed information about a specific resource e.g. wsk -i action get foo --summary\n* `wsk -i activation poll`, a very useful command when we want to debug some error or see the exception stack traces during a function execution.  The simple example of this could be that we star this command on one terminal and fire the action on another to see  poll window showing exceptions/errors/stacktraces if any during execution.\n====\n\n[[references]]\n== References\n\n* {uri-openwhisk-openshift}[Apache OpenWhisk on OpenShift]\n* {uri-openwhisk-docs-prefix}/actions.md[OpenWhisk Actions]\n* {uri-openwhisk-docs-prefix}/cli.md[Setup OpenWhisk CLI]\n* {uri-openwhisk-docs-prefix}/packages.md[Packages]\n* {uri-openwhisk-docs-prefix}/webactions.md[Web Action]\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fredhat-developer-demos%2Ffaas-tutorial","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fredhat-developer-demos%2Ffaas-tutorial","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fredhat-developer-demos%2Ffaas-tutorial/lists"}