|
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 com.sun.jmx.namespace; |
|
27 |
|
28 import com.sun.jmx.defaults.JmxProperties; |
|
29 import java.io.IOException; |
|
30 import java.util.logging.Level; |
|
31 import java.util.logging.Logger; |
|
32 |
|
33 import javax.management.AttributeNotFoundException; |
|
34 import javax.management.InstanceNotFoundException; |
|
35 import javax.management.MBeanException; |
|
36 import javax.management.MBeanRegistrationException; |
|
37 |
|
38 import javax.management.MBeanServerConnection; |
|
39 import javax.management.MalformedObjectNameException; |
|
40 import javax.management.ObjectName; |
|
41 import javax.management.ReflectionException; |
|
42 import javax.management.namespace.JMXNamespaces; |
|
43 |
|
44 |
|
45 /** |
|
46 * An RoutingProxy narrows on a given name space in a |
|
47 * source object implementing MBeanServerConnection. |
|
48 * It is used to implement |
|
49 * {@code JMXNamespaces.narrowToNamespace(...)}. |
|
50 * This abstract class has two concrete subclasses: |
|
51 * <p>{@link RoutingConnectionProxy}: to cd in an MBeanServerConnection.</p> |
|
52 * <p>{@link RoutingServerProxy}: to cd in an MBeanServer.</p> |
|
53 * <p><b> |
|
54 * This API is a Sun internal API and is subject to changes without notice. |
|
55 * </b></p> |
|
56 * @since 1.7 |
|
57 */ |
|
58 public abstract class RoutingProxy<T extends MBeanServerConnection> |
|
59 extends RoutingMBeanServerConnection<T> { |
|
60 |
|
61 /** |
|
62 * A logger for this class. |
|
63 **/ |
|
64 private static final Logger LOG = JmxProperties.NAMESPACE_LOGGER; |
|
65 |
|
66 // The source MBeanServerConnection |
|
67 private final T source; |
|
68 |
|
69 // The name space we're narrowing to (usually some name space in |
|
70 // the source MBeanServerConnection |
|
71 private final String sourceNs; |
|
72 |
|
73 // The name space we pretend to be mounted in (usually "") |
|
74 private final String targetNs; |
|
75 |
|
76 // The name of the JMXNamespace that handles the source name space |
|
77 private final ObjectName handlerName; |
|
78 private final ObjectNameRouter router; |
|
79 final boolean forwardsContext; |
|
80 private volatile String defaultDomain = null; |
|
81 |
|
82 /** |
|
83 * Creates a new instance of RoutingProxy |
|
84 */ |
|
85 protected RoutingProxy(T source, |
|
86 String sourceNs, |
|
87 String targetNs, |
|
88 boolean forwardsContext) { |
|
89 if (source == null) throw new IllegalArgumentException("null"); |
|
90 this.sourceNs = JMXNamespaces.normalizeNamespaceName(sourceNs); |
|
91 |
|
92 // Usually sourceNs is not null, except when implementing |
|
93 // Client Contexts |
|
94 // |
|
95 if (sourceNs.equals("")) { |
|
96 this.handlerName = null; |
|
97 } else { |
|
98 // System.err.println("sourceNs: "+sourceNs); |
|
99 this.handlerName = |
|
100 JMXNamespaces.getNamespaceObjectName(this.sourceNs); |
|
101 try { |
|
102 // System.err.println("handlerName: "+handlerName); |
|
103 if (!source.isRegistered(handlerName)) |
|
104 throw new IllegalArgumentException(sourceNs + |
|
105 ": no such name space"); |
|
106 } catch (IOException x) { |
|
107 throw new IllegalArgumentException("source stale: "+x,x); |
|
108 } |
|
109 } |
|
110 this.source = source; |
|
111 this.targetNs = (targetNs==null?"": |
|
112 JMXNamespaces.normalizeNamespaceName(targetNs)); |
|
113 this.router = |
|
114 new ObjectNameRouter(this.targetNs,this.sourceNs); |
|
115 this.forwardsContext = forwardsContext; |
|
116 |
|
117 if (LOG.isLoggable(Level.FINER)) |
|
118 LOG.finer("RoutingProxy for " + this.sourceNs + " created"); |
|
119 } |
|
120 |
|
121 @Override |
|
122 public T source() { return source; } |
|
123 |
|
124 ObjectNameRouter getObjectNameRouter() { |
|
125 // TODO: uncomment this when contexts are added |
|
126 // if (forwardsContext) |
|
127 // return ObjectNameRouter.wrapWithContext(router); |
|
128 // else |
|
129 return router; |
|
130 } |
|
131 |
|
132 @Override |
|
133 public ObjectName toSource(ObjectName targetName) |
|
134 throws MalformedObjectNameException { |
|
135 if (targetName == null) return null; |
|
136 if (targetName.getDomain().equals("") && targetNs.equals("")) { |
|
137 try { |
|
138 if (defaultDomain == null) |
|
139 defaultDomain = getDefaultDomain(); |
|
140 } catch(Exception x) { |
|
141 LOG.log(Level.FINEST,"Failed to get default domain",x); |
|
142 } |
|
143 if (defaultDomain != null) |
|
144 targetName = targetName.withDomain(defaultDomain); |
|
145 } |
|
146 final ObjectNameRouter r = getObjectNameRouter(); |
|
147 return r.toSourceContext(targetName,true); |
|
148 } |
|
149 |
|
150 @Override |
|
151 protected ObjectName newSourceMBeanName(ObjectName targetName) |
|
152 throws MBeanRegistrationException { |
|
153 if (targetName != null) return super.newSourceMBeanName(targetName); |
|
154 |
|
155 // OK => we can accept null if sourceNs is empty. |
|
156 if (sourceNs.equals("")) return null; |
|
157 |
|
158 throw new MBeanRegistrationException( |
|
159 new IllegalArgumentException( |
|
160 "Can't use null ObjectName with namespaces")); |
|
161 } |
|
162 |
|
163 @Override |
|
164 public ObjectName toTarget(ObjectName sourceName) |
|
165 throws MalformedObjectNameException { |
|
166 if (sourceName == null) return null; |
|
167 final ObjectNameRouter r = getObjectNameRouter(); |
|
168 return r.toTargetContext(sourceName,false); |
|
169 } |
|
170 |
|
171 private Object getAttributeFromHandler(String attributeName) |
|
172 throws IOException { |
|
173 |
|
174 try { |
|
175 return source().getAttribute(handlerName,attributeName); |
|
176 } catch (RuntimeException ex) { |
|
177 throw makeCompliantRuntimeException(ex); |
|
178 } catch (IOException x) { |
|
179 throw x; |
|
180 } catch (MBeanException ex) { |
|
181 throw new IOException("Failed to get "+attributeName+": "+ |
|
182 ex.getMessage(), |
|
183 ex.getTargetException()); |
|
184 } catch (AttributeNotFoundException ex) { |
|
185 throw new IOException("Failed to get "+attributeName+": "+ |
|
186 ex.getMessage(),ex); |
|
187 } catch (InstanceNotFoundException ex) { |
|
188 throw new IOException("Failed to get "+attributeName+": "+ |
|
189 ex.getMessage(),ex); |
|
190 } catch (ReflectionException ex) { |
|
191 throw new IOException("Failed to get "+attributeName+": "+ |
|
192 ex.getMessage(),ex); |
|
193 } |
|
194 } |
|
195 |
|
196 // We cannot call getMBeanCount() on the underlying |
|
197 // MBeanServerConnection, because it would return the number of |
|
198 // 'top-level' MBeans, not the number of MBeans in the name space |
|
199 // we are narrowing to. Instead we're calling getMBeanCount() on |
|
200 // the JMXNamespace that handles the source name space. |
|
201 // |
|
202 // There is however one particular case when the sourceNs is empty. |
|
203 // In that case, there's no handler - and the 'source' is the top |
|
204 // level namespace. In that particular case, handlerName will be null, |
|
205 // and we directly invoke the top level source(). |
|
206 // This later complex case is only used when implementing ClientContexts. |
|
207 // |
|
208 @Override |
|
209 public Integer getMBeanCount() throws IOException { |
|
210 try { |
|
211 if (handlerName == null) return source().getMBeanCount(); |
|
212 return (Integer) getAttributeFromHandler("MBeanCount"); |
|
213 } catch (RuntimeException ex) { |
|
214 throw makeCompliantRuntimeException(ex); |
|
215 } |
|
216 } |
|
217 |
|
218 // We cannot call getDomains() on the underlying |
|
219 // MBeanServerConnection, because it would return the domains of |
|
220 // 'top-level' MBeans, not the domains of MBeans in the name space |
|
221 // we are narrowing to. Instead we're calling getDomains() on |
|
222 // the JMXNamespace that handles the source name space. |
|
223 // |
|
224 // There is however one particular case when the sourceNs is empty. |
|
225 // In that case, there's no handler - and the 'source' is the top |
|
226 // level namespace. In that particular case, handlerName will be null, |
|
227 // and we directly invoke the top level source(). |
|
228 // This later complex case is only used when implementing ClientContexts. |
|
229 // |
|
230 @Override |
|
231 public String[] getDomains() throws IOException { |
|
232 try { |
|
233 if (handlerName == null) return source().getDomains(); |
|
234 return (String[]) getAttributeFromHandler("Domains"); |
|
235 } catch (RuntimeException ex) { |
|
236 throw makeCompliantRuntimeException(ex); |
|
237 } |
|
238 } |
|
239 |
|
240 // We cannot call getDefaultDomain() on the underlying |
|
241 // MBeanServerConnection, because it would return the default domain of |
|
242 // 'top-level' namespace, not the default domain in the name space |
|
243 // we are narrowing to. Instead we're calling getDefaultDomain() on |
|
244 // the JMXNamespace that handles the source name space. |
|
245 // |
|
246 // There is however one particular case when the sourceNs is empty. |
|
247 // In that case, there's no handler - and the 'source' is the top |
|
248 // level namespace. In that particular case, handlerName will be null, |
|
249 // and we directly invoke the top level source(). |
|
250 // This later complex case is only used when implementing ClientContexts. |
|
251 // |
|
252 @Override |
|
253 public String getDefaultDomain() throws IOException { |
|
254 try { |
|
255 if (handlerName == null) { |
|
256 defaultDomain = source().getDefaultDomain(); |
|
257 } else { |
|
258 defaultDomain =(String) |
|
259 getAttributeFromHandler("DefaultDomain"); |
|
260 } |
|
261 return defaultDomain; |
|
262 } catch (RuntimeException ex) { |
|
263 throw makeCompliantRuntimeException(ex); |
|
264 } |
|
265 } |
|
266 |
|
267 public String getSourceNamespace() { |
|
268 return sourceNs; |
|
269 } |
|
270 |
|
271 public String getTargetNamespace() { |
|
272 return targetNs; |
|
273 } |
|
274 |
|
275 @Override |
|
276 public String toString() { |
|
277 return super.toString()+", sourceNs="+ |
|
278 sourceNs + (targetNs.equals("")?"": |
|
279 (" mounted on targetNs="+targetNs)); |
|
280 } |
|
281 |
|
282 } |