Showing the changes to java/pkg/javax/management/openmbean/CompositeDataInvocationHandler.java (1 of 18352 files). Show all
Unified · Split
| @@ -0,0 +1,248 @@ | ||
| 1 | +/* | |
| 2 | + * Copyright (c) 2005, 2008, Oracle and/or its affiliates. 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. Oracle designates this | |
| 8 | + * particular file as subject to the "Classpath" exception as provided | |
| 9 | + * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| 22 | + * or visit www.oracle.com if you need additional information or have any | |
| 23 | + * questions. | |
| 24 | + */ | |
| 25 | + | |
| 26 | +package javax.management.openmbean; | |
| 27 | + | |
| 28 | +import com.sun.jmx.mbeanserver.MXBeanLookup; | |
| 29 | +import com.sun.jmx.mbeanserver.MXBeanMapping; | |
| 30 | +import com.sun.jmx.mbeanserver.MXBeanMappingFactory; | |
| 31 | +import com.sun.jmx.mbeanserver.DefaultMXBeanMappingFactory; | |
| 32 | +import java.lang.reflect.InvocationHandler; | |
| 33 | +import java.lang.reflect.Method; | |
| 34 | +import java.lang.reflect.Proxy; | |
| 35 | + | |
| 36 | +/** | |
| 37 | + <p>An {@link InvocationHandler} that forwards getter methods to a | |
| 38 | + {@link CompositeData}. If you have an interface that contains | |
| 39 | + only getter methods (such as {@code String getName()} or | |
| 40 | + {@code boolean isActive()}) then you can use this class in | |
| 41 | + conjunction with the {@link Proxy} class to produce an implementation | |
| 42 | + of the interface where each getter returns the value of the | |
| 43 | + corresponding item in a {@code CompositeData}.</p> | |
| 44 | + | |
| 45 | + <p>For example, suppose you have an interface like this: | |
| 46 | + | |
| 47 | + <blockquote> | |
| 48 | + <pre> | |
| 49 | + public interface NamedNumber { | |
| 50 | + public int getNumber(); | |
| 51 | + public String getName(); | |
| 52 | + } | |
| 53 | + </pre> | |
| 54 | + </blockquote> | |
| 55 | + | |
| 56 | + and a {@code CompositeData} constructed like this: | |
| 57 | + | |
| 58 | + <blockquote> | |
| 59 | + <pre> | |
| 60 | + CompositeData cd = | |
| 61 | + new {@link CompositeDataSupport}( | |
| 62 | + someCompositeType, | |
| 63 | + new String[] {"number", "name"}, | |
| 64 | + new Object[] {<b>5</b>, "five"} | |
| 65 | + ); | |
| 66 | + </pre> | |
| 67 | + </blockquote> | |
| 68 | + | |
| 69 | + then you can construct an object implementing {@code NamedNumber} | |
| 70 | + and backed by the object {@code cd} like this: | |
| 71 | + | |
| 72 | + <blockquote> | |
| 73 | + <pre> | |
| 74 | + InvocationHandler handler = | |
| 75 | + new CompositeDataInvocationHandler(cd); | |
| 76 | + NamedNumber nn = (NamedNumber) | |
| 77 | + Proxy.newProxyInstance(NamedNumber.class.getClassLoader(), | |
| 78 | + new Class[] {NamedNumber.class}, | |
| 79 | + handler); | |
| 80 | + </pre> | |
| 81 | + </blockquote> | |
| 82 | + | |
| 83 | + A call to {@code nn.getNumber()} will then return <b>5</b>.</p> | |
| 84 | + | |
| 85 | + <p>If the first letter of the property defined by a getter is a | |
| 86 | + capital, then this handler will look first for an item in the | |
| 87 | + {@code CompositeData} beginning with a capital, then, if that is | |
| 88 | + not found, for an item beginning with the corresponding lowercase | |
| 89 | + letter or code point. For a getter called {@code getNumber()}, the | |
| 90 | + handler will first look for an item called {@code Number}, then for | |
| 91 | + {@code number}. If the getter is called {@code getnumber()}, then | |
| 92 | + the item must be called {@code number}.</p> | |
| 93 | + | |
| 94 | + <p>If the method given to {@link #invoke invoke} is the method | |
| 95 | + {@code boolean equals(Object)} inherited from {@code Object}, then | |
| 96 | + it will return true if and only if the argument is a {@code Proxy} | |
| 97 | + whose {@code InvocationHandler} is also a {@code | |
| 98 | + CompositeDataInvocationHandler} and whose backing {@code | |
| 99 | + CompositeData} is equal (not necessarily identical) to this | |
| 100 | + object's. If the method given to {@code invoke} is the method | |
| 101 | + {@code int hashCode()} inherited from {@code Object}, then it will | |
| 102 | + return a value that is consistent with this definition of {@code | |
| 103 | + equals}: if two objects are equal according to {@code equals}, then | |
| 104 | + they will have the same {@code hashCode}.</p> | |
| 105 | + | |
| 106 | + @since 1.6 | |
| 107 | +*/ | |
| 108 | +public class CompositeDataInvocationHandler implements InvocationHandler { | |
| 109 | + /** | |
| 110 | + <p>Construct a handler backed by the given {@code | |
| 111 | + CompositeData}.</p> | |
| 112 | + | |
| 113 | + @param compositeData the {@code CompositeData} that will supply | |
| 114 | + information to getters. | |
| 115 | + | |
| 116 | + @throws IllegalArgumentException if {@code compositeData} | |
| 117 | + is null. | |
| 118 | + */ | |
| 119 | + public CompositeDataInvocationHandler(CompositeData compositeData) { | |
| 120 | + this(compositeData, null); | |
| 121 | + } | |
| 122 | + | |
| 123 | + /** | |
| 124 | + <p>Construct a handler backed by the given {@code | |
| 125 | + CompositeData}.</p> | |
| 126 | + | |
| 127 | + @param mbsc the {@code MBeanServerConnection} related to this | |
| 128 | + {@code CompositeData}. This is only relevant if a method in | |
| 129 | + the interface for which this is an invocation handler returns | |
| 130 | + a type that is an MXBean interface. Otherwise, it can be null. | |
| 131 | + | |
| 132 | + @param compositeData the {@code CompositeData} that will supply | |
| 133 | + information to getters. | |
| 134 | + | |
| 135 | + @throws IllegalArgumentException if {@code compositeData} | |
| 136 | + is null. | |
| 137 | + */ | |
| 138 | + CompositeDataInvocationHandler(CompositeData compositeData, | |
| 139 | + MXBeanLookup lookup) { | |
| 140 | + if (compositeData == null) | |
| 141 | + throw new IllegalArgumentException("compositeData"); | |
| 142 | + this.compositeData = compositeData; | |
| 143 | + this.lookup = lookup; | |
| 144 | + } | |
| 145 | + | |
| 146 | + /** | |
| 147 | + Return the {@code CompositeData} that was supplied to the | |
| 148 | + constructor. | |
| 149 | + @return the {@code CompositeData} that this handler is backed | |
| 150 | + by. This is never null. | |
| 151 | + */ | |
| 152 | + public CompositeData getCompositeData() { | |
| 153 | + assert compositeData != null; | |
| 154 | + return compositeData; | |
| 155 | + } | |
| 156 | + | |
| 157 | + public Object invoke(Object proxy, Method method, Object[] args) | |
| 158 | + throws Throwable { | |
| 159 | + final String methodName = method.getName(); | |
| 160 | + | |
| 161 | + // Handle the methods from java.lang.Object | |
| 162 | + if (method.getDeclaringClass() == Object.class) { | |
| 163 | + if (methodName.equals("toString") && args == null) | |
| 164 | + return "Proxy[" + compositeData + "]"; | |
| 165 | + else if (methodName.equals("hashCode") && args == null) | |
| 166 | + return compositeData.hashCode() + 0x43444948; | |
| 167 | + else if (methodName.equals("equals") && args.length == 1 | |
| 168 | + && method.getParameterTypes()[0] == Object.class) | |
| 169 | + return equals(proxy, args[0]); | |
| 170 | + else { | |
| 171 | + /* Either someone is calling invoke by hand, or | |
| 172 | + it is a non-final method from Object overriden | |
| 173 | + by the generated Proxy. At the time of writing, | |
| 174 | + the only non-final methods in Object that are not | |
| 175 | + handled above are finalize and clone, and these | |
| 176 | + are not overridden in generated proxies. */ | |
| 177 | + return method.invoke(this, args); | |
| 178 | + } | |
| 179 | + } | |
| 180 | + | |
| 181 | + String propertyName = DefaultMXBeanMappingFactory.propertyName(method); | |
| 182 | + if (propertyName == null) { | |
| 183 | + throw new IllegalArgumentException("Method is not getter: " + | |
| 184 | + method.getName()); | |
| 185 | + } | |
| 186 | + Object openValue; | |
| 187 | + if (compositeData.containsKey(propertyName)) | |
| 188 | + openValue = compositeData.get(propertyName); | |
| 189 | + else { | |
| 190 | + String decap = DefaultMXBeanMappingFactory.decapitalize(propertyName); | |
| 191 | + if (compositeData.containsKey(decap)) | |
| 192 | + openValue = compositeData.get(decap); | |
| 193 | + else { | |
| 194 | + final String msg = | |
| 195 | + "No CompositeData item " + propertyName + | |
| 196 | + (decap.equals(propertyName) ? "" : " or " + decap) + | |
| 197 | + " to match " + methodName; | |
| 198 | + throw new IllegalArgumentException(msg); | |
| 199 | + } | |
| 200 | + } | |
| 201 | + MXBeanMapping mapping = | |
| 202 | + MXBeanMappingFactory.DEFAULT.mappingForType(method.getGenericReturnType(), | |
| 203 | + MXBeanMappingFactory.DEFAULT); | |
| 204 | + return mapping.fromOpenValue(openValue); | |
| 205 | + } | |
| 206 | + | |
| 207 | + /* This method is called when equals(Object) is | |
| 208 | + * called on our proxy and hence forwarded to us. For example, if we | |
| 209 | + * are a proxy for an interface like this: | |
| 210 | + * public interface GetString { | |
| 211 | + * public String string(); | |
| 212 | + * } | |
| 213 | + * then we must compare equal to another CompositeDataInvocationHandler | |
| 214 | + * proxy for the same interface and where string() returns the same value. | |
| 215 | + * | |
| 216 | + * You might think that we should also compare equal to another | |
| 217 | + * object that implements GetString directly rather than using | |
| 218 | + * Proxy, provided that its string() returns the same result as | |
| 219 | + * ours, and in fact an earlier version of this class did that (by | |
| 220 | + * converting the other object into a CompositeData and comparing | |
| 221 | + * that with ours). But in fact that doesn't make a great deal of | |
| 222 | + * sense because there's absolutely no guarantee that the | |
| 223 | + * resulting equals would be reflexive (otherObject.equals(this) | |
| 224 | + * might be false even if this.equals(otherObject) is true), and, | |
| 225 | + * especially, there's no way we could generate a hashCode() that | |
| 226 | + * would be equal to otherObject.hashCode() when | |
| 227 | + * this.equals(otherObject), because we don't know how | |
| 228 | + * otherObject.hashCode() is computed. | |
| 229 | + */ | |
| 230 | + private boolean equals(Object proxy, Object other) { | |
| 231 | + if (other == null) | |
| 232 | + return false; | |
| 233 | + | |
| 234 | + final Class<?> proxyClass = proxy.getClass(); | |
| 235 | + final Class<?> otherClass = other.getClass(); | |
| 236 | + if (proxyClass != otherClass) | |
| 237 | + return false; | |
| 238 | + InvocationHandler otherih = Proxy.getInvocationHandler(other); | |
| 239 | + if (!(otherih instanceof CompositeDataInvocationHandler)) | |
| 240 | + return false; | |
| 241 | + CompositeDataInvocationHandler othercdih = | |
| 242 | + (CompositeDataInvocationHandler) otherih; | |
| 243 | + return compositeData.equals(othercdih.compositeData); | |
| 244 | + } | |
| 245 | + | |
| 246 | + private final CompositeData compositeData; | |
| 247 | + private final MXBeanLookup lookup; | |
| 248 | +} | |