1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 package org.apache.commons.jexl3.internal.introspection;
18
19 import java.lang.invoke.MethodHandle;
20 import java.lang.invoke.MethodHandles;
21 import java.lang.invoke.MethodType;
22
23 /**
24 * Utility for Java9+ backport in Java8 of class and module related methods.
25 */
26 final class ClassTool {
27 /** The Class.getModule() method. */
28 private static final MethodHandle GET_MODULE;
29 /** The Class.getPackageName() method. */
30 private static final MethodHandle GET_PKGNAME;
31 /** The Module.isExported(String packageName) method. */
32 private static final MethodHandle IS_EXPORTED;
33
34 static {
35 final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
36 MethodHandle getModule = null;
37 MethodHandle getPackageName = null;
38 MethodHandle isExported = null;
39 try {
40 final Class<?> modulec = ClassTool.class.getClassLoader().loadClass("java.lang.Module");
41 if (modulec != null) {
42 getModule = LOOKUP.findVirtual(Class.class, "getModule", MethodType.methodType(modulec));
43 if (getModule != null) {
44 getPackageName = LOOKUP.findVirtual(Class.class, "getPackageName", MethodType.methodType(String.class));
45 if (getPackageName != null) {
46 isExported = LOOKUP.findVirtual(modulec, "isExported", MethodType.methodType(boolean.class, String.class));
47 }
48 }
49 }
50 } catch (final Exception e) {
51 // ignore all
52 }
53 GET_MODULE = getModule;
54 GET_PKGNAME = getPackageName;
55 IS_EXPORTED = isExported;
56 }
57
58 /**
59 * Gets the package name of a class (class.getPackage() may return null).
60 *
61 * @param clz the class
62 * @return the class package name
63 */
64 static String getPackageName(final Class<?> clz) {
65 String pkgName = "";
66 if (clz != null) {
67 // use native if we can
68 if (GET_PKGNAME != null) {
69 try {
70 return (String) GET_PKGNAME.invoke(clz);
71 } catch (final Throwable xany) {
72 return "";
73 }
74 }
75 // remove array
76 Class<?> clazz = clz;
77 while (clazz.isArray()) {
78 clazz = clazz.getComponentType();
79 }
80 // mimic getPackageName()
81 if (clazz.isPrimitive()) {
82 return "java.lang";
83 }
84 // remove enclosing
85 Class<?> walk = clazz.getEnclosingClass();
86 while (walk != null) {
87 clazz = walk;
88 walk = walk.getEnclosingClass();
89 }
90 final Package pkg = clazz.getPackage();
91 // pkg may be null for unobvious reasons
92 if (pkg == null) {
93 final String name = clazz.getName();
94 final int dot = name.lastIndexOf('.');
95 if (dot > 0) {
96 pkgName = name.substring(0, dot);
97 }
98 } else {
99 pkgName = pkg.getName();
100 }
101 }
102 return pkgName;
103 }
104
105 /**
106 * Checks whether a class is exported by its module (Java 9+).
107 * The code performs the following sequence through reflection (since the same jar can run
108 * on a Java8 or Java9+ runtime and the module features does not exist on 8).
109 * {@code
110 * Module module = declarator.getModule();
111 * return module.isExported(declarator.getPackageName());
112 * }
113 * This is required since some classes and methods may not be exported thus not callable through
114 * reflection.
115 *
116 * @param declarator the class
117 * @return true if class is exported or no module support exists
118 */
119 static boolean isExported(final Class<?> declarator) {
120 if (IS_EXPORTED != null) {
121 try {
122 final Object module = GET_MODULE.invoke(declarator);
123 if (module != null) {
124 final String pkgName = (String) GET_PKGNAME.invoke(declarator);
125 return (Boolean) IS_EXPORTED.invoke(module, pkgName);
126 }
127 } catch (final Throwable e) {
128 // ignore
129 }
130 }
131 return true;
132 }
133
134 }