├── .gitignore ├── MAINTAINERS.md ├── simplemm └── 1 │ ├── saved_model.pb │ └── variables │ ├── variables.index │ └── variables.data-00000-of-00001 ├── .github └── dco.yml ├── .travis.yml ├── Makefile ├── com └── ibm │ └── tfservtest │ └── tfcaller │ ├── Tfcaller.java │ └── Session.java ├── CONTRIBUTING.md ├── README.md └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | .DS_Store 3 | -------------------------------------------------------------------------------- /MAINTAINERS.md: -------------------------------------------------------------------------------- 1 | # MAINTAINERS 2 | 3 | Christopher Ferris - chrisfer@us.ibm.com 4 | -------------------------------------------------------------------------------- /simplemm/1/saved_model.pb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IBM/ai-on-z-tensorflow-zcx/main/simplemm/1/saved_model.pb -------------------------------------------------------------------------------- /simplemm/1/variables/variables.index: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IBM/ai-on-z-tensorflow-zcx/main/simplemm/1/variables/variables.index -------------------------------------------------------------------------------- /.github/dco.yml: -------------------------------------------------------------------------------- 1 | # This enables DCO bot for you, please take a look https://github.com/probot/dco 2 | # for more details. 3 | require: 4 | members: false 5 | -------------------------------------------------------------------------------- /simplemm/1/variables/variables.data-00000-of-00001: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IBM/ai-on-z-tensorflow-zcx/main/simplemm/1/variables/variables.data-00000-of-00001 -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | 3 | before_install: 4 | - echo "#" 5 | - echo "#" 6 | - echo "TravisCI is unbelively powerful, but you need to do your research first." 7 | - echo "#" 8 | - echo "#" 9 | 10 | script: 11 | - echo "#" 12 | - echo "#" 13 | - echo "Please take a look https://docs.travis-ci.com/user/tutorial/ for you options." 14 | - echo "#" 15 | - echo "#" 16 | 17 | 18 | after_success: 19 | - echo "#" 20 | - echo "#" 21 | - echo "Don't forget to enable it in the GitHub repository also.." 22 | - echo "#" 23 | - echo "#" 24 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | JFLAGS = -g 2 | JC = javac 3 | SRC_DIR = ./com/ibm/tfservtest/tfcaller 4 | MODEL_JAR = ".:./commons-logging-1.1.3.jar:./httpclient-4.5.13.jar:./httpcore-4.4.14.jar:./javax.json-1.0.jar" 5 | MODELCLASSPATH := -classpath $(MODEL_JAR):. 6 | 7 | .SUFFIXES: .java .class 8 | .java.class: 9 | $(JC) $(JFLAGS) $(OUTPUT) $(MODELCLASSPATH) $*.java 10 | 11 | CLASSES = \ 12 | $(SRC_DIR)/Session.java \ 13 | $(SRC_DIR)/Tfcaller.java 14 | 15 | default: classes 16 | 17 | classes: $(CLASSES:.java=.class) 18 | 19 | run: 20 | java $(MODELCLASSPATH) com.ibm.tfservtest.tfcaller.Tfcaller $(HOSTIP) $(PORT) $(MODEL_DIR) $(PAYLOAD) 21 | 22 | clean: 23 | $(RM) ./com/ibm/tfservtest/tfcaller/*.class 24 | -------------------------------------------------------------------------------- /com/ibm/tfservtest/tfcaller/Tfcaller.java: -------------------------------------------------------------------------------- 1 | package com.ibm.tfservtest.tfcaller; 2 | 3 | /* 4 | * (C) Copyright IBM Corp. 2021. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 12 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 13 | * specific language governing permissions and limitations under the License. 14 | */ 15 | 16 | public class Tfcaller { 17 | public static int rc = 0; // exit return code 18 | public static final String testTensor = "[[1.0,2.0,3.0,4.0,5.0]]"; 19 | public static final String INPUTREQS = "required arguments are: host port modelpath payload"; 20 | public static final String INPUTEX = "example: tfcaller 127.0.0.1 8501 /v1/models/simplemm 1.0,2.0,3.0,4.0,5.0"; 21 | 22 | /** 23 | * Our goal here is to provide a simple z/OS Java appliction that can interface with TensorFlow serving. 24 | * 25 | * This is intended to demonstrate the use alongside TensorFlow Serving, hosted in a zCX or Linux on Z instance - with 26 | * the simplemm model found in the project repository. 27 | * 28 | * This uses standard apache http classes to issue rest calls. There may be better approaches to consider depending 29 | * on your application needs and available products. This includes numerous open-source REST java packages, 30 | * built-in capabilities of CICS and/or Websphere Liberty, and IBM products like z/OS Connect EE. 31 | * 32 | * simplemm model does the following: 33 | * 1. takes an input tensor of shape [1,5] 34 | * 2. matrix multiply against a weight tensor of shape [5,1] with weights defined as 1.0 35 | * 3. result is a tensor of shape [1,1] 36 | * 37 | * For example, an input of [1.0,2.0,3.0,4.0,5.0] is multipled against [[1.0],[1.0],[1.0],[1.0],[1.0]] 38 | * will produce a result of [[15.0]] 39 | * 40 | * @param args 41 | */ 42 | public static void main(String[] args) { 43 | System.out.println("Starting TFServing Simple Interface Example "); 44 | 45 | if (args.length != 4) { 46 | System.out.println(INPUTREQS); 47 | System.out.println(INPUTEX); 48 | return; 49 | } 50 | 51 | Tfcaller myapp = new Tfcaller(); 52 | myapp.processRequest(args[0],args[1],args[2],args[3]); 53 | 54 | } 55 | 56 | private void processRequest(String host, String port, String modelpath, String payload) { 57 | 58 | //session tf = new session("9.47.86.127", "8507", "/v1/models/simplemm"); 59 | Session tf = new Session(host, port, modelpath); 60 | String predict = tf.predict(payload); 61 | System.out.println(predict); 62 | System.out.println("Processing complete!"); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Contributing In General 2 | Our project welcomes external contributions. If you have an itch, please feel 3 | free to scratch it. 4 | 5 | To contribute code or documentation, please submit a **FIXME** [pull request](https://github.com/ibm/repo-template/pulls). 6 | 7 | A good way to familiarize yourself with the codebase and contribution process is 8 | to look for and tackle low-hanging fruit in the **FIXME** [issue tracker](https://github.com/ibm/repo-template/issues). 9 | Before embarking on a more ambitious contribution, please quickly [get in touch](#communication) with us. 10 | 11 | **Note: We appreciate your effort, and want to avoid a situation where a contribution 12 | requires extensive rework (by you or by us), sits in backlog for a long time, or 13 | cannot be accepted at all!** 14 | 15 | ### Proposing new features 16 | 17 | If you would like to implement a new feature, please **FIXME** [raise an issue](https://github.com/ibm/repo-template/issues) 18 | before sending a pull request so the feature can be discussed. This is to avoid 19 | you wasting your valuable time working on a feature that the project developers 20 | are not interested in accepting into the code base. 21 | 22 | ### Fixing bugs 23 | 24 | If you would like to fix a bug, please **FIXME** [raise an issue](https://github.com/ibm/repo-template/issues) before sending a 25 | pull request so it can be tracked. 26 | 27 | ### Merge approval 28 | 29 | The project maintainers use LGTM (Looks Good To Me) in comments on the code 30 | review to indicate acceptance. A change requires LGTMs from two of the 31 | maintainers of each component affected. 32 | 33 | For a list of the maintainers, see the [MAINTAINERS.md](MAINTAINERS.md) page. 34 | 35 | ## Legal 36 | 37 | Each source file must include a license header for the Apache 38 | Software License 2.0. Using the SPDX format is the simplest approach. 39 | e.g. 40 | 41 | ``` 42 | /* 43 | Copyright All Rights Reserved. 44 | 45 | SPDX-License-Identifier: Apache-2.0 46 | */ 47 | ``` 48 | 49 | We have tried to make it as easy as possible to make contributions. This 50 | applies to how we handle the legal aspects of contribution. We use the 51 | same approach - the [Developer's Certificate of Origin 1.1 (DCO)](https://github.com/hyperledger/fabric/blob/master/docs/source/DCO1.1.txt) - that the Linux® Kernel [community](https://elinux.org/Developer_Certificate_Of_Origin) 52 | uses to manage code contributions. 53 | 54 | We simply ask that when submitting a patch for review, the developer 55 | must include a sign-off statement in the commit message. 56 | 57 | Here is an example Signed-off-by line, which indicates that the 58 | submitter accepts the DCO: 59 | 60 | ``` 61 | Signed-off-by: John Doe 62 | ``` 63 | 64 | You can include this automatically when you commit a change to your 65 | local git repository using the following command: 66 | 67 | ``` 68 | git commit -s 69 | ``` 70 | 71 | ## Communication 72 | **FIXME** Please feel free to connect with us on our [Slack channel](link). 73 | 74 | ## Setup 75 | **FIXME** Please add any special setup instructions for your project to help the developer 76 | become productive quickly. 77 | 78 | ## Testing 79 | **FIXME** Please provide information that helps the developer test any changes they make 80 | before submitting. 81 | 82 | ## Coding style guidelines 83 | **FIXME** Optional, but recommended: please share any specific style guidelines you might 84 | have for your project. 85 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Demonstrating interaction between z/OS and TensorFlow Serving 2 | 3 | This project intends to demonstrate a call to TensorFlow Serving using REST API from a simple z/OS Java app. 4 | The purpose is to create a simple and easily deployable scenario that can be used on z/OS to understand serving concepts. 5 | 6 | This model performns a matrix multiplication of a [1,5] input tensor by a [5,1] weights tensor, where all weights are defined with a value of 1. This results in an output tensor shape of [1,1]. 7 | 8 | For example, with this model an input tensor of [[1,2,3,4,5]] is multiplied by a weights tensor of [[1],[1],[1],[1],[1]], 9 | resulting in a value of [[15.]] 10 | 11 | Note that since this project is intended for deployment to z/OS, we are avoiding managing dependecies through sub-moduling or Maven/Ant. 12 | The intent is to quickly try this project out without installing additional software. Required Jar files can be pulled from the references below. 13 | 14 | ## Deploying TensorFlow Serving on z/OS Container Extensions 15 | 16 | - TensorFlow Serving can be obtained from the IBM Container Image Repository or built from source for Linux on Z 17 | - [IBM Container Image Repository](https://ibm.github.io/ibm-z-oss-hub/main/main.html) 18 | - [Source](https://github.com/linux-on-ibm-z/docs/wiki/Building-TensorFlow-Serving) 19 | - Details on the TFServing API can be found here: https://www.tensorflow.org/tfx/serving/api_rest 20 | - This example assumes a TensorFlow Serving V2.x REST API and has been tested up to TensorFlow 2.7 21 | 22 | - To deploy the example model, you can follow this procedure: 23 | - SFTP the TensorFlow saved model to zCX. This should be the model folder and all subfolders. 24 | - The saved model can be found in the model subdirectory of this project. 25 | - The notebook used to create the model is simplemm.ipynb 26 | - Create a new docker volume. 27 | - e.g., `docker volume create tfmodels` 28 | - **tfmodels** is the volume name we create to use in subsequent steps 29 | - Copy the model directory into the docker volume. 30 | - One approach is to create a simple container using the volume to allow a docker cp: 31 | - `docker container create --name tfsimple -v tfmodels:/models simple_image` 32 | - `docker cp tfsimple:/models` 33 | - **tfsimple** is the container name we create to facilitate the copy via docker CP. 34 | - **simple_image** can be any base image, and it can be deleted after this copy. 35 | - **models** is a directory we choose to copy the model into. 36 | - Run the TFServing image: 37 | - `docker run -d --rm -p 8507:8501 -v tfmodels:/models -e MODEL_NAME= ` 38 | - 8501 is the default TFServing REST port. Here it is mapped to zCX port 8507. 39 | - **model_name** is the TensorFlow model name; this is commonly the directory name that holds the saved_model.pb file 40 | 41 | ## Updating and deploying the z/OS application 42 | 43 | - First, deploy the project to your host system. This program is intended for Unix environments. 44 | - Suggestion on how to get github projects to z/OS can be found here: https://github.com/IBM/IBM-Z-zOS 45 | - The following Jar files must be in the project root directory; 46 | - Apache Commons Logging: [commons-logging-1.1.3.jar](http://archive.apache.org/dist/commons/logging/binaries/) 47 | - Apache HttpClient: [httpclient-4.5.13.jar](https://repo1.maven.org/maven2/org/apache/httpcomponents/httpclient/4.5.13/) 48 | - Apache Httpcore: [httpcore-4.4.14.jar](https://repo1.maven.org/maven2/org/apache/httpcomponents/httpcore/4.4.14/) 49 | - Json Processing API [javax.json-1.0.jar](https://repo1.maven.org/maven2/org/glassfish/javax.json/1.0/) 50 | - We suggest using SFTP to transmit the jars to the z/OS environment. 51 | - Note: If different versions are used, update the Makefile to reference the correect version. 52 | - run `make` to build compile the .java files 53 | - issue the following command to try a TensorFlow Serving request: 54 | - `make run HOSTIP=tf-serve-ip PORT=tf-serv-port MODEL_DIR=model-dir PAYLOAD=input-tensor` 55 | - **tf-serve-ip** is the IP address of the server or instance hosting TensorFlow Serving 56 | - **tf-serv-port** is the TF Serving REST port 57 | - **model-dir** is the model path, which should typically be /v1/models/simplemm for the sample model. 58 | - **input-tensor** consists of 5 comma separated decimal values. 59 | 60 | For example, if we mapped the TFServing REST port to 8507, we would use something like this (changing the IP address 61 | to one in use by zCX): 62 | `make run HOSTIP=127.0.0.1 PORT=8507 MODEL_DIR=/v1/models/simplemm PAYLOAD=1.0,2.0,3.0,4.0,5.0` 63 | 64 | In addition to verbose messages, the result for test would show: 65 | { 66 | "predictions": [[15.0] 67 | ] 68 | } 69 | 70 | -------------------------------------------------------------------------------- /com/ibm/tfservtest/tfcaller/Session.java: -------------------------------------------------------------------------------- 1 | package com.ibm.tfservtest.tfcaller; 2 | 3 | import java.io.IOException; 4 | import java.util.Arrays; 5 | import java.util.List; 6 | 7 | import javax.json.Json; 8 | import javax.json.JsonArrayBuilder; 9 | 10 | import org.apache.http.HttpEntity; 11 | import org.apache.http.client.methods.CloseableHttpResponse; 12 | import org.apache.http.client.methods.HttpPost; 13 | import org.apache.http.entity.StringEntity; 14 | import org.apache.http.impl.client.CloseableHttpClient; 15 | import org.apache.http.impl.client.HttpClients; 16 | import org.apache.http.util.EntityUtils; 17 | 18 | /* 19 | * (C) Copyright IBM Corp. 2021. 20 | * 21 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 22 | * the License. You may obtain a copy of the License at 23 | * 24 | * http://www.apache.org/licenses/LICENSE-2.0 25 | * 26 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 27 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 28 | * specific language governing permissions and limitations under the License. 29 | */ 30 | 31 | 32 | public class Session { 33 | private static final String PREDICT = "predict"; 34 | private static final String INSTANCES = "instances"; 35 | private static final Integer OK = 200; 36 | private String host; 37 | private String port; 38 | private String model; 39 | 40 | public Session(String host, String port, String model) { 41 | this.host = host; 42 | this.port = port; 43 | this.model = model; 44 | } 45 | 46 | /** 47 | Format and send a POST request to the TensorFlow serving instance. 48 | 49 | This is intended for use with the example model provided alongside this Java sample. It will need changes to support more 50 | different models. 51 | 52 | A similar curl request can be constructed as follows to test 53 | curl -d '{"instances": [[1.0,2.0,3.0,4.0,5.0]]}' -X POST http://ip:port/v1/models/simplemm:predict 54 | 55 | TensorFlow Serving REST API documentation can be found here: 56 | https://www.tensorflow.org/tfx/serving/api_rest#json_mapping 57 | */ 58 | public String predict(String inTensor) { 59 | String result = ""; 60 | 61 | String url = ("http://" + this.host + ":" + this.port + this.model + ":" + PREDICT); 62 | System.out.println("Serving URL is: " + url); 63 | try { 64 | System.out.println("Building POST request"); 65 | CloseableHttpClient client = HttpClients.createDefault(); 66 | HttpPost httpPost = new HttpPost(url); 67 | 68 | // Send the input to be parsed 69 | JsonArrayBuilder jab = parsePlainTextInput(inTensor); 70 | 71 | // Create the payload for TensorFlow 72 | String payload = Json.createObjectBuilder() 73 | .add(INSTANCES, Json.createArrayBuilder().add(jab)) 74 | .build() 75 | .toString(); 76 | 77 | StringEntity entity = new StringEntity(payload); 78 | System.out.println("Payload created as: " + payload); 79 | 80 | httpPost.setEntity(entity); 81 | httpPost.setHeader("Accept", "application/json"); 82 | httpPost.setHeader("Content-type", "application/json"); 83 | 84 | System.out.println("POST request will be: " + httpPost.toString()); 85 | //execute the request 86 | CloseableHttpResponse response = client.execute(httpPost); 87 | 88 | //let's make sure the response worked - code=200 89 | if (response.getStatusLine().getStatusCode() != OK) 90 | System.out.println("Something went wrong! Response: " + response.toString()); 91 | 92 | HttpEntity responseEntity = response.getEntity(); 93 | result = EntityUtils.toString(responseEntity); 94 | client.close(); 95 | 96 | } catch (IOException e) { 97 | e.printStackTrace(); 98 | } 99 | 100 | return result; 101 | } 102 | 103 | private JsonArrayBuilder parsePlainTextInput(String tensor){ 104 | 105 | /* Parse the tensor provided as input, and cready a JsonArray of the decimal (double) 106 | values found in a comma separated list. 107 | 108 | In a real world use case that handles structured and/or transactional data, you would have 109 | different logic based on the requirements of the service and/or model you are calling. 110 | 111 | This is straightforward mechanism to parse the input expected by our model. 112 | */ 113 | JsonArrayBuilder jarray = Json.createArrayBuilder(); 114 | List convertedCountriesList = Arrays.asList(tensor.split(",", -1)); 115 | for (String entry : convertedCountriesList) { 116 | jarray.add(Double.parseDouble(entry)); 117 | } 118 | return jarray; 119 | } 120 | 121 | public String getModel() { 122 | return model; 123 | } 124 | public String getHost() { 125 | return host; 126 | } 127 | public void setHost(String host) { 128 | this.host = host; 129 | } 130 | public String getPort() { 131 | return port; 132 | } 133 | public void setPort(String port) { 134 | this.port = port; 135 | } 136 | 137 | public void setModel(String model) { 138 | this.model = model; 139 | } 140 | 141 | } 142 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | --------------------------------------------------------------------------------