Skip to content

Commit

Permalink
Merge pull request #1 from felipefinhane/master
Browse files Browse the repository at this point in the history
feat: Implement Clojure Project
  • Loading branch information
rpagliuca committed Aug 21, 2019
2 parents 66571c1 + 2406518 commit 329fbdd
Show file tree
Hide file tree
Showing 9 changed files with 379 additions and 0 deletions.
38 changes: 38 additions & 0 deletions clojure-pedestal/.gitignore
@@ -0,0 +1,38 @@
# Project related

# Java related
pom.xml
pom.xml.asc
*jar
*.class

# Leiningen
classes/
lib/
native/
checkouts/
target/
.lein-*
repl-port
.nrepl-port
.repl

# Temp Files
*.orig
*~
.*.swp
.*.swo
*.tmp
*.bak

# OS X
.DS_Store

# Logging
*.log
/logs/

# Builds
out/
build/

29 changes: 29 additions & 0 deletions clojure-pedestal/Capstanfile
@@ -0,0 +1,29 @@

#
# Name of the base image. Capstan will download this automatically from
# Cloudius S3 repository.
#
#base: cloudius/osv
base: cloudius/osv-openjdk8

#
# The command line passed to OSv to start up the application.
#
#cmdline: /java.so -cp /hello-api-clojure/app.jar clojure.main -m hello-api-clojure
cmdline: /java.so -jar /hello-api-clojure/app.jar

#
# The command to use to build the application.
# You can use any build tool/command (make/rake/lein/boot) - this runs locally on your machine
#
# For Leiningen, you can use:
#build: lein uberjar
# For Boot, you can use:
#build: boot build

#
# List of files that are included in the generated image.
#
files:
/hello-api-clojure/app.jar: ./target/hello-api-clojure-0.0.1-SNAPSHOT-standalone.jar

8 changes: 8 additions & 0 deletions clojure-pedestal/Dockerfile
@@ -0,0 +1,8 @@
FROM java:8-alpine
MAINTAINER Your Name <you@example.com>

ADD target/hello-api-clojure-0.0.1-SNAPSHOT-standalone.jar /hello-api-clojure/app.jar

EXPOSE 8080

CMD ["java", "-jar", "/hello-api-clojure/app.jar"]
45 changes: 45 additions & 0 deletions clojure-pedestal/README.md
@@ -0,0 +1,45 @@
# hello-api-clojure

FIXME

## Getting Started

1. Start the application: `lein run`
2. Go to [localhost:8080](http://localhost:8080/) to see: `Hello World!`
3. Read your app's source code at src/hello_api_clojure/service.clj. Explore the docs of functions
that define routes and responses.
4. Run your app's tests with `lein test`. Read the tests at test/hello_api_clojure/service_test.clj.
5. Learn more! See the [Links section below](#links).


## Configuration

To configure logging see config/logback.xml. By default, the app logs to stdout and logs/.
To learn more about configuring Logback, read its [documentation](http://logback.qos.ch/documentation.html).


## Developing your service

1. Start a new REPL: `lein repl`
2. Start your service in dev-mode: `(def dev-serv (run-dev))`
3. Connect your editor to the running REPL session.
Re-evaluated code will be seen immediately in the service.

### [Docker](https://www.docker.com/) container support

1. Configure your service to accept incoming connections (edit service.clj and add ::http/host "0.0.0.0" )
2. Build an uberjar of your service: `lein uberjar`
3. Build a Docker image: `sudo docker build -t hello-api-clojure .`
4. Run your Docker image: `docker run -p 8080:8080 hello-api-clojure`

### [OSv](http://osv.io/) unikernel support with [Capstan](http://osv.io/capstan/)

1. Build and run your image: `capstan run -f "8080:8080"`

Once the image it built, it's cached. To delete the image and build a new one:

1. `capstan rmi hello-api-clojure; capstan build`


## Links
* [Other Pedestal examples](http://pedestal.io/samples)
52 changes: 52 additions & 0 deletions clojure-pedestal/config/logback.xml
@@ -0,0 +1,52 @@
<!-- Logback configuration. See http://logback.qos.ch/manual/index.html -->
<!-- Scanning is currently turned on; This will impact performance! -->
<configuration scan="true" scanPeriod="10 seconds">
<!-- Silence Logback's own status messages about config parsing
<statusListener class="ch.qos.logback.core.status.NopStatusListener" /> -->

<!-- Simple file output -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- encoder defaults to ch.qos.logback.classic.encoder.PatternLayoutEncoder -->
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} %X{io.pedestal} - %msg%n</pattern>
</encoder>

<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!-- rollover daily -->
<fileNamePattern>logs/hello-api-clojure-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<!-- or whenever the file size reaches 64 MB -->
<maxFileSize>64 MB</maxFileSize>
</rollingPolicy>

<!-- Safely log to the same file from multiple JVMs. Degrades performance! -->
<prudent>true</prudent>
</appender>


<!-- Console output -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<!-- encoder defaults to ch.qos.logback.classic.encoder.PatternLayoutEncoder -->
<encoder>
<pattern>%-5level %logger{36} %X{io.pedestal} - %msg%n</pattern>
</encoder>
<!-- Only log level INFO and above -->
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>INFO</level>
</filter>
</appender>


<!-- Enable FILE and STDOUT appenders for all log messages.
By default, only log at level INFO and above. -->
<root level="INFO">
<appender-ref ref="FILE" />
<appender-ref ref="STDOUT" />
</root>

<!-- For loggers in the these namespaces, log at all levels. -->
<logger name="user" level="ALL" />
<!-- To log pedestal internals, enable this and change ThresholdFilter to DEBUG
<logger name="io.pedestal" level="ALL" />
-->

</configuration>
26 changes: 26 additions & 0 deletions clojure-pedestal/project.clj
@@ -0,0 +1,26 @@
(defproject hello-api-clojure "0.0.1-SNAPSHOT"
:description "FIXME: write description"
:url "http://example.com/FIXME"
:license {:name "Eclipse Public License"
:url "http://www.eclipse.org/legal/epl-v10.html"}
:dependencies [[org.clojure/clojure "1.10.1"]
[io.pedestal/pedestal.service "0.5.7"]

;; Remove this line and uncomment one of the next lines to
;; use Immutant or Tomcat instead of Jetty:
[io.pedestal/pedestal.jetty "0.5.7"]
;; [io.pedestal/pedestal.immutant "0.5.7"]
;; [io.pedestal/pedestal.tomcat "0.5.7"]

[ch.qos.logback/logback-classic "1.2.3" :exclusions [org.slf4j/slf4j-api]]
[org.slf4j/jul-to-slf4j "1.7.26"]
[org.slf4j/jcl-over-slf4j "1.7.26"]
[org.slf4j/log4j-over-slf4j "1.7.26"]]
:min-lein-version "2.0.0"
:resource-paths ["config", "resources"]
;; If you use HTTP/2 or ALPN, use the java-agent to pull in the correct alpn-boot dependency
;:java-agents [[org.mortbay.jetty.alpn/jetty-alpn-agent "2.0.5"]]
:profiles {:dev {:aliases {"run-dev" ["trampoline" "run" "-m" "hello-api-clojure.server/run-dev"]}
:dependencies [[io.pedestal/pedestal.service-tools "0.5.7"]]}
:uberjar {:aot [hello-api-clojure.server]}}
:main ^{:skip-aot true} hello-api-clojure.server)
56 changes: 56 additions & 0 deletions clojure-pedestal/src/hello_api_clojure/server.clj
@@ -0,0 +1,56 @@
(ns hello-api-clojure.server
(:gen-class) ; for -main method in uberjar
(:require [io.pedestal.http :as server]
[io.pedestal.http.route :as route]
[hello-api-clojure.service :as service]))

;; This is an adapted service map, that can be started and stopped
;; From the REPL you can call server/start and server/stop on this service
(defonce runnable-service (server/create-server service/service))

(defn run-dev
"The entry-point for 'lein run-dev'"
[& args]
(println "\nCreating your [DEV] server...")
(-> service/service ;; start with production configuration
(merge {:env :dev
;; do not block thread that starts web server
::server/join? false
;; Routes can be a function that resolve routes,
;; we can use this to set the routes to be reloadable
::server/routes #(route/expand-routes (deref #'service/routes))
;; all origins are allowed in dev mode
::server/allowed-origins {:creds true :allowed-origins (constantly true)}
;; Content Security Policy (CSP) is mostly turned off in dev mode
::server/secure-headers {:content-security-policy-settings {:object-src "'none'"}}})
;; Wire up interceptor chains
server/default-interceptors
server/dev-interceptors
server/create-server
server/start))

(defn -main
"The entry-point for 'lein run'"
[& args]
(println "\nCreating your server...")
(server/start runnable-service))

;; If you package the service up as a WAR,
;; some form of the following function sections is required (for io.pedestal.servlet.ClojureVarServlet).

;;(defonce servlet (atom nil))
;;
;;(defn servlet-init
;; [_ config]
;; ;; Initialize your app here.
;; (reset! servlet (server/servlet-init service/service nil)))
;;
;;(defn servlet-service
;; [_ request response]
;; (server/servlet-service @servlet request response))
;;
;;(defn servlet-destroy
;; [_]
;; (server/servlet-destroy @servlet)
;; (reset! servlet nil))

86 changes: 86 additions & 0 deletions clojure-pedestal/src/hello_api_clojure/service.clj
@@ -0,0 +1,86 @@
(ns hello-api-clojure.service
(:require [io.pedestal.http :as http]
[io.pedestal.http.route :as route]
[io.pedestal.http.body-params :as body-params]
[ring.util.response :as ring-resp]))

(defn about-page
[request]
(ring-resp/response (format "Clojure %s - served from %s"
(clojure-version)
(route/url-for ::about-page))))

(defn home-page
[request]
(ring-resp/response "Hello World!"))

(defn get-json
[request]
(ring-resp/response {:foo "foo" :list [1 2 3 4]}))

;; Defines "/" and "/about" routes with their associated :get handlers.
;; The interceptors defined after the verb map (e.g., {:get home-page}
;; apply to / and its children (/about).
(def common-interceptors [(body-params/body-params) http/html-body])

;; Tabular routes
(def routes #{["/" :get (conj common-interceptors `home-page)]
["/about" :get (conj common-interceptors `about-page)]})

;; Map-based routes
;(def routes `{"/" {:interceptors [(body-params/body-params) http/html-body]
; :get home-page
; "/about" {:get about-page}}})

;; Terse/Vector-based routes
;(def routes
; `[[["/" {:get home-page}
; ^:interceptors [(body-params/body-params) http/html-body]
; ["/about" {:get about-page}]]]])


;; Consumed by hello-api-clojure.server/create-server
;; See http/default-interceptors for additional options you can configure
(def service {:env :prod
;; You can bring your own non-default interceptors. Make
;; sure you include routing and set it up right for
;; dev-mode. If you do, many other keys for configuring
;; default interceptors will be ignored.
;; ::http/interceptors []
::http/routes routes

;; Uncomment next line to enable CORS support, add
;; string(s) specifying scheme, host and port for
;; allowed source(s):
;;
;; "http://localhost:8080"
;;
;;::http/allowed-origins ["scheme://host:port"]

;; Tune the Secure Headers
;; and specifically the Content Security Policy appropriate to your service/application
;; For more information, see: https://content-security-policy.com/
;; See also: https://github.com/pedestal/pedestal/issues/499
;;::http/secure-headers {:content-security-policy-settings {:object-src "'none'"
;; :script-src "'unsafe-inline' 'unsafe-eval' 'strict-dynamic' https: http:"
;; :frame-ancestors "'none'"}}

;; Root for resource interceptor that is available by default.
::http/resource-path "/public"

;; Either :jetty, :immutant or :tomcat (see comments in project.clj)
;; This can also be your own chain provider/server-fn -- http://pedestal.io/reference/architecture-overview#_chain_provider
::http/type :jetty
;;::http/host "localhost"
::http/port 8080
;; Options to pass to the container (Jetty)
::http/container-options {:h2c? true
:h2? false
;:keystore "test/hp/keystore.jks"
;:key-password "password"
;:ssl-port 8443
:ssl? false
;; Alternatively, You can specify you're own Jetty HTTPConfiguration
;; via the `:io.pedestal.http.jetty/http-configuration` container option.
;:io.pedestal.http.jetty/http-configuration (org.eclipse.jetty.server.HttpConfiguration.)
}})
39 changes: 39 additions & 0 deletions clojure-pedestal/test/hello_api_clojure/service_test.clj
@@ -0,0 +1,39 @@
(ns hello-api-clojure.service-test
(:require [clojure.test :refer :all]
[io.pedestal.test :refer :all]
[io.pedestal.http :as bootstrap]
[hello-api-clojure.service :as service]))

(def service
(::bootstrap/service-fn (bootstrap/create-servlet service/service)))

(deftest home-page-test
(is (=
(:body (response-for service :get "/"))
"Hello World!"))
(is (=
(:headers (response-for service :get "/"))
{"Content-Type" "text/html;charset=UTF-8"
"Strict-Transport-Security" "max-age=31536000; includeSubdomains"
"X-Frame-Options" "DENY"
"X-Content-Type-Options" "nosniff"
"X-XSS-Protection" "1; mode=block"
"X-Download-Options" "noopen"
"X-Permitted-Cross-Domain-Policies" "none"
"Content-Security-Policy" "object-src 'none'; script-src 'unsafe-inline' 'unsafe-eval' 'strict-dynamic' https: http:;"})))

(deftest about-page-test
(is (.contains
(:body (response-for service :get "/about"))
"Clojure 1.9"))
(is (=
(:headers (response-for service :get "/about"))
{"Content-Type" "text/html;charset=UTF-8"
"Strict-Transport-Security" "max-age=31536000; includeSubdomains"
"X-Frame-Options" "DENY"
"X-Content-Type-Options" "nosniff"
"X-XSS-Protection" "1; mode=block"
"X-Download-Options" "noopen"
"X-Permitted-Cross-Domain-Policies" "none"
"Content-Security-Policy" "object-src 'none'; script-src 'unsafe-inline' 'unsafe-eval' 'strict-dynamic' https: http:;"})))

0 comments on commit 329fbdd

Please sign in to comment.