From 83c8b397cad04bc871ba937cbb85c8d063c096df Mon Sep 17 00:00:00 2001 From: Grzegorz Grzybek Date: Thu, 6 Apr 2023 15:13:52 +0200 Subject: [PATCH] [#1803] Work in progress (136/138 tests pass) --- .../war/internal/WarExtenderContext.java | 7 +- pax-web-itest/pax-web-tck/pom.xml | 329 ++++++++++++++++++ .../pax/web/tck/AbstractOsgiTestBase.java | 82 +++++ .../pax/web/tck/CleanIntegrationTest.java | 61 ++++ .../test/resources/controlled-exam.properties | 28 ++ .../src/test/resources/log4j2-osgi.properties | 51 +++ .../src/test/resources/log4j2-test.properties | 49 +++ ...pache.servicemix.bundles.javax-inject.link | 21 ++ pax-web-itest/pom.xml | 6 + .../spi/servlet/OsgiServletContext.java | 4 +- .../tomcat/internal/TomcatServerWrapper.java | 8 +- 11 files changed, 642 insertions(+), 4 deletions(-) create mode 100644 pax-web-itest/pax-web-tck/pom.xml create mode 100644 pax-web-itest/pax-web-tck/src/test/java/org/ops4j/pax/web/tck/AbstractOsgiTestBase.java create mode 100644 pax-web-itest/pax-web-tck/src/test/java/org/ops4j/pax/web/tck/CleanIntegrationTest.java create mode 100644 pax-web-itest/pax-web-tck/src/test/resources/controlled-exam.properties create mode 100644 pax-web-itest/pax-web-tck/src/test/resources/log4j2-osgi.properties create mode 100644 pax-web-itest/pax-web-tck/src/test/resources/log4j2-test.properties create mode 100644 pax-web-itest/pax-web-tck/src/test/resources/org.apache.servicemix.bundles.javax-inject.link diff --git a/pax-web-extender-war/src/main/java/org/ops4j/pax/web/extender/war/internal/WarExtenderContext.java b/pax-web-extender-war/src/main/java/org/ops4j/pax/web/extender/war/internal/WarExtenderContext.java index 5cfadbbb21..803e1eac31 100644 --- a/pax-web-extender-war/src/main/java/org/ops4j/pax/web/extender/war/internal/WarExtenderContext.java +++ b/pax-web-extender-war/src/main/java/org/ops4j/pax/web/extender/war/internal/WarExtenderContext.java @@ -786,6 +786,11 @@ public void destroy() throws Exception { return; } + List awaiting = webApplicationQueue.get(webApp.getContextPath()); + if (awaiting != null) { + awaiting.remove(bundle); + } + // Pax Web before 8 interacted here with dependency manager in order to tell the WebApp to unregister // a WebAppDependencyHolder registration. But we're handling the lifecycle in different way now. webApp.stop(); @@ -851,8 +856,8 @@ public void run() { // (at least attempted to be deployed) BundleWebApplication app = webApplications.get(awaitingWab); LOG.info("Redeploying {}, because {} is now available", app, contextPath); - app.deploy(); it.remove(); + app.deploy(); break; } } diff --git a/pax-web-itest/pax-web-tck/pom.xml b/pax-web-itest/pax-web-tck/pom.xml new file mode 100644 index 0000000000..7d6719a48a --- /dev/null +++ b/pax-web-itest/pax-web-tck/pom.xml @@ -0,0 +1,329 @@ + + + + + 4.0.0 + + + org.ops4j.pax.web + pax-web-itest + 8.0.19-SNAPSHOT + ../pom.xml + + + org.ops4j.pax.web.itest + pax-web-tck + + OPS4J Pax Web - Container Tests - TCK + + Integration tests for TCK + + + + + + + src/test/resources + true + + + src/test/resources-binary + false + + + + + + + + + org.apache.maven.plugins + maven-failsafe-plugin + + + **/*Test.java + + + + file:./src/test/resources/controlled-exam.properties + + false + alphabetical + runtime + 1 + false + false + + + + integration-test + + integration-test + + + + verify + + verify + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + true + + + + + + + org.apache.servicemix.tooling + depends-maven-plugin + + + generate-depends-file + + generate-depends-file + + + + + + + + + + + + + + org.ops4j.pax.web + pax-web-api + + provided + + + org.ops4j.pax.web + pax-web-spi + provided + + + org.ops4j.pax.web + pax-web-runtime + runtime + + + org.ops4j.pax.web + pax-web-tomcat + runtime + + + org.ops4j.pax.web + pax-web-tomcat-common + runtime + + + + org.ops4j.pax.web.itest + pax-web-itest-common + test + + + + org.ops4j.pax.web + pax-web-compatibility-annotation13 + + + + + + + org.ops4j.base + ops4j-base-lang + test + + + + org.ops4j.base + ops4j-base-util-property + test + + + + org.ops4j.pax.logging + pax-logging-api + runtime + + + org.ops4j.pax.logging + pax-logging-log4j2 + runtime + + + + org.ops4j.pax.exam + pax-exam + test + + + org.ops4j.pax.exam + pax-exam-junit4 + test + + + org.ops4j.pax.exam + pax-exam-container-native + test + + + org.ops4j.pax.exam + pax-exam-link-mvn + test + + + org.ops4j.pax.exam + pax-exam-extender-service + test + + + + + + org.osgi + osgi.core + + + org.osgi + osgi.cmpn + + + + org.apache.felix + org.apache.felix.configadmin + runtime + + + org.apache.felix + org.apache.felix.metatype + + test + + + + + org.osgi + org.osgi.test.cases.webcontainer + 8.1.0-SNAPSHOT + + + geronimo-servlet_2.5_spec + org.apache.geronimo.specs + + + junit-jupiter-api + org.junit.jupiter + + + junit-jupiter-engine + org.junit.jupiter + + + junit-jupiter-params + org.junit.jupiter + + + junit-platform-commons + org.junit.platform + + + junit-platform-engine + org.junit.platform + + + junit-platform-launcher + org.junit.platform + + + junit-vintage-engine + org.junit.vintage + + + mockito-core + org.mockito + + + + + + + + jakarta.annotation + jakarta.annotation-api + test + + + jakarta.servlet + jakarta.servlet-api + + test + + + + + + org.apache.servicemix.bundles + org.apache.servicemix.bundles.javax-inject + runtime + + + + + + org.slf4j + slf4j-api + test + + + org.apache.logging.log4j + log4j-slf4j-impl + test + + + + + + commons-io + commons-io + test + + + + + diff --git a/pax-web-itest/pax-web-tck/src/test/java/org/ops4j/pax/web/tck/AbstractOsgiTestBase.java b/pax-web-itest/pax-web-tck/src/test/java/org/ops4j/pax/web/tck/AbstractOsgiTestBase.java new file mode 100644 index 0000000000..a07bcb883f --- /dev/null +++ b/pax-web-itest/pax-web-tck/src/test/java/org/ops4j/pax/web/tck/AbstractOsgiTestBase.java @@ -0,0 +1,82 @@ +/* + * Copyright 2019 OPS4J. + * + * 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 org.ops4j.pax.web.tck; + +import org.apache.commons.io.IOUtils; +import org.ops4j.pax.exam.CoreOptions; +import org.ops4j.pax.exam.Option; +import org.ops4j.pax.exam.ProbeBuilder; +import org.ops4j.pax.exam.TestProbeBuilder; +import org.ops4j.pax.tinybundles.core.TinyBundles; +import org.ops4j.pax.web.itest.AbstractControlledTestBase; +import org.osgi.framework.Constants; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; + +import static org.ops4j.pax.exam.CoreOptions.systemProperty; +import static org.ops4j.pax.exam.OptionUtils.combine; + +/** + *

Base class for all OSGi Pax Exam tests not related to any special container (Jetty, Tomcat or Undertow).

+ */ +public abstract class AbstractOsgiTestBase extends AbstractControlledTestBase { + + protected Option[] baseConfigure() { + // pax-web-itest-container-common private packages org.ops4j.pax.web.itest package, but here + // there's no additional bundle, so we have to create it on-fly + InputStream helper = TinyBundles.bundle() + .set("Bundle-ManifestVersion", "2") + .set("Export-Package", "org.ops4j.pax.web.itest") + .set("DynamicImport-Package", "*") + .add(AbstractControlledTestBase.class) + .set("Bundle-SymbolicName", "infra") + .build(); + File dir = new File("target/bundles"); + dir.mkdirs(); + String bundleURL = null; + try { + File bundle = new File(dir, "infra-bundle.jar"); + IOUtils.copy(helper, new FileOutputStream(bundle)); + bundleURL = bundle.toURI().toURL().toString(); + } catch (IOException e) { + throw new RuntimeException(e.getMessage(), e); + } + + Option[] options = super.baseConfigure(); + options = combine(options, defaultLoggingConfig()); + // with PAXLOGGING-308 we can simply point to _native_ configuration file understood directly + // by selected pax-logging backend + options = combine(options, systemProperty("org.ops4j.pax.logging.property.file") + .value("../etc/log4j2-osgi.properties")); + + return combine(options, CoreOptions.bundle(bundleURL)); + } + + /** + * Configuring symbolic name in test probe we can easily locate related log entries in the output. + * @param builder + * @return + */ + @ProbeBuilder + public TestProbeBuilder probeBuilder(TestProbeBuilder builder) { + builder.setHeader(Constants.BUNDLE_SYMBOLICNAME, PROBE_SYMBOLIC_NAME); + return builder; + } + +} diff --git a/pax-web-itest/pax-web-tck/src/test/java/org/ops4j/pax/web/tck/CleanIntegrationTest.java b/pax-web-itest/pax-web-tck/src/test/java/org/ops4j/pax/web/tck/CleanIntegrationTest.java new file mode 100644 index 0000000000..f09fe54167 --- /dev/null +++ b/pax-web-itest/pax-web-tck/src/test/java/org/ops4j/pax/web/tck/CleanIntegrationTest.java @@ -0,0 +1,61 @@ +/* + * Copyright 2019 OPS4J. + * + * 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 org.ops4j.pax.web.tck; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.ops4j.pax.exam.Configuration; +import org.ops4j.pax.exam.Option; +import org.ops4j.pax.exam.junit.PaxExam; +import org.osgi.framework.Bundle; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Arrays; +import java.util.Set; +import java.util.TreeSet; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.ops4j.pax.exam.OptionUtils.combine; + +@RunWith(PaxExam.class) +public class CleanIntegrationTest extends AbstractOsgiTestBase { + + public static final Logger LOG = LoggerFactory.getLogger(CleanIntegrationTest.class); + + @Configuration + public Option[] configure() { + return combine(baseConfigure(), paxWebCore()); + } + + @Test + public void justRun() { + Set bundles = new TreeSet<>((b1, b2) -> (int) (b1.getBundleId() - b2.getBundleId())); + bundles.addAll(Arrays.asList(context.getBundles())); + for (Bundle b : bundles) { + String info = String.format("#%02d: %s/%s (%s)", + b.getBundleId(), b.getSymbolicName(), b.getVersion(), b.getLocation()); + LOG.info(info); + } + + Bundle api = bundle("org.ops4j.pax.web.pax-web-api"); + Bundle spi = bundle("org.ops4j.pax.web.pax-web-spi"); + assertThat(api.getState(), equalTo(Bundle.ACTIVE)); + assertThat(spi.getState(), equalTo(Bundle.ACTIVE)); + } + +} diff --git a/pax-web-itest/pax-web-tck/src/test/resources/controlled-exam.properties b/pax-web-itest/pax-web-tck/src/test/resources/controlled-exam.properties new file mode 100644 index 0000000000..9378373b05 --- /dev/null +++ b/pax-web-itest/pax-web-tck/src/test/resources/controlled-exam.properties @@ -0,0 +1,28 @@ +# +# Copyright 2021 OPS4J. +# +# 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. +# + +# default, javaee, test (default), cdi +# for "cdi" we use PaxExam.delegate = org.ops4j.pax.exam.junit.impl.InjectingRunner +# otherwise it's PaxExam.delegate = org.ops4j.pax.exam.junit.impl.ProbeRunner +# "test" adds some default @Configuration Option[] +# "default" allows manual control over provided @Configuration/Option[] +pax.exam.system = default + +# 10000 ms by default +pax.exam.service.timeout = 3600000 + +# "pax-logging" by default which adds link:classpath:META-INF/links/org.ops4j.pax.logging.api.link;start-level=2 +pax.exam.logging = custom diff --git a/pax-web-itest/pax-web-tck/src/test/resources/log4j2-osgi.properties b/pax-web-itest/pax-web-tck/src/test/resources/log4j2-osgi.properties new file mode 100644 index 0000000000..259cd3037d --- /dev/null +++ b/pax-web-itest/pax-web-tck/src/test/resources/log4j2-osgi.properties @@ -0,0 +1,51 @@ +# +# Copyright 2021 OPS4J. +# +# 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. +# + +# this is logging configuration used by pax-logging and passed directly to pax-logging-log4j2 +# without asynchronous ConfigAdmin configuration + +status = INFO +verbose = false +dest = out + +appender.stdout.type = console +appender.stdout.name = stdout +appender.stdout.layout.type = PatternLayout +appender.stdout.layout.pattern = \ - OSGi - %d{HH:mm:ss.SSS} [%thread] %-5level (%F:%L) %logger - %msg%n +appender.stdout.filter.threshold.type = ThresholdFilter +appender.stdout.filter.threshold.level = info + +appender.file.type = RollingFile +appender.file.name = file +appender.file.append = true +appender.file.fileName = target/logs/test.log +appender.file.filePattern = target/logs/test-%i.log.gz +appender.file.layout.type = PatternLayout +appender.file.layout.pattern = %d{HH:mm:ss.SSS} [%thread] %-5level (%F:%L) %logger - %msg%n +appender.file.policy.type = SizeBasedTriggeringPolicy +appender.file.policy.size = 10MB +appender.file.strategy.type = DefaultRolloverStrategy +appender.file.strategy.max = 10 + +logger.exam.name = org.ops4j.pax.exam +logger.exam.level = error + +logger.web.name = org.ops4j.pax.web +logger.web.level = trace + +rootLogger.level = warn +rootLogger.appenderRef.console.ref = stdout +rootLogger.appenderRef.file.ref = file diff --git a/pax-web-itest/pax-web-tck/src/test/resources/log4j2-test.properties b/pax-web-itest/pax-web-tck/src/test/resources/log4j2-test.properties new file mode 100644 index 0000000000..8c320cdf66 --- /dev/null +++ b/pax-web-itest/pax-web-tck/src/test/resources/log4j2-test.properties @@ -0,0 +1,49 @@ +# +# Copyright 2021 OPS4J. +# +# 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. +# + +# this is logging configuration for pax-exam itself. This file should not be used to configure +# logging used in the container started by pax-exam - even if it's native container sharing classpath with +# surefire/failsafe runner + +status = INFO +verbose = false +dest = out + +appender.stdout.type = console +appender.stdout.name = stdout +appender.stdout.layout.type = PatternLayout +appender.stdout.layout.pattern = \ - EXAM - %d{HH:mm:ss.SSS} [%thread] %-5level (%F:%L) %logger - %msg%n +appender.stdout.filter.threshold.type = ThresholdFilter +appender.stdout.filter.threshold.level = info + +appender.file.type = RollingFile +appender.file.name = file +appender.file.append = true +appender.file.fileName = target/logs/pax-exam-test.log +appender.file.filePattern = target/logs/pax-exam-test-%i.log.gz +appender.file.layout.type = PatternLayout +appender.file.layout.pattern = %d{HH:mm:ss.SSS} [%thread] %-5level (%F:%L) %logger - %msg%n +appender.file.policy.type = SizeBasedTriggeringPolicy +appender.file.policy.size = 10MB +appender.file.strategy.type = DefaultRolloverStrategy +appender.file.strategy.max = 10 + +logger.exam.name = org.ops4j.pax.exam +logger.exam.level = debug + +rootLogger.level = warn +rootLogger.appenderRef.console.ref = stdout +rootLogger.appenderRef.file.ref = file diff --git a/pax-web-itest/pax-web-tck/src/test/resources/org.apache.servicemix.bundles.javax-inject.link b/pax-web-itest/pax-web-tck/src/test/resources/org.apache.servicemix.bundles.javax-inject.link new file mode 100644 index 0000000000..2474e0a2b7 --- /dev/null +++ b/pax-web-itest/pax-web-tck/src/test/resources/org.apache.servicemix.bundles.javax-inject.link @@ -0,0 +1,21 @@ +# +# Copyright 2019 OPS4J. +# +# 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. +# + +# Using mvn: will work with org.ops4j.pax.exam.nat.internal.NativeTestContainer, +# because we have test-scoped dependency to pax-url-aether +# when running with org.ops4j.pax.exam.karaf.container.internal.KarafTestContainer, pax-url-aether +# would need to be installed separately +mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.javax-inject/${dependency.org.apache.servicemix.bundles.javax-inject} diff --git a/pax-web-itest/pom.xml b/pax-web-itest/pom.xml index d0f2a92f5f..2e21fd053c 100644 --- a/pax-web-itest/pom.xml +++ b/pax-web-itest/pom.xml @@ -114,6 +114,12 @@ + + tck + + pax-web-tck + + diff --git a/pax-web-spi/src/main/java/org/ops4j/pax/web/service/spi/servlet/OsgiServletContext.java b/pax-web-spi/src/main/java/org/ops4j/pax/web/service/spi/servlet/OsgiServletContext.java index ea09f35993..725eff7a3d 100644 --- a/pax-web-spi/src/main/java/org/ops4j/pax/web/service/spi/servlet/OsgiServletContext.java +++ b/pax-web-spi/src/main/java/org/ops4j/pax/web/service/spi/servlet/OsgiServletContext.java @@ -54,6 +54,7 @@ import org.osgi.framework.FrameworkUtil; import org.osgi.framework.ServiceReference; import org.osgi.framework.ServiceRegistration; +import org.osgi.framework.Version; import org.osgi.framework.wiring.BundleWiring; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -195,7 +196,8 @@ public void register() { if (bc != null) { Dictionary properties = new Hashtable<>(); properties.put(PaxWebConstants.SERVICE_PROPERTY_WEB_SYMBOLIC_NAME, bundle.getSymbolicName()); - properties.put(PaxWebConstants.SERVICE_PROPERTY_WEB_VERSION, bundle.getVersion()); + properties.put(PaxWebConstants.SERVICE_PROPERTY_WEB_VERSION, + bundle.getVersion() == null ? Version.emptyVersion.toString() : bundle.getVersion().toString()); properties.put(PaxWebConstants.SERVICE_PROPERTY_WEB_SERVLETCONTEXT_PATH, osgiContextModel.getContextPath()); properties.put(PaxWebConstants.SERVICE_PROPERTY_WEB_SERVLETCONTEXT_NAME, osgiContextModel.getName()); registration = bc.registerService(ServletContext.class, this, properties); diff --git a/pax-web-tomcat/src/main/java/org/ops4j/pax/web/service/tomcat/internal/TomcatServerWrapper.java b/pax-web-tomcat/src/main/java/org/ops4j/pax/web/service/tomcat/internal/TomcatServerWrapper.java index 69c79cb414..f628f35e9c 100644 --- a/pax-web-tomcat/src/main/java/org/ops4j/pax/web/service/tomcat/internal/TomcatServerWrapper.java +++ b/pax-web-tomcat/src/main/java/org/ops4j/pax/web/service/tomcat/internal/TomcatServerWrapper.java @@ -1044,8 +1044,8 @@ public void visitOsgiContextModelChange(OsgiContextModelChange change) { } } - // and the highest ranked context should be registered as OSGi service (if it wasn't registered) - highestRankedContext.register(); +// // and the highest ranked context should be registered as OSGi service (if it wasn't registered) +// highestRankedContext.register(); } if (hasStopped) { @@ -2168,6 +2168,10 @@ private void ensureServletContextStarted(PaxWebStandardContext context) { // swap dynamic to normal context dynamicContext.rememberAttributesFromSCIs(); context.setOsgiServletContext(highestRankedContext); + + // only now, according to https://docs.osgi.org/specification/osgi.cmpn/7.0.0/service.war.html#d0e100694 + // register the servlet context + highestRankedContext.register(); } catch (Exception e) { LOG.error(e.getMessage(), e); }