|
10 | 10 | [clj-commons.primitive-math :as p]
|
11 | 11 | [potemkin :as potemkin :refer [doit doary]])
|
12 | 12 | (:import
|
| 13 | + [clojure.lang DynamicClassLoader] |
13 | 14 | [java.io IOException]
|
14 | 15 | [io.netty.bootstrap Bootstrap ServerBootstrap]
|
15 | 16 | [io.netty.buffer ByteBuf Unpooled]
|
|
55 | 56 | FastThreadLocalThread GenericFutureListener Future]
|
56 | 57 | [java.io InputStream File]
|
57 | 58 | [java.nio ByteBuffer]
|
| 59 | + [java.nio.channels ClosedChannelException] |
58 | 60 | [io.netty.util.internal SystemPropertyUtil]
|
59 | 61 | [java.util.concurrent
|
60 | 62 | ConcurrentHashMap
|
|
199 | 201 | (->> (bs/convert x (bs/stream-of ByteBuf) {:chunk-size chunk-size})
|
200 | 202 | (s/onto nil)))
|
201 | 203 |
|
| 204 | +(defn ensure-dynamic-classloader |
| 205 | + "Ensure the context class loader has a valid loader chain to |
| 206 | + prevent `ClassNotFoundException`. |
| 207 | + https://github.com/clj-commons/aleph/issues/603." |
| 208 | + [] |
| 209 | + (let [thread (Thread/currentThread) |
| 210 | + context-class-loader (.getContextClassLoader thread) |
| 211 | + compiler-class-loader (.getClassLoader clojure.lang.Compiler)] |
| 212 | + (when-not (instance? DynamicClassLoader context-class-loader) |
| 213 | + (.setContextClassLoader |
| 214 | + thread |
| 215 | + (DynamicClassLoader. (or context-class-loader |
| 216 | + compiler-class-loader)))))) |
| 217 | + |
| 218 | +(defn- operation-complete [^Future f d] |
| 219 | + (cond |
| 220 | + (.isSuccess f) |
| 221 | + (d/success! d (.getNow f)) |
| 222 | + |
| 223 | + (.isCancelled f) |
| 224 | + (d/error! d (CancellationException. "future is cancelled.")) |
| 225 | + |
| 226 | + (some? (.cause f)) |
| 227 | + (if (instance? ClosedChannelException (.cause f)) |
| 228 | + (d/success! d false) |
| 229 | + (d/error! d (.cause f))) |
| 230 | + |
| 231 | + :else |
| 232 | + (d/error! d (IllegalStateException. "future in unknown state")))) |
| 233 | + |
202 | 234 | (defn wrap-future
|
203 | 235 | [^Future f]
|
204 | 236 | (when f
|
205 | 237 | (if (.isSuccess f)
|
206 | 238 | (d/success-deferred (.getNow f) nil)
|
207 | 239 | (let [d (d/deferred nil)
|
208 |
| - ;; workaround for the issue with executing RT.readString |
209 |
| - ;; on a Netty thread that doesn't have appropriate class loader |
210 |
| - ;; more information here: |
211 |
| - ;; https://github.com/clj-commons/aleph/issues/365 |
212 |
| - class-loader (or (clojure.lang.RT/baseLoader) |
213 |
| - (clojure.lang.RT/makeClassLoader))] |
| 240 | + ;; Ensure the same bindings are installed on the Netty thread (vars, |
| 241 | + ;; classloader) than the thread registering the |
| 242 | + ;; `operationComplete` callback. |
| 243 | + bound-operation-complete (bound-fn* operation-complete)] |
214 | 244 | (.addListener f
|
215 | 245 | (reify GenericFutureListener
|
216 | 246 | (operationComplete [_ _]
|
217 |
| - (try |
218 |
| - (. clojure.lang.Var |
219 |
| - (pushThreadBindings {clojure.lang.Compiler/LOADER |
220 |
| - class-loader})) |
221 |
| - (cond |
222 |
| - (.isSuccess f) |
223 |
| - (d/success! d (.getNow f)) |
224 |
| - |
225 |
| - (.isCancelled f) |
226 |
| - (d/error! d (CancellationException. "future is cancelled.")) |
227 |
| - |
228 |
| - (some? (.cause f)) |
229 |
| - (if (instance? java.nio.channels.ClosedChannelException (.cause f)) |
230 |
| - (d/success! d false) |
231 |
| - (d/error! d (.cause f))) |
232 |
| - |
233 |
| - :else |
234 |
| - (d/error! d (IllegalStateException. "future in unknown state"))) |
235 |
| - (finally |
236 |
| - (. clojure.lang.Var (popThreadBindings))))))) |
| 247 | + (ensure-dynamic-classloader) |
| 248 | + (bound-operation-complete f d)))) |
237 | 249 | d))))
|
238 | 250 |
|
239 | 251 | (defn allocate [x]
|
|
0 commit comments