You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by pp...@apache.org on 2020/04/03 12:52:44 UTC

[camel-quarkus] branch master updated: Revisit #1017 Be even less intrusive when registering our TransformerFactory

This is an automated email from the ASF dual-hosted git repository.

ppalaga pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/camel-quarkus.git


The following commit(s) were added to refs/heads/master by this push:
     new d668d45  Revisit #1017 Be even less intrusive when registering our TransformerFactory
d668d45 is described below

commit d668d45cfa64926ef3289e04b15ad53ecf703283
Author: Peter Palaga <pp...@redhat.com>
AuthorDate: Fri Apr 3 11:00:00 2020 +0200

    Revisit #1017 Be even less intrusive when registering our TransformerFactory
---
 .../camel/quarkus/core/graal/ResourceUtils.java    | 46 ++++++++++++++
 extensions-support/xalan/deployment/pom.xml        |  4 ++
 .../deployment/CamelXalanBuildTimeConfig.java      | 50 ---------------
 .../deployment/XalanNativeImageProcessor.java      | 71 ++++++++++++++++------
 extensions-support/xalan/runtime/pom.xml           |  4 ++
 .../javax.xml.transform.TransformerFactory         |  1 -
 6 files changed, 106 insertions(+), 70 deletions(-)

diff --git a/extensions-core/core/runtime/src/main/java/org/apache/camel/quarkus/core/graal/ResourceUtils.java b/extensions-core/core/runtime/src/main/java/org/apache/camel/quarkus/core/graal/ResourceUtils.java
new file mode 100644
index 0000000..5a6d886
--- /dev/null
+++ b/extensions-core/core/runtime/src/main/java/org/apache/camel/quarkus/core/graal/ResourceUtils.java
@@ -0,0 +1,46 @@
+/*
+ * 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.camel.quarkus.core.graal;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+
+import com.oracle.svm.core.jdk.Resources;
+
+/**
+ * Helper methods invoked from generated bytecode during image processing
+ */
+public class ResourceUtils {
+
+    /**
+     * Write a string resource into the native image.
+     *
+     * @param resourcePath the path under which the given {@code content} should be stored
+     * @param content      the content that will be serialized using {@code UTF-8}
+     */
+    public static void registerResources(String resourcePath, String content) {
+        try {
+            try (InputStream in = new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8))) {
+                Resources.registerResource(resourcePath, in);
+            }
+        } catch (Exception e) {
+            throw new RuntimeException("Failed to store resource " + resourcePath, e);
+        }
+    }
+
+}
diff --git a/extensions-support/xalan/deployment/pom.xml b/extensions-support/xalan/deployment/pom.xml
index e7fb841..3e05d05 100644
--- a/extensions-support/xalan/deployment/pom.xml
+++ b/extensions-support/xalan/deployment/pom.xml
@@ -47,6 +47,10 @@
         </dependency>
         <dependency>
             <groupId>org.apache.camel.quarkus</groupId>
+            <artifactId>camel-quarkus-core-deployment</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel.quarkus</groupId>
             <artifactId>camel-quarkus-support-xalan</artifactId>
         </dependency>
     </dependencies>
diff --git a/extensions-support/xalan/deployment/src/main/java/org/apache/camel/quarkus/support/xalan/deployment/CamelXalanBuildTimeConfig.java b/extensions-support/xalan/deployment/src/main/java/org/apache/camel/quarkus/support/xalan/deployment/CamelXalanBuildTimeConfig.java
deleted file mode 100644
index 8180a5f..0000000
--- a/extensions-support/xalan/deployment/src/main/java/org/apache/camel/quarkus/support/xalan/deployment/CamelXalanBuildTimeConfig.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * 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.camel.quarkus.support.xalan.deployment;
-
-import java.util.Optional;
-
-import io.quarkus.runtime.annotations.ConfigItem;
-import io.quarkus.runtime.annotations.ConfigPhase;
-import io.quarkus.runtime.annotations.ConfigRoot;
-
-@ConfigRoot(name = "camel.xalan", phase = ConfigPhase.BUILD_TIME)
-public class CamelXalanBuildTimeConfig {
-
-    /**
-     * A fully qualified class name to set as the {@code javax.xml.transform.TransformerFactory} system property early
-     * at the application startup.
-     * <p>
-     * The system property effectively overrides any service providers defined in
-     * {@code META-INF/services/javax.xml.transform.TransformerFactory} files available in the class path. If the option
-     * is not present in your {@code application.properties}, the default value is used and the service providers are
-     * overridden anyway. To avoid overriding the service providers, set it to an empty value in
-     * {@code application.properties}:
-     * 
-     * <pre>
-     * quarkus.camel.xalan.transformer-factory =
-     * </pre>
-     * <p>
-     * Note that any custom transformer factory you pass will only work in native mode if all necessary classes are
-     * registered for reflection and all necessary resources are included in the native image. This may already be the
-     * case for {@code com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl} if you depend on
-     * {@code io.quarkus:quarkus-jaxb} or {@code org.apache.xalan.xsltc.trax.TransformerFactoryImpl} if you depend on
-     * {@code org.apache.camel.quarkus:camel-quarkus-support-xalan}.
-     */
-    @ConfigItem(defaultValue = "org.apache.camel.quarkus.support.xalan.XalanTransformerFactory")
-    public Optional<String> transformerFactory;
-}
diff --git a/extensions-support/xalan/deployment/src/main/java/org/apache/camel/quarkus/support/xalan/deployment/XalanNativeImageProcessor.java b/extensions-support/xalan/deployment/src/main/java/org/apache/camel/quarkus/support/xalan/deployment/XalanNativeImageProcessor.java
index b412e7c..4311694 100644
--- a/extensions-support/xalan/deployment/src/main/java/org/apache/camel/quarkus/support/xalan/deployment/XalanNativeImageProcessor.java
+++ b/extensions-support/xalan/deployment/src/main/java/org/apache/camel/quarkus/support/xalan/deployment/XalanNativeImageProcessor.java
@@ -16,18 +16,31 @@
  */
 package org.apache.camel.quarkus.support.xalan.deployment;
 
+import java.nio.charset.StandardCharsets;
 import java.util.Arrays;
 import java.util.List;
 
 import io.quarkus.deployment.annotations.BuildProducer;
 import io.quarkus.deployment.annotations.BuildStep;
-import io.quarkus.deployment.builditem.SystemPropertyBuildItem;
+import io.quarkus.deployment.builditem.GeneratedNativeImageClassBuildItem;
+import io.quarkus.deployment.builditem.GeneratedResourceBuildItem;
 import io.quarkus.deployment.builditem.nativeimage.NativeImageResourceBuildItem;
 import io.quarkus.deployment.builditem.nativeimage.NativeImageResourceBundleBuildItem;
 import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem;
-import io.quarkus.deployment.builditem.nativeimage.ServiceProviderBuildItem;
+import io.quarkus.gizmo.CatchBlockCreator;
+import io.quarkus.gizmo.ClassCreator;
+import io.quarkus.gizmo.ClassOutput;
+import io.quarkus.gizmo.MethodCreator;
+import io.quarkus.gizmo.TryBlock;
+import org.apache.camel.quarkus.core.graal.ResourceUtils;
+import org.apache.camel.quarkus.support.xalan.XalanTransformerFactory;
+import org.graalvm.nativeimage.hosted.Feature;
+
+import static io.quarkus.gizmo.MethodDescriptor.ofMethod;
 
 class XalanNativeImageProcessor {
+    private static final String TRANSFORMER_FACTORY_SERVICE_FILE_PATH = "META-INF/services/javax.xml.transform.TransformerFactory";
+
     @BuildStep
     ReflectiveClassBuildItem reflectiveClasses() {
         return new ReflectiveClassBuildItem(
@@ -58,26 +71,46 @@ class XalanNativeImageProcessor {
 
     @BuildStep
     void installTransformerFactory(
-            CamelXalanBuildTimeConfig config,
-            BuildProducer<SystemPropertyBuildItem> properties,
-            BuildProducer<ServiceProviderBuildItem> serviceProviders,
-            BuildProducer<NativeImageResourceBuildItem> nativeResources) {
+            BuildProducer<GeneratedNativeImageClassBuildItem> nativeImageClass,
+            BuildProducer<GeneratedResourceBuildItem> generatedResources) {
+
+        final String serviceProviderFileContent = XalanTransformerFactory.class.getName() + "\n";
+
+        /* This is primarily for the JVM mode */
+        generatedResources.produce(
+                new GeneratedResourceBuildItem(TRANSFORMER_FACTORY_SERVICE_FILE_PATH,
+                        serviceProviderFileContent.getBytes(StandardCharsets.UTF_8)));
+
+        /* A low level way to embed only our service file in the native image.
+         * There are at least two META-INF/services/javax.xml.transform.TransformerFactory files
+         * in the class path: ours and the one from xalan.jar. As of GraalVM 19.3.1-java8, 19.3.1-java11,
+         * 20.0.0-java8 and 20.0.0-java11, there is no way to ensure that ServiceProviderBuildItem
+         * or NativeImageResourceBuildItem will pick the service file preferred by us.
+         * We are thus forced to use this low level mechanism to ensure that.
+         */
+        final ClassCreator file = new ClassCreator(new ClassOutput() {
+            @Override
+            public void write(String s, byte[] bytes) {
+                nativeImageClass.produce(new GeneratedNativeImageClassBuildItem(s, bytes));
+            }
+        }, getClass().getName() + "AutoFeature", null,
+                Object.class.getName(), Feature.class.getName());
+        file.addAnnotation("com.oracle.svm.core.annotate.AutomaticFeature");
+        final MethodCreator beforeAn = file.getMethodCreator("beforeAnalysis", "V",
+                Feature.BeforeAnalysisAccess.class.getName());
+        final TryBlock overallCatch = beforeAn.tryBlock();
+        overallCatch.invokeStaticMethod(
+                ofMethod(ResourceUtils.class, "registerResources", void.class,
+                        String.class, String.class),
+                overallCatch.load(TRANSFORMER_FACTORY_SERVICE_FILE_PATH),
+                overallCatch.load(serviceProviderFileContent));
 
-        config.transformerFactory
-                .ifPresent(val -> properties.produce(
-                        /*
-                         * If we do not do this, the service provider defined in xalan.jar's
-                         * META-INF/services/javax.xml.transform.TransformerFactory
-                         * wins over our factory on Java 11+ native
-                         * I any case, the user has an option to pass his preferred factory instead of ours
-                         */
-                        new SystemPropertyBuildItem("javax.xml.transform.TransformerFactory", val)));
+        final CatchBlockCreator print = overallCatch.addCatch(Throwable.class);
+        print.invokeVirtualMethod(ofMethod(Throwable.class, "printStackTrace", void.class), print.getCaughtException());
 
-        serviceProviders.produce(
-                new ServiceProviderBuildItem("javax.xml.transform.TransformerFactory",
-                        "org.apache.camel.quarkus.support.xalan.XalanTransformerFactory"));
+        beforeAn.returnValue(null);
+        file.close();
 
-        nativeResources.produce(new NativeImageResourceBuildItem("META-INF/services/javax.xml.transform.TransformerFactory"));
     }
 
 }
diff --git a/extensions-support/xalan/runtime/pom.xml b/extensions-support/xalan/runtime/pom.xml
index 9942545..2e0aeef 100644
--- a/extensions-support/xalan/runtime/pom.xml
+++ b/extensions-support/xalan/runtime/pom.xml
@@ -46,6 +46,10 @@
             <artifactId>quarkus-core</artifactId>
         </dependency>
         <dependency>
+            <groupId>org.apache.camel.quarkus</groupId>
+            <artifactId>camel-quarkus-core</artifactId>
+        </dependency>
+        <dependency>
             <groupId>xalan</groupId>
             <artifactId>xalan</artifactId>
         </dependency>
diff --git a/extensions-support/xalan/runtime/src/main/resources/META-INF/services/javax.xml.transform.TransformerFactory b/extensions-support/xalan/runtime/src/main/resources/META-INF/services/javax.xml.transform.TransformerFactory
deleted file mode 100644
index 202f3ae..0000000
--- a/extensions-support/xalan/runtime/src/main/resources/META-INF/services/javax.xml.transform.TransformerFactory
+++ /dev/null
@@ -1 +0,0 @@
-org.apache.camel.quarkus.support.xalan.XalanTransformerFactory