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 com.sun.jmx.defaults.JmxProperties; |
|
29 import com.sun.jmx.namespace.ObjectNameRouter; |
|
30 import com.sun.jmx.namespace.serial.RewritingProcessor; |
|
31 import com.sun.jmx.namespace.RoutingConnectionProxy; |
|
32 import com.sun.jmx.namespace.RoutingServerProxy; |
|
33 |
|
34 import java.util.logging.Level; |
|
35 import java.util.logging.Logger; |
|
36 |
|
37 import javax.management.InstanceNotFoundException; |
|
38 import javax.management.MBeanServer; |
|
39 import javax.management.MBeanServerConnection; |
|
40 import javax.management.MalformedObjectNameException; |
|
41 import javax.management.ObjectName; |
|
42 |
|
43 /** |
|
44 * Static constants and utility methods to help work with |
|
45 * JMX name spaces. There are no instances of this class. |
|
46 * @since 1.7 |
|
47 */ |
|
48 public class JMXNamespaces { |
|
49 |
|
50 /** |
|
51 * A logger for this class. |
|
52 **/ |
|
53 private static final Logger LOG = JmxProperties.NAMESPACE_LOGGER; |
|
54 |
|
55 /** Creates a new instance of JMXNamespaces */ |
|
56 private JMXNamespaces() { |
|
57 } |
|
58 |
|
59 /** |
|
60 * The name space separator. This is an alias for {@link |
|
61 * ObjectName#NAMESPACE_SEPARATOR}. |
|
62 **/ |
|
63 public static final String NAMESPACE_SEPARATOR = |
|
64 ObjectName.NAMESPACE_SEPARATOR; |
|
65 private static final int NAMESPACE_SEPARATOR_LENGTH = |
|
66 NAMESPACE_SEPARATOR.length(); |
|
67 |
|
68 |
|
69 /** |
|
70 * Creates a new {@code MBeanServerConnection} proxy on a |
|
71 * {@linkplain javax.management.namespace sub name space} |
|
72 * of the given parent. |
|
73 * |
|
74 * @param parent The parent {@code MBeanServerConnection} that contains |
|
75 * the name space. |
|
76 * @param namespace The {@linkplain javax.management.namespace |
|
77 * name space} in which to narrow. |
|
78 * @return A new {@code MBeanServerConnection} proxy that shows the content |
|
79 * of that name space. |
|
80 * @throws IllegalArgumentException if either argument is null, |
|
81 * or the name space does not exist, or if a proxy for that name space |
|
82 * cannot be created. The {@linkplain Throwable#getCause() cause} of |
|
83 * this exception will be an {@link InstanceNotFoundException} if and only |
|
84 * if the name space is found not to exist. |
|
85 */ |
|
86 public static MBeanServerConnection narrowToNamespace( |
|
87 MBeanServerConnection parent, |
|
88 String namespace) { |
|
89 if (LOG.isLoggable(Level.FINER)) |
|
90 LOG.finer("Making MBeanServerConnection for: " +namespace); |
|
91 return RoutingConnectionProxy.cd(parent, namespace, true); |
|
92 } |
|
93 |
|
94 /** |
|
95 * Creates a new {@code MBeanServer} proxy on a |
|
96 * {@linkplain javax.management.namespace sub name space} |
|
97 * of the given parent. |
|
98 * |
|
99 * @param parent The parent {@code MBeanServer} that contains |
|
100 * the name space. |
|
101 * @param namespace The {@linkplain javax.management.namespace |
|
102 * name space} in which to narrow. |
|
103 * @return A new {@code MBeanServer} proxy that shows the content |
|
104 * of that name space. |
|
105 * @throws IllegalArgumentException if either argument is null, |
|
106 * or the name space does not exist, or if a proxy for that name space |
|
107 * cannot be created. The {@linkplain Throwable#getCause() cause} of |
|
108 * this exception will be an {@link InstanceNotFoundException} if and only |
|
109 * if the name space is found not to exist. |
|
110 */ |
|
111 public static MBeanServer narrowToNamespace(MBeanServer parent, |
|
112 String namespace) { |
|
113 if (LOG.isLoggable(Level.FINER)) |
|
114 LOG.finer("Making MBeanServer for: " +namespace); |
|
115 return RoutingServerProxy.cd(parent, namespace, true); |
|
116 } |
|
117 |
|
118 /** |
|
119 * Returns an object that is the same as the given object except that |
|
120 * any {@link ObjectName} it might contain has its domain modified. |
|
121 * The returned object might be identical to the given object if it |
|
122 * does not contain any {@code ObjectName} values or if none of them |
|
123 * were modified. |
|
124 * This method will replace a prefix ({@code toRemove}) from the path of |
|
125 * the ObjectNames contained in {@code obj} by another prefix |
|
126 * ({@code toAdd}). |
|
127 * Therefore, all contained ObjectNames must have a path that start with |
|
128 * the given {@code toRemove} prefix. If one of them doesn't, an {@link |
|
129 * IllegalArgumentException} is thrown. |
|
130 * <p> |
|
131 * For instance, if {@code obj} contains the ObjectName |
|
132 * {@code x//y//z//d:k=x}, and {@code toAdd} is {@code v//w}, and |
|
133 * {@code toRemove} |
|
134 * is {@code x//y} this method will return a copy of {@code obj} that |
|
135 * contains {@code v//w//z//d:k=x}.<br> |
|
136 * On the other hand, if {@code obj} contains the ObjectName |
|
137 * {@code x//y//z//d:k=x}, and {@code toAdd} is {@code v//w}, and |
|
138 * {@code toRemove} is {@code v} this method |
|
139 * will raise an exception, because {@code x//y//z//d:k=x} doesn't start |
|
140 * with {@code v} |
|
141 * </p> |
|
142 * <p>Note: the default implementation of this method can use the |
|
143 * Java serialization framework to clone and replace ObjectNames in the |
|
144 * provided {@code obj}. It will usually fail if {@code obj} is not |
|
145 * Java serializable, or contains objects which are not Java |
|
146 * serializable. |
|
147 * </p> |
|
148 * @param obj The object to deep-rewrite |
|
149 * @param toRemove a prefix already present in contained ObjectNames. |
|
150 * If {@code toRemove} is the empty string {@code ""}, nothing |
|
151 * will be removed from the contained ObjectNames. |
|
152 * @param toAdd the prefix that will replace (@code toRemove} in contained |
|
153 * ObjectNames. |
|
154 * If {@code toAdd} is the empty string {@code ""}, nothing |
|
155 * will be added to the contained ObjectNames. |
|
156 * @return the rewritten object, or possibly {@code obj} if nothing needed |
|
157 * to be changed. |
|
158 * @throws IllegalArgumentException if {@code obj} couldn't be rewritten or |
|
159 * if {@code toRemove} or {@code toAdd} is null. |
|
160 **/ |
|
161 public static <T> T deepReplaceHeadNamespace(T obj, String toRemove, String toAdd) { |
|
162 final RewritingProcessor processor = |
|
163 RewritingProcessor.newRewritingProcessor(toAdd,toRemove); |
|
164 return processor.rewriteOutput(obj); |
|
165 } |
|
166 |
|
167 /** |
|
168 * Appends {@code namespace} to {@code path}. |
|
169 * This methods appends {@code namespace} to {@code path} to obtain a |
|
170 * a <i>full path</i>, and normalizes the result thus obtained: |
|
171 * <ul> |
|
172 * <li>If {@code path} is empty, the full path is |
|
173 * {@code namespace}.</li> |
|
174 * <li>Otherwise, if {@code namespace} is empty, |
|
175 * the full path is {@code path}</li> |
|
176 * <li>Otherwise, and this is the regular case, the full path is the |
|
177 * result of the concatenation of |
|
178 * {@code path}+{@value #NAMESPACE_SEPARATOR}+{@code namespace}</li> |
|
179 * <li>finally, the full path is normalized: multiple consecutive |
|
180 * occurrences of {@value #NAMESPACE_SEPARATOR} are replaced by a |
|
181 * single {@value #NAMESPACE_SEPARATOR} in the result, and trailing |
|
182 * occurences of {@value #NAMESPACE_SEPARATOR} are removed. |
|
183 * </li> |
|
184 * </ul> |
|
185 * @param path a name space path prefix |
|
186 * @param namespace a name space name to append to the path |
|
187 * @return a syntactically valid name space path, or "" if both parameters |
|
188 * are null or empty. |
|
189 * @throws IllegalArgumentException if either argument is null or ends with |
|
190 * an odd number of {@code /} characters. |
|
191 **/ |
|
192 public static String concat(String path, String namespace) { |
|
193 if (path == null || namespace == null) |
|
194 throw new IllegalArgumentException("Null argument"); |
|
195 checkTrailingSlashes(path); |
|
196 checkTrailingSlashes(namespace); |
|
197 final String result; |
|
198 if (path.equals("")) result=namespace; |
|
199 else if (namespace.equals("")) result=path; |
|
200 else result=path+NAMESPACE_SEPARATOR+namespace; |
|
201 return ObjectNameRouter.normalizeNamespacePath(result,false,true,false); |
|
202 } |
|
203 |
|
204 /** |
|
205 * Returns a syntactically valid name space path. |
|
206 * If the provided {@code namespace} ends with {@code "//"}, |
|
207 * recursively strips trailing {@code "//"}. Each sequence of an |
|
208 * even number of {@code "/"} characters is also replaced by {@code "//"}, |
|
209 * for example {@code "foo//bar////baz/////buh"} will become |
|
210 * {@code "foo//bar//baz///buh"}. |
|
211 * |
|
212 * @param namespace A name space path |
|
213 * @return {@code ""} - if the provided {@code namespace} resolves to |
|
214 * the empty string; otherwise a syntactically valid name space string |
|
215 * stripped of trailing and redundant {@code "//"}. |
|
216 * @throws IllegalArgumentException if {@code namespace} is null or |
|
217 * is not syntactically valid (e.g. it contains |
|
218 * invalid characters like ':', or it ends with an odd |
|
219 * number of '/'). |
|
220 */ |
|
221 public static String normalizeNamespaceName(String namespace) { |
|
222 if (namespace == null) |
|
223 throw new IllegalArgumentException("Null namespace"); |
|
224 final String sourcePath = |
|
225 ObjectNameRouter.normalizeNamespacePath(namespace,false,true,false); |
|
226 if (sourcePath.equals("")) return sourcePath; |
|
227 |
|
228 // Will throw an IllegalArgumentException if the namespace name |
|
229 // is not syntactically valid... |
|
230 // |
|
231 getNamespaceObjectName(sourcePath); |
|
232 return sourcePath; |
|
233 } |
|
234 |
|
235 |
|
236 /** |
|
237 * Return a canonical handler name for the provided {@code namespace}, |
|
238 * The handler name returned will be |
|
239 * {@link #normalizeNamespaceName normalizeNamespaceName}{@code (namespace) + |
|
240 * "//:type=JMXNamespace"}. |
|
241 * |
|
242 * @param namespace A name space path |
|
243 * @return a canonical ObjectName for a name space handler. |
|
244 * @see #normalizeNamespaceName |
|
245 * @throws IllegalArgumentException if the provided |
|
246 * {@code namespace} is null or not valid. |
|
247 */ |
|
248 public static ObjectName getNamespaceObjectName(String namespace) { |
|
249 if (namespace == null || namespace.equals("")) |
|
250 throw new IllegalArgumentException("Null or empty namespace"); |
|
251 final String sourcePath = |
|
252 ObjectNameRouter.normalizeNamespacePath(namespace,false, |
|
253 true,false); |
|
254 try { |
|
255 // We could use ObjectName.valueOf here - but throwing an |
|
256 // IllegalArgumentException that contains just the supplied |
|
257 // namespace instead of the whole ObjectName seems preferable. |
|
258 return ObjectName.getInstance(sourcePath+ |
|
259 NAMESPACE_SEPARATOR+":"+ |
|
260 JMXNamespace.TYPE_ASSIGNMENT); |
|
261 } catch (MalformedObjectNameException x) { |
|
262 throw new IllegalArgumentException("Invalid namespace: " + |
|
263 namespace,x); |
|
264 } |
|
265 } |
|
266 |
|
267 /** |
|
268 * Returns an ObjectName pattern that can be used to query for all MBeans |
|
269 * contained in the given name space. |
|
270 * For instance, if {@code namespace="foo//bar"}, this method will |
|
271 * return {@code "foo//bar//*:*"} |
|
272 * @return an ObjectName pattern that selects all MBeans in the given |
|
273 * name space. |
|
274 **/ |
|
275 public static ObjectName getWildcardFor(String namespace) { |
|
276 return insertPath(namespace,ObjectName.WILDCARD); |
|
277 } |
|
278 |
|
279 |
|
280 /** |
|
281 * Returns an ObjectName that can be used to access an MBean |
|
282 * contained in the given name space. |
|
283 * For instance, if {@code path="foo//bar"}, and |
|
284 * {@code to="domain:type=Thing"} this method will |
|
285 * return {@code "foo//bar//domain:type=Thing"} |
|
286 * @return an ObjectName that can be used to invoke an MBean located in a |
|
287 * sub name space. |
|
288 * @throws IllegalArgumentException if {@code path} ends with an |
|
289 * odd number of {@code /} characters. |
|
290 **/ |
|
291 public static ObjectName insertPath(String path, ObjectName to) { |
|
292 if (path == null || to == null) |
|
293 throw new IllegalArgumentException("Null argument"); |
|
294 checkTrailingSlashes(path); |
|
295 String prefix = path; |
|
296 if (!prefix.equals("")) |
|
297 prefix = ObjectNameRouter.normalizeNamespacePath( |
|
298 prefix + NAMESPACE_SEPARATOR,false,false,false); |
|
299 return to.withDomain( |
|
300 ObjectNameRouter.normalizeDomain( |
|
301 prefix+to.getDomain(),false)); |
|
302 } |
|
303 |
|
304 /** |
|
305 * Returns the normalized name space path of the name space expected to |
|
306 * contain {@code ObjectName}. |
|
307 * For instance, for {@code "foo//domain:type=Thing"} this will be |
|
308 * {@code "foo"}. For {@code "//foo//bar//domain:type=Thing"} this will be |
|
309 * {@code "foo//bar"}. For {@code //foo//bar//baz//domain:type=Thing} |
|
310 * this will be {@code "foo//bar//baz"}. For |
|
311 * {@code //foo//bar//baz//:type=JMXNamespace} |
|
312 * this will be {@code "foo//bar"}. |
|
313 * |
|
314 * @param name an {@code ObjectName} |
|
315 * @return the name space path of the name space that could contain such |
|
316 * a name. If {@code name} has no name space, returns {@code ""}. |
|
317 * @throws IllegalArgumentException if {@code name} is null. |
|
318 **/ |
|
319 public static String getContainingNamespace(ObjectName name) { |
|
320 return getNormalizedPath(name,true); |
|
321 } |
|
322 |
|
323 |
|
324 static String getNormalizedPath(ObjectName name, |
|
325 boolean removeLeadingSep) { |
|
326 if (name == null) |
|
327 throw new IllegalArgumentException("Null name"); |
|
328 String domain = |
|
329 ObjectNameRouter.normalizeDomain(name.getDomain(),removeLeadingSep); |
|
330 int end = domain.length(); |
|
331 |
|
332 // special case of domain part being a single '/' |
|
333 // |
|
334 if (domain.endsWith(NAMESPACE_SEPARATOR+"/")) |
|
335 return domain.substring(0,end-NAMESPACE_SEPARATOR_LENGTH-1); |
|
336 |
|
337 // special case of namespace handler |
|
338 // |
|
339 if (domain.endsWith(NAMESPACE_SEPARATOR)) |
|
340 domain = domain.substring(0,end-NAMESPACE_SEPARATOR_LENGTH); |
|
341 |
|
342 int last = domain.lastIndexOf(NAMESPACE_SEPARATOR); |
|
343 if (last < 0) return ""; |
|
344 if (last == 0) return domain; |
|
345 |
|
346 // special case of domain part starting with '/' |
|
347 // last=0 is not possible - we took care of this above. |
|
348 if (domain.charAt(last-1) == '/') last--; |
|
349 |
|
350 return domain.substring(0,last); |
|
351 } |
|
352 |
|
353 private static void checkTrailingSlashes(String path) { |
|
354 int i; |
|
355 for (i = path.length() - 1; i >= 0 && path.charAt(i) == '/'; i--) |
|
356 continue; |
|
357 if (path.length() - i % 2 == 0) |
|
358 throw new IllegalArgumentException("Path ends with odd number of /"); |
|
359 } |
|
360 } |
|