1 /* 2 * Copyright (c) 2015, 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 package jdk.dynalink.beans.test; 26 27 import static jdk.dynalink.StandardNamespace.ELEMENT; 28 import static jdk.dynalink.StandardNamespace.METHOD; 29 import static jdk.dynalink.StandardNamespace.PROPERTY; 30 import static jdk.dynalink.StandardOperation.CALL; 31 import static jdk.dynalink.StandardOperation.GET; 32 import static jdk.dynalink.StandardOperation.SET; 33 34 import java.lang.invoke.MethodHandles; 35 import java.lang.invoke.MethodType; 36 import java.util.ArrayList; 37 import java.util.Collections; 38 import java.util.HashMap; 39 import java.util.Map; 40 import java.util.function.Consumer; 41 import java.util.function.Predicate; 42 import java.util.regex.Pattern; 43 import java.util.stream.Stream; 44 import jdk.dynalink.CallSiteDescriptor; 45 import jdk.dynalink.DynamicLinkerFactory; 46 import jdk.dynalink.Namespace; 47 import jdk.dynalink.NamespaceOperation; 48 import jdk.dynalink.NoSuchDynamicMethodException; 49 import jdk.dynalink.Operation; 50 import jdk.dynalink.beans.StaticClass; 51 import jdk.dynalink.support.SimpleRelinkableCallSite; 52 import org.testng.Assert; 53 import org.testng.annotations.Test; 54 55 public class BeansLinkerTest { 56 public static class Bean1 { 57 public final int answer = 42; 58 59 public String getName() { 60 return "bean1"; 61 } 62 63 public String someMethod(final String x) { 64 return x + "-foo"; 65 } 66 } 67 68 @Test 69 public static void testPublicFieldPropertyUnnamedGetter() { 70 testGetterPermutations(PROPERTY, (op) -> Assert.assertEquals(42, call(op, new Bean1(), "answer"))); 71 } 72 73 @Test 74 public static void testPublicFieldPropertyNamedGetter() { 75 testGetterPermutations(PROPERTY, (op) -> Assert.assertEquals(42, call(op.named("answer"), new Bean1()))); 76 } 77 78 @Test 79 public static void testGetterPropertyUnnamedGetter() { 80 testGetterPermutations(PROPERTY, (op) -> Assert.assertEquals("bean1", call(op, new Bean1(), "name"))); 81 } 82 83 @Test 84 public static void testGetterPropertyNamedGetter() { 85 testGetterPermutations(PROPERTY, (op) -> Assert.assertEquals("bean1", call(op.named("name"), new Bean1()))); 86 } 87 88 @Test 89 public static void testMethodUnnamedGetter() { 90 testGetterPermutations(METHOD, (op) -> Assert.assertEquals("bar-foo", call(call(op, new Bean1(), "someMethod"), new Bean1(), "bar"))); 91 } 92 93 @Test 94 public static void testMethodNamedGetter() { 95 testGetterPermutations(METHOD, (op) -> Assert.assertEquals("bar-foo", call(call(op.named("someMethod"), new Bean1()), new Bean1(), "bar"))); 96 } 97 98 private static final Map<String, String> MAP1 = new HashMap<>(); 99 static { 100 MAP1.put("foo", "bar"); 101 } 102 103 @Test 104 public static void testElementUnnamedGetter() { 105 testGetterPermutations(ELEMENT, (op) -> Assert.assertEquals("bar", call(op, MAP1, "foo"))); 106 } 107 108 @Test 109 public static void testElementNamedGetter() { 110 testGetterPermutations(ELEMENT, (op) -> Assert.assertEquals("bar", call(op.named("foo"), MAP1))); 111 } 112 113 public static class Bean2 { 114 public int answer; 115 private String name; 116 117 public void setName(final String name) { 118 this.name = name; 119 } 120 } 121 122 @Test 123 public static void testUnnamedFieldSetter() { 124 testSetterPermutations(PROPERTY, (op) -> { 125 final Bean2 bean2 = new Bean2(); 126 call(op, bean2, "answer", 12); 127 Assert.assertEquals(bean2.answer, 12); 128 }); 129 } 130 131 @Test 132 public static void testNamedFieldSetter() { 133 testSetterPermutations(PROPERTY, (op) -> { 134 final Bean2 bean2 = new Bean2(); 135 call(op.named("answer"), bean2, 14); 136 Assert.assertEquals(bean2.answer, 14); 137 }); 138 } 139 140 @Test 141 public static void testUnnamedPropertySetter() { 142 testSetterPermutations(PROPERTY, (op) -> { 143 final Bean2 bean2 = new Bean2(); 144 call(op, bean2, "name", "boo"); 145 Assert.assertEquals(bean2.name, "boo"); 146 }); 147 } 148 149 @Test 150 public static void testNamedPropertySetter() { 151 testSetterPermutations(PROPERTY, (op) -> { 152 final Bean2 bean2 = new Bean2(); 153 call(op.named("name"), bean2, "blah"); 154 Assert.assertEquals(bean2.name, "blah"); 155 }); 156 } 157 158 private static final Pattern GET_ELEMENT_THEN_PROPERTY_PATTERN = Pattern.compile(".*ELEMENT.*PROPERTY.*"); 159 160 @Test 161 public static void testUnnamedElementAndPropertyGetter() { 162 final Map<String, Object> map = new HashMap<>(); 163 map.put("empty", true); 164 testGetterPermutations(GET_ELEMENT_THEN_PROPERTY_PATTERN, 4, (op) -> Assert.assertEquals(true, call(op, map, "empty"))); 165 } 166 167 @Test 168 public static void testNamedElementAndPropertyGetter() { 169 final Map<String, Object> map = new HashMap<>(); 170 map.put("empty", true); 171 testGetterPermutations(GET_ELEMENT_THEN_PROPERTY_PATTERN, 4, (op) -> Assert.assertEquals(true, call(op.named("empty"), map))); 172 } 173 174 private static final Pattern GET_PROPERTY_THEN_ELEMENT_PATTERN = Pattern.compile(".*PROPERTY.*ELEMENT.*"); 175 176 @Test 177 public static void testUnnamedPropertyAndElementGetter() { 178 final Map<String, Object> map = new HashMap<>(); 179 map.put("empty", true); 180 testGetterPermutations(GET_PROPERTY_THEN_ELEMENT_PATTERN, 4, (op) -> Assert.assertEquals(false, call(op, map, "empty"))); 181 } 182 183 @Test 184 public static void testNamedPropertyAndElementGetter() { 185 final Map<String, Object> map = new HashMap<>(); 186 map.put("empty", true); 187 testGetterPermutations(GET_PROPERTY_THEN_ELEMENT_PATTERN, 4, (op) -> Assert.assertEquals(false, call(op.named("empty"), map))); 188 } 189 190 public static class MapWithProperty extends HashMap<String, Object> { 191 private String name; 192 193 public void setName(final String name) { 194 this.name = name; 195 } 196 } 197 198 @Test 199 public static void testUnnamedPropertyAndElementSetter() { 200 final MapWithProperty map = new MapWithProperty(); 201 map.put("name", "element"); 202 203 call(SET.withNamespaces(PROPERTY, ELEMENT), map, "name", "property"); 204 Assert.assertEquals("property", map.name); 205 Assert.assertEquals("element", map.get("name")); 206 207 call(SET.withNamespaces(ELEMENT, PROPERTY), map, "name", "element2"); 208 Assert.assertEquals("property", map.name); 209 Assert.assertEquals("element2", map.get("name")); 210 } 211 212 @Test 213 public static void testMissingMembersAtLinkTime() { 214 testPermutations(GETTER_PERMUTATIONS, (op) -> expectNoSuchDynamicMethodException(()-> call(op.named("foo"), new Object()))); 215 testPermutations(SETTER_PERMUTATIONS, (op) -> expectNoSuchDynamicMethodException(()-> call(op.named("foo"), new Object(), "newValue"))); 216 } 217 218 @Test 219 public static void testMissingMembersAtRunTime() { 220 call(GET.withNamespace(ELEMENT), new ArrayList<>(), "foo"); 221 Stream.of(new HashMap(), new ArrayList(), new Object[0]).forEach((receiver) -> { 222 testPermutations(GETTER_PERMUTATIONS, (op) -> { System.err.println(op + " " + receiver.getClass().getName()); Assert.assertNull(call(op, receiver, "foo"));}); 223 // No assertion for the setter; we just expect it to silently succeed 224 testPermutations(SETTER_PERMUTATIONS, (op) -> call(op, receiver, "foo", "newValue")); 225 }); 226 } 227 228 public static class A { 229 public static class Inner {} 230 } 231 232 public static class B extends A { 233 public static class Inner {} 234 } 235 236 @Test 237 public static void testInnerClassGetter() { 238 Object inner1 = call(GET.withNamespace(PROPERTY), StaticClass.forClass(A.class), "Inner"); 239 Assert.assertTrue(inner1 instanceof StaticClass); 240 Assert.assertEquals(A.Inner.class, ((StaticClass) inner1).getRepresentedClass()); 241 242 Object inner2 = call(GET.withNamespace(PROPERTY), StaticClass.forClass(B.class), "Inner"); 243 Assert.assertTrue(inner2 instanceof StaticClass); 244 Assert.assertEquals(B.Inner.class, ((StaticClass) inner2).getRepresentedClass()); 245 } 246 247 private static void expectNoSuchDynamicMethodException(final Runnable r) { 248 try { 249 r.run(); 250 Assert.fail("Should've thrown NoSuchDynamicMethodException"); 251 } catch(final NoSuchDynamicMethodException e) { 252 } 253 } 254 255 private static NamespaceOperation[] GETTER_PERMUTATIONS = new NamespaceOperation[] { 256 GET.withNamespaces(PROPERTY), 257 GET.withNamespaces(METHOD), 258 GET.withNamespaces(ELEMENT), 259 GET.withNamespaces(PROPERTY, ELEMENT), 260 GET.withNamespaces(PROPERTY, METHOD), 261 GET.withNamespaces(ELEMENT, PROPERTY), 262 GET.withNamespaces(ELEMENT, METHOD), 263 GET.withNamespaces(METHOD, PROPERTY), 264 GET.withNamespaces(METHOD, ELEMENT), 265 GET.withNamespaces(PROPERTY, ELEMENT, METHOD), 266 GET.withNamespaces(PROPERTY, METHOD, ELEMENT), 267 GET.withNamespaces(ELEMENT, PROPERTY, METHOD), 268 GET.withNamespaces(ELEMENT, METHOD, PROPERTY), 269 GET.withNamespaces(METHOD, PROPERTY, ELEMENT), 270 GET.withNamespaces(METHOD, ELEMENT, PROPERTY) 271 }; 272 273 private static NamespaceOperation[] SETTER_PERMUTATIONS = new NamespaceOperation[] { 274 SET.withNamespaces(PROPERTY), 275 SET.withNamespaces(ELEMENT), 276 SET.withNamespaces(PROPERTY, ELEMENT), 277 SET.withNamespaces(ELEMENT, PROPERTY) 278 }; 279 280 private static void testPermutations(final NamespaceOperation[] ops, final Operation requiredOp, final Namespace requiredNamespace, final int expectedCount, final Consumer<NamespaceOperation> test) { 281 testPermutationsWithFilter(ops, (op)->NamespaceOperation.contains(op, requiredOp, requiredNamespace), expectedCount, test); 282 } 283 284 private static void testPermutations(final NamespaceOperation[] ops, final Pattern regex, final int expectedCount, final Consumer<NamespaceOperation> test) { 285 testPermutationsWithFilter(ops, (op)->regex.matcher(op.toString()).matches(), expectedCount, test); 286 } 287 288 private static void testPermutations(final NamespaceOperation[] ops, final Consumer<NamespaceOperation> test) { 289 testPermutationsWithFilter(ops, (op)->true, ops.length, test); 290 } 291 292 private static void testPermutationsWithFilter(final NamespaceOperation[] ops, final Predicate<NamespaceOperation> filter, final int expectedCount, final Consumer<NamespaceOperation> test) { 293 final int[] counter = new int[1]; 294 Stream.of(ops).filter(filter).forEach((op)-> { counter[0]++; test.accept(op); }); 295 Assert.assertEquals(counter[0], expectedCount); 296 } 297 298 private static void testGetterPermutations(final Namespace requiredNamespace, final Consumer<NamespaceOperation> test) { 299 testPermutations(GETTER_PERMUTATIONS, GET, requiredNamespace, 11, test); 300 } 301 302 private static void testGetterPermutations(final Pattern regex, final int expectedCount, final Consumer<NamespaceOperation> test) { 303 testPermutations(GETTER_PERMUTATIONS, regex, expectedCount, test); 304 } 305 306 private static void testSetterPermutations(final Namespace requiredNamespace, final Consumer<NamespaceOperation> test) { 307 testPermutations(SETTER_PERMUTATIONS, SET, requiredNamespace, 3, test); 308 } 309 310 private static Object call(final Operation op, final Object... args) { 311 try { 312 return new DynamicLinkerFactory().createLinker().link( 313 new SimpleRelinkableCallSite(new CallSiteDescriptor( 314 MethodHandles.publicLookup(), op, t(args.length)))) 315 .dynamicInvoker().invokeWithArguments(args); 316 } catch (final Error|RuntimeException e) { 317 throw e; 318 } catch (final Throwable t) { 319 throw new RuntimeException(t); 320 } 321 } 322 323 private static Object call(final Object... args) { 324 return call(CALL, args); 325 } 326 327 private static MethodType t(final int argCount) { 328 return MethodType.methodType(Object.class, Collections.nCopies(argCount, Object.class)); 329 } 330 }