This is an automated email from the ASF dual-hosted git repository. fhanik pushed a commit to branch 9.0.x in repository https://gitbox.apache.org/repos/asf/tomcat.git
The following commit(s) were added to refs/heads/9.0.x by this push: new d653016 Add an experimental minimal distribution to be published d653016 is described below commit d65301648aae1d1de4cb227d9305f26ea6e08da1 Author: Filip Hanik <fha...@pivotal.io> AuthorDate: Mon Jul 27 13:35:03 2020 -0700 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 --- build.xml | 343 +++++++++++++++- .../org/apache/tomcat/util/IntrospectionUtils.java | 7 + .../tomcat/util/XReflectionIntrospectionUtils.java | 33 ++ res/graal/build-tomcat-native-image.sh | 3 +- .../ObjectReflectionPropertyInspector.java | 282 +++++++++++++ .../xreflection/ReflectionLessCodeGenerator.java | 273 +++++++++++++ .../util/xreflection/ReflectionProperty.java | 119 ++++++ .../tomcat/util/xreflection/SetPropertyClass.java | 436 +++++++++++++++++++++ .../native-image/native-image.properties | 16 + .../native-image/tomcat-reflection.json | 2 + .../native-image/tomcat-resource.json | 51 +++ res/maven/mvn-pub.xml | 10 + res/maven/tomcat-embed-programmatic.pom | 35 ++ .../apache/catalina/startup/EmbeddedTomcat.java | 4 +- 14 files changed, 1610 insertions(+), 4 deletions(-) diff --git a/build.xml b/build.xml index 8e02cb5..bd87179 100644 --- a/build.xml +++ b/build.xml @@ -153,11 +153,13 @@ <!-- Embedded JARs & source JARs --> <property name="tomcat-embed-core.jar" value="${tomcat.embed}/tomcat-embed-core.jar"/> + <property name="tomcat-embed-programmatic.jar" value="${tomcat.embed}/tomcat-embed-programmatic.jar"/> <property name="tomcat-embed-jasper.jar" value="${tomcat.embed}/tomcat-embed-jasper.jar"/> <property name="tomcat-embed-el.jar" value="${tomcat.embed}/tomcat-embed-el.jar"/> <property name="tomcat-embed-websocket.jar" value="${tomcat.embed}/tomcat-embed-websocket.jar"/> <property name="tomcat-embed-core-sources.jar" value="${tomcat.embed.sources}/tomcat-embed-core-src.jar"/> + <property name="tomcat-embed-programmatic-sources.jar" value="${tomcat.embed.sources}/tomcat-embed-programmatic-src.jar"/> <property name="tomcat-embed-jasper-sources.jar" value="${tomcat.embed.sources}/tomcat-embed-jasper-src.jar"/> <property name="tomcat-embed-el-sources.jar" value="${tomcat.embed.sources}/tomcat-embed-el-src.jar"/> <property name="tomcat-embed-websocket-sources.jar" value="${tomcat.embed.sources}/tomcat-embed-websocket-src.jar"/> @@ -427,6 +429,268 @@ <exclude name="org/apache/catalina/ssi/**" /> </patternset> + <patternset id="files.tomcat-embed-programmatic"> + <patternset refid="files.jaspic-api" /> + <patternset refid="files.tomcat-juli" /> + + <!-- Servlet --> + <include name="javax/servlet/**" /> + <include name="javax/servlet/annotation/**" /> + <include name="javax/servlet/http/**" /> + <exclude name="javax/servlet/descriptor/**" /> + <exclude name="javax/servlet/jsp/**"/> + <exclude name="javax/servlet/resources/**" /> + + <!-- AprLifecycleListener is configured by default --> + <include name="**/AprLifecycleListener.*"/> + <include name="**/AprStatus.*"/> + + <!-- Remove dynamic mbean descriptors --> + <exclude name="**/mbeans-descriptors.xml"/> + <exclude name="**/mbeans-descriptors.dtd"/> + <!-- No JMX Support - But we can't remove the classes --> + <include name="org/apache/tomcat/util/modeler/LocalStrings.properties" /> + <include name="org/apache/tomcat/util/modeler/AttributeInfo*" /> + <include name="org/apache/tomcat/util/modeler/BaseModelMBean*" /> + <include name="org/apache/tomcat/util/modeler/FeatureInfo*" /> + <include name="org/apache/tomcat/util/modeler/ManagedBean*" /> + <include name="org/apache/tomcat/util/modeler/NoDescriptorRegistry*" /> + <include name="org/apache/tomcat/util/modeler/NotificationInfo*" /> + <include name="org/apache/tomcat/util/modeler/Registry*" /> + <include name="org/apache/tomcat/util/modeler/RegistryMBean*" /> + <include name="org/apache/tomcat/util/modeler/Util*" /> + <include name="org/apache/tomcat/util/modeler/modules/ModelerSource*" /> + + <!-- Apache Tomcat Core Code --> + <include name="org/apache/catalina/*"/> + <include name="org/apache/catalina/authenticator/**"/> + <include name="org/apache/catalina/connector/**"/> + <include name="org/apache/catalina/core/**"/> + <include name="org/apache/catalina/deploy/**"/> + <include name="org/apache/catalina/filters/**"/> + <include name="org/apache/catalina/loader/**"/> + <include name="org/apache/catalina/mapper/**"/> + <include name="org/apache/catalina/mbeans/**"/> + <include name="org/apache/catalina/realm/**"/> + <include name="org/apache/catalina/security/**"/> + <include name="org/apache/catalina/session/**"/> + <include name="org/apache/catalina/startup/**"/> + <include name="org/apache/catalina/util/**"/> + <include name="org/apache/catalina/valves/*"/> + <include name="org/apache/catalina/webresources/**"/> + <include name="org/apache/coyote/**" /> + <include name="org/apache/naming/**" /> + <include name="org/apache/tomcat/*" /> + <include name="org/apache/tomcat/util/*" /> + <include name="org/apache/tomcat/util/bcel/**" /> + <include name="org/apache/tomcat/util/buf/**" /> + <include name="org/apache/tomcat/util/codec/**" /> + <include name="org/apache/tomcat/util/collections/**" /> + <include name="org/apache/tomcat/util/compat/**" /> + <include name="org/apache/tomcat/util/descriptor/**" /> + <include name="org/apache/tomcat/util/file/**" /> + <include name="org/apache/tomcat/util/http/**" /> + <include name="org/apache/tomcat/util/json/**" /> + <include name="org/apache/tomcat/util/log/**" /> + <include name="org/apache/tomcat/util/net/**" /> + <include name="org/apache/tomcat/util/res/**" /> + <include name="org/apache/tomcat/util/security/**" /> + <include name="org/apache/tomcat/util/threads/**" /> + + <!-- Some of the core code depends on the jar scanner --> + <include name="org/apache/tomcat/util/scan/StandardJarScan*" /> + <include name="org/apache/tomcat/util/scan/LocalStrings.properties" /> + + <!-- Minimize JNI to what doesn't throw an error --> + <include name="org/apache/tomcat/jni/Library.class" /> + <include name="org/apache/tomcat/jni/LibraryNotFoundError*" /> + <exclude name="org/apache/tomcat/jni" /> + + <!-- Remove connectors that are not default --> + <exclude name="**/AbstractAjpProtocol.class"/> + <exclude name="**/Ajp*.class"/> + <exclude name="**/AjpApr*.class"/> + <exclude name="**/Http11Apr*.class"/> + <exclude name="**/Http11Nio2*.class"/> + <exclude name="**/JniLifecycleListener.class"/> + <exclude name="org/apache/tomcat/**/*Nio2*.class"/> + <exclude name="org/apache/tomcat/**/Apr*.class"/> + + <!-- Minimize bundles to the default set --> + <exclude name="org/apache/**/LocalStrings_*.properties"/> + + <!-- Remove digester support --> + <exclude name="org/apache/catalina/**/*Rule.class" /> + <exclude name="org/apache/catalina/**/*RuleSet*.class" /> + <exclude name="org/apache/tomcat/**/*RuleSet*.class" /> + + <!-- Remove various Apache Tomcat modules not used by default in an embedded setting --> + <exclude name="org/apache/catalina/ant/**" /> + <exclude name="org/apache/catalina/ha/**" /> + <exclude name="org/apache/catalina/manager/**" /> + <exclude name="org/apache/catalina/ssi/**" /> + <exclude name="org/apache/catalina/storeconfig/**" /> + <exclude name="org/apache/catalina/tribes/**" /> + <exclude name="org/apache/catalina/valves/rewrite/**" /> + <exclude name="org/apache/tomcat/buildutil" /> + <exclude name="org/apache/tomcat/dbcp" /> + + <!-- Remove authenticator classes --> + <exclude name="org/apache/catalina/authenticator/BasicAuthenticator*" /> + <exclude name="org/apache/catalina/authenticator/DigestAuthenticator*" /> + <exclude name="org/apache/catalina/authenticator/FormAuthenticator*" /> + <exclude name="org/apache/catalina/authenticator/SingleSignOnE*" /> + <exclude name="org/apache/catalina/authenticator/SingleSignOnL*" /> + <exclude name="org/apache/catalina/authenticator/SingleSignOnS*" /> + <exclude name="org/apache/catalina/authenticator/Spnego*" /> + <exclude name="org/apache/catalina/authenticator/SSLAuthenticator*" /> + <exclude name="org/apache/catalina/authenticator/jaspcic/Simple*" /> + + <!-- Remove optional filter classes --> + <exclude name="org/apache/catalina/filters/**" /> + + <!-- Remove Realm classes --> + <exclude name="org/apache/catalina/realm/AuthenticatedUserRealm.class" /> + <exclude name="org/apache/catalina/realm/CombinedRealm.class" /> + <exclude name="org/apache/catalina/realm/DataSourceRealm.class" /> + <exclude name="org/apache/catalina/realm/JAASCallbackHandler.class" /> + <exclude name="org/apache/catalina/realm/JAASMemoryLoginModule.class" /> + <exclude name="org/apache/catalina/realm/JAASRealm.class" /> + <exclude name="org/apache/catalina/realm/JDBCRealm.class" /> + <exclude name="org/apache/catalina/realm/JNDIRealm$User.class" /> + <exclude name="org/apache/catalina/realm/JNDIRealm.class" /> + <exclude name="org/apache/catalina/realm/LockOutRealm*" /> + <exclude name="org/apache/catalina/realm/MemoryRealm.class" /> + <exclude name="org/apache/catalina/realm/NestedCredentialHandler.class" /> + <exclude name="org/apache/catalina/realm/UserDatabaseRealm.class" /> + + <!-- Remove optional servlets --> + <exclude name="org/apache/catalina/servlets/**" /> + + <!-- Remove all but the standard session management --> + <exclude name="org/apache/catalina/session/JDBC*" /> + <exclude name="org/apache/catalina/session/File*" /> + <exclude name="org/apache/catalina/session/Persistent*" /> + <exclude name="org/apache/catalina/session/StoreBase*" /> + + <!-- Remove users --> + <exclude name="org/apache/catalina/users/**" /> + + <!-- Remove optional valves --> + <exclude name="org/apache/catalina/valves/Crawler*" /> + <exclude name="org/apache/catalina/valves/ExtendedAccessLogValve*" /> + <exclude name="org/apache/catalina/valves/ExtendedAccessLogValve*" /> + <exclude name="org/apache/catalina/valves/HealthCheckValve*" /> + <exclude name="org/apache/catalina/valves/JDBCAccessLogValve*" /> + <exclude name="org/apache/catalina/valves/LoadBalancerDrainingValve*" /> + <exclude name="org/apache/catalina/valves/PersistentValve*" /> + + <!-- Remove web resources classes, default is the EmptyResourceSet --> + <exclude name="org/apache/catalina/webresources/AbstractArchiveResource*" /> + <exclude name="org/apache/catalina/webresources/AbstractSingleArchiveResource*" /> + <exclude name="org/apache/catalina/webresources/ExtractingRoot*" /> + <exclude name="org/apache/catalina/webresources/FileResource*" /> + <exclude name="org/apache/catalina/webresources/JarResource*" /> + <exclude name="org/apache/catalina/webresources/JarWarResource*" /> + <exclude name="org/apache/catalina/webresources/TomcatJarInputStream*" /> + <exclude name="org/apache/catalina/webresources/TrackedInputStream*" /> + <exclude name="org/apache/catalina/webresources/VirtualResource*" /> + <exclude name="org/apache/catalina/webresources/WarResource*" /> + <exclude name="org/apache/catalina/webresources/WarResource*" /> + <exclude name="org/apache/catalina/webresources/war/WarURLConnection*" /> + + <!-- Remove most JNDI stuff --> + <exclude name="org/apache/naming/EjbRef*" /> + <exclude name="org/apache/naming/factory/BeanFactory*" /> + <exclude name="org/apache/naming/factory/DataSourceLinkFactory*" /> + <exclude name="org/apache/naming/factory/EjbFactory*" /> + <exclude name="org/apache/naming/factory/LookupFactory*" /> + <exclude name="org/apache/naming/factory/MailSessionFactory*" /> + <exclude name="org/apache/naming/factory/OpenEjbFactory*" /> + <exclude name="org/apache/naming/factory/ResourceEnvFactory*" /> + <exclude name="org/apache/naming/factory/ResourceFactory*" /> + <exclude name="org/apache/naming/factory/ResourceLinkFactory*" /> + <exclude name="org/apache/naming/factory/SendMailFactory*" /> + <exclude name="org/apache/naming/factory/TransactionFactory*" /> + <exclude name="org/apache/naming/factory/webservices/*" /> + <exclude name="org/apache/naming/LookupRef*" /> + <exclude name="org/apache/naming/NameParserImpl*" /> + <exclude name="org/apache/naming/NamingContextBindingsEnumeration*" /> + <exclude name="org/apache/naming/NamingContext*" /> + <exclude name="org/apache/naming/NamingContextEnumeration*" /> + <exclude name="org/apache/naming/NamingEntry*" /> + <exclude name="org/apache/naming/ResourceEnvRef*" /> + <exclude name="org/apache/naming/ResourceLinkRef*" /> + <exclude name="org/apache/naming/ResourceRef*" /> + <exclude name="org/apache/naming/SelectorContext*" /> + <exclude name="org/apache/naming/ServiceRef*" /> + <exclude name="org/apache/naming/TransactionRef*" /> + + <!-- remove BCEL support --> + <exclude name="org/apache/tomcat/util/bcel/**" /> + + <!-- remove web descriptor elements --> + <exclude name="org/apache/tomcat/util/descriptor/DigesterFactory*" /> + <exclude name="org/apache/tomcat/util/descriptor/InputSourceUtil*" /> + <exclude name="org/apache/tomcat/util/descriptor/LocalResolver*" /> + <exclude name="org/apache/tomcat/util/descriptor/web/AbsoluteOrderingRule*" /> + <exclude name="org/apache/tomcat/util/descriptor/web/CallMethodMultiRule*" /> + <exclude name="org/apache/tomcat/util/descriptor/web/CallParamMultiRule*" /> + <exclude name="org/apache/tomcat/util/descriptor/web/ContextHandler*" /> + <exclude name="org/apache/tomcat/util/descriptor/web/ContextResourceLink*" /> + <exclude name="org/apache/tomcat/util/descriptor/web/ContextTransaction*" /> + <exclude name="org/apache/tomcat/util/descriptor/web/FragmentJarScannerCallback*" /> + <exclude name="org/apache/tomcat/util/descriptor/web/IgnoreAnnotationsRule*" /> + <exclude name="org/apache/tomcat/util/descriptor/web/InjectionTarget*" /> + <exclude name="org/apache/tomcat/util/descriptor/web/JspConfigDescriptorImpl*" /> + <exclude name="org/apache/tomcat/util/descriptor/web/JspPropertyGroup*" /> + <exclude name="org/apache/tomcat/util/descriptor/web/JspPropertyGroupDescriptorImpl*" /> + <exclude name="org/apache/tomcat/util/descriptor/web/LifecycleCallbackRule*" /> + <exclude name="org/apache/tomcat/util/descriptor/web/MappedNameRule*" /> + <exclude name="org/apache/tomcat/util/descriptor/web/MultipartDef*" /> + <exclude name="org/apache/tomcat/util/descriptor/web/NameRule*" /> + <exclude name="org/apache/tomcat/util/descriptor/web/RelativeOrderingRule*" /> + <exclude name="org/apache/tomcat/util/descriptor/web/SecurityCollection*" /> + <exclude name="org/apache/tomcat/util/descriptor/web/SecurityRoleRef*" /> + <exclude name="org/apache/tomcat/util/descriptor/web/ServiceQnameRule*" /> + <exclude name="org/apache/tomcat/util/descriptor/web/ServletDef*" /> + <exclude name="org/apache/tomcat/util/descriptor/web/ServletDefCreateRule*" /> + <exclude name="org/apache/tomcat/util/descriptor/web/SessionConfig*" /> + <exclude name="org/apache/tomcat/util/descriptor/web/SetAuthConstraintRule*" /> + <exclude name="org/apache/tomcat/util/descriptor/web/SetDenyUncoveredHttpMethodsRule*" /> + <exclude name="org/apache/tomcat/util/descriptor/web/SetDistributableRule*" /> + <exclude name="org/apache/tomcat/util/descriptor/web/SetJspConfig*" /> + <exclude name="org/apache/tomcat/util/descriptor/web/SetLoginConfig*" /> + <exclude name="org/apache/tomcat/util/descriptor/web/SetOverrideRule*" /> + <exclude name="org/apache/tomcat/util/descriptor/web/SetPublicIdRule*" /> + <exclude name="org/apache/tomcat/util/descriptor/web/SetSessionConfig*" /> + <exclude name="org/apache/tomcat/util/descriptor/web/SoapHeaderRule*" /> + <exclude name="org/apache/tomcat/util/descriptor/web/TaglibDescriptorImpl*" /> + <exclude name="org/apache/tomcat/util/descriptor/web/TaglibLocationRule*" /> + <exclude name="org/apache/tomcat/util/descriptor/web/VersionRule*" /> + <exclude name="org/apache/tomcat/util/descriptor/web/WebXml*" /> + <exclude name="org/apache/tomcat/util/descriptor/web/WebXmlParser*" /> + <exclude name="org/apache/tomcat/util/descriptor/XmlErrorHandler*" /> + <exclude name="org/apache/tomcat/util/descriptor/XmlIdentifiers*" /> + + <!-- remove taglibrary support --> + <exclude name="org/apache/tomcat/util/descriptor/tagplugin/**"/> + <exclude name="org/apache/tomcat/util/descriptor/tld/**"/> + + <!-- Remove Digester Support --> + <exclude name="org/apache/tomcat/util/digester/**"/> + + <!-- TODO - For now, remove SSL until we have an example --> + <exclude name="org/apache/tomcat/util/net/jsse/**"/> + <exclude name="org/apache/tomcat/util/net/openssl/**"/> + + <!-- Remove without-reflection code generator --> + <exclude name="org/apache/tomcat/util/xreflection/**" /> + + <!-- Remove DefaultServlet --> + <exclude name="org/apache/catalina/servlets/DefaultServlet*.*" /> + </patternset> + <patternset id="files.catalina-tribes"> <include name="org/apache/catalina/tribes/**" /> </patternset> @@ -1435,9 +1699,86 @@ </target> + <target name="embed-programmatic-jar" description="Create non reflection embedded jar" + depends="embed-jars,embed-sources"> + + <!-- temporary directory to store the generated code --> + <tempfile property="xreflect.directory" destDir="${java.io.tmpdir}" prefix="apache-tomcat-xreflect-"/> + <echo>Generating Reflection Less Introspection to: ${xreflect.directory}</echo> + <mkdir dir="${xreflect.directory}"/> + <copy todir="${xreflect.directory}/sources"> + <fileset dir="java"/> + <fileset dir="res/graal/java"/> + </copy> + <copy todir="${xreflect.directory}/classes"> + <fileset dir="${tomcat.classes}"/> + </copy> + + <!-- Compile the sources needed to generate XReflectionIntrospectionUtils --> + <javac includes="org/apache/tomcat/util/xreflection/*.java" + srcdir="${xreflect.directory}/sources" + destdir="${xreflect.directory}/classes" + debug="${compile.debug}" + deprecation="${compile.deprecation}" + source="${compile.source}" + target="${compile.target}" + encoding="ISO-8859-1" + includeAntRuntime="true" > + <compilerarg value="-XDignore.symbol.file"/> + <classpath refid="compile.classpath" /> + </javac> + + <!-- Generate the reflection less XReflectionIntrospectionUtils .java file --> + <java classname="org.apache.tomcat.util.xreflection.ObjectReflectionPropertyInspector" + classpath="${xreflect.directory}/classes"> + <arg value="${xreflect.directory}/sources"/> + </java> + + <!-- Compile the generated XReflectionIntrospectionUtils.java class --> + <delete file="${xreflect.directory}/classes/org/apache/tomcat/util/XReflectionIntrospectionUtils.class"/> + <javac includes="org/apache/tomcat/util/XReflectionIntrospectionUtils.java" + srcdir="${xreflect.directory}/sources" + destdir="${xreflect.directory}/classes" + debug="${compile.debug}" + deprecation="${compile.deprecation}" + source="${compile.source}" + target="${compile.target}" + encoding="ISO-8859-1" + includeAntRuntime="true" > + <compilerarg value="-XDignore.symbol.file"/> + <classpath refid="compile.classpath" /> + </javac> + + <!-- Create the binary JAR file --> + <jarIt jarfile="${tomcat-embed-programmatic.jar}" + filesDir="${xreflect.directory}/classes" + filesId="files.tomcat-embed-programmatic" + notice="${tomcat.manifests}/servlet-api.jar.notice" + license="${tomcat.manifests}/servlet-api.jar.license" + addOSGi="false" + addGraal="true" + graalPrefix="org.apache.tomcat.embed/tomcat-embed-programmatic" + graalFiles="res/graal/tomcat-embed-programmatic/native-image" + libs="embed/*.jar"/> + + <!-- Create the source JAR file --> + <jarIt jarfile="${tomcat-embed-programmatic-sources.jar}" + filesDir="${xreflect.directory}/sources" + filesId="files.tomcat-embed-programmatic" + notice="${tomcat.manifests}/servlet-api.jar.notice" + license="${tomcat.manifests}/servlet-api.jar.license"/> + + <!-- Leave generated code in an easy place for review --> + <copy todir="${java.io.tmpdir}" + file="${xreflect.directory}/sources/org/apache/tomcat/util/XReflectionIntrospectionUtils.java" + overwrite="true"/> + <!-- Delete our temporary compilation directory --> + <delete dir="${xreflect.directory}"/> + </target> + <target name="embed" description="Creates the experimental embedded release" - depends="embed-jars,embed-sources" > + depends="embed-jars,embed-sources,embed-programmatic-jar" > <fixcrlf srcdir="${tomcat.embed}" eol="crlf" encoding="ISO-8859-1" fixlast="false" > diff --git a/java/org/apache/tomcat/util/IntrospectionUtils.java b/java/org/apache/tomcat/util/IntrospectionUtils.java index 1d830e1..e1cb4b3 100644 --- a/java/org/apache/tomcat/util/IntrospectionUtils.java +++ b/java/org/apache/tomcat/util/IntrospectionUtils.java @@ -60,6 +60,10 @@ public final class IntrospectionUtils { log.debug("IntrospectionUtils: setProperty(" + o.getClass() + " " + name + "=" + value + ")"); + if (actualMethod == null && XReflectionIntrospectionUtils.isEnabled()) { + return XReflectionIntrospectionUtils.setPropertyInternal(o, name, value, invokeSetProperty); + } + String setter = "set" + capitalize(name); try { @@ -222,6 +226,9 @@ public final class IntrospectionUtils { } public static Object getProperty(Object o, String name) { + if (XReflectionIntrospectionUtils.isEnabled()) { + return XReflectionIntrospectionUtils.getPropertyInternal(o, name); + } String getter = "get" + capitalize(name); String isGetter = "is" + capitalize(name); diff --git a/java/org/apache/tomcat/util/XReflectionIntrospectionUtils.java b/java/org/apache/tomcat/util/XReflectionIntrospectionUtils.java new file mode 100644 index 0000000..88de048 --- /dev/null +++ b/java/org/apache/tomcat/util/XReflectionIntrospectionUtils.java @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.tomcat.util; + +final class XReflectionIntrospectionUtils { + + static boolean isEnabled() { + return false; + } + + static Object getPropertyInternal(Object o, String name) { + throw new UnsupportedOperationException(); + } + + static boolean setPropertyInternal(Object o, String name, String value, boolean invokeSetProperty) { + throw new UnsupportedOperationException(); + } + +} diff --git a/res/graal/build-tomcat-native-image.sh b/res/graal/build-tomcat-native-image.sh index 082e129..e557927 100755 --- a/res/graal/build-tomcat-native-image.sh +++ b/res/graal/build-tomcat-native-image.sh @@ -51,11 +51,12 @@ native-image \ --initialize-at-run-time=org.apache,javax.servlet \ -H:+TraceClassInitialization \ -H:+PrintClassInitialization \ +-H:+PrintAnalysisCallTree \ -H:Name=tc-graal-image \ -H:+ReportExceptionStackTraces \ --allow-incomplete-classpath \ --no-fallback \ --cp ../embed/tomcat-embed-core.jar:../embed/tomcat-embed-websocket.jar:../embed/tomcat-embed-el.jar:tomcat-embedded-sample.jar:../embed/annotations-api.jar \ +-cp ../embed/tomcat-embed-programmatic.jar:tomcat-embedded-sample.jar \ org.apache.catalina.startup.EmbeddedTomcat cd $CURDIR \ No newline at end of file diff --git a/res/graal/java/org/apache/tomcat/util/xreflection/ObjectReflectionPropertyInspector.java b/res/graal/java/org/apache/tomcat/util/xreflection/ObjectReflectionPropertyInspector.java new file mode 100644 index 0000000..f2ca6fe --- /dev/null +++ b/res/graal/java/org/apache/tomcat/util/xreflection/ObjectReflectionPropertyInspector.java @@ -0,0 +1,282 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.tomcat.util.xreflection; + + +import java.io.File; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.net.InetAddress; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +import org.apache.tomcat.util.IntrospectionUtils; + +public final class ObjectReflectionPropertyInspector { + + public static void main(String... args) throws Exception { + if (args.length == 0) { + System.err.println("Usage:\n\t"+ + "org.apache.tomcat.util.xreflection.ObjectReflectionPropertyInspector" + + " <destination directory>" + ); + System.exit(1); + } + + File outputDir = new File(args[0]); + if (!outputDir.exists() || !outputDir.isDirectory()) { + System.err.println("Invalid output directory: "+ outputDir.getAbsolutePath()); + System.exit(1); + } + + + Set<SetPropertyClass> baseClasses = getKnownClasses() + .stream() + .map(c -> processClass(c)) + .collect(Collectors.toSet()); + generateCode( + baseClasses, + "org.apache.tomcat.util", + outputDir, + "XReflectionIntrospectionUtils" + ); + } + + private static final Set<Class<?>> getKnownClasses() throws ClassNotFoundException { + return + Collections.unmodifiableSet(new HashSet<>( + Arrays.asList( + Class.forName("org.apache.catalina.authenticator.jaspic.SimpleAuthConfigProvider"), + Class.forName("org.apache.catalina.authenticator.jaspic.PersistentProviderRegistrations$Property"), + Class.forName("org.apache.catalina.authenticator.jaspic.PersistentProviderRegistrations$Provider"), + Class.forName("org.apache.catalina.connector.Connector"), + Class.forName("org.apache.catalina.core.AprLifecycleListener"), + Class.forName("org.apache.catalina.core.ContainerBase"), + Class.forName("org.apache.catalina.core.StandardContext"), + Class.forName("org.apache.catalina.core.StandardEngine"), + Class.forName("org.apache.catalina.core.StandardHost"), + Class.forName("org.apache.catalina.core.StandardServer"), + Class.forName("org.apache.catalina.core.StandardService"), + Class.forName("org.apache.catalina.filters.AddDefaultCharsetFilter"), + Class.forName("org.apache.catalina.filters.RestCsrfPreventionFilter"), + Class.forName("org.apache.catalina.loader.ParallelWebappClassLoader"), + Class.forName("org.apache.catalina.loader.WebappClassLoaderBase"), + Class.forName("org.apache.catalina.realm.UserDatabaseRealm"), + Class.forName("org.apache.catalina.valves.AccessLogValve"), + Class.forName("org.apache.coyote.AbstractProtocol"), + Class.forName("org.apache.coyote.ajp.AbstractAjpProtocol"), + Class.forName("org.apache.coyote.ajp.AjpAprProtocol"), + Class.forName("org.apache.coyote.ajp.AjpNio2Protocol"), + Class.forName("org.apache.coyote.ajp.AjpNioProtocol"), + Class.forName("org.apache.coyote.http11.AbstractHttp11JsseProtocol"), + Class.forName("org.apache.coyote.http11.AbstractHttp11Protocol"), + Class.forName("org.apache.coyote.http11.Http11AprProtocol"), + Class.forName("org.apache.coyote.http11.Http11Nio2Protocol"), + Class.forName("org.apache.coyote.http11.Http11NioProtocol"), + Class.forName("org.apache.tomcat.util.descriptor.web.ContextResource"), + Class.forName("org.apache.tomcat.util.descriptor.web.ResourceBase"), + Class.forName("org.apache.tomcat.util.modeler.AttributeInfo"), + Class.forName("org.apache.tomcat.util.modeler.FeatureInfo"), + Class.forName("org.apache.tomcat.util.modeler.ManagedBean"), + Class.forName("org.apache.tomcat.util.modeler.OperationInfo"), + Class.forName("org.apache.tomcat.util.modeler.ParameterInfo"), + Class.forName("org.apache.tomcat.util.net.AbstractEndpoint"), + Class.forName("org.apache.tomcat.util.net.AprEndpoint"), + Class.forName("org.apache.tomcat.util.net.Nio2Endpoint"), + Class.forName("org.apache.tomcat.util.net.NioEndpoint"), + Class.forName("org.apache.tomcat.util.net.NioSelectorPool"), + Class.forName("org.apache.tomcat.util.net.SocketProperties") + ) + ) + ); + } + + //types of properties that IntrospectionUtils.setProperty supports + private static final Set<Class<?>> ALLOWED_TYPES = Collections.unmodifiableSet(new HashSet<>( + Arrays.asList( + Boolean.TYPE, + Integer.TYPE, + Long.TYPE, + String.class, + InetAddress.class + ) + )); + private static Map<Class<?>, SetPropertyClass> classes = new HashMap<>(); + + public static void generateCode(Set<SetPropertyClass> baseClasses, String packageName, File location, String className) throws Exception { + String packageDirectory = packageName.replace('.','/'); + File sourceFileLocation = new File(location, packageDirectory); + ReflectionLessCodeGenerator.generateCode(sourceFileLocation, className, packageName, baseClasses); + } + + + private static boolean isAllowedField(Field field) { + return ALLOWED_TYPES.contains(field.getType()) && !Modifier.isFinal(field.getModifiers()); + } + + private static boolean isAllowedSetMethod(Method method) { + return method.getName().startsWith("set") && + method.getParameterTypes() != null && + method.getParameterTypes().length == 1 && + ALLOWED_TYPES.contains(method.getParameterTypes()[0]) && + !Modifier.isPrivate(method.getModifiers()); + } + + private static boolean isAllowedGetMethod(Method method) { + return (method.getName().startsWith("get") || method.getName().startsWith("is")) && + (method.getParameterTypes() == null || + method.getParameterTypes().length == 0) && + ALLOWED_TYPES.contains(method.getReturnType()) && + !Modifier.isPrivate(method.getModifiers()); + } + + + private static SetPropertyClass getOrCreateSetPropertyClass(Class<?> clazz) { + boolean base = (clazz.getSuperclass() == null || clazz.getSuperclass() == Object.class); + SetPropertyClass spc = classes.get(clazz); + if (spc == null) { + spc = new SetPropertyClass(clazz, base ? null : getOrCreateSetPropertyClass(clazz.getSuperclass())); + classes.put(clazz, spc); + } + return spc; + } + + static Method findGetter(Class<?> declaringClass, String propertyName) { + for (String getterName : Arrays.asList("get" + IntrospectionUtils.capitalize(propertyName), "is" + propertyName)) { + try { + Method method = declaringClass.getMethod(getterName); + if (!Modifier.isPrivate(method.getModifiers())) { + return method; + } + } catch (NoSuchMethodException e) { + } + } + try { + Method method = declaringClass.getMethod("getProperty", String.class, String.class); + if (!Modifier.isPrivate(method.getModifiers())) { + return method; + } + } catch (NoSuchMethodException e) { + } + + return null; + } + + static Method findSetter(Class<?> declaringClass, String propertyName, Class<?> propertyType) { + try { + Method method = declaringClass.getMethod("set" + IntrospectionUtils.capitalize(propertyName), propertyType); + if (!Modifier.isPrivate(method.getModifiers())) { + return method; + } + } catch (NoSuchMethodException e) { + } + try { + Method method = declaringClass.getMethod("setProperty", String.class, String.class); + if (!Modifier.isPrivate(method.getModifiers())) { + return method; + } + } catch (NoSuchMethodException e) { + } + return null; + } + + static String decapitalize(String name) { + if (name == null || name.length() == 0) { + return name; + } + if (name.length() > 1 && Character.isUpperCase(name.charAt(1)) && + Character.isUpperCase(name.charAt(0))) { + return name; + } + char chars[] = name.toCharArray(); + chars[0] = Character.toLowerCase(chars[0]); + return new String(chars); + } + + + static SetPropertyClass processClass(Class<?> clazz) { + SetPropertyClass spc = getOrCreateSetPropertyClass(clazz); + final Method[] methods = clazz.getDeclaredMethods(); + for (Method method : methods) { + if (isAllowedSetMethod(method)) { + String propertyName = decapitalize(method.getName().substring(3)); + Class<?> propertyType = method.getParameterTypes()[0]; + Method getter = findGetter(clazz, propertyName); + Method setter = findSetter(clazz, propertyName, propertyType); + ReflectionProperty property = new ReflectionProperty( + spc.getClazz().getName(), + propertyName, + propertyType, + setter, + getter + ); + spc.addProperty(property); + } else if (isAllowedGetMethod(method)) { + boolean startsWithIs = method.getName().startsWith("is"); + String propertyName = decapitalize(method.getName().substring(startsWithIs ? 2 : 3)); + Class<?> propertyType = method.getReturnType(); + Method getter = findGetter(clazz, propertyName); + Method setter = findSetter(clazz, propertyName, propertyType); + ReflectionProperty property = new ReflectionProperty( + spc.getClazz().getName(), + propertyName, + propertyType, + setter, + getter + ); + spc.addProperty(property); + } + } + + final Field[] fields = clazz.getDeclaredFields(); + for (Field field : fields) { + if (isAllowedField(field)) { + Method getter = findGetter( + field.getDeclaringClass(), + IntrospectionUtils.capitalize(field.getName()) + ); + Method setter = findSetter( + field.getDeclaringClass(), + IntrospectionUtils.capitalize(field.getName()), + field.getType() + ); + ReflectionProperty property = new ReflectionProperty( + spc.getClazz().getName(), + field.getName(), + field.getType(), + setter, + getter + ); + spc.addProperty(property); + } + } + + if (!spc.isBaseClass()) { + SetPropertyClass parent = getOrCreateSetPropertyClass(spc.getClazz().getSuperclass()); + parent.addSubClass(spc); + return processClass(parent.getClazz()); + } else { + return spc; + } + } +} diff --git a/res/graal/java/org/apache/tomcat/util/xreflection/ReflectionLessCodeGenerator.java b/res/graal/java/org/apache/tomcat/util/xreflection/ReflectionLessCodeGenerator.java new file mode 100644 index 0000000..a06d78d --- /dev/null +++ b/res/graal/java/org/apache/tomcat/util/xreflection/ReflectionLessCodeGenerator.java @@ -0,0 +1,273 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.tomcat.util.xreflection; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.Set; + +final class ReflectionLessCodeGenerator { + private static final String INDENT = " "; + + static StringBuilder getIndent(int multiplier) { + StringBuilder indent = new StringBuilder(); + while ((multiplier--) > 0) { + indent.append(INDENT); + } + return indent; + } + + static void generateCode( + File directory, + String className, + String packageName, + Set<SetPropertyClass> baseClasses + ) throws IOException { + //begin - class + StringBuilder code = new StringBuilder(AL20_HEADER) + .append("package ") + .append(packageName) + .append(";") + .append(System.lineSeparator()) + .append(System.lineSeparator()) + .append("final class ") + .append(className) + .append(" {") + .append(System.lineSeparator()) + .append(System.lineSeparator()); + + //begin - isEnabled method + code.append(getIndent(1)) + .append("static boolean isEnabled() {") + .append(System.lineSeparator()) + .append(getIndent(2)) + .append("return true;") + .append(System.lineSeparator()) + .append(getIndent(1)) + .append("}") + .append(System.lineSeparator()) + .append(System.lineSeparator()) + ; + //end - isEnabled method + + //begin - getInetAddress method + code.append(getIndent(1)) + .append("private static java.net.InetAddress getInetAddress(String value) {") + .append(System.lineSeparator()) + .append(getIndent(2)) + .append("try {") + .append(System.lineSeparator()) + .append(getIndent(3)) + .append("return java.net.InetAddress.getByName(value);") + .append(System.lineSeparator()) + .append(getIndent(2)) + .append("} catch (java.net.UnknownHostException x) { throw new RuntimeException(x); }") + .append(System.lineSeparator()) + .append(getIndent(1)) + .append("}") + .append(System.lineSeparator()) + .append(System.lineSeparator()) + ; + //end - getInetAddress method + + //begin - getPropertyInternal method + code.append(getIndent(1)) + .append("static Object getPropertyInternal(Object ") + .append(SetPropertyClass.OBJECT_VAR_NAME) + .append(", String ") + .append(SetPropertyClass.NAME_VAR_NAME) + .append(") {") + .append(System.lineSeparator()) + .append(getIndent(2)) + .append("Class<?> checkThisClass = o.getClass();") + .append(System.lineSeparator()) + .append(getIndent(2)) + .append("Object result = null;") + .append(System.lineSeparator()) + .append(getIndent(2)) + .append("while (checkThisClass != Object.class && result == null) {") + .append(System.lineSeparator()) + .append(getIndent(3)) + .append("switch (checkThisClass.getName()) {") + .append(System.lineSeparator()); + + //generate case statements for getPropertyInternal + generateCaseStatementsForGetPropertyInternal(baseClasses, code); + + + code + .append(getIndent(3)) + .append("}") + .append(System.lineSeparator()) + .append(getIndent(3)) + .append("checkThisClass = checkThisClass.getSuperclass();") + .append(System.lineSeparator()) + .append(getIndent(2)) + .append("}") + .append(System.lineSeparator()) + .append(getIndent(2)) + .append("return result;") + .append(System.lineSeparator()) + .append(getIndent(1)) + .append("}") + .append(System.lineSeparator()); + //end - getPropertyInternal method + + //begin - getPropertyForXXX methods + generateGetPropertyForMethods(baseClasses, code); + //end - getPropertyForXXX methods + + //begin - setPropertyInternal method + code.append(getIndent(1)) + .append("static boolean setPropertyInternal(Object ") + .append(SetPropertyClass.OBJECT_VAR_NAME) + .append(", String ") + .append(SetPropertyClass.NAME_VAR_NAME) + .append(", String ") + .append(SetPropertyClass.VALUE_VAR_NAME) + .append(", boolean ") + .append(SetPropertyClass.SETP_VAR_NAME) + .append(") {") + .append(System.lineSeparator()) + .append(getIndent(2)) + .append("Class<?> checkThisClass = o.getClass();") + .append(System.lineSeparator()) + .append(getIndent(2)) + .append("while (checkThisClass != Object.class) {") + .append(System.lineSeparator()) + .append(getIndent(3)) + .append("switch (checkThisClass.getName()) {") + .append(System.lineSeparator()); + + //generate case statements for setPropertyInternal + generateCaseStatementsForSetPropertyInternal(baseClasses, code); + + + code + .append(getIndent(3)) + .append("}") + .append(System.lineSeparator()) + .append(getIndent(3)) + .append("checkThisClass = checkThisClass.getSuperclass();") + .append(System.lineSeparator()) + .append(getIndent(2)) + .append("}") + .append(System.lineSeparator()) + .append(getIndent(2)) + .append("return false;") + .append(System.lineSeparator()) + .append(getIndent(1)) + .append("}") + .append(System.lineSeparator()); + //end - setPropertyInternal method + + //begin - setPropertyForXXX methods + generateSetPropertyForMethods(baseClasses, code); + //end - setPropertyForXXX methods + + code.append("}") + .append(System.lineSeparator()); + //end - class + File destination = new File(directory, className+".java"); + BufferedWriter writer = new BufferedWriter(new FileWriter(destination, false)); + writer.write(code.toString()); + writer.flush(); + writer.close(); + + } + + private static void generateCaseStatementForSetPropertyInternal(SetPropertyClass clazz, StringBuilder code) { + for (SetPropertyClass child : clazz.getChildren()) { + generateCaseStatementForSetPropertyInternal(child, code); + } + if (!clazz.isAbstract()) { + code.append(clazz.generateInvocationSetForPropertyCaseStatement(4)); + } + } + + private static void generateCaseStatementsForSetPropertyInternal(Set<SetPropertyClass> baseClasses, StringBuilder code) { + for (SetPropertyClass clazz : baseClasses) { + generateCaseStatementForSetPropertyInternal(clazz, code); + } + } + + private static void generateSetPropertyForMethod(SetPropertyClass clazz, StringBuilder code) { + for (SetPropertyClass child : clazz.getChildren()) { + generateSetPropertyForMethod(child, code); + } + code.append(clazz.generateSetPropertyForMethod()) + .append(System.lineSeparator()) + .append(System.lineSeparator()); + } + + private static void generateSetPropertyForMethods(Set<SetPropertyClass> baseClasses, StringBuilder code) { + for (SetPropertyClass clazz : baseClasses) { + generateSetPropertyForMethod(clazz, code); + } + } + + + + private static void generateCaseStatementForGetPropertyInternal(SetPropertyClass clazz, StringBuilder code) { + for (SetPropertyClass child : clazz.getChildren()) { + generateCaseStatementForGetPropertyInternal(child, code); + } + if (!clazz.isAbstract()) { + code.append(clazz.generateInvocationGetForPropertyCaseStatement(4)); + } + } + + private static void generateCaseStatementsForGetPropertyInternal(Set<SetPropertyClass> baseClasses, StringBuilder code) { + for (SetPropertyClass clazz : baseClasses) { + generateCaseStatementForGetPropertyInternal(clazz, code); + } + } + + private static void generateGetPropertyForMethod(SetPropertyClass clazz, StringBuilder code) { + for (SetPropertyClass child : clazz.getChildren()) { + generateGetPropertyForMethod(child, code); + } + code.append(clazz.generateGetPropertyForMethod()) + .append(System.lineSeparator()) + .append(System.lineSeparator()); + } + + private static void generateGetPropertyForMethods(Set<SetPropertyClass> baseClasses, StringBuilder code) { + for (SetPropertyClass clazz : baseClasses) { + generateGetPropertyForMethod(clazz, code); + } + } + + private static final String AL20_HEADER = "/*\n" + + " * Licensed to the Apache Software Foundation (ASF) under one or more\n" + + " * contributor license agreements. See the NOTICE file distributed with\n" + + " * this work for additional information regarding copyright ownership.\n" + + " * The ASF licenses this file to You under the Apache License, Version 2.0\n" + + " * (the \"License\"); you may not use this file except in compliance with\n" + + " * the License. You may obtain a copy of the License at\n" + + " *\n" + + " * http://www.apache.org/licenses/LICENSE-2.0\n" + + " *\n" + + " * Unless required by applicable law or agreed to in writing, software\n" + + " * distributed under the License is distributed on an \"AS IS\" BASIS,\n" + + " * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n" + + " * See the License for the specific language governing permissions and\n" + + " * limitations under the License.\n" + + " */\n"; +} diff --git a/res/graal/java/org/apache/tomcat/util/xreflection/ReflectionProperty.java b/res/graal/java/org/apache/tomcat/util/xreflection/ReflectionProperty.java new file mode 100644 index 0000000..f74f7c0 --- /dev/null +++ b/res/graal/java/org/apache/tomcat/util/xreflection/ReflectionProperty.java @@ -0,0 +1,119 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.tomcat.util.xreflection; + +import java.lang.reflect.Method; +import java.net.InetAddress; + +final class ReflectionProperty { + private final String clazz; + private final String propertyName; + private final Class<?> propertyType; + private final Method setMethod; + private final Method getMethod; + + ReflectionProperty(String clazz, String propertyName, Class<?> propertyType, Method setMethod, Method getMethod) { + this.clazz = clazz; + this.propertyName = propertyName; + this.propertyType = propertyType; + this.setMethod = setMethod; + this.getMethod = getMethod; + } + + public String getPropertyName() { + return propertyName; + } + + public Class<?> getPropertyType() { + return propertyType; + } + + public boolean hasSetPropertySetter() { + return hasSetter() && "setProperty".equals(setMethod.getName()); + } + + public boolean hasGetPropertyGetter() { + return hasGetter() && "getProperty".equals(getMethod.getName()); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + ReflectionProperty property1 = (ReflectionProperty) o; + + if (!clazz.equals(property1.clazz)) return false; + return propertyName.equals(property1.propertyName); + } + + @Override + public int hashCode() { + int result = clazz.hashCode(); + result = 31 * result + propertyName.hashCode(); + return result; + } + + public String getClazz() { + return clazz; + } + + public Method getGetMethod() { + return getMethod; + } + + public String getConversion(String valueVarName) { + if (getPropertyType() == String.class) { + return valueVarName; + } + if (getPropertyType() == Boolean.TYPE) { + return "Boolean.valueOf(" + valueVarName + ")"; + } + if (getPropertyType() == Long.TYPE) { + return "Long.valueOf(" + valueVarName + ")"; + } + if (getPropertyType() == Integer.TYPE) { + return "Integer.valueOf(" + valueVarName + ")"; + } + if (getPropertyType() == InetAddress.class) { + return "getInetAddress(" + valueVarName + ")"; + } + throw new IllegalStateException("Unexpected Type:" + getPropertyType()); + + } + + public boolean hasSetter() { + return setMethod != null; + } + + public boolean hasGetter() { + return getMethod != null; + } + + public Method getSetMethod() { + return setMethod; + } + + @Override + public String toString() { + final StringBuffer sb = new StringBuffer("ReflectionProperty{"); + sb.append("name='").append(propertyName).append('\''); + sb.append(", type=").append(propertyType); + sb.append('}'); + return sb.toString(); + } +} diff --git a/res/graal/java/org/apache/tomcat/util/xreflection/SetPropertyClass.java b/res/graal/java/org/apache/tomcat/util/xreflection/SetPropertyClass.java new file mode 100644 index 0000000..55a68c4 --- /dev/null +++ b/res/graal/java/org/apache/tomcat/util/xreflection/SetPropertyClass.java @@ -0,0 +1,436 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.tomcat.util.xreflection; + +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.HashSet; +import java.util.Set; + +import org.apache.tomcat.util.IntrospectionUtils; + +final class SetPropertyClass { + + static final String OBJECT_VAR_NAME = "o"; + static final String NAME_VAR_NAME = "name"; + static final String VALUE_VAR_NAME = "value"; + static final String SETP_VAR_NAME = "invokeSetProperty"; + + private final SetPropertyClass parent; + private final Class<?> clazz; + private Set<SetPropertyClass> children = new HashSet<>(); + private Set<ReflectionProperty> properties = new HashSet<>(); + private final boolean isAbstract; + private final Method genericSetPropertyMethod; + private final Method genericGetPropertyMethod; + + SetPropertyClass(Class<?> clazz, SetPropertyClass parent) { + this.clazz = clazz; + this.parent = parent; + this.isAbstract = Modifier.isAbstract(clazz.getModifiers()); + Method classSetter, classGetter; + try { + classSetter = clazz.getDeclaredMethod("setProperty", String.class, String.class); + } catch (NoSuchMethodException e) { + try { + classSetter = clazz.getDeclaredMethod("setProperty", String.class, Object.class); + } catch (NoSuchMethodException x) { + classSetter = null; + } + } + try { + classGetter = clazz.getDeclaredMethod("getProperty", String.class); + } catch (NoSuchMethodException e) { + classGetter = null; + } + genericSetPropertyMethod = classSetter; + genericGetPropertyMethod = classGetter; + } + + boolean isAbstract() { + return isAbstract; + } + + void addSubClass(SetPropertyClass clazz) { + this.children.add(clazz); + } + + boolean isBaseClass() { + return parent == null; + } + + public Set<SetPropertyClass> getChildren() { + return children; + } + + public Set<ReflectionProperty> getProperties() { + return properties; + } + + public Method getGenericSetPropertyMethod() { + return genericSetPropertyMethod; + } + + public Method getGenericGetPropertyMethod() { + return genericGetPropertyMethod; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + SetPropertyClass that = (SetPropertyClass) o; + + return clazz.equals(that.clazz); + } + + @Override + public int hashCode() { + return clazz.hashCode(); + } + + public SetPropertyClass getParent() { + return parent; + } + + public Class<?> getClazz() { + return clazz; + } + + @Override + public String toString() { + final StringBuffer sb = new StringBuffer("SetPropertyClass{"); + sb.append("clazz=").append(clazz.getName()); + sb.append('}'); + return sb.toString(); + } + + public void addProperty(ReflectionProperty property) { + properties.add(property); + } + + + + public String generateSetPropertyMethod(ReflectionProperty property) { + //this property has a setProperty method + if (property.hasSetPropertySetter()) { + return "((" + this.getClazz().getName().replace('$','.') + ")" + OBJECT_VAR_NAME + ")." + + property.getSetMethod().getName() + "(" + NAME_VAR_NAME + ", " + VALUE_VAR_NAME + ");"; + } + + //direct setter + if (property.hasSetter()) { + return "((" + this.getClazz().getName().replace('$','.') + ")" + OBJECT_VAR_NAME + ")." + + property.getSetMethod().getName() + "(" + property.getConversion(VALUE_VAR_NAME) + ");"; + } + return null; + } + + public String generateGetPropertyMethod(ReflectionProperty property) { + //this property has a getProperty method + if (property.hasGetPropertyGetter()) { + return "result = ((" + this.getClazz().getName().replace('$','.') + ")" + OBJECT_VAR_NAME + ")." + + property.getGetMethod().getName() + "(" + NAME_VAR_NAME + ");"; + } + + //direct getter + if (property.hasGetter()) { + return "result = ((" + this.getClazz().getName().replace('$','.') + ")" + OBJECT_VAR_NAME + ")." + + property.getGetMethod().getName() + "();"; + } + return null; + } + + public String generateSetPropertyForMethod() { + StringBuilder code = new StringBuilder(ReflectionLessCodeGenerator.getIndent(1)) + .append(generatesSetPropertyForMethodHeader()) + .append(System.lineSeparator()) + .append(ReflectionLessCodeGenerator.getIndent(2)) + .append("switch (") + .append(NAME_VAR_NAME) + .append(") {") + .append(System.lineSeparator()); + + //case statements for each property + for (ReflectionProperty property : getProperties()) { + String invocation = generateSetPropertyMethod(property); + if (invocation != null) { + code.append(ReflectionLessCodeGenerator.getIndent(3)) + .append("case \"") + .append(property.getPropertyName()) + .append("\" : ") + .append(System.lineSeparator()); + + code.append(ReflectionLessCodeGenerator.getIndent(4)) + .append(invocation) + .append(System.lineSeparator()) + .append(ReflectionLessCodeGenerator.getIndent(4)) + .append("return true;") + .append(System.lineSeparator()) + ; + + } else { + code.append(ReflectionLessCodeGenerator.getIndent(3)) + .append("//no set" + IntrospectionUtils.capitalize(property.getPropertyName())+ " method found on this class") + .append(System.lineSeparator()) + ; + } + } + + + + //end switch statement + code.append(ReflectionLessCodeGenerator.getIndent(2)) + .append("}") + .append(System.lineSeparator()); + + //we have a generic setProperty(String, String) method, invoke it + if (getGenericSetPropertyMethod() != null) { + ReflectionProperty p = new ReflectionProperty( + clazz.getName(), + "property", + String.class, + getGenericSetPropertyMethod(), + null + ); + code.append(ReflectionLessCodeGenerator.getIndent(2)) + .append("if (") + .append(SETP_VAR_NAME) + .append(") {") + .append(System.lineSeparator()) + .append(ReflectionLessCodeGenerator.getIndent(3)) + .append(generateSetPropertyMethod(p)) + .append(System.lineSeparator()) + .append(ReflectionLessCodeGenerator.getIndent(3)) + .append("return true;") + .append(System.lineSeparator()) + .append(ReflectionLessCodeGenerator.getIndent(2)) + .append("}") + .append(System.lineSeparator()); + } + + //invoke parent or return false + code.append(ReflectionLessCodeGenerator.getIndent(2)) + .append("return ") + .append(getSetPropertyForExitStatement()) + .append(System.lineSeparator()) + .append(ReflectionLessCodeGenerator.getIndent(1)) + .append("}"); + + return code.toString(); + } + + private String getSetPropertyForExitStatement() { + + return (getParent() != null) ? + //invoke the parent if we have one + getParent().generateParentSetPropertyForMethodInvocation() : + //if we invoke setProperty, return true, return false otherwise + getGenericSetPropertyMethod() != null ? "true;" : "false;"; + } + + public String generateInvocationSetForPropertyCaseStatement(int level) { + StringBuilder code = new StringBuilder(ReflectionLessCodeGenerator.getIndent(level)) + .append("case \"") + .append(getClazz().getName()) + .append("\" : ") + .append(System.lineSeparator()) + .append(ReflectionLessCodeGenerator.getIndent(level+1)) + .append("return ") + .append(generateParentSetPropertyForMethodInvocation()) + .append(System.lineSeparator()); + return code.toString(); + } + + public String generateParentSetPropertyForMethodInvocation() { + String[] classParts = clazz.getName().split("\\.|\\$"); + StringBuilder methodInvocation = new StringBuilder("setPropertyFor"); + for (String s : classParts) { + methodInvocation.append(IntrospectionUtils.capitalize(s)); + } + methodInvocation.append("(") + .append(OBJECT_VAR_NAME) + .append(", ") + .append(NAME_VAR_NAME) + .append(", ") + .append(VALUE_VAR_NAME) + .append(", ") + .append(SETP_VAR_NAME) + .append(");"); + return methodInvocation.toString(); + } + + public String generatesSetPropertyForMethodHeader() { + String[] classParts = clazz.getName().split("\\.|\\$"); + StringBuilder methodInvocation = new StringBuilder("private static boolean setPropertyFor"); + for (String s : classParts) { + methodInvocation.append(IntrospectionUtils.capitalize(s)); + } + methodInvocation.append("(Object ") + .append(OBJECT_VAR_NAME) + .append(", String ") + .append(NAME_VAR_NAME) + .append(", String ") + .append(VALUE_VAR_NAME) + .append(", boolean ") + .append(SETP_VAR_NAME) + .append(") {"); + return methodInvocation.toString(); + } + + public String generateInvocationGetForPropertyCaseStatement(int level) { + StringBuilder code = new StringBuilder(ReflectionLessCodeGenerator.getIndent(level)) + .append("case \"") + .append(getClazz().getName()) + .append("\" : ") + .append(System.lineSeparator()) + .append(ReflectionLessCodeGenerator.getIndent(level+1)) + .append("result = ") + .append(generateParentGetPropertyForMethodInvocation()) + .append(System.lineSeparator()) + .append(ReflectionLessCodeGenerator.getIndent(level+1)) + .append("break;") + .append(System.lineSeparator()) + ; + return code.toString(); + } + + public String generateParentGetPropertyForMethodInvocation() { + String[] classParts = clazz.getName().split("\\.|\\$"); + StringBuilder methodInvocation = new StringBuilder("getPropertyFor"); + for (String s : classParts) { + methodInvocation.append(IntrospectionUtils.capitalize(s)); + } + methodInvocation.append("(") + .append(OBJECT_VAR_NAME) + .append(", ") + .append(NAME_VAR_NAME) + .append(");"); + return methodInvocation.toString(); + } + + public String generatesGetPropertyForMethodHeader() { + String[] classParts = clazz.getName().split("\\.|\\$"); + StringBuilder methodInvocation = new StringBuilder("private static Object getPropertyFor"); + for (String s : classParts) { + methodInvocation.append(IntrospectionUtils.capitalize(s)); + } + methodInvocation.append("(Object ") + .append(OBJECT_VAR_NAME) + .append(", String ") + .append(NAME_VAR_NAME) + .append(") {"); + return methodInvocation.toString(); + } + + private String getGetPropertyForExitStatement() { + if (getParent() != null) { + return getParent().generateParentGetPropertyForMethodInvocation(); + } + return "null;"; + } + + + public String generateGetPropertyForMethod() { + StringBuilder code = new StringBuilder(ReflectionLessCodeGenerator.getIndent(1)) + .append(generatesGetPropertyForMethodHeader()) + .append(System.lineSeparator()) + .append(ReflectionLessCodeGenerator.getIndent(2)) + .append("Object result = null;") + .append(System.lineSeparator()) + .append(ReflectionLessCodeGenerator.getIndent(2)) + .append("switch (") + .append(NAME_VAR_NAME) + .append(") {") + .append(System.lineSeparator()); + + //case statements for each property + for (ReflectionProperty property : getProperties()) { + String invocation = generateGetPropertyMethod(property); + if (invocation != null) { + code.append(ReflectionLessCodeGenerator.getIndent(3)) + .append("case \"") + .append(property.getPropertyName()) + .append("\" : ") + .append(System.lineSeparator()); + + code.append(ReflectionLessCodeGenerator.getIndent(4)) + .append(invocation) + .append(System.lineSeparator()) + .append(ReflectionLessCodeGenerator.getIndent(4)) + .append("break;") + .append(System.lineSeparator()) + ; + + } else { + code.append(ReflectionLessCodeGenerator.getIndent(3)) + .append("//no get" + IntrospectionUtils.capitalize(property.getPropertyName())+ " method found on this class") + .append(System.lineSeparator()) + ; + } + } + + //end switch statement + code.append(ReflectionLessCodeGenerator.getIndent(2)) + .append("}") + .append(System.lineSeparator()); + + //invoke parent or return null + code.append(ReflectionLessCodeGenerator.getIndent(2)) + .append("if (result == null) {") + .append(System.lineSeparator()) + .append(ReflectionLessCodeGenerator.getIndent(3)) + .append("result = ") + .append(getGetPropertyForExitStatement()) + .append(System.lineSeparator()) + .append(ReflectionLessCodeGenerator.getIndent(2)) + .append("}") + .append(System.lineSeparator()) + ; + + //we have a generic getProperty(String, String) method, invoke it + if (getGenericGetPropertyMethod() != null) { + ReflectionProperty p = new ReflectionProperty( + clazz.getName(), + "property", + String.class, + null, + getGenericGetPropertyMethod() + ); + code.append(ReflectionLessCodeGenerator.getIndent(2)) + .append("if (result == null) {") + .append(System.lineSeparator()) + .append(ReflectionLessCodeGenerator.getIndent(3)) + .append(generateGetPropertyMethod(p)) + .append(System.lineSeparator()) + .append(ReflectionLessCodeGenerator.getIndent(2)) + .append("}") + .append(System.lineSeparator()); + } + code.append(ReflectionLessCodeGenerator.getIndent(2)) + .append("return result;") + .append(System.lineSeparator()) + .append(ReflectionLessCodeGenerator.getIndent(1)) + .append("}") + .append(System.lineSeparator()); + + + + return code.toString(); + } +} diff --git a/res/graal/tomcat-embed-programmatic/native-image/native-image.properties b/res/graal/tomcat-embed-programmatic/native-image/native-image.properties new file mode 100644 index 0000000..29b501f --- /dev/null +++ b/res/graal/tomcat-embed-programmatic/native-image/native-image.properties @@ -0,0 +1,16 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +Args = -H:ReflectionConfigurationResources=${.}/tomcat-reflection.json -H:ResourceConfigurationResources=${.}/tomcat-resource.json \ No newline at end of file diff --git a/res/graal/tomcat-embed-programmatic/native-image/tomcat-reflection.json b/res/graal/tomcat-embed-programmatic/native-image/tomcat-reflection.json new file mode 100644 index 0000000..0d4f101 --- /dev/null +++ b/res/graal/tomcat-embed-programmatic/native-image/tomcat-reflection.json @@ -0,0 +1,2 @@ +[ +] diff --git a/res/graal/tomcat-embed-programmatic/native-image/tomcat-resource.json b/res/graal/tomcat-embed-programmatic/native-image/tomcat-resource.json new file mode 100644 index 0000000..bf5ca80 --- /dev/null +++ b/res/graal/tomcat-embed-programmatic/native-image/tomcat-resource.json @@ -0,0 +1,51 @@ +{ + "bundles":[ + {"name":"javax.servlet.LocalStrings"}, + {"name":"javax.servlet.http.LocalStrings"}, + {"name":"org.apache.catalina.authenticator.LocalStrings"}, + {"name":"org.apache.catalina.authenticator.jaspic.LocalStrings"}, + {"name":"org.apache.catalina.connector.LocalStrings"}, + {"name":"org.apache.catalina.core.LocalStrings"}, + {"name":"org.apache.catalina.deploy.LocalStrings"}, + {"name":"org.apache.catalina.loader.LocalStrings"}, + {"name":"org.apache.catalina.mapper.LocalStrings"}, + {"name":"org.apache.catalina.realm.LocalStrings"}, + {"name":"org.apache.catalina.security.LocalStrings"}, + {"name":"org.apache.catalina.session.LocalStrings"}, + {"name":"org.apache.catalina.startup.LocalStrings"}, + {"name":"org.apache.catalina.util.LocalStrings"}, + {"name":"org.apache.catalina.valves.LocalStrings"}, + {"name":"org.apache.catalina.webresources.LocalStrings"}, + {"name":"org.apache.coyote.LocalStrings"}, + {"name":"org.apache.coyote.http11.LocalStrings"}, + {"name":"org.apache.coyote.http11.filters.LocalStrings"}, + {"name":"org.apache.coyote.http11.upgrade.LocalStrings"}, + {"name":"org.apache.coyote.http2.LocalStrings"}, + {"name":"org.apache.naming.LocalStrings"}, + {"name":"org.apache.naming.factory.LocalStrings"}, + {"name":"org.apache.tomcat.util.LocalStrings"}, + {"name":"org.apache.tomcat.util.buf.LocalStrings"}, + {"name":"org.apache.tomcat.util.codec.binary.LocalStrings"}, + {"name":"org.apache.tomcat.util.compat.LocalStrings"}, + {"name":"org.apache.tomcat.util.http.LocalStrings"}, + {"name":"org.apache.tomcat.util.http.parser.LocalStrings"}, + {"name":"org.apache.tomcat.util.json.LocalStrings"}, + {"name":"org.apache.tomcat.util.modeler.LocalStrings"}, + {"name":"org.apache.tomcat.util.net.LocalStrings"}, + {"name":"org.apache.tomcat.util.scan.LocalStrings"}, + {"name":"org.apache.tomcat.util.security.LocalStrings"}, + {"name":"org.apache.tomcat.util.threads.res.LocalStrings"} + ], + "resources":[ + {"pattern":".*/Authenticators.properties$"}, + {"pattern":".*/MimeTypeMappings.properties$"}, + {"pattern":".*/catalina.properties$"}, + {"pattern":".*/CharsetMapperDefault.properties$"}, + {"pattern":".*/ServerInfo.properties$"}, + {"pattern":".*/RestrictedServlets.properties$"}, + {"pattern":".*/RestrictedListeners.properties$"}, + {"pattern":".*/RestrictedFilters.properties$"}, + {"pattern":".*/*.dtd$"}, + {"pattern":".*/*.xsd$"} + ] +} diff --git a/res/maven/mvn-pub.xml b/res/maven/mvn-pub.xml index 15e9380..e811bb2 100644 --- a/res/maven/mvn-pub.xml +++ b/res/maven/mvn-pub.xml @@ -347,6 +347,11 @@ file="${tomcat.embed.path}/tomcat-embed-core.jar" pom="tomcat-embed-core.pom" src="${tomcat.embed.src.path}/tomcat-embed-core-src.jar"/> + <doMavenInstall artifactId="tomcat-embed-programmatic" + groupId="org.apache.tomcat.experimental" + file="${tomcat.embed.path}/tomcat-embed-programmatic.jar" + pom="tomcat-embed-programmatic.pom" + src="${tomcat.embed.src.path}/tomcat-embed-programmatic-src.jar"/> <doMavenInstall artifactId="tomcat-embed-jasper" groupId="org.apache.tomcat.embed" file="${tomcat.embed.path}/tomcat-embed-jasper.jar" @@ -455,6 +460,11 @@ file="${tomcat.embed.path}/tomcat-embed-core.jar" pom="tomcat-embed-core.pom" src="${tomcat.embed.src.path}/tomcat-embed-core-src.jar"/> + <doMavenDeploy artifactId="tomcat-embed-programmatic" + groupId="org.apache.tomcat.experimental" + file="${tomcat.embed.path}/tomcat-embed-programmatic.jar" + pom="tomcat-embed-programmatic.pom" + src="${tomcat.embed.src.path}/tomcat-embed-programmatic-src.jar"/> <doMavenDeploy artifactId="tomcat-embed-jasper" groupId="org.apache.tomcat.embed" file="${tomcat.embed.path}/tomcat-embed-jasper.jar" diff --git a/res/maven/tomcat-embed-programmatic.pom b/res/maven/tomcat-embed-programmatic.pom new file mode 100644 index 0000000..84fe088 --- /dev/null +++ b/res/maven/tomcat-embed-programmatic.pom @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 + http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <groupId>org.apache.tomcat.experimental</groupId> + <artifactId>tomcat-embed-programmatic</artifactId> + <version>@MAVEN.DEPLOY.VERSION@</version> + <description>Exerimental Minimal Tomcat for Programmatic Use</description> + <url>https://tomcat.apache.org/</url> + <licenses> + <license> + <name>Apache License, Version 2.0</name> + <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url> + <distribution>repo</distribution> + </license> + </licenses> +</project> diff --git a/test/org/apache/catalina/startup/EmbeddedTomcat.java b/test/org/apache/catalina/startup/EmbeddedTomcat.java index b594c5a..9338761 100644 --- a/test/org/apache/catalina/startup/EmbeddedTomcat.java +++ b/test/org/apache/catalina/startup/EmbeddedTomcat.java @@ -35,7 +35,6 @@ import org.apache.catalina.connector.Connector; import org.apache.juli.logging.LogFactory; import org.apache.tomcat.util.scan.StandardJarScanFilter; import org.apache.tomcat.util.scan.StandardJarScanner; -import org.apache.tomcat.websocket.server.WsContextListener; @Ignore public class EmbeddedTomcat { @@ -67,7 +66,7 @@ public class EmbeddedTomcat { CounterServlet counterServlet = new CounterServlet(); Tomcat.addServlet(ctx, "counterServlet", counterServlet); ctx.addServletMappingDecoded("/", "counterServlet"); - ctx.addApplicationListener(WsContextListener.class.getName()); + //ctx.addApplicationListener(new WsContextListener()); tomcat.start(); Thread.sleep(60*1000); @@ -88,6 +87,7 @@ public class EmbeddedTomcat { @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + req.getSession(true); resp.setContentType("text/plain"); resp.getWriter().print("OK: " + req.getRequestURL() + "[" + callCount.incrementAndGet()+ "]"); } --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org