Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes #2480, support web-fragment.xml #2513

Merged
merged 1 commit into from May 20, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
@@ -0,0 +1,15 @@
package io.quarkus.undertow.deployment;

import java.util.Optional;

import org.eclipse.microprofile.config.ConfigProvider;
import org.jboss.metadata.property.PropertyResolver;

public class MPConfigPropertyResolver implements PropertyResolver {

@Override
public String resolve(String propertyName) {
Optional<String> val = ConfigProvider.getConfig().getOptionalValue(propertyName, String.class);
return val.orElse(null);
}
}
Expand Up @@ -138,7 +138,6 @@ public class UndertowBuildStep {
public static final DotName DECLARE_ROLES = DotName.createSimple(DeclareRoles.class.getName());
public static final DotName MULTIPART_CONFIG = DotName.createSimple(MultipartConfig.class.getName());
public static final DotName SERVLET_SECURITY = DotName.createSimple(ServletSecurity.class.getName());
public static final String WEB_XML = "META-INF/web.xml";
protected static final String META_INF_RESOURCES = "META-INF/resources";

@Inject
Expand Down Expand Up @@ -190,50 +189,6 @@ void runtimeReinit(BuildProducer<RuntimeReinitializedClassBuildItem> producer) {
producer.produce(new RuntimeReinitializedClassBuildItem("org.wildfly.common.os.Process"));
}

@BuildStep
HotDeploymentConfigFileBuildItem configFile() {
return new HotDeploymentConfigFileBuildItem(WEB_XML);
}

@BuildStep
WebMetadataBuildItem createWebMetadata(ApplicationArchivesBuildItem applicationArchivesBuildItem,
Consumer<AdditionalBeanBuildItem> additionalBeanBuildItemConsumer) throws Exception {

WebMetaData result;
Path webXml = applicationArchivesBuildItem.getRootArchive().getChildPath(WEB_XML);
if (webXml != null) {
Set<String> additionalBeans = new HashSet<>();

final XMLInputFactory inputFactory = XMLInputFactory.newInstance();
MetaDataElementParser.DTDInfo dtdInfo = new MetaDataElementParser.DTDInfo();
inputFactory.setXMLResolver(dtdInfo);
try (FileInputStream in = new FileInputStream(webXml.toFile())) {
final XMLStreamReader xmlReader = inputFactory.createXMLStreamReader(in);
result = WebMetaDataParser.parse(xmlReader, dtdInfo, PropertyReplacers.noop());
}
if (result.getServlets() != null) {
for (ServletMetaData i : result.getServlets()) {
additionalBeans.add(i.getServletClass());
}
}
if (result.getFilters() != null) {
for (FilterMetaData i : result.getFilters()) {
additionalBeans.add(i.getFilterClass());
}
}
if (result.getListeners() != null) {
for (ListenerMetaData i : result.getListeners()) {
additionalBeans.add(i.getListenerClass());
}
}
additionalBeanBuildItemConsumer
.accept(AdditionalBeanBuildItem.builder().setUnremovable().addBeanClasses(additionalBeans).build());
} else {
result = new WebMetaData();
}
return new WebMetadataBuildItem(result);
}

@BuildStep
public void kubernetes(HttpConfig config, BuildProducer<KubernetesPortBuildItem> portProducer) {
portProducer.produce(new KubernetesPortBuildItem(config.port, "http"));
Expand Down
@@ -0,0 +1,128 @@
package io.quarkus.undertow.deployment;

import java.io.FileInputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;

import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;

import org.jboss.metadata.merge.web.spec.WebCommonMetaDataMerger;
import org.jboss.metadata.parser.servlet.WebFragmentMetaDataParser;
import org.jboss.metadata.parser.servlet.WebMetaDataParser;
import org.jboss.metadata.parser.util.MetaDataElementParser;
import org.jboss.metadata.parser.util.NoopXMLResolver;
import org.jboss.metadata.property.PropertyReplacers;
import org.jboss.metadata.web.spec.FilterMetaData;
import org.jboss.metadata.web.spec.ListenerMetaData;
import org.jboss.metadata.web.spec.ServletMetaData;
import org.jboss.metadata.web.spec.WebFragmentMetaData;
import org.jboss.metadata.web.spec.WebMetaData;

import io.quarkus.arc.deployment.AdditionalBeanBuildItem;
import io.quarkus.deployment.ApplicationArchive;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.builditem.ApplicationArchivesBuildItem;
import io.quarkus.deployment.builditem.HotDeploymentConfigFileBuildItem;

/**
* Build step that handles web.xml and web-fragment.xml parsing
*/
public class WebXmlParsingBuildStep {

public static final String WEB_XML = "META-INF/web.xml";
private static final String WEB_FRAGMENT_XML = "META-INF/web-fragment.xml";

@BuildStep
List<HotDeploymentConfigFileBuildItem> configFile() {
return Arrays.asList(new HotDeploymentConfigFileBuildItem(WEB_XML),
new HotDeploymentConfigFileBuildItem(WEB_FRAGMENT_XML));
}

@BuildStep(applicationArchiveMarkers = WEB_FRAGMENT_XML)
WebMetadataBuildItem createWebMetadata(ApplicationArchivesBuildItem applicationArchivesBuildItem,
Consumer<AdditionalBeanBuildItem> additionalBeanBuildItemConsumer) throws Exception {

WebMetaData result;
Path webXml = applicationArchivesBuildItem.getRootArchive().getChildPath(WEB_XML);
if (webXml != null) {
Set<String> additionalBeans = new HashSet<>();

final XMLInputFactory inputFactory = XMLInputFactory.newInstance();
MetaDataElementParser.DTDInfo dtdInfo = new MetaDataElementParser.DTDInfo();
inputFactory.setXMLResolver(dtdInfo);
try (FileInputStream in = new FileInputStream(webXml.toFile())) {
final XMLStreamReader xmlReader = inputFactory.createXMLStreamReader(in);
result = WebMetaDataParser.parse(xmlReader, dtdInfo,
PropertyReplacers.resolvingReplacer(new MPConfigPropertyResolver()));
}
if (result.getServlets() != null) {
for (ServletMetaData i : result.getServlets()) {
additionalBeans.add(i.getServletClass());
}
}
if (result.getFilters() != null) {
for (FilterMetaData i : result.getFilters()) {
additionalBeans.add(i.getFilterClass());
}
}
if (result.getListeners() != null) {
for (ListenerMetaData i : result.getListeners()) {
additionalBeans.add(i.getListenerClass());
}
}
additionalBeanBuildItemConsumer
.accept(AdditionalBeanBuildItem.builder().setUnremovable().addBeanClasses(additionalBeans).build());
} else {
result = new WebMetaData();
}
Map<String, WebFragmentMetaData> webFragments = parseWebFragments(applicationArchivesBuildItem);
for (Map.Entry<String, WebFragmentMetaData> e : webFragments.entrySet()) {
//merge in any web fragments
//at the moment this is fairly simplistic, as it does not handle all the ordering and metadata complete bits
//of the spec. I am not sure how important this is, and it is very complex and does not 100% map to the quarkus
//deployment model. If there is demand for it we can look at adding it later

WebCommonMetaDataMerger.augment(result, e.getValue(), null, false);
}

return new WebMetadataBuildItem(result);
}

/**
* parse web-fragment.xml
*/
public Map<String, WebFragmentMetaData> parseWebFragments(ApplicationArchivesBuildItem applicationArchivesBuildItem) {
Map<String, WebFragmentMetaData> webFragments = new HashMap<>();
for (ApplicationArchive archive : applicationArchivesBuildItem.getAllApplicationArchives()) {
Path webFragment = archive.getChildPath(WEB_FRAGMENT_XML);
if (webFragment != null && Files.isRegularFile(webFragment)) {
try (FileInputStream is = new FileInputStream(webFragment.toFile())) {
final XMLInputFactory inputFactory = XMLInputFactory.newInstance();
inputFactory.setXMLResolver(NoopXMLResolver.create());
XMLStreamReader xmlReader = inputFactory.createXMLStreamReader(is);

WebFragmentMetaData webFragmentMetaData = WebFragmentMetaDataParser.parse(xmlReader,
PropertyReplacers.resolvingReplacer(new MPConfigPropertyResolver()));
webFragments.put(archive.getArchiveRoot().getFileName().toString(), webFragmentMetaData);

} catch (XMLStreamException e) {
throw new RuntimeException("Failed to parse " + webFragment + " " + e.getLocation(), e);
} catch (IOException e) {
throw new RuntimeException("Failed to parse " + webFragment, e);
}
}
}
return webFragments;
}

}
@@ -0,0 +1,89 @@
/*
* Copyright 2018 Red Hat, Inc.
*
* Licensed 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 io.quarkus.undertow.test;

import static org.hamcrest.Matchers.is;

import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.asset.StringAsset;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.test.QuarkusUnitTest;
import io.restassured.RestAssured;

/**
* tests a web fragment. Normally this would be in a different jar however we don't really have that facility yet in
* QuarkusUnitTest
*/
public class ServletWebFragmentXmlMergingTestCase {

static final String WEB_FRAGMENT_XML = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
"\n" +
"<web-fragment version=\"3.0\"\n" +
" xmlns=\"http://java.sun.com/xml/ns/javaee\"\n" +
" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n" +
" xsi:schemaLocation=\"http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-fragment_3_0.xsd\"\n"
+
" metadata-complete=\"false\">\n" +
"<servlet>\n" +
" <servlet-name>mapped</servlet-name>\n" +
" <servlet-class>" + WebXmlServlet.class.getName() + "</servlet-class>\n" +
" </servlet>\n" +
"\n" +
" <servlet-mapping>\n" +
" <servlet-name>mapped</servlet-name>\n" +
" <url-pattern>/mapped</url-pattern>\n" +
" </servlet-mapping>" +
"</web-fragment>";

static final String WEB_XML = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
"\n" +
"<web-app version=\"3.0\"\n" +
" xmlns=\"http://java.sun.com/xml/ns/javaee\"\n" +
" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n" +
" xsi:schemaLocation=\"http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd\"\n"
+
" metadata-complete=\"false\">\n" +
"\n" +
" <filter> \n" +
" <filter-name>mapped-filter</filter-name>\n" +
" <filter-class>" + WebXmlFilter.class.getName() + "</filter-class> \n" +
" </filter> \n" +
" <filter-mapping> \n" +
" <filter-name>mapped-filter</filter-name>\n" +
" <url-pattern>/*</url-pattern> \n" +
" </filter-mapping> " +
"\n" +
"</web-app>";

@RegisterExtension
static QuarkusUnitTest runner = new QuarkusUnitTest()
.setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
.addClasses(WebXmlServlet.class)
.addAsManifestResource(new StringAsset(WEB_FRAGMENT_XML), "web-fragment.xml")
.addAsManifestResource(new StringAsset(WEB_XML), "web.xml"));

@Test
public void testWebFragmentMerging() {
RestAssured.when().get("/mapped").then()
.statusCode(200)
.body(is("web xml filter\nweb xml servlet"));
}

}
@@ -0,0 +1,68 @@
/*
* Copyright 2018 Red Hat, Inc.
*
* Licensed 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 io.quarkus.undertow.test;

import static org.hamcrest.Matchers.is;

import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.asset.StringAsset;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.test.QuarkusUnitTest;
import io.restassured.RestAssured;

/**
* tests a web fragment. Normally this would be in a different jar however we don't really have that facility yet in
* QuarkusUnitTest
*/
public class ServletWebFragmentXmlTestCase {

static final String WEB_FRAGMENT_XML = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
"\n" +
"<web-fragment version=\"3.0\"\n" +
" xmlns=\"http://java.sun.com/xml/ns/javaee\"\n" +
" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n" +
" xsi:schemaLocation=\"http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-fragment_3_0.xsd\"\n"
+
" metadata-complete=\"false\">\n" +
"<servlet>\n" +
" <servlet-name>mapped</servlet-name>\n" +
" <servlet-class>" + WebXmlServlet.class.getName() + "</servlet-class>\n" +
" </servlet>\n" +
"\n" +
" <servlet-mapping>\n" +
" <servlet-name>mapped</servlet-name>\n" +
" <url-pattern>/mapped</url-pattern>\n" +
" </servlet-mapping>" +
"</web-fragment>";

@RegisterExtension
static QuarkusUnitTest runner = new QuarkusUnitTest()
.setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
.addClasses(WebXmlServlet.class)
.addAsManifestResource(new StringAsset(WEB_FRAGMENT_XML), "web-fragment.xml"));

@Test
public void testWebFragment() {
RestAssured.when().get("/mapped").then()
.statusCode(200)
.body(is("web xml servlet"));
}

}