Skip to content

Commit a9eae94

Browse files
committed
Add an experimental minimal distribution to be published
- For usage with optimized for size Java and GraalVM native compilation images - Maven groupId is set to org.apache.tomcat.experimental to classify it during evaluation of viability - Does not ship as part of the standard Apache Tomcat library and sources
1 parent b1820ad commit a9eae94

File tree

14 files changed

+1609
-4
lines changed

14 files changed

+1609
-4
lines changed

build.xml

Lines changed: 342 additions & 1 deletion
Large diffs are not rendered by default.

java/org/apache/tomcat/util/IntrospectionUtils.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,10 @@ public static boolean setProperty(Object o, String name, String value,
6060
log.debug("IntrospectionUtils: setProperty(" +
6161
o.getClass() + " " + name + "=" + value + ")");
6262

63+
if (actualMethod == null && XReflectionIntrospectionUtils.isEnabled()) {
64+
return XReflectionIntrospectionUtils.setPropertyInternal(o, name, value, invokeSetProperty);
65+
}
66+
6367
String setter = "set" + capitalize(name);
6468

6569
try {
@@ -222,6 +226,9 @@ else if (c == '\r')
222226
}
223227

224228
public static Object getProperty(Object o, String name) {
229+
if (XReflectionIntrospectionUtils.isEnabled()) {
230+
return XReflectionIntrospectionUtils.getPropertyInternal(o, name);
231+
}
225232
String getter = "get" + capitalize(name);
226233
String isGetter = "is" + capitalize(name);
227234

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
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.tomcat.util;
18+
19+
final class XReflectionIntrospectionUtils {
20+
21+
static boolean isEnabled() {
22+
return false;
23+
}
24+
25+
static Object getPropertyInternal(Object o, String name) {
26+
throw new UnsupportedOperationException();
27+
}
28+
29+
static boolean setPropertyInternal(Object o, String name, String value, boolean invokeSetProperty) {
30+
throw new UnsupportedOperationException();
31+
}
32+
33+
}

res/graal/build-tomcat-native-image.sh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,12 @@ native-image \
5151
--initialize-at-run-time=org.apache,jakarta.servlet \
5252
-H:+TraceClassInitialization \
5353
-H:+PrintClassInitialization \
54+
-H:+PrintAnalysisCallTree \
5455
-H:Name=tc-graal-image \
5556
-H:+ReportExceptionStackTraces \
5657
--allow-incomplete-classpath \
5758
--no-fallback \
58-
-cp ../embed/tomcat-embed-core.jar:../embed/tomcat-embed-websocket.jar:../embed/tomcat-embed-el.jar:tomcat-embedded-sample.jar:../embed/annotations-api.jar \
59+
-cp ../embed/tomcat-embed-programmatic.jar:tomcat-embedded-sample.jar \
5960
org.apache.catalina.startup.EmbeddedTomcat
6061

6162
cd $CURDIR
Lines changed: 281 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,281 @@
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.tomcat.util.xreflection;
18+
19+
20+
import java.io.File;
21+
import java.lang.reflect.Field;
22+
import java.lang.reflect.Method;
23+
import java.lang.reflect.Modifier;
24+
import java.net.InetAddress;
25+
import java.util.Arrays;
26+
import java.util.Collections;
27+
import java.util.HashMap;
28+
import java.util.HashSet;
29+
import java.util.Map;
30+
import java.util.Set;
31+
import java.util.stream.Collectors;
32+
33+
import org.apache.tomcat.util.IntrospectionUtils;
34+
35+
public final class ObjectReflectionPropertyInspector {
36+
37+
public static void main(String... args) throws Exception {
38+
if (args.length == 0) {
39+
System.err.println("Usage:\n\t"+
40+
"org.apache.tomcat.util.xreflection.ObjectReflectionPropertyInspector" +
41+
" <destination directory>"
42+
);
43+
System.exit(1);
44+
}
45+
46+
File outputDir = new File(args[0]);
47+
if (!outputDir.exists() || !outputDir.isDirectory()) {
48+
System.err.println("Invalid output directory: "+ outputDir.getAbsolutePath());
49+
System.exit(1);
50+
}
51+
52+
53+
Set<SetPropertyClass> baseClasses = getKnownClasses()
54+
.stream()
55+
.map(c -> processClass(c))
56+
.collect(Collectors.toSet());
57+
generateCode(
58+
baseClasses,
59+
"org.apache.tomcat.util",
60+
outputDir,
61+
"XReflectionIntrospectionUtils"
62+
);
63+
}
64+
65+
private static final Set<Class<?>> getKnownClasses() throws ClassNotFoundException {
66+
return
67+
Collections.unmodifiableSet(new HashSet<>(
68+
Arrays.asList(
69+
Class.forName("org.apache.catalina.authenticator.jaspic.SimpleAuthConfigProvider"),
70+
Class.forName("org.apache.catalina.authenticator.jaspic.PersistentProviderRegistrations$Property"),
71+
Class.forName("org.apache.catalina.authenticator.jaspic.PersistentProviderRegistrations$Provider"),
72+
Class.forName("org.apache.catalina.connector.Connector"),
73+
Class.forName("org.apache.catalina.core.AprLifecycleListener"),
74+
Class.forName("org.apache.catalina.core.ContainerBase"),
75+
Class.forName("org.apache.catalina.core.StandardContext"),
76+
Class.forName("org.apache.catalina.core.StandardEngine"),
77+
Class.forName("org.apache.catalina.core.StandardHost"),
78+
Class.forName("org.apache.catalina.core.StandardServer"),
79+
Class.forName("org.apache.catalina.core.StandardService"),
80+
Class.forName("org.apache.catalina.filters.AddDefaultCharsetFilter"),
81+
Class.forName("org.apache.catalina.filters.RestCsrfPreventionFilter"),
82+
Class.forName("org.apache.catalina.loader.ParallelWebappClassLoader"),
83+
Class.forName("org.apache.catalina.loader.WebappClassLoaderBase"),
84+
Class.forName("org.apache.catalina.realm.UserDatabaseRealm"),
85+
Class.forName("org.apache.catalina.valves.AccessLogValve"),
86+
Class.forName("org.apache.coyote.AbstractProtocol"),
87+
Class.forName("org.apache.coyote.ajp.AbstractAjpProtocol"),
88+
Class.forName("org.apache.coyote.ajp.AjpAprProtocol"),
89+
Class.forName("org.apache.coyote.ajp.AjpNio2Protocol"),
90+
Class.forName("org.apache.coyote.ajp.AjpNioProtocol"),
91+
Class.forName("org.apache.coyote.http11.AbstractHttp11JsseProtocol"),
92+
Class.forName("org.apache.coyote.http11.AbstractHttp11Protocol"),
93+
Class.forName("org.apache.coyote.http11.Http11AprProtocol"),
94+
Class.forName("org.apache.coyote.http11.Http11Nio2Protocol"),
95+
Class.forName("org.apache.coyote.http11.Http11NioProtocol"),
96+
Class.forName("org.apache.tomcat.util.descriptor.web.ContextResource"),
97+
Class.forName("org.apache.tomcat.util.descriptor.web.ResourceBase"),
98+
Class.forName("org.apache.tomcat.util.modeler.AttributeInfo"),
99+
Class.forName("org.apache.tomcat.util.modeler.FeatureInfo"),
100+
Class.forName("org.apache.tomcat.util.modeler.ManagedBean"),
101+
Class.forName("org.apache.tomcat.util.modeler.OperationInfo"),
102+
Class.forName("org.apache.tomcat.util.modeler.ParameterInfo"),
103+
Class.forName("org.apache.tomcat.util.net.AbstractEndpoint"),
104+
Class.forName("org.apache.tomcat.util.net.AprEndpoint"),
105+
Class.forName("org.apache.tomcat.util.net.Nio2Endpoint"),
106+
Class.forName("org.apache.tomcat.util.net.NioEndpoint"),
107+
Class.forName("org.apache.tomcat.util.net.SocketProperties")
108+
)
109+
)
110+
);
111+
}
112+
113+
//types of properties that IntrospectionUtils.setProperty supports
114+
private static final Set<Class<?>> ALLOWED_TYPES = Collections.unmodifiableSet(new HashSet<>(
115+
Arrays.asList(
116+
Boolean.TYPE,
117+
Integer.TYPE,
118+
Long.TYPE,
119+
String.class,
120+
InetAddress.class
121+
)
122+
));
123+
private static Map<Class<?>, SetPropertyClass> classes = new HashMap<>();
124+
125+
public static void generateCode(Set<SetPropertyClass> baseClasses, String packageName, File location, String className) throws Exception {
126+
String packageDirectory = packageName.replace('.','/');
127+
File sourceFileLocation = new File(location, packageDirectory);
128+
ReflectionLessCodeGenerator.generateCode(sourceFileLocation, className, packageName, baseClasses);
129+
}
130+
131+
132+
private static boolean isAllowedField(Field field) {
133+
return ALLOWED_TYPES.contains(field.getType()) && !Modifier.isFinal(field.getModifiers());
134+
}
135+
136+
private static boolean isAllowedSetMethod(Method method) {
137+
return method.getName().startsWith("set") &&
138+
method.getParameterTypes() != null &&
139+
method.getParameterTypes().length == 1 &&
140+
ALLOWED_TYPES.contains(method.getParameterTypes()[0]) &&
141+
!Modifier.isPrivate(method.getModifiers());
142+
}
143+
144+
private static boolean isAllowedGetMethod(Method method) {
145+
return (method.getName().startsWith("get") || method.getName().startsWith("is")) &&
146+
(method.getParameterTypes() == null ||
147+
method.getParameterTypes().length == 0) &&
148+
ALLOWED_TYPES.contains(method.getReturnType()) &&
149+
!Modifier.isPrivate(method.getModifiers());
150+
}
151+
152+
153+
private static SetPropertyClass getOrCreateSetPropertyClass(Class<?> clazz) {
154+
boolean base = (clazz.getSuperclass() == null || clazz.getSuperclass() == Object.class);
155+
SetPropertyClass spc = classes.get(clazz);
156+
if (spc == null) {
157+
spc = new SetPropertyClass(clazz, base ? null : getOrCreateSetPropertyClass(clazz.getSuperclass()));
158+
classes.put(clazz, spc);
159+
}
160+
return spc;
161+
}
162+
163+
static Method findGetter(Class<?> declaringClass, String propertyName) {
164+
for (String getterName : Arrays.asList("get" + IntrospectionUtils.capitalize(propertyName), "is" + propertyName)) {
165+
try {
166+
Method method = declaringClass.getMethod(getterName);
167+
if (!Modifier.isPrivate(method.getModifiers())) {
168+
return method;
169+
}
170+
} catch (NoSuchMethodException e) {
171+
}
172+
}
173+
try {
174+
Method method = declaringClass.getMethod("getProperty", String.class, String.class);
175+
if (!Modifier.isPrivate(method.getModifiers())) {
176+
return method;
177+
}
178+
} catch (NoSuchMethodException e) {
179+
}
180+
181+
return null;
182+
}
183+
184+
static Method findSetter(Class<?> declaringClass, String propertyName, Class<?> propertyType) {
185+
try {
186+
Method method = declaringClass.getMethod("set" + IntrospectionUtils.capitalize(propertyName), propertyType);
187+
if (!Modifier.isPrivate(method.getModifiers())) {
188+
return method;
189+
}
190+
} catch (NoSuchMethodException e) {
191+
}
192+
try {
193+
Method method = declaringClass.getMethod("setProperty", String.class, String.class);
194+
if (!Modifier.isPrivate(method.getModifiers())) {
195+
return method;
196+
}
197+
} catch (NoSuchMethodException e) {
198+
}
199+
return null;
200+
}
201+
202+
static String decapitalize(String name) {
203+
if (name == null || name.length() == 0) {
204+
return name;
205+
}
206+
if (name.length() > 1 && Character.isUpperCase(name.charAt(1)) &&
207+
Character.isUpperCase(name.charAt(0))) {
208+
return name;
209+
}
210+
char chars[] = name.toCharArray();
211+
chars[0] = Character.toLowerCase(chars[0]);
212+
return new String(chars);
213+
}
214+
215+
216+
static SetPropertyClass processClass(Class<?> clazz) {
217+
SetPropertyClass spc = getOrCreateSetPropertyClass(clazz);
218+
final Method[] methods = clazz.getDeclaredMethods();
219+
for (Method method : methods) {
220+
if (isAllowedSetMethod(method)) {
221+
String propertyName = decapitalize(method.getName().substring(3));
222+
Class<?> propertyType = method.getParameterTypes()[0];
223+
Method getter = findGetter(clazz, propertyName);
224+
Method setter = findSetter(clazz, propertyName, propertyType);
225+
ReflectionProperty property = new ReflectionProperty(
226+
spc.getClazz().getName(),
227+
propertyName,
228+
propertyType,
229+
setter,
230+
getter
231+
);
232+
spc.addProperty(property);
233+
} else if (isAllowedGetMethod(method)) {
234+
boolean startsWithIs = method.getName().startsWith("is");
235+
String propertyName = decapitalize(method.getName().substring(startsWithIs ? 2 : 3));
236+
Class<?> propertyType = method.getReturnType();
237+
Method getter = findGetter(clazz, propertyName);
238+
Method setter = findSetter(clazz, propertyName, propertyType);
239+
ReflectionProperty property = new ReflectionProperty(
240+
spc.getClazz().getName(),
241+
propertyName,
242+
propertyType,
243+
setter,
244+
getter
245+
);
246+
spc.addProperty(property);
247+
}
248+
}
249+
250+
final Field[] fields = clazz.getDeclaredFields();
251+
for (Field field : fields) {
252+
if (isAllowedField(field)) {
253+
Method getter = findGetter(
254+
field.getDeclaringClass(),
255+
IntrospectionUtils.capitalize(field.getName())
256+
);
257+
Method setter = findSetter(
258+
field.getDeclaringClass(),
259+
IntrospectionUtils.capitalize(field.getName()),
260+
field.getType()
261+
);
262+
ReflectionProperty property = new ReflectionProperty(
263+
spc.getClazz().getName(),
264+
field.getName(),
265+
field.getType(),
266+
setter,
267+
getter
268+
);
269+
spc.addProperty(property);
270+
}
271+
}
272+
273+
if (!spc.isBaseClass()) {
274+
SetPropertyClass parent = getOrCreateSetPropertyClass(spc.getClazz().getSuperclass());
275+
parent.addSubClass(spc);
276+
return processClass(parent.getClazz());
277+
} else {
278+
return spc;
279+
}
280+
}
281+
}

0 commit comments

Comments
 (0)