Browse code
(init)
Ed L authored on 29/05/2020 16:22:02
Showing 5 changed files
Showing 5 changed files
0 | 15 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,16 @@ |
1 |
+{:paths ["src" "resources"] |
|
2 |
+ :java-opts ["--add-modules=java.management"] |
|
3 |
+ :deps {org.clojure/clojure {:mvn/version "1.10.1"} |
|
4 |
+ org.clojure/java.jmx {:mvn/version "1.0.0"}} |
|
5 |
+ :aliases |
|
6 |
+ {:test {:extra-paths ["test"] |
|
7 |
+ :extra-deps {org.clojure/test.check {:mvn/version "1.0.0"}}} |
|
8 |
+ :runner |
|
9 |
+ {:extra-deps {com.cognitect/test-runner |
|
10 |
+ {:git/url "https://github.com/cognitect-labs/test-runner" |
|
11 |
+ :sha "f7ef16dc3b8332b0d77bc0274578ad5270fbfedd"}} |
|
12 |
+ :main-opts ["-m" "cognitect.test-runner" |
|
13 |
+ "-d" "test"]} |
|
14 |
+ :uberjar {:extra-deps {seancorfield/depstar {:mvn/version "1.0.94"}} |
|
15 |
+ :main-opts ["-m" "hf.depstar.uberjar" "jmx-test.jar" |
|
16 |
+ "-C" "-m" "co.fwoar.jmx-test"]}}} |
0 | 17 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,116 @@ |
1 |
+(ns co.fwoar.CrazyBean |
|
2 |
+ (:import [javax.management MBeanInfo AttributeList MBeanAttributeInfo |
|
3 |
+ MBeanOperationInfo MBeanParameterInfo]) |
|
4 |
+ (:require [clojure.string :as str]) |
|
5 |
+ (:gen-class |
|
6 |
+ :implements [javax.management.DynamicMBean] |
|
7 |
+ :init init |
|
8 |
+ :state state |
|
9 |
+ :constructors {[Object] []})) |
|
10 |
+ |
|
11 |
+(def ^{:private true} guess-attribute-map |
|
12 |
+ {"java.lang.Integer" "int" |
|
13 |
+ "java.lang.Boolean" "boolean" |
|
14 |
+ "java.lang.Long" "long"}) |
|
15 |
+ |
|
16 |
+(defn- guess-attribute-typename |
|
17 |
+ "Guess the attribute typename for MBeanAttributeInfo based on the attribute value." |
|
18 |
+ [value] |
|
19 |
+ (let [classname (.getName (class value))] |
|
20 |
+ (get guess-attribute-map classname classname))) |
|
21 |
+ |
|
22 |
+(defn- build-attribute-info |
|
23 |
+ "Construct an MBeanAttributeInfo. Normally called with a key/value pair from a Clojure map." |
|
24 |
+ ([attr-name attr-value] |
|
25 |
+ (build-attribute-info |
|
26 |
+ (name attr-name) |
|
27 |
+ (guess-attribute-typename attr-value) |
|
28 |
+ (name attr-name) true false false)) |
|
29 |
+ ([name type desc readable? writable? is?] (MBeanAttributeInfo. name type desc readable? writable? is? ))) |
|
30 |
+ |
|
31 |
+(defn- map->attribute-infos |
|
32 |
+ "Construct an MBeanAttributeInfo[] from a Clojure associative." |
|
33 |
+ [attr-map] |
|
34 |
+ (into-array MBeanAttributeInfo |
|
35 |
+ (map (fn [[attr-name value]] (build-attribute-info attr-name value)) |
|
36 |
+ attr-map))) |
|
37 |
+ |
|
38 |
+;; todo mv to util |
|
39 |
+(defn zip [& cols] (apply map vector cols)) |
|
40 |
+ |
|
41 |
+(defn unmangle |
|
42 |
+ "Given the name of a class that implements a Clojure function, returns the function's name in Clojure. Note: If the true Clojure function name |
|
43 |
+ contains any underscores (a rare occurrence), the unmangled name will |
|
44 |
+ contain hyphens at those locations instead." |
|
45 |
+ [class-name] |
|
46 |
+ (.replace (str/replace class-name #"^(.+)\$(.+)__\d+$" "$1/$2") |
|
47 |
+ \_ \-)) |
|
48 |
+ |
|
49 |
+(defn fn-by-name |
|
50 |
+ "lookup string to fn at run time" |
|
51 |
+ [ns-symbol fn-name] |
|
52 |
+ (intern (clojure.lang.Namespace/find ns-symbol) (symbol (name fn-name)))) |
|
53 |
+;; /util |
|
54 |
+ |
|
55 |
+(defn -init [derefable] |
|
56 |
+ [[] derefable]) |
|
57 |
+ |
|
58 |
+(defn build-parameter-info [param opts] |
|
59 |
+ (let [{:keys [name type description] |
|
60 |
+ :or {name (.getName param) |
|
61 |
+ type (.getName param) |
|
62 |
+ description ""}} opts] |
|
63 |
+ (MBeanParameterInfo. name type description))) |
|
64 |
+ |
|
65 |
+(defn parameter-infos [params-seq] |
|
66 |
+ (into-array MBeanParameterInfo |
|
67 |
+ (map |
|
68 |
+ (fn [[i param]] |
|
69 |
+ (build-parameter-info param {:name (str "p" i)})) |
|
70 |
+ (zip (range 0 (count params-seq)) params-seq)))) |
|
71 |
+ |
|
72 |
+(defn build-operation-info [ifn opts] |
|
73 |
+ (let [{:keys [name description argv return-type impact] |
|
74 |
+ :or {name (unmangle (.getName (class ifn))) |
|
75 |
+ description (unmangle (.getName (class ifn))) |
|
76 |
+ argv nil |
|
77 |
+ return-type "void" |
|
78 |
+ impact MBeanOperationInfo/ACTION}} opts |
|
79 |
+ params (parameter-infos argv)] |
|
80 |
+ (MBeanOperationInfo. name description params return-type impact))) |
|
81 |
+ |
|
82 |
+(defn operation-infos [ops-seq] |
|
83 |
+ (into-array (map (fn [[ifn opts]] (build-operation-info ifn opts)) |
|
84 |
+ ops-seq))) |
|
85 |
+ |
|
86 |
+; TODO: rest of the arguments, as needed |
|
87 |
+(defn generate-mbean-info [clj-bean] |
|
88 |
+ (MBeanInfo. |
|
89 |
+ (.. clj-bean getClass getName) ; class name |
|
90 |
+ "Clojure Dynamic MBean" ; description |
|
91 |
+ (map->attribute-infos (dissoc @(.state clj-bean) ; attributes |
|
92 |
+ :operations)) |
|
93 |
+ nil ; constructors |
|
94 |
+ (operation-infos (@(.state clj-bean) :operations)) ; operations |
|
95 |
+ nil)) ; notifications |
|
96 |
+(defn -getMBeanInfo |
|
97 |
+ [this] |
|
98 |
+ (generate-mbean-info this)) |
|
99 |
+ |
|
100 |
+(defn -getAttribute |
|
101 |
+ [this attr] |
|
102 |
+ (@(.state this) (keyword attr))) |
|
103 |
+ |
|
104 |
+(defn -getAttributes |
|
105 |
+ [this attrs] |
|
106 |
+ (let [result (AttributeList.)] |
|
107 |
+ (doseq [attr attrs] |
|
108 |
+ (.add result (.getAttribute this attr))) |
|
109 |
+ result)) |
|
110 |
+ |
|
111 |
+(defn -invoke [this name args sig] |
|
112 |
+ ;; this might be a huge security hole fyi |
|
113 |
+ ;; who knows what kind of havoc this could wreak! |
|
114 |
+ (let [[ns-name fn-name] (str/split name #"\$") |
|
115 |
+ ifn (fn-by-name (symbol ns-name) fn-name)] |
|
116 |
+ (apply ifn (seq args)))) |
0 | 117 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,107 @@ |
1 |
+(ns co.fwoar.bean |
|
2 |
+ (:import [javax.management DynamicMBean MBeanInfo AttributeList MBeanAttributeInfo |
|
3 |
+ MBeanOperationInfo MBeanParameterInfo]) |
|
4 |
+ (:require [clojure.string :as str])) |
|
5 |
+ |
|
6 |
+(defn- build-attribute-info |
|
7 |
+ "Construct an MBeanAttributeInfo. Normally called with a key/value pair from a Clojure map." |
|
8 |
+ ([attr-name attr-value desc readable? writable? is?] |
|
9 |
+ (MBeanAttributeInfo. (name attr-name) |
|
10 |
+ attr-value |
|
11 |
+ (name desc) |
|
12 |
+ readable? writable? is? ))) |
|
13 |
+ |
|
14 |
+(defn- map->attribute-infos |
|
15 |
+ "Construct an MBeanAttributeInfo[] from a Clojure associative." |
|
16 |
+ [attr-map] |
|
17 |
+ (into-array MBeanAttributeInfo |
|
18 |
+ (map (fn [[attr-name {:keys [type write?]}]] |
|
19 |
+ (build-attribute-info attr-name type attr-name true write? false)) |
|
20 |
+ attr-map))) |
|
21 |
+ |
|
22 |
+;; todo mv to util |
|
23 |
+ |
|
24 |
+(defn unmangle |
|
25 |
+ "Given the name of a class that implements a Clojure function, returns the function's name in Clojure. Note: If the true Clojure function name |
|
26 |
+ contains any underscores (a rare occurrence), the unmangled name will |
|
27 |
+ contain hyphens at those locations instead." |
|
28 |
+ [class-name] |
|
29 |
+ (.replace (str/replace class-name #"^(.+)\$(.+)__\d+$" "$1/$2") |
|
30 |
+ \_ \-)) |
|
31 |
+ |
|
32 |
+(defn fn-by-name |
|
33 |
+ "lookup string to fn at run time" |
|
34 |
+ [ns-symbol fn-name] |
|
35 |
+ (intern (clojure.lang.Namespace/find ns-symbol) (symbol (name fn-name)))) |
|
36 |
+;; /util |
|
37 |
+ |
|
38 |
+(defn -init [derefable] |
|
39 |
+ [[] derefable]) |
|
40 |
+ |
|
41 |
+(defn build-parameter-info [param opts] |
|
42 |
+ (let [{:keys [name type description] |
|
43 |
+ :or {name (.getName param) |
|
44 |
+ type (.getName param) |
|
45 |
+ description ""}} opts] |
|
46 |
+ (MBeanParameterInfo. name type description))) |
|
47 |
+ |
|
48 |
+(defn parameter-infos [params-seq] |
|
49 |
+ (->> (sequence (map (fn [i param] |
|
50 |
+ (build-parameter-info param {:name (str "p" i)}))) |
|
51 |
+ (iterate inc 0) |
|
52 |
+ params-seq) |
|
53 |
+ (into-array MBeanParameterInfo))) |
|
54 |
+ |
|
55 |
+(defn build-operation-info [ifn opts] |
|
56 |
+ (let [{:keys [name description argv return-type impact] |
|
57 |
+ :or {name (unmangle (.getName (class ifn))) |
|
58 |
+ description (unmangle (.getName (class ifn))) |
|
59 |
+ argv nil |
|
60 |
+ return-type "void" |
|
61 |
+ impact MBeanOperationInfo/ACTION}} opts |
|
62 |
+ params (parameter-infos argv)] |
|
63 |
+ (MBeanOperationInfo. name description params return-type impact))) |
|
64 |
+ |
|
65 |
+(defn operation-infos [ops-seq] |
|
66 |
+ (into-array MBeanOperationInfo |
|
67 |
+ (map (fn [[ifn opts]] (build-operation-info ifn opts)) |
|
68 |
+ ops-seq))) |
|
69 |
+ |
|
70 |
+; TODO: rest of the arguments, as needed |
|
71 |
+(defn generate-mbean-info [state clj-bean] |
|
72 |
+ (MBeanInfo. |
|
73 |
+ (.. clj-bean getClass getName) ; class name |
|
74 |
+ "Clojure Dynamic MBean" ; description |
|
75 |
+ (map->attribute-infos (dissoc state ; attributes |
|
76 |
+ :operations)) |
|
77 |
+ nil ; constructors |
|
78 |
+ (operation-infos (state :operations)) ; operations |
|
79 |
+ nil)) ; notifications |
|
80 |
+ |
|
81 |
+(defn handle-set [state {:keys [name value] :as at}] |
|
82 |
+ (println at) |
|
83 |
+ (swap! state assoc-in [(keyword name) :value] value)) |
|
84 |
+ |
|
85 |
+(deftype MaBean [state] |
|
86 |
+ DynamicMBean |
|
87 |
+ (getMBeanInfo [this] |
|
88 |
+ (generate-mbean-info @state this)) |
|
89 |
+ |
|
90 |
+ (getAttribute [this attr] |
|
91 |
+ (:value (@state (keyword attr)))) |
|
92 |
+ |
|
93 |
+ (getAttributes [this attrs] |
|
94 |
+ (let [result (AttributeList.)] |
|
95 |
+ (doseq [attr attrs] |
|
96 |
+ (.add result (.getAttribute this attr))) |
|
97 |
+ result)) |
|
98 |
+ |
|
99 |
+ (setAttribute [this attribute] |
|
100 |
+ (handle-set state (bean attribute))) |
|
101 |
+ |
|
102 |
+ (invoke [_ name args _] |
|
103 |
+ ;; this might be a huge security hole fyi |
|
104 |
+ ;; who knows what kind of havoc this could wreak! |
|
105 |
+ (let [[ns-name fn-name] (str/split name #"\$") |
|
106 |
+ ifn (fn-by-name (symbol ns-name) fn-name)] |
|
107 |
+ (apply ifn (seq args))))) |