|
1 /* |
|
2 * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. |
|
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
|
4 * |
|
5 * This code is free software; you can redistribute it and/or modify it |
|
6 * under the terms of the GNU General Public License version 2 only, as |
|
7 * published by the Free Software Foundation. Sun designates this |
|
8 * particular file as subject to the "Classpath" exception as provided |
|
9 * by Sun in the LICENSE file that accompanied this code. |
|
10 * |
|
11 * This code is distributed in the hope that it will be useful, but WITHOUT |
|
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
|
14 * version 2 for more details (a copy is included in the LICENSE file that |
|
15 * accompanied this code). |
|
16 * |
|
17 * You should have received a copy of the GNU General Public License version |
|
18 * 2 along with this work; if not, write to the Free Software Foundation, |
|
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
|
20 * |
|
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, |
|
22 * CA 95054 USA or visit www.sun.com if you need additional information or |
|
23 * have any questions. |
|
24 */ |
|
25 |
|
26 package javax.management.namespace; |
|
27 |
|
28 import java.io.IOException; |
|
29 import java.util.Set; |
|
30 import javax.management.MBeanServer; |
|
31 import javax.management.MBeanServerConnection; |
|
32 import javax.management.MalformedObjectNameException; |
|
33 import javax.management.ObjectName; |
|
34 |
|
35 /** |
|
36 * This class makes it possible to navigate easily within a hierarchical |
|
37 * namespace view. |
|
38 * |
|
39 * <pre> |
|
40 * MBeanServerConnnection rootConnection = ...; |
|
41 * |
|
42 * // create a view at the local root of the namespace hierarchy. |
|
43 * // |
|
44 * JMXNamespaceView view = new JMXNamespaceView(rootConnection); |
|
45 * |
|
46 * // list all top level namespaces |
|
47 * String[] list = view.list(); |
|
48 * |
|
49 * // select one namespace from the list |
|
50 * String whereToGo = ... ; |
|
51 * |
|
52 * // go down to the selected namespace: |
|
53 * view = view.down(whereToGo); |
|
54 * System.out.println("I am now in: " + view.where()); |
|
55 * System.out.println("I can see these MBeans:" + |
|
56 * view.getMBeanServerConnection().queryNames(null,null)); |
|
57 * |
|
58 * // list sub namespaces in current view ('whereToGo') |
|
59 * list = view.list(); |
|
60 * System.out.println("Here are the sub namespaces of "+view.where()+": "+ |
|
61 * Arrays.toString(list)); |
|
62 * |
|
63 * // go up one level |
|
64 * view = view.up(); |
|
65 * System.out.println("I am now back to: " + |
|
66 * (view.isRoot() ? "root namespace" : view.where())); |
|
67 * </pre> |
|
68 * @since 1.7 |
|
69 */ |
|
70 public class JMXNamespaceView { |
|
71 |
|
72 private static final ObjectName ALL_NAMESPACES; |
|
73 static { |
|
74 try { |
|
75 ALL_NAMESPACES = ObjectName.getInstance("*" + |
|
76 JMXNamespaces.NAMESPACE_SEPARATOR + ":"+ |
|
77 JMXNamespace.TYPE_ASSIGNMENT); |
|
78 } catch (MalformedObjectNameException x) { |
|
79 throw new ExceptionInInitializerError(x); |
|
80 } |
|
81 } |
|
82 private static final int NAMESPACE_SEPARATOR_LENGTH = |
|
83 JMXNamespaces.NAMESPACE_SEPARATOR.length(); |
|
84 |
|
85 private final JMXNamespaceView parent; |
|
86 private final MBeanServerConnection here; |
|
87 private final String where; |
|
88 |
|
89 private static MBeanServerConnection checkRoot(MBeanServerConnection root) { |
|
90 if (root == null) |
|
91 throw new IllegalArgumentException( |
|
92 "namespaceRoot: null is not a valid value"); |
|
93 return root; |
|
94 } |
|
95 |
|
96 /** |
|
97 * Creates a view at the top of a JMX namespace hierarchy. |
|
98 * @param namespaceRoot The {@code MBeanServerConnection} at the |
|
99 * top of the hierarchy. |
|
100 */ |
|
101 public JMXNamespaceView(MBeanServerConnection namespaceRoot) { |
|
102 this(null,checkRoot(namespaceRoot),""); |
|
103 } |
|
104 |
|
105 // This constructor should remain private. A user can only create |
|
106 // JMXNamespaceView at the top of the hierarchy. |
|
107 // JMXNamespaceView sub nodes are created by their parent nodes. |
|
108 private JMXNamespaceView(JMXNamespaceView parent, |
|
109 MBeanServerConnection here, String where) { |
|
110 this.parent = parent; |
|
111 this.here = here; |
|
112 this.where = where; |
|
113 } |
|
114 |
|
115 /** |
|
116 * Returns the path leading to the namespace in this view, from |
|
117 * the top of the hierarchy. |
|
118 * @return The path to the namespace in this view. |
|
119 */ |
|
120 public String where() { |
|
121 return where; |
|
122 } |
|
123 |
|
124 /** |
|
125 * Lists all direct sub namespaces in this view. The returned strings |
|
126 * do not contain the {@code //} separator. |
|
127 * |
|
128 * @return A list of direct sub name spaces accessible from this |
|
129 * namespace. |
|
130 * @throws IOException if the attempt to list the namespaces fails because |
|
131 * of a communication problem. |
|
132 */ |
|
133 public String[] list() throws IOException { |
|
134 final Set<ObjectName> names = |
|
135 here.queryNames(ALL_NAMESPACES,null); |
|
136 final String[] res = new String[names.size()]; |
|
137 int i = 0; |
|
138 for (ObjectName dirName : names) { |
|
139 final String dir = dirName.getDomain(); |
|
140 res[i++]=dir.substring(0,dir.length()-NAMESPACE_SEPARATOR_LENGTH); |
|
141 } |
|
142 return res; |
|
143 } |
|
144 |
|
145 /** |
|
146 * Go down into a sub namespace. |
|
147 * @param namespace the namespace to go down to. It can contain one or |
|
148 * more {@code //} separators, to traverse intermediate namespaces, but |
|
149 * it must not begin or end with {@code //} or contain an empty |
|
150 * intermediate namespace. If it is the empty string, then {@code this} is |
|
151 * returned. |
|
152 * @return A view of the named sub namespace. |
|
153 * @throws IllegalArgumentException if the {@code namespace} begins or |
|
154 * ends with {@code //}. |
|
155 */ |
|
156 public JMXNamespaceView down(String namespace) { |
|
157 if (namespace.equals("")) return this; |
|
158 if (namespace.startsWith(JMXNamespaces.NAMESPACE_SEPARATOR)) |
|
159 throw new IllegalArgumentException(namespace+": can't start with "+ |
|
160 JMXNamespaces.NAMESPACE_SEPARATOR); |
|
161 |
|
162 // This is a convenience to handle paths like xxx//yyy |
|
163 final String[] elts = |
|
164 namespace.split(JMXNamespaces.NAMESPACE_SEPARATOR); |
|
165 |
|
166 // Go down the path, creating all sub namespaces along the way. |
|
167 // Usually there will be a single element in the given namespace |
|
168 // name, but we don't want to forbid things like |
|
169 // down("xxx//yyy/www"); |
|
170 // |
|
171 JMXNamespaceView previous = this; |
|
172 String cursor = where; |
|
173 for (String elt : elts) { |
|
174 // empty path elements are not allowed. It means we |
|
175 // had something like "xxx////yyy" |
|
176 if (elt.equals("")) |
|
177 throw new IllegalArgumentException(namespace+ |
|
178 ": invalid path element"); |
|
179 |
|
180 // compute the "where" for the child. |
|
181 cursor = JMXNamespaces.concat(cursor, elt); |
|
182 |
|
183 // create the child... |
|
184 final JMXNamespaceView next = |
|
185 makeJMXNamespaceView(root(), previous, cursor); |
|
186 |
|
187 // the current child will be the parent of the next child... |
|
188 previous = next; |
|
189 } |
|
190 |
|
191 // We return the last child that was created. |
|
192 return previous; |
|
193 } |
|
194 |
|
195 /** |
|
196 * Go back up one level. If this view is at the root of the |
|
197 * hierarchy, returns {@code null}. |
|
198 * @return A view of the parent namespace, or {@code null} if we're at |
|
199 * the root of the hierarchy. |
|
200 */ |
|
201 public JMXNamespaceView up() { |
|
202 return parent; |
|
203 } |
|
204 |
|
205 /** |
|
206 * Tells whether this view is at the root of the hierarchy. |
|
207 * @return {@code true} if this view is at the root of the hierachy. |
|
208 */ |
|
209 public boolean isRoot() { |
|
210 return parent == null; |
|
211 } |
|
212 |
|
213 /** |
|
214 * Returns the view at the root of the hierarchy. |
|
215 * If we are already at the root, this is {@code this}. |
|
216 * @return the view at the root of the hierarchy. |
|
217 */ |
|
218 public JMXNamespaceView root() { |
|
219 if (parent == null) return this; |
|
220 return parent.root(); |
|
221 } |
|
222 |
|
223 /** |
|
224 * A MBeanServerConnection to the namespace shown by this view. |
|
225 * This is what would have been obtained by doing: |
|
226 * <pre> |
|
227 * JMX.narrowToNamespace(this.root().getMBeanServerConnection(), |
|
228 * this.where()); |
|
229 * </pre> |
|
230 * @return A MBeanServerConnection to the namespace shown by this view. |
|
231 */ |
|
232 public MBeanServerConnection getMBeanServerConnection() { |
|
233 return here; |
|
234 } |
|
235 |
|
236 /** |
|
237 * <p>Get the name of the JMXNamespaceMBean handling the namespace shown by |
|
238 * this view, relative to the root of the hierarchy. If we are at the root |
|
239 * of the hierarchy, this method returns {@code null}.</p> |
|
240 * |
|
241 * <p>You can use this method to make a proxy for the JMXNamespaceMBean |
|
242 * as follows:</p> |
|
243 * |
|
244 * <pre> |
|
245 * JMXNamespaceView view = ...; |
|
246 * ObjectName namespaceMBeanName = view.getJMXNamespaceMBeanName(); |
|
247 * JMXNamespaceMBean namespaceMBean = JMX.newMBeanProxy( |
|
248 * view.root().getMBeanServerConnection(), namespaceMBeanName, |
|
249 * JMXNamespaceMBean.class); |
|
250 * </pre> |
|
251 * |
|
252 * @return The name of the {@code JMXNamespaceMBean} handling the namespace |
|
253 * shown by this view, or {@code null}. |
|
254 */ |
|
255 public ObjectName getJMXNamespaceMBeanName() { |
|
256 if (parent == null) |
|
257 return null; |
|
258 else |
|
259 return JMXNamespaces.getNamespaceObjectName(where); |
|
260 } |
|
261 |
|
262 @Override |
|
263 public int hashCode() { |
|
264 return where.hashCode(); |
|
265 } |
|
266 |
|
267 /** |
|
268 * Returns true if this object is equal to the given object. The |
|
269 * two objects are equal if the other object is also a {@code |
|
270 * JMXNamespaceView} and both objects have the same {@linkplain #root root} |
|
271 * MBeanServerConnection and the same {@linkplain #where path}. |
|
272 * @param o the other object to compare to. |
|
273 * @return true if both objects are equal. |
|
274 */ |
|
275 @Override |
|
276 public boolean equals(Object o) { |
|
277 if (o==this) return true; |
|
278 if (! (o instanceof JMXNamespaceView)) return false; |
|
279 if (!where.equals(((JMXNamespaceView)o).where)) return false; |
|
280 return root().getMBeanServerConnection().equals( |
|
281 ((JMXNamespaceView)o).root().getMBeanServerConnection()); |
|
282 } |
|
283 |
|
284 private JMXNamespaceView makeJMXNamespaceView(final JMXNamespaceView root, |
|
285 final JMXNamespaceView directParent, final String pathFromRoot) { |
|
286 if (pathFromRoot.equals("")) return root; |
|
287 |
|
288 return new JMXNamespaceView(directParent, |
|
289 narrowToNamespace(root.getMBeanServerConnection(), |
|
290 pathFromRoot),pathFromRoot); |
|
291 } |
|
292 |
|
293 private MBeanServerConnection narrowToNamespace(MBeanServerConnection root, |
|
294 String path) { |
|
295 if (root instanceof MBeanServer) |
|
296 return JMXNamespaces.narrowToNamespace((MBeanServer)root, path); |
|
297 return JMXNamespaces.narrowToNamespace(root, path); |
|
298 } |
|
299 |
|
300 } |