├── .gitignore ├── Makefile ├── resources └── public │ ├── css │ └── tudu.css │ └── index.html ├── src └── tudu │ ├── data.clj │ ├── api.clj │ ├── core.clj │ ├── store.clj │ ├── web.clj │ └── ui.cljs ├── project.clj ├── README.rst └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /classes 3 | /checkouts 4 | pom.xml 5 | pom.xml.asc 6 | *.jar 7 | *.class 8 | /.lein-* 9 | /.nrepl-port 10 | .hgignore 11 | .hg/ 12 | *-init.clj 13 | figwheel_server.log 14 | resources/public/js/compiled/ 15 | tudu.*.db 16 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: setup 2 | 3 | setup: 4 | mkdir -p tmp 5 | cd tmp && git clone https://github.com/omcljs/om.git && cd om && git checkout 0d31bd9baeb2bcbef96b4ebc4eef960fd46df1f0 && lein install 6 | rm -rf tmp/om 7 | 8 | clean: 9 | lein clean 10 | rm -f *-init.clj figwheel_server.log 11 | -------------------------------------------------------------------------------- /resources/public/css/tudu.css: -------------------------------------------------------------------------------- 1 | .todo-list-item-title.status-close{ 2 | text-decoration: line-through; 3 | } 4 | 5 | body{ 6 | margin: 2em 15%; 7 | font-size: 16pt; 8 | font-family: helvetica; 9 | color: #333; 10 | background-color: #fefefe; 11 | } 12 | 13 | .new-todo-item-create, .form-group>label, .form-group>input{ 14 | margin: 0.2em 0; 15 | width: 50%; 16 | margin-right: 50%; 17 | } 18 | -------------------------------------------------------------------------------- /resources/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Tudu 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/tudu/data.clj: -------------------------------------------------------------------------------- 1 | (ns tudu.data 2 | (:require 3 | [cognitect.transit :as transit] 4 | om.next.server) 5 | (:import [java.io ByteArrayInputStream ByteArrayOutputStream])) 6 | 7 | (defn to-string [data] 8 | (let [out (ByteArrayOutputStream.) 9 | writer (om.next.server/writer out)] 10 | (transit/write writer data) 11 | (.toString out))) 12 | 13 | (defn parse [data] 14 | (let [reader (om.next.server/reader data)] 15 | (transit/read reader))) 16 | -------------------------------------------------------------------------------- /src/tudu/api.clj: -------------------------------------------------------------------------------- 1 | (ns tudu.api 2 | (:require 3 | tudu.store 4 | [om.next.server :as om])) 5 | 6 | (defmulti readf om/dispatch) 7 | (defmulti mutatef om/dispatch) 8 | 9 | (def parser (om/parser {:read readf :mutate mutatef})) 10 | 11 | (defn query [{:keys [body request]}] 12 | (let [query-env {:app-env (:tudu/env request)} 13 | body (parser query-env body)] 14 | {:status 200 :body body})) 15 | 16 | (defn not-found [_] 17 | {:status 404 :body {:error "Not Found"}}) 18 | 19 | (defmethod readf :default [env key params] 20 | :not-found) 21 | 22 | (defmethod readf :tudu/items [{:keys [app-env]} key {:keys [id] :as params}] 23 | (let [{:keys [db]} app-env] 24 | {:value (vec (tudu.store/get-tasks db))})) 25 | 26 | (defmethod mutatef :default [_ key params] 27 | :notfound) 28 | 29 | (defmethod mutatef 'tudu.item/close [{:keys [app-env]} _ {:keys [id]}] 30 | (let [{:keys [db]} app-env] 31 | (tudu.store/close-task db id) 32 | {})) 33 | 34 | (defmethod mutatef 'tudu.item/create [{:keys [app-env]} _ {:keys [value]}] 35 | (let [{:keys [db]} app-env 36 | tempid (:id value) 37 | {:keys [id]} (tudu.store/create-task db (dissoc value :id)) 38 | response (if (and id tempid) 39 | {tempid id} 40 | {})] 41 | {:value {:tempids response}})) 42 | 43 | -------------------------------------------------------------------------------- /src/tudu/core.clj: -------------------------------------------------------------------------------- 1 | (ns tudu.core 2 | (:require 3 | tudu.store 4 | tudu.web 5 | [com.stuartsierra.component :as component]) 6 | (:gen-class)) 7 | 8 | (defn system [{:keys [web-info db-info]}] 9 | (component/system-map 10 | :db (tudu.store/new-db db-info) 11 | :web-api (component/using (tudu.web/new-web-api web-info) 12 | [:db]))) 13 | 14 | (defn new-system [host port base-path] 15 | (system {:web-info {:host host 16 | :port port 17 | :base-path base-path} 18 | :db-info {:classname "org.h2.Driver" 19 | :subprotocol "h2:file" 20 | :subname "./tudu" 21 | :user "tudu" 22 | :password "" 23 | :naming {:keys clojure.string/lower-case 24 | :fields clojure.string/upper-case} 25 | :make-pool? true 26 | :excess-timeout 99 27 | :idle-timeout 88 28 | :minimum-pool-size 5 29 | :maximum-pool-size 20 30 | :test-connection-on-checkout true 31 | :test-connection-query "SELECT 1"}})) 32 | 33 | (defn start [host port base-path] 34 | (component/start (new-system host port base-path))) 35 | 36 | (defn stop [system] 37 | (component/stop system)) 38 | 39 | (defn -main [& args] 40 | (start "localhost" 8080 "/")) 41 | -------------------------------------------------------------------------------- /project.clj: -------------------------------------------------------------------------------- 1 | (defproject tudu "0.1.0-SNAPSHOT" 2 | :description "An example project that builds an end to end todo list" 3 | :url "http://github.com/marianoguerra-atik/tudu" 4 | :license {:name "Eclipse Public License" 5 | :url "http://www.eclipse.org/legal/epl-v10.html"} 6 | :dependencies [[org.clojure/clojure "1.8.0"] 7 | [org.clojure/clojurescript "1.7.228"] 8 | [org.omcljs/om "1.0.0-alpha30"] 9 | 10 | [com.h2database/h2 "1.4.190"] 11 | [org.clojure/java.jdbc "0.4.2"] 12 | [com.stuartsierra/component "0.3.1"] 13 | [org.immutant/web "2.1.2"] 14 | [com.cognitect/transit-clj "0.8.285"] 15 | [bidi "1.21.1"]] 16 | 17 | :main tudu.core 18 | :aot :all 19 | :plugins [[lein-cljsbuild "1.1.2"] 20 | [lein-figwheel "0.5.0-3"]] 21 | 22 | :source-paths ["src"] 23 | :clean-targets ^{:protect false} ["resources/public/js/compiled" 24 | "target"] 25 | 26 | :profiles {:uberjar {:prep-tasks ["compile" ["cljsbuild" "once" "prod"]]}} 27 | :cljsbuild {:builds [{:id "dev" 28 | :source-paths ["src"] 29 | :figwheel true 30 | :compiler {:main "tudu.ui" 31 | :asset-path "js/compiled/out" 32 | :output-to "resources/public/js/compiled/tudu.js" 33 | :output-dir "resources/public/js/compiled/out" 34 | :source-map-timestamp true }} 35 | {:id "prod" 36 | :source-paths ["src"] 37 | :compiler {:main "tudu.ui" 38 | :asset-path "js/compiled/out" 39 | :output-to "resources/public/js/compiled/tudu.js" 40 | :optimizations :advanced}}]} 41 | 42 | :figwheel { :css-dirs ["resources/public/css"] }) 43 | -------------------------------------------------------------------------------- /src/tudu/store.clj: -------------------------------------------------------------------------------- 1 | (ns tudu.store 2 | (:require 3 | [com.stuartsierra.component :as component] 4 | [clojure.java.jdbc :as j])) 5 | 6 | (defprotocol Store 7 | (get-tasks [this]) 8 | (create-task [this task]) 9 | (close-task [this id])) 10 | 11 | (defn create-tables [db] 12 | (j/with-db-connection [c db] 13 | (j/execute! c [(j/create-table-ddl "IF NOT EXISTS tudu_task" 14 | [:m_id "int AUTO_INCREMENT PRIMARY KEY"] 15 | [:m_title "varchar(256) NOT NULL"] 16 | [:m_status "varchar(32) NOT NULL"])]))) 17 | 18 | (defn query [db q] 19 | (j/with-db-connection [c db] 20 | (j/query c q))) 21 | 22 | (defn insert [db table data] 23 | (j/with-db-connection [c db] 24 | (let [result (j/insert! db table data) 25 | id (get (first result) (keyword "scope_identity()"))] 26 | (if (nil? id) 27 | result 28 | {:id id})))) 29 | 30 | (defn update-by-id [db table data] 31 | (println "updating on" table (:m_id data) " -> " data) 32 | (j/with-db-connection [c db] 33 | (let [id (:m_id data) 34 | result (j/update! db table data ["m_id = ?" id])] 35 | (println "update on" table id "updated" result " -> " data) 36 | {:result result :id id :data data}))) 37 | 38 | (defn row->task [{:keys [m_id m_title m_status]}] 39 | {:id m_id :title m_title :status (keyword m_status)}) 40 | 41 | (defn task->row [{:keys [id title status]}] 42 | {:m_id id 43 | :m_title title 44 | :m_status (when status (name status))}) 45 | 46 | (defrecord DB [classname subprotocol subname user password] 47 | component/Lifecycle 48 | (start [{:keys [] :as component}] 49 | (println "Starting DB" (str classname " " subprotocol " " subname " " user)) 50 | (let [new-component (assoc component 51 | :classname classname 52 | :subprotocol subprotocol 53 | :subname subname 54 | :user user 55 | :password password)] 56 | (try 57 | (create-tables new-component) 58 | (catch Exception e 59 | (prn "Error creating tables" e))) 60 | 61 | new-component)) 62 | 63 | (stop [{:keys [classname subprotocol subname user] :as component}] 64 | (println "Stopping DB" (str classname " " subprotocol " " subname " " user)) 65 | component) 66 | 67 | Store 68 | (get-tasks[this] 69 | (map row->task (query this ["SELECT * FROM tudu_task"]))) 70 | (create-task [this task] 71 | (insert this :tudu_task (task->row (assoc task :status :open)))) 72 | (close-task [this id] 73 | (update-by-id this :tudu_task {:m_id id :m_status "close"}))) 74 | 75 | (defn new-db [db-info] 76 | (map->DB db-info)) 77 | -------------------------------------------------------------------------------- /src/tudu/web.clj: -------------------------------------------------------------------------------- 1 | (ns tudu.web 2 | (:require 3 | tudu.data 4 | [tudu.api :as api] 5 | [com.stuartsierra.component :as component] 6 | [bidi.ring :refer [make-handler]] 7 | [ring.middleware.resource :refer [wrap-resource]] 8 | [immutant.web.middleware :refer [wrap-websocket]] 9 | [immutant.web :as web])) 10 | 11 | (defn- wrap-add-component [app component] 12 | (fn [req] 13 | (app (assoc req :tudu/env component)))) 14 | 15 | (defn- start-component-web-api-handler [component host port path app] 16 | (let [new-handler (web/run (wrap-add-component app component) 17 | {:host host :port port :path path}) 18 | new-component (assoc component 19 | :handler new-handler 20 | :host host 21 | :port port 22 | :path path 23 | :app app)] 24 | new-component)) 25 | 26 | (defn response-to-transit [{:keys [body headers] :as response}] 27 | (assoc response 28 | :body (tudu.data/to-string body) 29 | :headers (assoc headers "Content-Type" "application/transit+json"))) 30 | 31 | (defn call-handler [handler has-body {:keys [body] :as request}] 32 | (let [req-body (when has-body (tudu.data/parse body))] 33 | (try 34 | (handler {:body req-body :request request}) 35 | (catch Throwable ex 36 | (println ex "Error calling handler") 37 | {:status 500 :body {:error "Internal Error"}})))) 38 | 39 | (defn wrap-handler [handler has-body] 40 | (fn [request] 41 | (response-to-transit (call-handler handler has-body request)))) 42 | 43 | (def req-handlers {:query (wrap-handler api/query true) 44 | :not-found (wrap-handler api/not-found false)}) 45 | 46 | (def routes ["/" {"query" {:post :query} 47 | true :not-found}]) 48 | 49 | (defrecord WebAPI [host port base-path app db] 50 | component/Lifecycle 51 | (start [{:keys [handler] :as component}] 52 | (println "Starting Web API at" (str host ":" port base-path)) 53 | (if (nil? handler) 54 | (let [app (-> (make-handler routes req-handlers) 55 | (wrap-resource "public"))] 56 | (start-component-web-api-handler component host port base-path app)) 57 | 58 | (do 59 | (println "Existing Web API, Stoping and Starting New") 60 | (web/stop handler) 61 | (start-component-web-api-handler component host port base-path app)))) 62 | 63 | (stop [{:keys [handler host port path] :as component}] 64 | (println "Stopping Web API at" (str host ":" port path)) 65 | (if (nil? handler) 66 | (println "No Handler, No Op") 67 | 68 | (do 69 | (web/stop handler) 70 | ; dissoc one of a record's base fields, you get a plain map 71 | (assoc component :handler nil))))) 72 | 73 | (defn new-web-api [config] 74 | (map->WebAPI config)) 75 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | tudu 2 | ==== 3 | 4 | An example project that builds an end to end todo list using 5 | clojure/immutant on the backend and clojurescript/om.next on the frontend 6 | 7 | Setup 8 | ----- 9 | 10 | First you need `leiningen `_ installed and running, 11 | follow the link for installation instructions. 12 | 13 | We need a local version of om.next installed since it's still in alpha, we 14 | will use alpha30 to start with, we may upgrade it later in the development. 15 | 16 | To make it easy just run once, it will install the required version of om.next locally for you:: 17 | 18 | make setup 19 | 20 | Get up and running 21 | ------------------ 22 | 23 | To get the app up and running in dev mode, you'll need to start the front end 24 | and the backend. 25 | 26 | To start the front end, run:: 27 | 28 | lein figwheel 29 | 30 | To start the backend, run:: 31 | 32 | lein run 33 | 34 | The app will now be accessible at http://localhost:8080/index.html 35 | 36 | Test the backend from the command line 37 | -------------------------------------- 38 | 39 | for this you will need to install `transito `_:: 40 | 41 | sudo pip install transito 42 | 43 | and then run:: 44 | 45 | echo '[:tudu/items]' | transito http post http://localhost:8080/query e2t - 46 | echo '[(tudu.item/create {:value {:title "My Task"}})]' | transito http post http://localhost:8080/query e2t - 47 | echo '[(tudu.item/close {:id 2})]' | transito http post http://localhost:8080/query e2t - 48 | 49 | Interactive Backend Development 50 | ------------------------------- 51 | 52 | start a repl:: 53 | 54 | lein repl 55 | 56 | in the repl:: 57 | 58 | (require 'tudu.core :reload-all) 59 | (def sys (tudu.core/start "localhost" 8080 "/")) 60 | (tudu.core/stop sys) 61 | 62 | if you want to be really sure a namespace was recompiled run:: 63 | 64 | ; change the path to the path of your module 65 | (load-file "src/tudu/web.clj") 66 | (load-file "src/tudu/api.clj") 67 | 68 | every time you want to reload changes evaluate again the first line and stop 69 | start the system if the changes involve the system. 70 | 71 | To use the repl from vim I use `vim-slime `_ 72 | 73 | Compiling the Frontend 74 | ---------------------- 75 | 76 | To compile the frontend and have live reload capabilities run:: 77 | 78 | lein figwheel 79 | 80 | You should also be running the backend on another shell, then visit: 81 | 82 | http://localhost:8080/index.html 83 | 84 | Note that http://localhost:8080/ won't work, it has to be http://localhost:8080/index.html 85 | 86 | You can now make changes on the frontend and it will live reload. 87 | 88 | Build for Deployment 89 | -------------------- 90 | 91 | To build:: 92 | 93 | lein uberjar 94 | 95 | To run the build (stop the backend if you are running it for development to avoid port collisions):: 96 | 97 | cd target 98 | java -jar tudu-0.1.0-SNAPSHOT-standalone.jar 99 | 100 | License 101 | ------- 102 | 103 | Copyright © 2016 marianoguerra 104 | 105 | Distributed under the Eclipse Public License either version 1.0 or (at 106 | your option) any later version. 107 | -------------------------------------------------------------------------------- /src/tudu/ui.cljs: -------------------------------------------------------------------------------- 1 | (ns tudu.ui 2 | (:require 3 | [om.next :as om :refer-macros [defui]] 4 | [cognitect.transit :as transit] 5 | [om.dom :as dom]) 6 | (:import [goog.net XhrIo])) 7 | 8 | (enable-console-print!) 9 | 10 | (def clean-item-editing {:title ""}) 11 | 12 | (def initial-state {:tudu.item/editing clean-item-editing 13 | :tudu/items nil}) 14 | 15 | (defn close-item [c id] 16 | (om/transact! c `[(tudu.item/close {:id ~id}) :tudu/items])) 17 | 18 | (defui TodoItemUI 19 | static om/IQuery 20 | (query [this] 21 | '[:id :status :title]) 22 | static om/Ident 23 | (ident [this {:keys [id]}] 24 | [:tudu.items/by-id id]) 25 | Object 26 | (render [this] 27 | (let [{:keys [id status title]} (om/props this) 28 | closed? (= status :close) 29 | class-name (str "todo-list-item-title status-" (name status))] 30 | (dom/div #js {:className "todo-list-item" :key (str "todo-item-" id)} 31 | (dom/input #js {:type "checkbox" :disabled closed? 32 | :checked closed? 33 | :onClick #(close-item this id)}) 34 | 35 | (dom/span #js {:className class-name} title))))) 36 | 37 | (def todo-item-ui (om/factory TodoItemUI {:keyfn :id})) 38 | 39 | (defn status-to-order [{:keys [status]}] 40 | (if (= status :open) 0 1)) 41 | 42 | (defn compare-todo-items [{a-status :status a-id :id} {b-status :status b-id :id}] 43 | (let [status-compare (compare (status-to-order a-status) 44 | (status-to-order b-status))] 45 | (if (zero? status-compare) 46 | (if (or (om.tempid/tempid? a-id) (om.tempid/tempid? b-id)) 47 | 0 48 | (compare a-id b-id)) 49 | status-compare))) 50 | 51 | (defui TodoListUI 52 | Object 53 | (render [this] 54 | (let [{:keys [tudu/items]} (om/props this)] 55 | (dom/div #js {:className "todo-list-items"} 56 | (map todo-item-ui 57 | (sort-by compare-todo-items items)))))) 58 | 59 | (def todo-list-ui (om/factory TodoListUI)) 60 | 61 | (defn on-change-cb [c callback] 62 | (fn [e] 63 | (let [target (.-target e) 64 | value (.-value target)] 65 | (callback c value)))) 66 | 67 | (defn task-title-change [c text] 68 | (om/transact! c `[(tudu.item.editing/set-title {:value ~text}) 69 | :tudu.item/editing])) 70 | 71 | (defn create-task [c task] 72 | (let [full-task (assoc task :id (om/tempid) :status :open)] 73 | (om/transact! c `[(tudu.item/create {:value ~full-task}) 74 | :tudu.item/editing]))) 75 | 76 | (defui NewTodoItemUI 77 | static om/IQuery 78 | (query [this] 79 | '[:title]) 80 | Object 81 | (render [this] 82 | (let [{:keys [title] :as editing} (om/props this) 83 | on-title-change (on-change-cb this task-title-change)] 84 | (dom/div #js {:className "new-todo-item-form"} 85 | (dom/div #js {:className "form-group"} 86 | (dom/label nil "Task") 87 | (dom/input #js {:value title 88 | :onChange on-title-change})) 89 | (dom/button #js {:className "new-todo-item-create" 90 | :onClick #(create-task this editing)} 91 | "Create"))))) 92 | 93 | (def new-todo-item-ui (om/factory NewTodoItemUI)) 94 | 95 | (defui UI 96 | static om/IQuery 97 | (query [this] 98 | [{:tudu.item/editing (om/get-query NewTodoItemUI)} 99 | {:tudu/items (om/get-query TodoItemUI)}]) 100 | Object 101 | (render [this] 102 | (let [{:keys [tudu.item/editing] :as props} (om/props this)] 103 | (dom/div nil 104 | (new-todo-item-ui editing) 105 | (todo-list-ui props))))) 106 | 107 | (defmulti read om/dispatch) 108 | (defmulti mutate om/dispatch) 109 | 110 | (defmethod read :default [{:keys [state]} key params] 111 | (if-let [[_ value] (find @state key)] 112 | {:value value} 113 | :not-found)) 114 | 115 | (defn get-items [state k] 116 | (let [st @state] 117 | (into [] (map #(get-in st %)) (get st k)))) 118 | 119 | (defmethod read :tudu/items [{:keys [state] :as env} k params] 120 | (let [value (get-items state k)] 121 | {:value value :api true})) 122 | 123 | (defmethod mutate 'tudu.item/close [{:keys [state]} _ {:keys [id]}] 124 | {:api true 125 | :action #(swap! state assoc-in [:tudu.items/by-id id :status] :close)}) 126 | 127 | (defmethod mutate 'tudu.item.editing/set-title [{:keys [state]} _ {:keys [value]}] 128 | {:action #(swap! state assoc-in [:tudu.item/editing :title] value)}) 129 | 130 | (defmethod mutate 'tudu.item/create [{:keys [state]} _ {:keys [value]}] 131 | {:api true 132 | :action (fn [] 133 | (swap! state 134 | (let [{:keys [id]} value] 135 | #(-> % 136 | (assoc :tudu.item/editing clean-item-editing) 137 | (assoc-in [:tudu.items/by-id id] value) 138 | (update :tudu/items 139 | (fn [s] (conj s [:tudu.items/by-id id])))))))}) 140 | 141 | (def reader (om/reader)) 142 | (def writer (om/writer)) 143 | 144 | (defn parse-transit [data] 145 | (transit/read reader data)) 146 | 147 | (defn to-transit [data] 148 | (transit/write writer data)) 149 | 150 | (defn send-post [url data cb] 151 | (.send XhrIo url (fn [resp] 152 | (let [target (.-target resp) 153 | body-raw (.getResponse target) 154 | body (parse-transit body-raw) 155 | status (.getStatus target)] 156 | (cb {:status status :body body}))) 157 | "POST" (to-transit data) 158 | #js {"content-type" "application/transit+json"})) 159 | 160 | (defn send-query [query cb] 161 | (send-post "/query" query cb)) 162 | 163 | (defn send-to-api [{:keys [api] :as remotes} cb] 164 | (send-query api (fn [{:keys [body status]}] 165 | (when (= status 200) 166 | (cb body))))) 167 | 168 | (defn resolve-tempids [state tid->rid] 169 | (clojure.walk/prewalk #(if (om.tempid/tempid? %) (get tid->rid %) %) state)) 170 | 171 | (defn tempid-migrate [pure query tempids id-key] 172 | (if (empty? tempids) 173 | pure 174 | (resolve-tempids pure tempids))) 175 | 176 | (defonce reconciler (om/reconciler 177 | {:state initial-state 178 | :send send-to-api 179 | :remotes [:api] 180 | :id-key :db/id 181 | :migrate tempid-migrate 182 | :parser (om/parser {:read read :mutate mutate})})) 183 | 184 | (defonce root (atom nil)) 185 | 186 | (defn init [] 187 | (if (nil? @root) 188 | (let [target (js/document.getElementById "main-app-area")] 189 | (om/add-root! reconciler UI target) 190 | (reset! root UI)) 191 | (om/force-root-render! reconciler))) 192 | 193 | (init) 194 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC 2 | LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM 3 | CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. 4 | 5 | 1. DEFINITIONS 6 | 7 | "Contribution" means: 8 | 9 | a) in the case of the initial Contributor, the initial code and 10 | documentation distributed under this Agreement, and 11 | 12 | b) in the case of each subsequent Contributor: 13 | 14 | i) changes to the Program, and 15 | 16 | ii) additions to the Program; 17 | 18 | where such changes and/or additions to the Program originate from and are 19 | distributed by that particular Contributor. A Contribution 'originates' from 20 | a Contributor if it was added to the Program by such Contributor itself or 21 | anyone acting on such Contributor's behalf. Contributions do not include 22 | additions to the Program which: (i) are separate modules of software 23 | distributed in conjunction with the Program under their own license 24 | agreement, and (ii) are not derivative works of the Program. 25 | 26 | "Contributor" means any person or entity that distributes the Program. 27 | 28 | "Licensed Patents" mean patent claims licensable by a Contributor which are 29 | necessarily infringed by the use or sale of its Contribution alone or when 30 | combined with the Program. 31 | 32 | "Program" means the Contributions distributed in accordance with this 33 | Agreement. 34 | 35 | "Recipient" means anyone who receives the Program under this Agreement, 36 | including all Contributors. 37 | 38 | 2. GRANT OF RIGHTS 39 | 40 | a) Subject to the terms of this Agreement, each Contributor hereby grants 41 | Recipient a non-exclusive, worldwide, royalty-free copyright license to 42 | reproduce, prepare derivative works of, publicly display, publicly perform, 43 | distribute and sublicense the Contribution of such Contributor, if any, and 44 | such derivative works, in source code and object code form. 45 | 46 | b) Subject to the terms of this Agreement, each Contributor hereby grants 47 | Recipient a non-exclusive, worldwide, royalty-free patent license under 48 | Licensed Patents to make, use, sell, offer to sell, import and otherwise 49 | transfer the Contribution of such Contributor, if any, in source code and 50 | object code form. This patent license shall apply to the combination of the 51 | Contribution and the Program if, at the time the Contribution is added by the 52 | Contributor, such addition of the Contribution causes such combination to be 53 | covered by the Licensed Patents. The patent license shall not apply to any 54 | other combinations which include the Contribution. No hardware per se is 55 | licensed hereunder. 56 | 57 | c) Recipient understands that although each Contributor grants the licenses 58 | to its Contributions set forth herein, no assurances are provided by any 59 | Contributor that the Program does not infringe the patent or other 60 | intellectual property rights of any other entity. Each Contributor disclaims 61 | any liability to Recipient for claims brought by any other entity based on 62 | infringement of intellectual property rights or otherwise. As a condition to 63 | exercising the rights and licenses granted hereunder, each Recipient hereby 64 | assumes sole responsibility to secure any other intellectual property rights 65 | needed, if any. For example, if a third party patent license is required to 66 | allow Recipient to distribute the Program, it is Recipient's responsibility 67 | to acquire that license before distributing the Program. 68 | 69 | d) Each Contributor represents that to its knowledge it has sufficient 70 | copyright rights in its Contribution, if any, to grant the copyright license 71 | set forth in this Agreement. 72 | 73 | 3. REQUIREMENTS 74 | 75 | A Contributor may choose to distribute the Program in object code form under 76 | its own license agreement, provided that: 77 | 78 | a) it complies with the terms and conditions of this Agreement; and 79 | 80 | b) its license agreement: 81 | 82 | i) effectively disclaims on behalf of all Contributors all warranties and 83 | conditions, express and implied, including warranties or conditions of title 84 | and non-infringement, and implied warranties or conditions of merchantability 85 | and fitness for a particular purpose; 86 | 87 | ii) effectively excludes on behalf of all Contributors all liability for 88 | damages, including direct, indirect, special, incidental and consequential 89 | damages, such as lost profits; 90 | 91 | iii) states that any provisions which differ from this Agreement are offered 92 | by that Contributor alone and not by any other party; and 93 | 94 | iv) states that source code for the Program is available from such 95 | Contributor, and informs licensees how to obtain it in a reasonable manner on 96 | or through a medium customarily used for software exchange. 97 | 98 | When the Program is made available in source code form: 99 | 100 | a) it must be made available under this Agreement; and 101 | 102 | b) a copy of this Agreement must be included with each copy of the Program. 103 | 104 | Contributors may not remove or alter any copyright notices contained within 105 | the Program. 106 | 107 | Each Contributor must identify itself as the originator of its Contribution, 108 | if any, in a manner that reasonably allows subsequent Recipients to identify 109 | the originator of the Contribution. 110 | 111 | 4. COMMERCIAL DISTRIBUTION 112 | 113 | Commercial distributors of software may accept certain responsibilities with 114 | respect to end users, business partners and the like. While this license is 115 | intended to facilitate the commercial use of the Program, the Contributor who 116 | includes the Program in a commercial product offering should do so in a 117 | manner which does not create potential liability for other Contributors. 118 | Therefore, if a Contributor includes the Program in a commercial product 119 | offering, such Contributor ("Commercial Contributor") hereby agrees to defend 120 | and indemnify every other Contributor ("Indemnified Contributor") against any 121 | losses, damages and costs (collectively "Losses") arising from claims, 122 | lawsuits and other legal actions brought by a third party against the 123 | Indemnified Contributor to the extent caused by the acts or omissions of such 124 | Commercial Contributor in connection with its distribution of the Program in 125 | a commercial product offering. The obligations in this section do not apply 126 | to any claims or Losses relating to any actual or alleged intellectual 127 | property infringement. In order to qualify, an Indemnified Contributor must: 128 | a) promptly notify the Commercial Contributor in writing of such claim, and 129 | b) allow the Commercial Contributor tocontrol, and cooperate with the 130 | Commercial Contributor in, the defense and any related settlement 131 | negotiations. The Indemnified Contributor may participate in any such claim 132 | at its own expense. 133 | 134 | For example, a Contributor might include the Program in a commercial product 135 | offering, Product X. That Contributor is then a Commercial Contributor. If 136 | that Commercial Contributor then makes performance claims, or offers 137 | warranties related to Product X, those performance claims and warranties are 138 | such Commercial Contributor's responsibility alone. Under this section, the 139 | Commercial Contributor would have to defend claims against the other 140 | Contributors related to those performance claims and warranties, and if a 141 | court requires any other Contributor to pay any damages as a result, the 142 | Commercial Contributor must pay those damages. 143 | 144 | 5. NO WARRANTY 145 | 146 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON 147 | AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER 148 | EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR 149 | CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A 150 | PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the 151 | appropriateness of using and distributing the Program and assumes all risks 152 | associated with its exercise of rights under this Agreement , including but 153 | not limited to the risks and costs of program errors, compliance with 154 | applicable laws, damage to or loss of data, programs or equipment, and 155 | unavailability or interruption of operations. 156 | 157 | 6. DISCLAIMER OF LIABILITY 158 | 159 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY 160 | CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, 161 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION 162 | LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 163 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 164 | ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE 165 | EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY 166 | OF SUCH DAMAGES. 167 | 168 | 7. GENERAL 169 | 170 | If any provision of this Agreement is invalid or unenforceable under 171 | applicable law, it shall not affect the validity or enforceability of the 172 | remainder of the terms of this Agreement, and without further action by the 173 | parties hereto, such provision shall be reformed to the minimum extent 174 | necessary to make such provision valid and enforceable. 175 | 176 | If Recipient institutes patent litigation against any entity (including a 177 | cross-claim or counterclaim in a lawsuit) alleging that the Program itself 178 | (excluding combinations of the Program with other software or hardware) 179 | infringes such Recipient's patent(s), then such Recipient's rights granted 180 | under Section 2(b) shall terminate as of the date such litigation is filed. 181 | 182 | All Recipient's rights under this Agreement shall terminate if it fails to 183 | comply with any of the material terms or conditions of this Agreement and 184 | does not cure such failure in a reasonable period of time after becoming 185 | aware of such noncompliance. If all Recipient's rights under this Agreement 186 | terminate, Recipient agrees to cease use and distribution of the Program as 187 | soon as reasonably practicable. However, Recipient's obligations under this 188 | Agreement and any licenses granted by Recipient relating to the Program shall 189 | continue and survive. 190 | 191 | Everyone is permitted to copy and distribute copies of this Agreement, but in 192 | order to avoid inconsistency the Agreement is copyrighted and may only be 193 | modified in the following manner. The Agreement Steward reserves the right to 194 | publish new versions (including revisions) of this Agreement from time to 195 | time. No one other than the Agreement Steward has the right to modify this 196 | Agreement. The Eclipse Foundation is the initial Agreement Steward. The 197 | Eclipse Foundation may assign the responsibility to serve as the Agreement 198 | Steward to a suitable separate entity. Each new version of the Agreement will 199 | be given a distinguishing version number. The Program (including 200 | Contributions) may always be distributed subject to the version of the 201 | Agreement under which it was received. In addition, after a new version of 202 | the Agreement is published, Contributor may elect to distribute the Program 203 | (including its Contributions) under the new version. Except as expressly 204 | stated in Sections 2(a) and 2(b) above, Recipient receives no rights or 205 | licenses to the intellectual property of any Contributor under this 206 | Agreement, whether expressly, by implication, estoppel or otherwise. All 207 | rights in the Program not expressly granted under this Agreement are 208 | reserved. 209 | 210 | This Agreement is governed by the laws of the State of New York and the 211 | intellectual property laws of the United States of America. No party to this 212 | Agreement will bring a legal action under this Agreement more than one year 213 | after the cause of action arose. Each party waives its rights to a jury trial 214 | in any resulting litigation. 215 | --------------------------------------------------------------------------------