Skip to content

Commit

Permalink
Exposing ability to mount servlets at sub-context-paths [IMMUTANT-253]
Browse files Browse the repository at this point in the history
We no longer use RingServlet, but either a reified Servlet instance
wrapping a Ring handler or a proxied Servlet that sets the TCCL
appropriately before invoking the servlet's service method.

This is to support the mounting of Pedestal servlets comprised of
multiple "interceptors" rather than a single Ring handler.
  • Loading branch information
jcrossley3 committed Mar 25, 2013
1 parent 18843bb commit eb02bac
Show file tree
Hide file tree
Showing 12 changed files with 143 additions and 245 deletions.
6 changes: 3 additions & 3 deletions modules/core/src/main/clojure/immutant/runtime.clj
Expand Up @@ -63,11 +63,11 @@ bootstrapping process. Applications shouldn't use anything here."
[] []
(let [project (registry/get :project)] (let [project (registry/get :project)]
(when-let [handler (get-in project [:ring :handler])] (when-let [handler (get-in project [:ring :handler])]
(require-and-invoke "immutant.web/start*" (require-and-invoke "immutant.web/start-handler"
"/" "/"
(util/try-resolve handler) (util/try-resolve handler)
{:init (util/try-resolve (get-in project [:ring :init])) :init (util/try-resolve (get-in project [:ring :init]))
:destroy (util/try-resolve (get-in project [:ring :destroy]))}) :destroy (util/try-resolve (get-in project [:ring :destroy])))
(log/info "Initialized" (util/app-name) "from :ring options in project.clj") (log/info "Initialized" (util/app-name) "from :ring options in project.clj")
true))) true)))


Expand Down
2 changes: 1 addition & 1 deletion modules/core/src/test/clojure/immutant/runtime_test.clj
Expand Up @@ -84,7 +84,7 @@
:destroy guestbook.handler/destroy}}) :destroy guestbook.handler/destroy}})
(try (try
(initialize nil nil) (initialize nil nil)
(let [[path handler {:keys [init destroy]}] @a-value] (let [[path handler & {:keys [init destroy]}] @a-value]
(is (= "/" path)) (is (= "/" path))
(is (= "war-handler" (handler))) (is (= "war-handler" (handler)))
(is (= "init" (init))) (is (= "init" (init)))
Expand Down
@@ -1,4 +1,4 @@
(ns immutant.web) (ns immutant.web)


(defn start* [& args] (defn start-handler [& args]
(reset! immutant.runtime-test/a-value args)) (reset! immutant.runtime-test/a-value args))
81 changes: 30 additions & 51 deletions modules/web/src/main/clojure/immutant/web.clj
Expand Up @@ -19,21 +19,28 @@
"Associate one or more Ring handlers with your application, mounted "Associate one or more Ring handlers with your application, mounted
at unique context paths" at unique context paths"
(:require [clojure.tools.logging :as log] (:require [clojure.tools.logging :as log]
[immutant.util :as util]
[ring.util.codec :as codec] [ring.util.codec :as codec]
[ring.util.response :as response]) [ring.util.response :as response]
(:use [immutant.web.internal :exclude [current-servlet-request]]) [immutant.web.servlet :as servlet])
(:use [immutant.web.middleware :only [add-middleware]]) (:use [immutant.web.internal :only [start* stop*]]
[immutant.web.middleware :only [add-middleware]])
(:import javax.servlet.http.HttpServletRequest)) (:import javax.servlet.http.HttpServletRequest))


(declare stop start*) (defn start-servlet
"Can be used to mount a servlet in lieu of a typical Ring handler"
[sub-context-path servlet]
(log/info "Registering servlet at sub-context path:" sub-context-path)
(start* sub-context-path
(servlet/proxy-servlet servlet)
{}))


(defn ^HttpServletRequest current-servlet-request (defn start-handler
"Returns the currently active HttpServletRequest. This will only "Typically not called directly; use start instead"
return a value within an active ring handler. Standard ring handlers [sub-context-path handler & {:keys [init destroy] :as opts}]
should never need to access this value." (log/info "Registering ring handler at sub-context path:" sub-context-path)
[] (start* sub-context-path
immutant.web.internal/current-servlet-request) (servlet/create-servlet (add-middleware handler opts))
opts))


(defmacro start (defmacro start
"Registers a Ring handler that will be called when requests "Registers a Ring handler that will be called when requests
Expand All @@ -51,53 +58,25 @@
(let [[path args] (if (even? (count args)) (let [[path args] (if (even? (count args))
[(first args) (next args)] [(first args) (next args)]
["/" args]) ["/" args])
[handler & {:as opts}] args] [handler & opts] args]
(if (symbol? handler) (if (symbol? handler)
`(start* ~path (var ~handler) ~opts) `(start-handler ~path (var ~handler) ~@opts)
`(start* ~path ~handler ~opts)))) `(start-handler ~path ~handler ~@opts))))

(defn ^{:no-doc true} start*
[sub-context-path handler {:keys [init destroy] :as opts}]
(util/if-in-immutant
(let [handler (add-middleware handler opts)
sub-context-path (normalize-subcontext-path sub-context-path)
servlet-name (servlet-name sub-context-path)]
(if-let [existing-info (get-servlet-info servlet-name)]
(do
(log/debug "Updating ring handler at sub-context path:" sub-context-path)
(store-servlet-info!
servlet-name
(assoc existing-info :handler handler)))
(do
(log/info "Registering ring handler at sub-context path:" sub-context-path)
(store-servlet-info!
servlet-name
{:wrapper (install-servlet "org.immutant.web.servlet.RingServlet"
sub-context-path)
:sub-context sub-context-path
:handler handler
:destroy destroy})
(util/at-exit #(stop sub-context-path))
(and init (init))))
nil)
(log/warn "web/start called outside of Immutant, ignoring")))



(defn stop (defn stop
"Deregisters the Ring handler attached to the given sub-context-path. "Deregisters the Ring handler or servlet mounted at the given sub-context-path.
If no sub-context-path is given, \"/\" is assumed." If no sub-context-path is given, \"/\" is assumed."
([] ([]
(stop "/")) (stop "/"))
([sub-context-path] ([sub-context-path]
(util/if-in-immutant (stop* sub-context-path)))
(let [sub-context-path (normalize-subcontext-path sub-context-path)]
(if-let [{:keys [wrapper destroy]} (remove-servlet-info! (servlet-name sub-context-path))] (defn ^HttpServletRequest current-servlet-request
(do "Returns the currently active HttpServletRequest. This will only
(log/info "Deregistering ring handler at sub-context path:" sub-context-path) return a value within an active ring handler. Standard ring handlers
(remove-servlet sub-context-path wrapper) should never need to access this value."
(and destroy (destroy))) []
(log/warn "Attempted to deregister ring handler at sub-context path:" sub-context-path ", but none found"))) immutant.web.internal/current-servlet-request)
(log/warn "web/stop called outside of Immutant, ignoring"))))


(defn wrap-resource (defn wrap-resource
"Temporary workaround for its non-context-aware namesake from "Temporary workaround for its non-context-aware namesake from
Expand Down
30 changes: 29 additions & 1 deletion modules/web/src/main/clojure/immutant/web/internal.clj
Expand Up @@ -76,20 +76,22 @@
vh vh
[vh]))) [vh])))


(defn install-servlet [servlet-class sub-context-path] (defn install-servlet [servlet sub-context-path]
(let [context (registry/get "web-context") (let [context (registry/get "web-context")
name (servlet-name sub-context-path) name (servlet-name sub-context-path)
wrapper (.createWrapper context) wrapper (.createWrapper context)
mapper (-> (registry/get "jboss.web") mapper (-> (registry/get "jboss.web")
(.getService) (.getService)
(.getMapper)) (.getMapper))
servlet-class (.getName (class servlet))
complete (promise)] complete (promise)]
(if (and (if (and
(when-context-available (when-context-available
context context
(doto wrapper (doto wrapper
(.setName name) (.setName name)
(.setServletClass servlet-class) (.setServletClass servlet-class)
(.setServlet servlet)
(.setEnabled true) (.setEnabled true)
(.setDynamic true) (.setDynamic true)
(.setAsyncSupported true) (.setAsyncSupported true)
Expand Down Expand Up @@ -120,5 +122,31 @@
(catch Exception _))))) (catch Exception _)))))
nil) nil)


(defn stop*
"Deregisters the Ring handler attached to the given sub-context-path."
[sub-context-path]
(util/if-in-immutant
(let [sub-context-path (normalize-subcontext-path sub-context-path)]
(if-let [{:keys [wrapper destroy]} (remove-servlet-info! (servlet-name sub-context-path))]
(do
(log/info "Deregistering ring handler at sub-context path:" sub-context-path)
(remove-servlet sub-context-path wrapper)
(and destroy (destroy)))
(log/warn "Attempted to deregister ring handler at sub-context path:" sub-context-path ", but none found")))
(log/warn "web/stop called outside of Immutant, ignoring")))


(defn start*
[sub-context-path servlet {:keys [init destroy] :as opts}]
(util/if-in-immutant
(let [sub-context-path (normalize-subcontext-path sub-context-path)
servlet-name (servlet-name sub-context-path)]
(when (get-servlet-info servlet-name)
(stop* sub-context-path))
(store-servlet-info!
servlet-name
{:wrapper (install-servlet servlet sub-context-path)
:destroy destroy})
(util/at-exit #(stop* sub-context-path))
(and init (init)))
(log/warn "web/start called outside of Immutant, ignoring")))


50 changes: 0 additions & 50 deletions modules/web/src/main/clojure/immutant/web/ring.clj

This file was deleted.

65 changes: 65 additions & 0 deletions modules/web/src/main/clojure/immutant/web/servlet.clj
@@ -0,0 +1,65 @@
;; Copyright 2008-2013 Red Hat, Inc, and individual contributors.
;;
;; This is free software; you can redistribute it and/or modify it
;; under the terms of the GNU Lesser General Public License as
;; published by the Free Software Foundation; either version 2.1 of
;; the License, or (at your option) any later version.
;;
;; This software is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
;; Lesser General Public License for more details.
;;
;; You should have received a copy of the GNU Lesser General Public
;; License along with this software; if not, write to the Free
;; Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
;; 02110-1301 USA, or see the FSF site: http://www.fsf.org.

(ns ^{:no-doc true} immutant.web.servlet
(:use [immutant.web.internal :only [current-servlet-request]]
[immutant.util :only [with-tccl]])
(:require [ring.util.servlet :as servlet])
(:import javax.servlet.http.HttpServletRequest))

(defn- context [^HttpServletRequest request]
(str (.getContextPath request)
(.getServletPath request)))

;; we don't use .getPathInfo since it is decoded. See IMMUTANT-195
(defn- path-info [^HttpServletRequest request]
(let [path-info (.substring (.getRequestURI request)
(.length (context request)))]
(if (.isEmpty path-info)
"/"
path-info)))

(defn create-servlet [handler]
(reify javax.servlet.Servlet
(service [_ request response]
(with-tccl
(.setCharacterEncoding response "UTF-8")

This comment has been minimized.

Copy link
@vizanto

vizanto May 13, 2013

Why?
This makes my image proxying code return: Content-Type:image/jpeg;charset=UTF-8

This comment has been minimized.

Copy link
@jcrossley3

jcrossley3 May 13, 2013

Author Member

We can't think of a good reason, actually. :)
Testing its removal now.

(if-let [response-map (binding [current-servlet-request request]
(handler
(assoc (servlet/build-request-map request)
:context (context request)
:path-info (path-info request))))]
(servlet/update-servlet-response response response-map)
(throw (NullPointerException. "Handler returned nil.")))))
(init [_ _])
(destroy [_])))

(deftype ServletProxy [servlet]
javax.servlet.Servlet
(init [_ config]
(with-tccl (.init servlet config)))
(service [_ request response]
(with-tccl (.service servlet request response)))
(destroy [_]
(.destroy servlet))
(getServletConfig [_]
(.getServletConfig servlet))
(getServletInfo [_]
(.getServletInfo servlet)))

(defn proxy-servlet [servlet]
(ServletProxy. servlet))
Expand Up @@ -26,7 +26,6 @@
import java.util.List; import java.util.List;


import org.immutant.web.ring.processors.RingApplicationRecognizer; import org.immutant.web.ring.processors.RingApplicationRecognizer;
import org.immutant.web.ring.processors.RingServletClojureRuntimeInstaller;
import org.immutant.web.ring.processors.RingWebApplicationInstaller; import org.immutant.web.ring.processors.RingWebApplicationInstaller;
import org.immutant.web.ring.processors.WebContextRegisteringProcessor; import org.immutant.web.ring.processors.WebContextRegisteringProcessor;


Expand Down Expand Up @@ -71,7 +70,6 @@ protected void addDeploymentProcessors(final DeploymentProcessorTarget processor


processorTarget.addDeploymentProcessor( WebExtension.SUBSYSTEM_NAME, Phase.DEPENDENCIES, 1, new WebDependenciesProcessor() ); processorTarget.addDeploymentProcessor( WebExtension.SUBSYSTEM_NAME, Phase.DEPENDENCIES, 1, new WebDependenciesProcessor() );


processorTarget.addDeploymentProcessor( WebExtension.SUBSYSTEM_NAME, Phase.INSTALL, 1, new RingServletClojureRuntimeInstaller() );
processorTarget.addDeploymentProcessor( WebExtension.SUBSYSTEM_NAME, Phase.INSTALL, 2100, new VirtualHostInstaller() ); processorTarget.addDeploymentProcessor( WebExtension.SUBSYSTEM_NAME, Phase.INSTALL, 2100, new VirtualHostInstaller() );
processorTarget.addDeploymentProcessor( WebExtension.SUBSYSTEM_NAME, Phase.INSTALL, Phase.INSTALL_WAR_DEPLOYMENT + 1, new WebContextRegisteringProcessor() ); processorTarget.addDeploymentProcessor( WebExtension.SUBSYSTEM_NAME, Phase.INSTALL, Phase.INSTALL_WAR_DEPLOYMENT + 1, new WebContextRegisteringProcessor() );
} }
Expand Down

This file was deleted.

0 comments on commit eb02bac

Please sign in to comment.