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 | } | ||