├── .github ├── pull_approval_log.md └── workflows │ ├── fly-deploy.yml │ └── clojure.yml ├── doc └── intro.md ├── .gitignore ├── src └── fizz_buzz │ ├── spec.clj │ ├── openapi.clj │ └── core.clj ├── Examples ├── fizzbuzz_example.sh └── FizzApi │ └── app.py ├── Makefile ├── .dockerignore ├── test └── fizz_buzz │ └── core_test.clj ├── ingress.yaml ├── Dockerfile ├── project.clj ├── CHANGELOG.md ├── fly.toml ├── deployment.yaml ├── README.md └── LICENSE /.github/pull_approval_log.md: -------------------------------------------------------------------------------- 1 | - Approving pull request #14 on behalf of the user: moyet at 2025-11-16 21:15:55 UTC. -------------------------------------------------------------------------------- /doc/intro.md: -------------------------------------------------------------------------------- 1 | # Introduction to fizz-buzz 2 | 3 | TODO: write [great documentation](http://jacobian.org/writing/what-to-write/) 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /classes 3 | /checkouts 4 | profiles.clj 5 | pom.xml 6 | pom.xml.asc 7 | *.jar 8 | *.class 9 | /.lein-* 10 | /.nrepl-port 11 | .hgignore 12 | .hg/ 13 | .idea/ 14 | .lsp/ 15 | .clj-kondo/ 16 | *.iml 17 | -------------------------------------------------------------------------------- /src/fizz_buzz/spec.clj: -------------------------------------------------------------------------------- 1 | (ns fizz-buzz.spec 2 | (:require [clojure.spec.alpha :as s])) 3 | 4 | (s/def ::fizz-value integer?) 5 | (s/def ::fizz-name keyword? ) 6 | (s/def ::fizz-buzz-rules (s/map-of ::fizz-name ::fizz-value)) 7 | 8 | -------------------------------------------------------------------------------- /Examples/fizzbuzz_example.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # An example script for using FizzaaS, you could use almost any other language for this. 4 | 5 | echo "Testing Fizzbuzz as a Service" 6 | 7 | url="127.0.0.1:3000/fizzbuzz/[1-200]" 8 | 9 | echo $(curl -s $url | jq -r .result) 10 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Stuff 2 | DOCKER_USERNAME ?= moyet 3 | APPLICATION_NAME ?= fizzbuzz 4 | GIT_SHA := $(shell git rev-parse --short HEAD) 5 | 6 | 7 | compile: 8 | lein uberjar 9 | 10 | lein-test: 11 | lein test 12 | 13 | lein-run: 14 | lein run 15 | 16 | build: 17 | docker build --tag ${DOCKER_USERNAME}/${APPLICATION_NAME}:${GIT_SHA} . 18 | 19 | push: 20 | docker push ${DOCKER_USERNAME}/${APPLICATION_NAME}:${GIT_SHA} 21 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | # flyctl launch added from .gitignore 2 | target 3 | classes 4 | checkouts 5 | **/profiles.clj 6 | **/pom.xml 7 | **/pom.xml.asc 8 | **/*.jar 9 | **/*.class 10 | .lein-* 11 | .nrepl-port 12 | **/.hgignore 13 | **/.hg 14 | **/.idea 15 | **/.lsp 16 | **/.clj-kondo 17 | **/*.iml 18 | 19 | # flyctl launch added from .idea/.gitignore 20 | # Default ignored files 21 | .idea/shelf 22 | .idea/workspace.xml 23 | fly.toml 24 | -------------------------------------------------------------------------------- /test/fizz_buzz/core_test.clj: -------------------------------------------------------------------------------- 1 | (ns fizz-buzz.core-test 2 | (:require [clojure.test :refer :all] 3 | [fizz-buzz.core :refer :all])) 4 | 5 | (deftest a-test 6 | (testing "That 4 returns a 4" 7 | (is (= {:result 4} (fizz-buzz 4)))) 8 | (testing "That 5 returns a Buzz" 9 | (is (= {:result "Buzz"} (fizz-buzz 5)))) 10 | (testing "That 15 returns a FizzBuzz" 11 | (is (= {:result "FizzBuzz"} (fizz-buzz 15))))) 12 | -------------------------------------------------------------------------------- /ingress.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.k8s.io/v1 2 | kind: Ingress 3 | metadata: 4 | name: fizzbuzz-ingress 5 | namespace: development 6 | spec: 7 | rules: 8 | - host: fizzbuzz.local 9 | http: 10 | paths: 11 | - path: / 12 | pathType: Prefix 13 | backend: 14 | service: 15 | name: fizzbuzz-service # This must match your Service name 16 | port: 17 | number: 80 18 | -------------------------------------------------------------------------------- /.github/workflows/fly-deploy.yml: -------------------------------------------------------------------------------- 1 | # See https://fly.io/docs/app-guides/continuous-deployment-with-github-actions/ 2 | 3 | name: Fly Deploy 4 | on: 5 | push: 6 | branches: 7 | - main 8 | jobs: 9 | deploy: 10 | name: Deploy app 11 | runs-on: ubuntu-latest 12 | concurrency: deploy-group # optional: ensure only one action runs at a time 13 | steps: 14 | - uses: actions/checkout@v4 15 | - uses: superfly/flyctl-actions/setup-flyctl@master 16 | - run: flyctl deploy --remote-only 17 | env: 18 | FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }} 19 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM clojure:lein-2.9.8 AS builder 2 | 3 | 4 | WORKDIR /app 5 | 6 | # Make a copy of the source code in the builder 7 | 8 | COPY . . 9 | 10 | # Then run leinigen to build an uberjar 11 | 12 | RUN lein uberjar 13 | 14 | 15 | # We now switch to another container 16 | 17 | FROM amazoncorretto:8u462-al2023-jre 18 | 19 | 20 | LABEL maintainer="Magnus Dreyer " 21 | 22 | WORKDIR /app 23 | 24 | COPY --from=builder /app/target/uberjar/fizz-buzz-0.1.0-SNAPSHOT-standalone.jar fizz-buzz.jar 25 | 26 | CMD ["java", "-jar", "/app/fizz-buzz.jar"] 27 | 28 | EXPOSE 3000 29 | -------------------------------------------------------------------------------- /project.clj: -------------------------------------------------------------------------------- 1 | (defproject fizz-buzz "0.1.0-SNAPSHOT" 2 | :description "FIXME: write description" 3 | :url "http://example.com/FIXME" 4 | :license {:name "EPL-2.0 OR GPL-2.0-or-later WITH Classpath-exception-2.0" 5 | :url "https://www.eclipse.org/legal/epl-2.0/"} 6 | :dependencies [[org.clojure/clojure "1.10.0"] 7 | [compojure "1.6.1"] 8 | [http-kit "2.3.0"] 9 | [ring/ring-defaults "0.3.2"] 10 | [org.clojure/data.json "0.2.6"] 11 | [metosin/ring-swagger "0.26.2"]] 12 | 13 | :main ^:skip-aot fizz_buzz.core 14 | :target-path "target/%s" 15 | :profiles {:uberjar {:aot :all}}) 16 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | All notable changes to this project will be documented in this file. This change log follows the conventions of [keepachangelog.com](http://keepachangelog.com/). 3 | 4 | ## [Unreleased] 5 | ### Changed 6 | - Add a new arity to `make-widget-async` to provide a different widget shape. 7 | 8 | ## [0.1.1] - 2022-10-18 9 | ### Changed 10 | - Documentation on how to make the widgets. 11 | 12 | ### Removed 13 | - `make-widget-sync` - we're all async, all the time. 14 | 15 | ### Fixed 16 | - Fixed widget maker to keep working when daylight savings switches over. 17 | 18 | ## 0.1.0 - 2022-10-18 19 | ### Added 20 | - Files from the new template. 21 | - Widget maker public API - `make-widget-sync`. 22 | 23 | [Unreleased]: https://github.com/your-name/fizz-buzz/compare/0.1.1...HEAD 24 | [0.1.1]: https://github.com/your-name/fizz-buzz/compare/0.1.0...0.1.1 25 | -------------------------------------------------------------------------------- /fly.toml: -------------------------------------------------------------------------------- 1 | # fly.toml app configuration file generated for fizzbuzz-as-a-service-solitary-sun-4916 on 2025-04-30T22:53:13+02:00 2 | # 3 | # See https://fly.io/docs/reference/configuration/ for information about how to use this file. 4 | # 5 | 6 | app = 'fizzbuzz-as-a-service-solitary-sun-4916' 7 | primary_region = 'arn' 8 | 9 | [build] 10 | 11 | [http_service] 12 | internal_port = 3000 13 | force_https = true 14 | auto_stop_machines = 'stop' 15 | auto_start_machines = true 16 | min_machines_running = 0 17 | processes = ['app'] 18 | 19 | [[services]] 20 | protocol = 'tcp' 21 | internal_port = 3000 22 | 23 | [[services.ports]] 24 | port = 80 25 | handlers = ['http'] 26 | 27 | [[services.ports]] 28 | port = 443 29 | handlers = ['tls', 'http'] 30 | 31 | [[vm]] 32 | memory = '1gb' 33 | cpu_kind = 'shared' 34 | cpus = 1 35 | memory_mb = 1024 36 | -------------------------------------------------------------------------------- /.github/workflows/clojure.yml: -------------------------------------------------------------------------------- 1 | name: Clojure CI 2 | 3 | on: 4 | push: 5 | branches: [ "main" ] 6 | pull_request: 7 | branches: [ "main" ] 8 | 9 | jobs: 10 | test: 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - name: Checkout code 15 | uses: actions/checkout@v3 16 | 17 | - name: Set up Java 18 | uses: actions/setup-java@v3 19 | with: 20 | distribution: 'temurin' 21 | java-version: '21' 22 | 23 | - name: Install Leiningen 24 | run: | 25 | curl https://raw.githubusercontent.com/technomancy/leiningen/stable/bin/lein > lein 26 | chmod +x lein 27 | sudo mv lein /usr/local/bin 28 | 29 | - name: Cache Maven/Lein deps 30 | uses: actions/cache@v3 31 | with: 32 | path: ~/.m2 33 | key: ${{ runner.os }}-m2-${{ hashFiles('**/project.clj') }} 34 | restore-keys: | 35 | ${{ runner.os }}-m2 36 | 37 | - name: Run tests 38 | run: lein test 39 | -------------------------------------------------------------------------------- /src/fizz_buzz/openapi.clj: -------------------------------------------------------------------------------- 1 | (ns fizz-buzz.openapi 2 | (:require [ring.swagger.swagger2 :as swagger] 3 | [schema.core :as s])) 4 | 5 | (s/defschema FizzBuzz {:id s/Str}) 6 | (def openapi-spec 7 | (swagger/swagger-json 8 | {:info {:title "FizzBuzz API" 9 | :version "1.0.0"} 10 | :paths {"/fizzbuzz/:id" {:get {:summery "FizzBuzz as a service" 11 | :parameters {:path {:id s/Int}} 12 | :responses {200 {:schema FizzBuzz 13 | :description "Found it!"} 14 | 404 {:description "Ohnoes."}}} 15 | :post {:summery "Custom fizzbuzz" 16 | :parameters {:path {:id s/Int}} 17 | :responses {200 {:schema FizzBuzz 18 | :description "Found it!"} 19 | 404 {:description "Ohnoes."}}}}}})) 20 | -------------------------------------------------------------------------------- /deployment.yaml: -------------------------------------------------------------------------------- 1 | # Namespace 2 | --- 3 | apiVersion: v1 4 | kind: Namespace 5 | metadata: 6 | name: development 7 | 8 | # Deployment 9 | --- 10 | apiVersion: apps/v1 11 | kind: Deployment 12 | metadata: 13 | name: fizz-buzz-as-a-service 14 | namespace: development # Ensure everything is in the same namespace 15 | labels: 16 | app: faas 17 | spec: 18 | replicas: 2 19 | selector: 20 | matchLabels: 21 | app: faas # Must match pod labels 22 | template: 23 | metadata: 24 | labels: 25 | app: faas # Must match service selector 26 | spec: 27 | containers: 28 | - name: fizzbuzz-api 29 | image: fizzbuzz-api 30 | imagePullPolicy: Never 31 | ports: 32 | - containerPort: 3000 33 | env: 34 | - name: FIZZ_NAME 35 | valueFrom: 36 | fieldRef: 37 | fieldPath: metadata.name 38 | - name: FIZZ_NAMESPACE 39 | valueFrom: 40 | fieldRef: 41 | fieldPath: metadata.namespace 42 | - name: FIZZ_IP 43 | valueFrom: 44 | fieldRef: 45 | fieldPath: status.podIP 46 | 47 | # Service 48 | --- 49 | apiVersion: v1 50 | kind: Service 51 | metadata: 52 | name: fizzbuzz-service 53 | namespace: development # Service must be in the same namespace as Deployment 54 | spec: 55 | type: LoadBalancer 56 | selector: 57 | app: faas # This must match the Deployment label 58 | ports: 59 | - protocol: TCP 60 | port: 80 61 | targetPort: 3000 62 | nodePort: 30001 # Expose on port 30001 (Minikube exposes it) 63 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # fizz-buzz-as-a-service 2 | 3 | Are you tired of rewriting fizz buzz at every job interview, then this 4 | package is for you. 5 | 6 | Just build the Docker container and run it on your favorite platform, 7 | and you now have a REST-api that should give you the results, you'll need. 8 | 9 | $ curl 127.0.0.1:3000/fizzbuzz/135 10 | 11 | $ {"result":"FizzBuzz"}% 12 | 13 | To parse the result you could use jq to remove that unused information like this: 14 | 15 | $ curl 127.0.0.1:3000/fizzbuzz/15 -s | jq -r .result 16 | 17 | ## Advanced uses 18 | 19 | If the interviewer then ask you how to handle a more varied input, fizzbuzz-as-a-service also have you covered. 20 | 21 | $ curl -X POST http://localhost:3000/fizzbuzz/105 \ 22 | -H "Content-Type: application/json" \ 23 | -d '{"Fazz":"3", "Bizz":"5", "Jazz":"7"}' 24 | 25 | ## Installation 26 | 27 | You'll need a system with leiningen and Docker installed 28 | 29 | $ sudo pacman -S leinigen docker jq 30 | 31 | To build to docker container just run make with: 32 | 33 | $ make uberjar build 34 | 35 | Which should build the docker container called fizzbuzz/fizzbuzz. 36 | 37 | ## Usage 38 | 39 | You can start the docker container on your own computer with 40 | 41 | $ docker run -dp 3000:3000 fizzbuzz/fizzbuzz 42 | 43 | To test that it works, try to call the service with curl, or 44 | another web-client: 45 | 46 | $ curl 127.0.0.1:3000/fizzbuzz/12 47 | 48 | **⚠️ Note:** If you're not seeing any response, double-check that you started the container with `-p 3000:3000`. The web server listens on port 3000 inside the container, and you need to publish that port to your host. 49 | 50 | 51 | ## Examples 52 | 53 | I have included a test shell script that will query the api 200 times, and write the results 54 | to the screen. 55 | 56 | $ ./fizzbuzz_example.sh 57 | 58 | ### Bugs 59 | 60 | ... 61 | 62 | ### Any Other Sections 63 | ### That You Think 64 | ### Might be Useful 65 | 66 | ## License 67 | 68 | Copyright © 2025 Magnus Dreyer 69 | 70 | This program and the accompanying materials are made available under the 71 | terms of the Eclipse Public License 2.0 which is available at 72 | http://www.eclipse.org/legal/epl-2.0. 73 | 74 | This Source Code may also be made available under the following Secondary 75 | Licenses when the conditions for such availability set forth in the Eclipse 76 | Public License, v. 2.0 are satisfied: GNU General Public License as published by 77 | the Free Software Foundation, either version 2 of the License, or (at your 78 | option) any later version, with the GNU Classpath Exception which is available 79 | at https://www.gnu.org/software/classpath/license.html. 80 | -------------------------------------------------------------------------------- /Examples/FizzApi/app.py: -------------------------------------------------------------------------------- 1 | from functools import wraps 2 | import os 3 | import logging 4 | import json 5 | import redis 6 | import requests 7 | from typing import Any 8 | 9 | logging.basicConfig(level=logging.INFO) 10 | logger = logging.getLogger(__name__) 11 | 12 | REDIS_HOST = os.getenv("REDIS_HOST", "127.0.0.1") 13 | REDIS_PORT = int(os.getenv("REDIS_PORT", "6379")) 14 | REDIS_TTL = int(os.getenv("REDIS_TTL", "180")) 15 | 16 | API_HOST = os.getenv("FIZZ_API_HOST", "192.168.0.79") 17 | API_PORT = int(os.getenv("FIZZ_API_PORT", "1337")) 18 | 19 | r = redis.Redis(host=REDIS_HOST, port=REDIS_PORT, decode_responses=True) 20 | 21 | def redis_cached(redis_client: redis.Redis, ttl: int = REDIS_TTL): 22 | def decorator(func): 23 | @wraps(func) 24 | def wrapper(*args, **kwargs): 25 | try: 26 | # Build a deterministic cache key from function name + args + kwargs 27 | key_body = { 28 | "args": args, 29 | "kwargs": kwargs, 30 | } 31 | cache_key = f"{func.__name__}:{json.dumps(key_body, default=repr, sort_keys=True)}" 32 | except Exception: 33 | cache_key = f"{func.__name__}:{repr((args, kwargs))}" 34 | 35 | try: 36 | cached = redis_client.get(cache_key) 37 | except Exception as e: 38 | logger.warning("Redis GET failed: %s", e) 39 | cached = None 40 | 41 | if cached is not None: 42 | logger.debug("cache hit for %s", cache_key) 43 | return cached 44 | 45 | result = func(*args, **kwargs) 46 | 47 | try: 48 | # Store as JSON string if possible, otherwise str() 49 | to_store = result if isinstance(result, str) else json.dumps(result, default=repr) 50 | redis_client.set(cache_key, to_store, ex=ttl) 51 | except Exception as e: 52 | logger.warning("Redis SET failed: %s", e) 53 | 54 | return result 55 | 56 | return wrapper 57 | 58 | return decorator 59 | 60 | @redis_cached(r) 61 | def fizzbuzz(num: int) -> str: 62 | url = f"http://{API_HOST}:{API_PORT}/fizzbuzz/{num}" 63 | try: 64 | res = requests.get(url, timeout=5) 65 | res.raise_for_status() 66 | data = res.json() 67 | return data.get("result", "") 68 | except requests.RequestException as e: 69 | logger.error("Request to %s failed: %s", url, e) 70 | raise 71 | 72 | if __name__ == "__main__": 73 | for i in range(1, 101): 74 | try: 75 | print(fizzbuzz(i)) 76 | except Exception as e: 77 | logger.error("Failed to get fizzbuzz for %d: %s", i, e) -------------------------------------------------------------------------------- /src/fizz_buzz/core.clj: -------------------------------------------------------------------------------- 1 | (ns fizz-buzz.core 2 | (:require [org.httpkit.server :as server] 3 | [clojure.data.json :as json] 4 | [compojure.core :refer :all] 5 | [compojure.route :as route] 6 | [ring.middleware.defaults :refer :all] 7 | [ring.util.response :as resp] 8 | [clojure.string :as str] 9 | [fizz-buzz.openapi :as openapi] 10 | [clojure.spec.alpha :as s] 11 | [fizz-buzz.spec :as spec]) 12 | (:gen-class)) 13 | 14 | (def defaults 15 | {:Fizz 3 16 | :Buzz 5}) 17 | 18 | (defn- x [i val nam] 19 | (when (= 0 (mod i val)) 20 | (-> nam 21 | name 22 | str/capitalize))) 23 | 24 | (defn fizz-buzz 25 | [i] 26 | (let [x2 #(x i (first %1) (second %1)) 27 | values {3 :fizz 28 | 5 :buzz} 29 | output (apply str (map x2 values))] 30 | {:result (if (= "" output) 31 | i 32 | output)})) 33 | 34 | (defn fizz-b 35 | ([i] (fizz-b i defaults)) 36 | ([i fizzings] 37 | (let [f (fn [s [kw number]] 38 | (if (zero? (mod i number)) 39 | (str s (name kw)) 40 | s)) 41 | re (reduce f "" fizzings)] 42 | (if (= "" re) 43 | (str i) 44 | re)))) 45 | 46 | 47 | (defn fb [req] 48 | (let [i (-> req 49 | :params 50 | :id 51 | Integer/parseInt)] 52 | (-> i 53 | fizz-buzz 54 | json/write-str 55 | resp/response 56 | (resp/status 200) 57 | (resp/header "Content-Type" "application/json")))) 58 | 59 | (defn fb-custom 60 | [req] 61 | (let [i (-> req 62 | :params 63 | :id 64 | Integer/parseInt)] 65 | (if-let [body (some-> req 66 | :body 67 | slurp)] 68 | (try 69 | (let [json-body (json/read-str body :key-fn keyword) 70 | body (into {} (map (fn [[kw v]] [kw (Integer/parseInt v)])) json-body)] 71 | (if (s/valid? ::spec/fizz-buzz-rules body) 72 | (-> {:result (fizz-b i body)} 73 | resp/response 74 | (resp/status 200) 75 | (resp/header "Content-Type" "application/json")) 76 | (do 77 | (println "error: " (s/explain ::spec/fizz-buzz-rules body)) 78 | (-> "Your data, doesn't match what we expected" 79 | resp/response 80 | (resp/status 400) 81 | (resp/header "Content-Type" "application/json"))))) 82 | (catch Exception e 83 | (do 84 | (println "Error:" (.getMessage e)) 85 | (-> "Your data, doesn't match what we expected" 86 | resp/response 87 | (resp/status 400) 88 | (resp/header "Content-Type" "application/json"))))) 89 | (-> {:result (fizz-b i)} 90 | resp/response 91 | (resp/header "Content-Type" "application/json") 92 | (resp/status 200))))) 93 | 94 | (defroutes app-routes 95 | (GET "/fizzbuzz/:id" [] fb) 96 | (POST "/fizzbuzz/:id" [] fb-custom) 97 | (GET "/openapi.json" [] (fn [_] 98 | (-> openapi/openapi-spec 99 | json/write-str 100 | resp/response 101 | (resp/status 200) 102 | (resp/header "Content-Type" "application/json")))) 103 | (route/not-found "Error, page still not found!\n")) 104 | 105 | (def custom-middleware 106 | (-> site-defaults 107 | (assoc-in [:security :anti-forgery] false))) ;; Disable CSRF 108 | 109 | (defn -main 110 | "This is our main entry point" 111 | [& args] 112 | (let [port (Integer/parseInt (or (System/getenv "PORT") "3000"))] 113 | ; Run the server with Ring.defaults middleware 114 | (server/run-server (wrap-defaults #'app-routes custom-middleware) 115 | {:port port :ip "0.0.0.0"}) 116 | ; Run the server without ring defaults 117 | ;(server/run-server #'app-routes {:port port}) 118 | (println (str "Running webserver at http:/127.0.0.1:" port "/")))) 119 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Eclipse Public License - v 2.0 2 | 3 | THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE 4 | PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION 5 | OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. 6 | 7 | 1. DEFINITIONS 8 | 9 | "Contribution" means: 10 | 11 | a) in the case of the initial Contributor, the initial content 12 | Distributed under this Agreement, and 13 | 14 | b) in the case of each subsequent Contributor: 15 | i) changes to the Program, and 16 | ii) additions to the Program; 17 | where such changes and/or additions to the Program originate from 18 | and are Distributed by that particular Contributor. A Contribution 19 | "originates" from a Contributor if it was added to the Program by 20 | such Contributor itself or anyone acting on such Contributor's behalf. 21 | Contributions do not include changes or additions to the Program that 22 | are not Modified Works. 23 | 24 | "Contributor" means any person or entity that Distributes the Program. 25 | 26 | "Licensed Patents" mean patent claims licensable by a Contributor which 27 | are necessarily infringed by the use or sale of its Contribution alone 28 | or when combined with the Program. 29 | 30 | "Program" means the Contributions Distributed in accordance with this 31 | Agreement. 32 | 33 | "Recipient" means anyone who receives the Program under this Agreement 34 | or any Secondary License (as applicable), including Contributors. 35 | 36 | "Derivative Works" shall mean any work, whether in Source Code or other 37 | form, that is based on (or derived from) the Program and for which the 38 | editorial revisions, annotations, elaborations, or other modifications 39 | represent, as a whole, an original work of authorship. 40 | 41 | "Modified Works" shall mean any work in Source Code or other form that 42 | results from an addition to, deletion from, or modification of the 43 | contents of the Program, including, for purposes of clarity any new file 44 | in Source Code form that contains any contents of the Program. Modified 45 | Works shall not include works that contain only declarations, 46 | interfaces, types, classes, structures, or files of the Program solely 47 | in each case in order to link to, bind by name, or subclass the Program 48 | or Modified Works thereof. 49 | 50 | "Distribute" means the acts of a) distributing or b) making available 51 | in any manner that enables the transfer of a copy. 52 | 53 | "Source Code" means the form of a Program preferred for making 54 | modifications, including but not limited to software source code, 55 | documentation source, and configuration files. 56 | 57 | "Secondary License" means either the GNU General Public License, 58 | Version 2.0, or any later versions of that license, including any 59 | exceptions or additional permissions as identified by the initial 60 | Contributor. 61 | 62 | 2. GRANT OF RIGHTS 63 | 64 | a) Subject to the terms of this Agreement, each Contributor hereby 65 | grants Recipient a non-exclusive, worldwide, royalty-free copyright 66 | license to reproduce, prepare Derivative Works of, publicly display, 67 | publicly perform, Distribute and sublicense the Contribution of such 68 | Contributor, if any, and such Derivative Works. 69 | 70 | b) Subject to the terms of this Agreement, each Contributor hereby 71 | grants Recipient a non-exclusive, worldwide, royalty-free patent 72 | license under Licensed Patents to make, use, sell, offer to sell, 73 | import and otherwise transfer the Contribution of such Contributor, 74 | if any, in Source Code or other form. This patent license shall 75 | apply to the combination of the Contribution and the Program if, at 76 | the time the Contribution is added by the Contributor, such addition 77 | of the Contribution causes such combination to be covered by the 78 | Licensed Patents. The patent license shall not apply to any other 79 | combinations which include the Contribution. No hardware per se is 80 | licensed hereunder. 81 | 82 | c) Recipient understands that although each Contributor grants the 83 | licenses to its Contributions set forth herein, no assurances are 84 | provided by any Contributor that the Program does not infringe the 85 | patent or other intellectual property rights of any other entity. 86 | Each Contributor disclaims any liability to Recipient for claims 87 | brought by any other entity based on infringement of intellectual 88 | property rights or otherwise. As a condition to exercising the 89 | rights and licenses granted hereunder, each Recipient hereby 90 | assumes sole responsibility to secure any other intellectual 91 | property rights needed, if any. For example, if a third party 92 | patent license is required to allow Recipient to Distribute the 93 | Program, it is Recipient's responsibility to acquire that license 94 | before distributing the Program. 95 | 96 | d) Each Contributor represents that to its knowledge it has 97 | sufficient copyright rights in its Contribution, if any, to grant 98 | the copyright license set forth in this Agreement. 99 | 100 | e) Notwithstanding the terms of any Secondary License, no 101 | Contributor makes additional grants to any Recipient (other than 102 | those set forth in this Agreement) as a result of such Recipient's 103 | receipt of the Program under the terms of a Secondary License 104 | (if permitted under the terms of Section 3). 105 | 106 | 3. REQUIREMENTS 107 | 108 | 3.1 If a Contributor Distributes the Program in any form, then: 109 | 110 | a) the Program must also be made available as Source Code, in 111 | accordance with section 3.2, and the Contributor must accompany 112 | the Program with a statement that the Source Code for the Program 113 | is available under this Agreement, and informs Recipients how to 114 | obtain it in a reasonable manner on or through a medium customarily 115 | used for software exchange; and 116 | 117 | b) the Contributor may Distribute the Program under a license 118 | different than this Agreement, provided that such license: 119 | i) effectively disclaims on behalf of all other Contributors all 120 | warranties and conditions, express and implied, including 121 | warranties or conditions of title and non-infringement, and 122 | implied warranties or conditions of merchantability and fitness 123 | for a particular purpose; 124 | 125 | ii) effectively excludes on behalf of all other Contributors all 126 | liability for damages, including direct, indirect, special, 127 | incidental and consequential damages, such as lost profits; 128 | 129 | iii) does not attempt to limit or alter the recipients' rights 130 | in the Source Code under section 3.2; and 131 | 132 | iv) requires any subsequent distribution of the Program by any 133 | party to be under a license that satisfies the requirements 134 | of this section 3. 135 | 136 | 3.2 When the Program is Distributed as Source Code: 137 | 138 | a) it must be made available under this Agreement, or if the 139 | Program (i) is combined with other material in a separate file or 140 | files made available under a Secondary License, and (ii) the initial 141 | Contributor attached to the Source Code the notice described in 142 | Exhibit A of this Agreement, then the Program may be made available 143 | under the terms of such Secondary Licenses, and 144 | 145 | b) a copy of this Agreement must be included with each copy of 146 | the Program. 147 | 148 | 3.3 Contributors may not remove or alter any copyright, patent, 149 | trademark, attribution notices, disclaimers of warranty, or limitations 150 | of liability ("notices") contained within the Program from any copy of 151 | the Program which they Distribute, provided that Contributors may add 152 | their own appropriate notices. 153 | 154 | 4. COMMERCIAL DISTRIBUTION 155 | 156 | Commercial distributors of software may accept certain responsibilities 157 | with respect to end users, business partners and the like. While this 158 | license is intended to facilitate the commercial use of the Program, 159 | the Contributor who includes the Program in a commercial product 160 | offering should do so in a manner which does not create potential 161 | liability for other Contributors. Therefore, if a Contributor includes 162 | the Program in a commercial product offering, such Contributor 163 | ("Commercial Contributor") hereby agrees to defend and indemnify every 164 | other Contributor ("Indemnified Contributor") against any losses, 165 | damages and costs (collectively "Losses") arising from claims, lawsuits 166 | and other legal actions brought by a third party against the Indemnified 167 | Contributor to the extent caused by the acts or omissions of such 168 | Commercial Contributor in connection with its distribution of the Program 169 | in a commercial product offering. The obligations in this section do not 170 | apply to any claims or Losses relating to any actual or alleged 171 | intellectual property infringement. In order to qualify, an Indemnified 172 | Contributor must: a) promptly notify the Commercial Contributor in 173 | writing of such claim, and b) allow the Commercial Contributor to control, 174 | and cooperate with the Commercial Contributor in, the defense and any 175 | related settlement negotiations. The Indemnified Contributor may 176 | participate in any such claim at its own expense. 177 | 178 | For example, a Contributor might include the Program in a commercial 179 | product offering, Product X. That Contributor is then a Commercial 180 | Contributor. If that Commercial Contributor then makes performance 181 | claims, or offers warranties related to Product X, those performance 182 | claims and warranties are such Commercial Contributor's responsibility 183 | alone. Under this section, the Commercial Contributor would have to 184 | defend claims against the other Contributors related to those performance 185 | claims and warranties, and if a court requires any other Contributor to 186 | pay any damages as a result, the Commercial Contributor must pay 187 | those damages. 188 | 189 | 5. NO WARRANTY 190 | 191 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT 192 | PERMITTED BY APPLICABLE LAW, THE PROGRAM IS PROVIDED ON AN "AS IS" 193 | BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR 194 | IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF 195 | TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR 196 | PURPOSE. Each Recipient is solely responsible for determining the 197 | appropriateness of using and distributing the Program and assumes all 198 | risks associated with its exercise of rights under this Agreement, 199 | including but not limited to the risks and costs of program errors, 200 | compliance with applicable laws, damage to or loss of data, programs 201 | or equipment, and unavailability or interruption of operations. 202 | 203 | 6. DISCLAIMER OF LIABILITY 204 | 205 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT 206 | PERMITTED BY APPLICABLE LAW, NEITHER RECIPIENT NOR ANY CONTRIBUTORS 207 | SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 208 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST 209 | PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 210 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 211 | ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE 212 | EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE 213 | POSSIBILITY OF SUCH DAMAGES. 214 | 215 | 7. GENERAL 216 | 217 | If any provision of this Agreement is invalid or unenforceable under 218 | applicable law, it shall not affect the validity or enforceability of 219 | the remainder of the terms of this Agreement, and without further 220 | action by the parties hereto, such provision shall be reformed to the 221 | minimum extent necessary to make such provision valid and enforceable. 222 | 223 | If Recipient institutes patent litigation against any entity 224 | (including a cross-claim or counterclaim in a lawsuit) alleging that the 225 | Program itself (excluding combinations of the Program with other software 226 | or hardware) infringes such Recipient's patent(s), then such Recipient's 227 | rights granted under Section 2(b) shall terminate as of the date such 228 | litigation is filed. 229 | 230 | All Recipient's rights under this Agreement shall terminate if it 231 | fails to comply with any of the material terms or conditions of this 232 | Agreement and does not cure such failure in a reasonable period of 233 | time after becoming aware of such noncompliance. If all Recipient's 234 | rights under this Agreement terminate, Recipient agrees to cease use 235 | and distribution of the Program as soon as reasonably practicable. 236 | However, Recipient's obligations under this Agreement and any licenses 237 | granted by Recipient relating to the Program shall continue and survive. 238 | 239 | Everyone is permitted to copy and distribute copies of this Agreement, 240 | but in order to avoid inconsistency the Agreement is copyrighted and 241 | may only be modified in the following manner. The Agreement Steward 242 | reserves the right to publish new versions (including revisions) of 243 | this Agreement from time to time. No one other than the Agreement 244 | Steward has the right to modify this Agreement. The Eclipse Foundation 245 | is the initial Agreement Steward. The Eclipse Foundation may assign the 246 | responsibility to serve as the Agreement Steward to a suitable separate 247 | entity. Each new version of the Agreement will be given a distinguishing 248 | version number. The Program (including Contributions) may always be 249 | Distributed subject to the version of the Agreement under which it was 250 | received. In addition, after a new version of the Agreement is published, 251 | Contributor may elect to Distribute the Program (including its 252 | Contributions) under the new version. 253 | 254 | Except as expressly stated in Sections 2(a) and 2(b) above, Recipient 255 | receives no rights or licenses to the intellectual property of any 256 | Contributor under this Agreement, whether expressly, by implication, 257 | estoppel or otherwise. All rights in the Program not expressly granted 258 | under this Agreement are reserved. Nothing in this Agreement is intended 259 | to be enforceable by any entity that is not a Contributor or Recipient. 260 | No third-party beneficiary rights are created under this Agreement. 261 | 262 | Exhibit A - Form of Secondary Licenses Notice 263 | 264 | "This Source Code may also be made available under the following 265 | Secondary Licenses when the conditions for such availability set forth 266 | in the Eclipse Public License, v. 2.0 are satisfied: {name license(s), 267 | version(s), and exceptions or additional permissions here}." 268 | 269 | Simply including a copy of this Agreement, including this Exhibit A 270 | is not sufficient to license the Source Code under Secondary Licenses. 271 | 272 | If it is not possible or desirable to put the notice in a particular 273 | file, then You may include the notice in a location (such as a LICENSE 274 | file in a relevant directory) where a recipient would be likely to 275 | look for such a notice. 276 | 277 | You may add additional accurate notices of copyright ownership. 278 | --------------------------------------------------------------------------------