Programming Anti-Patterns: Releasing Cthulhu
Some code is elegant. Some code is a quick fix hack.
Some code is maintainable. Some code release Cthulhu.
These are just a few gems I have found over the past year or so…
Math is for the Weak Pattern
if(result == 1) { obj.foo(0); } else if(result == 2) { obj.foo(1); } else if(result == 3) { obj.foo(2); } else if(result == 4) { obj.foo(3); }
Subtraction is a poor abstraction;
Scare the Next Guy Pattern
// ... Base DAO containing transaction logic for whole application ... if(connection.isOpen()) { aSimple.MethodCall(); // TODO: } // ... more complex TX logic ...
Because, what he doesn’t know … might be nothing … or might be something
Decoy Security Pattern
<pre>public Connection getNewConnection() { Long.Full.Package.Name.Connection c = new Long.Full.Package.Name.Connection(); return new Long.Full.Package.Name.Connection(); }
No cups in this shell game, just database connections
Missing Father Pattern
try { foo.bar(); } catch(Exception e) { throw e; }
If dad is never around, you end up playing catch with yourself
All codes have been modified and rewritten to protect the guilty.
Start Tomcat from the REPL
One of the first things that I wrote in Clojure was a few files that launched a tomcat server from a repl. My code was separated into 3 files:
tomcat.clj
created a Tomcat server through theEmbedded
class, which you could then start and stop from the repltomcat_loader.clj
which handled separating classloaders for tomcat itself and the webapps deployed to itclassloading.clj
which held generic utility functions for creating class loaders and searching file paths
Creating an embedded tomcat engine is an excercise in Clojure-Java iterop:
(defn create-embedded-tomcat [#^String hostname app-base contexts]
(let [embedded (Embedded.)
engine (.createEngine embedded)
host (.createHost embedded hostname app-base)
http-connector (.createConnector embedded hostname 8080 "http")
ssl-connector (.createConnector embedded hostname 8443 true)]
(.setParentClassLoader engine tomcat-shared-loader)
(doall (map #(% embedded host) contexts))
(.addChild engine host)
(.addEngine embedded engine)
(.addConnector embedded http-connector)
(.addConnector embedded ssl-connector)
embedded))
app-base
is a file path from which Tomcat will serve webapps out of, it’s webapps/
in the standalone distribution, but creating your own engine, you can now set it to anywhere you’d like. contexts
is a list of Context
objects, applications that you want Tomcat to serve that might not be in the app-base folder. I wrote a helper fn to create then:
(defn context [path war-file]
(fn [embedded host]
(let [context (.createContext embedded path war-file)]
(.addChild host context))))
(context "/myapp" "/home/awesomesauce/dinosaur-app.war")
The Embedded
object that create-embedded-tomcat
returns has .start
and .stop
methods on it, so no need for special Clojure fns for them, just invoke the methods directly on the object.
The classloading magic happens in tomcat.clj
‘s ns
header. It :uses tomcat_loader.clj
which creates 3 classloaders:
(def thread-context-loader (.. (Thread/currentThread) getContextClassLoader))
(def tomcat-common-loader (create-class-loader (expand-paths tomcat-common-path) thread-context-loader))
(def tomcat-catalina-loader (create-class-loader (expand-paths tomcat-catalina-path) tomcat-common-loader))
(def tomcat-shared-loader (create-class-loader (expand-paths tomcat-shared-path) tomcat-common-loader))
Each of these class-loaders are created via create-class-loader
which takes a “loader-map” and a parent classloader to create a URLClassLoader
. The 3 “loader-maps” are specified like so:
(def *catalina-home* (or (System/getProperty "catalina.home") (System/getProperty "user.home")))
(def tomcat-common-path
{*catalina-home*
{"common/classes" :dir
"common/i18n/" :glob
"common/endorsed/" :glob
"common/lib/" :glob}})
(def tomcat-catalina-path
{*catalina-home*
{"server/classes" :dir
"server/lib/" :glob}})
(def tomcat-shared-path
{*catalina-home*
{"shared/classes" :dir
"shared/lib/" :glob}})
So they’re simple maps that specify where to find jars and class files on the system. With this hierarchy of classloaders set up, Clojure then has the means to find the classes it needs when we ask it to instantiate a Tomcat server.
I haven’t run this code in a while and it’s getting late, but when I get a chance, I’ll scrub it and run it through it’s paces and post it somewhere you can get your hands on it.
2 comments