From a1f048e87d957fb174d658d7dd7aa319c0a343b2 Mon Sep 17 00:00:00 2001 From: melloware Date: Fri, 25 Oct 2024 09:41:01 -0400 Subject: [PATCH] MYFACES-4427: 5.0 Quarkus UberJar packaging support --- extensions/quarkus/deployment/pom.xml | 8 +- .../deployment/FacesUberJarProcessor.java | 136 ++++++++++++++++++ .../quarkus/deployment/MyFacesProcessor.java | 3 +- extensions/quarkus/pom.xml | 3 +- 4 files changed, 145 insertions(+), 5 deletions(-) create mode 100644 extensions/quarkus/deployment/src/main/java/org/apache/myfaces/core/extensions/quarkus/deployment/FacesUberJarProcessor.java diff --git a/extensions/quarkus/deployment/pom.xml b/extensions/quarkus/deployment/pom.xml index 7bfaa4326..8846f2a45 100644 --- a/extensions/quarkus/deployment/pom.xml +++ b/extensions/quarkus/deployment/pom.xml @@ -69,7 +69,11 @@ myfaces-quarkus ${project.version} - + + org.atteo + xml-combiner + ${xml-combiner.version} + @@ -89,4 +93,4 @@ - + \ No newline at end of file diff --git a/extensions/quarkus/deployment/src/main/java/org/apache/myfaces/core/extensions/quarkus/deployment/FacesUberJarProcessor.java b/extensions/quarkus/deployment/src/main/java/org/apache/myfaces/core/extensions/quarkus/deployment/FacesUberJarProcessor.java new file mode 100644 index 000000000..974d79ef9 --- /dev/null +++ b/extensions/quarkus/deployment/src/main/java/org/apache/myfaces/core/extensions/quarkus/deployment/FacesUberJarProcessor.java @@ -0,0 +1,136 @@ +/* + * 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.myfaces.core.extensions.quarkus.deployment; + +import static io.quarkus.deployment.pkg.PackageConfig.JarConfig.JarType.*; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.Collections; +import java.util.List; + +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.TransformerException; + +import io.quarkus.deployment.IsNormal; +import io.quarkus.deployment.pkg.builditem.UberJarMergedResourceBuildItem; +import org.atteo.xmlcombiner.XmlCombiner; +import org.jboss.logging.Logger; +import org.xml.sax.SAXException; + +import io.quarkus.deployment.annotations.BuildProducer; +import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.builditem.GeneratedResourceBuildItem; +import io.quarkus.deployment.pkg.PackageConfig; + +/** + * This class processes the faces configuration and web fragment XML files in an Uber JAR. + * It merges the contents of "META-INF/web-fragment.xml" and "META-INF/faces-config.xml" + * into single files if they are present in the resources. + */ +public class FacesUberJarProcessor +{ + + private static final Logger log = Logger.getLogger(FacesUberJarProcessor.class); + + /** + * Produces `UberJarMergedResourceBuildItem`s for each specified service file to be included in the Uber JAR. + *

+ * This build step is only executed in "normal" mode and registers each of the listed services in + * the `META-INF/services` directory. + * + * @param producer The build item producer for creating `UberJarMergedResourceBuildItem` instances. + */ + @BuildStep(onlyIf = IsNormal.class) + void uberJarServiceLoaders(BuildProducer producer) + { + List serviceFiles = List.of( + "jakarta.el.ExpressionFactory", + "jakarta.enterprise.inject.spi.Extension", + "jakarta.json.spi.JsonProvider", + "jakarta.servlet.ServletContainerInitializer", + "jakarta.websocket.ContainerProvider", + "jakarta.websocket.server.ServerEndpointConfig$Configurator", + "org.apache.myfaces.spi.AnnotationProvider", + "org.apache.myfaces.spi.InjectionProvider" + ); + + for (String serviceFile : serviceFiles) + { + producer.produce(new UberJarMergedResourceBuildItem("META-INF/services/" + serviceFile)); + } + } + + /** + * Merges specified XML files if the package type is UBER_JAR and generates them + * as resources in the Uber JAR. + * + * @param generatedResourcesProducer the producer to add generated resources + * @param packageConfig the package configuration to check for UBER_JAR type + */ + @BuildStep(onlyIf = IsNormal.class) + void uberJarXmlFiles(BuildProducer generatedResourcesProducer, + PackageConfig packageConfig) + { + if (packageConfig.jar().type() == UBER_JAR) + { + mergeAndGenerateResource("META-INF/web-fragment.xml", generatedResourcesProducer); + mergeAndGenerateResource("META-INF/faces-config.xml", generatedResourcesProducer); + } + } + + /** + * Merges all occurrences of the specified XML file found in the resources and + * generates a single combined version. + * + * @param filename the name of the XML file to be merged + * @param generatedResourcesProducer the producer to add the merged resource + */ + private void mergeAndGenerateResource(String filename, + BuildProducer generatedResourcesProducer) + { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + try + { + XmlCombiner combiner = new XmlCombiner(); + // Retrieve all instances of the specified file in the resources + List resources = Collections.list(getClass().getClassLoader().getResources(filename)); + + // Combine each resource file found + for (URL resource : resources) + { + try (InputStream is = resource.openStream()) + { + combiner.combine(is); + } + } + // Build the combined XML document + combiner.buildDocument(outputStream); + + // Produce the merged resource for inclusion in the Uber JAR + generatedResourcesProducer.produce(new GeneratedResourceBuildItem(filename, outputStream.toByteArray())); + } + catch (ParserConfigurationException | SAXException | TransformerException | IOException ex) + { + log.errorf("Unexpected error combining %s", filename, ex); + } + } +} \ No newline at end of file diff --git a/extensions/quarkus/deployment/src/main/java/org/apache/myfaces/core/extensions/quarkus/deployment/MyFacesProcessor.java b/extensions/quarkus/deployment/src/main/java/org/apache/myfaces/core/extensions/quarkus/deployment/MyFacesProcessor.java index 8f7a84715..7baee93cd 100644 --- a/extensions/quarkus/deployment/src/main/java/org/apache/myfaces/core/extensions/quarkus/deployment/MyFacesProcessor.java +++ b/extensions/quarkus/deployment/src/main/java/org/apache/myfaces/core/extensions/quarkus/deployment/MyFacesProcessor.java @@ -154,7 +154,6 @@ import io.quarkus.deployment.builditem.nativeimage.NativeImageResourceBundleBuildItem; import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem; import io.quarkus.deployment.builditem.nativeimage.RuntimeInitializedClassBuildItem; -import io.quarkus.deployment.pkg.steps.NativeOrNativeSourcesBuild; import io.quarkus.runtime.LaunchMode; import io.quarkus.runtime.configuration.ConfigUtils; import io.quarkus.undertow.deployment.ListenerBuildItem; @@ -588,7 +587,7 @@ void registerForMethodReflection(MyFacesRecorder recorder, BuildProducer reflectiveClass, CombinedIndexBuildItem combinedIndex) { diff --git a/extensions/quarkus/pom.xml b/extensions/quarkus/pom.xml index 085a43d0b..84d2edc1e 100644 --- a/extensions/quarkus/pom.xml +++ b/extensions/quarkus/pom.xml @@ -41,7 +41,8 @@ - 3.8.5 + 3.15.1 + 3.0.0