From d997a1171b29d01d9a2291510f082a8637202e0d Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Thu, 10 Jun 2021 09:04:50 -0500 Subject: [PATCH 1/3] Issue #6354 - OSGI manifest for slf4j-api packages lower limit should be 1.7 (#6381) Issue #6354 - OSGI manifest for slf4j-api packages lower limit should be 1.7 * Fixed OSGi manifest in all jars. * Make osgi tests work with slf4j < 2.0.0. This required to remove the dependency on SLF4J from the demos. Signed-off-by: Joakim Erdfelt Co-authored-by: Jan Bartel --- demos/demo-jetty-webapp/pom.xml | 9 ---- .../src/main/java/com/acme/ChatServlet.java | 22 +++------ .../src/main/java/com/acme/Dump.java | 8 ++-- .../main/java/com/acme/SecureModeServlet.java | 7 +-- .../src/main/java/com/acme/TestFilter.java | 7 +-- jetty-alpn/jetty-alpn-client/pom.xml | 2 +- .../jetty-alpn-conscrypt-client/pom.xml | 2 +- .../jetty-alpn-conscrypt-server/pom.xml | 2 +- jetty-annotations/pom.xml | 2 +- jetty-jndi/pom.xml | 2 +- jetty-osgi/jetty-osgi-boot/pom.xml | 47 +++++++++---------- jetty-osgi/test-jetty-osgi-context/pom.xml | 13 ++++- jetty-osgi/test-jetty-osgi-server/pom.xml | 13 ++++- jetty-osgi/test-jetty-osgi/pom.xml | 12 +++++ .../eclipse/jetty/osgi/test/TestOSGiUtil.java | 33 ++++++++++--- .../test/resources/simplelogger.properties | 1 + jetty-plus/pom.xml | 2 +- pom.xml | 5 +- 18 files changed, 108 insertions(+), 81 deletions(-) create mode 100644 jetty-osgi/test-jetty-osgi/src/test/resources/simplelogger.properties diff --git a/demos/demo-jetty-webapp/pom.xml b/demos/demo-jetty-webapp/pom.xml index 27c416ccdfe..5709abf0b98 100644 --- a/demos/demo-jetty-webapp/pom.xml +++ b/demos/demo-jetty-webapp/pom.xml @@ -126,15 +126,6 @@ - - org.slf4j - slf4j-api - - - org.eclipse.jetty - jetty-slf4j-impl - compile - org.eclipse.jetty jetty-servlets diff --git a/demos/demo-jetty-webapp/src/main/java/com/acme/ChatServlet.java b/demos/demo-jetty-webapp/src/main/java/com/acme/ChatServlet.java index 2856ff8f87d..6b456b02000 100644 --- a/demos/demo-jetty-webapp/src/main/java/com/acme/ChatServlet.java +++ b/demos/demo-jetty-webapp/src/main/java/com/acme/ChatServlet.java @@ -27,17 +27,12 @@ import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - // Simple asynchronous Chat room. // This does not handle duplicate usernames or multiple frames/tabs from the same browser // Some code is duplicated for clarity. @SuppressWarnings("serial") public class ChatServlet extends HttpServlet { - private static final Logger LOG = LoggerFactory.getLogger(ChatServlet.class); - private long asyncTimeout = 10000; @Override @@ -63,7 +58,7 @@ public class ChatServlet extends HttpServlet @Override public void onTimeout(AsyncEvent event) throws IOException { - LOG.debug("resume request"); + getServletContext().log("resume request"); AsyncContext async = _async.get(); if (async != null && _async.compareAndSet(async, null)) { @@ -102,10 +97,10 @@ public class ChatServlet extends HttpServlet String message = request.getParameter("message"); String username = request.getParameter("user"); - LOG.debug("doPost called. join={},message={},username={}", join, message, username); + getServletContext().log("doPost called. join=" + join + " message=" + message + " username=" + username); if (username == null) { - LOG.debug("no parameter user set, sending 503"); + getServletContext().log("no parameter user set, sending 503"); response.sendError(503, "user==null"); return; } @@ -125,14 +120,14 @@ public class ChatServlet extends HttpServlet { synchronized (member) { - LOG.debug("Queue size: {}", member._queue.size()); + getServletContext().log("Queue size: " + member._queue.size()); if (!member._queue.isEmpty()) { sendSingleMessage(response, member); } else { - LOG.debug("starting async"); + getServletContext().log("starting async"); AsyncContext async = request.startAsync(); async.setTimeout(asyncTimeout); async.addListener(member); @@ -147,7 +142,7 @@ public class ChatServlet extends HttpServlet Member member = room.get(username); if (member == null) { - LOG.debug("user: {} in room: {} doesn't exist. Creating new user.", username, room); + getServletContext().log("user: " + username + " in room: " + room + " doesn't exist. Creating new user."); member = new Member(username); room.put(username, member); } @@ -159,7 +154,7 @@ public class ChatServlet extends HttpServlet Map room = _rooms.get(path); if (room == null) { - LOG.debug("room: {} doesn't exist. Creating new room.", path); + getServletContext().log("room: " + path + " doesn't exist. Creating new room."); room = new HashMap<>(); _rooms.put(path, room); } @@ -192,7 +187,6 @@ public class ChatServlet extends HttpServlet private void sendMessageToAllMembers(String message, String username, Map room) { - LOG.debug("Sending message: {} from: {}", message, username); for (Member m : room.values()) { synchronized (m) @@ -202,10 +196,8 @@ public class ChatServlet extends HttpServlet // wakeup member if polling AsyncContext async = m._async.get(); - LOG.debug("Async found: {}", async); if (async != null & m._async.compareAndSet(async, null)) { - LOG.debug("dispatch"); async.dispatch(); } } diff --git a/demos/demo-jetty-webapp/src/main/java/com/acme/Dump.java b/demos/demo-jetty-webapp/src/main/java/com/acme/Dump.java index 07cb94ba26e..7160cbdd675 100644 --- a/demos/demo-jetty-webapp/src/main/java/com/acme/Dump.java +++ b/demos/demo-jetty-webapp/src/main/java/com/acme/Dump.java @@ -51,8 +51,6 @@ import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponseWrapper; import javax.servlet.http.Part; -import org.slf4j.LoggerFactory; - /** * Dump Servlet Request. */ @@ -116,7 +114,7 @@ public class Dump extends HttpServlet } catch (ServletException e) { - getServletContext().log(e.toString()); + getServletContext().log("Login fail", e); } } @@ -341,12 +339,12 @@ public class Dump extends HttpServlet } catch (IOException e2) { - LoggerFactory.getLogger(Dump.class).trace("IGNORED", e2); + getServletContext().log("Write fail", e2); } } catch (IOException e) { - LoggerFactory.getLogger(Dump.class).trace("IGNORED", e); + getServletContext().log("Output fail", e); } return; } diff --git a/demos/demo-jetty-webapp/src/main/java/com/acme/SecureModeServlet.java b/demos/demo-jetty-webapp/src/main/java/com/acme/SecureModeServlet.java index e5b99db1a60..27ffa980d61 100644 --- a/demos/demo-jetty-webapp/src/main/java/com/acme/SecureModeServlet.java +++ b/demos/demo-jetty-webapp/src/main/java/com/acme/SecureModeServlet.java @@ -27,17 +27,12 @@ import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - /** * Dump Servlet Request. */ @SuppressWarnings("serial") public class SecureModeServlet extends HttpServlet { - private static final Logger LOG = LoggerFactory.getLogger(SecureModeServlet.class); - @Override public void init(ServletConfig config) throws ServletException { @@ -116,7 +111,7 @@ public class SecureModeServlet extends HttpServlet try { out.println("check ability to log
"); - LOG.info("testing logging"); + getServletContext().log("testing logging"); out.println("status: SUCCESS - expected
"); } catch (SecurityException e) diff --git a/demos/demo-jetty-webapp/src/main/java/com/acme/TestFilter.java b/demos/demo-jetty-webapp/src/main/java/com/acme/TestFilter.java index 7493f1ba4b2..f09b0f50beb 100644 --- a/demos/demo-jetty-webapp/src/main/java/com/acme/TestFilter.java +++ b/demos/demo-jetty-webapp/src/main/java/com/acme/TestFilter.java @@ -27,9 +27,6 @@ import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - /** * TestFilter. * @@ -39,8 +36,6 @@ import org.slf4j.LoggerFactory; */ public class TestFilter implements Filter { - private static final Logger LOG = LoggerFactory.getLogger(TestFilter.class); - private boolean _remote; private ServletContext _context; private final Set _allowed = new HashSet(); @@ -54,7 +49,7 @@ public class TestFilter implements Filter _allowed.add("/jetty_banner.gif"); _allowed.add("/remote.html"); - LOG.debug("TestFilter#remote=" + _remote); + filterConfig.getServletContext().log("TestFilter#remote=" + _remote); } @Override diff --git a/jetty-alpn/jetty-alpn-client/pom.xml b/jetty-alpn/jetty-alpn-client/pom.xml index a577cfd699e..c27fb3dd79c 100644 --- a/jetty-alpn/jetty-alpn-client/pom.xml +++ b/jetty-alpn/jetty-alpn-client/pom.xml @@ -24,7 +24,7 @@ - org.eclipse.jetty.alpn;resolution:=optional,* + ${osgi.slf4j.import.packages},org.eclipse.jetty.alpn;resolution:=optional,* osgi.extender; filter:="(osgi.extender=osgi.serviceloader.processor)";resolution:=optional, osgi.serviceloader; filter:="(osgi.serviceloader=org.eclipse.jetty.io.ssl.ALPNProcessor$Client)";resolution:=optional;cardinality:=multiple diff --git a/jetty-alpn/jetty-alpn-conscrypt-client/pom.xml b/jetty-alpn/jetty-alpn-conscrypt-client/pom.xml index 5f1b5180a48..7e54dc25f4a 100644 --- a/jetty-alpn/jetty-alpn-conscrypt-client/pom.xml +++ b/jetty-alpn/jetty-alpn-conscrypt-client/pom.xml @@ -52,7 +52,7 @@ Conscrypt Client ALPN - org.conscrypt;version="${conscrypt.version}",* + ${osgi.slf4j.import.packages},org.conscrypt;version="${conscrypt.version}",* * osgi.extender; filter:="(osgi.extender=osgi.serviceloader.registrar)";resolution:=optional osgi.serviceloader; osgi.serviceloader=org.eclipse.jetty.io.ssl.ALPNProcessor$Client diff --git a/jetty-alpn/jetty-alpn-conscrypt-server/pom.xml b/jetty-alpn/jetty-alpn-conscrypt-server/pom.xml index 554fd99abc2..e9ca1cc140f 100644 --- a/jetty-alpn/jetty-alpn-conscrypt-server/pom.xml +++ b/jetty-alpn/jetty-alpn-conscrypt-server/pom.xml @@ -80,7 +80,7 @@ Conscrypt ALPN - org.conscrypt;version="${conscrypt.version}",* + ${osgi.slf4j.import.packages},org.conscrypt;version="${conscrypt.version}",* osgi.extender; filter:="(osgi.extender=osgi.serviceloader.registrar)";resolution:=optional osgi.serviceloader;osgi.serviceloader=org.eclipse.jetty.io.ssl.ALPNProcessor$Server <_nouses>true diff --git a/jetty-annotations/pom.xml b/jetty-annotations/pom.xml index 004c3e4775c..9429961dfc9 100644 --- a/jetty-annotations/pom.xml +++ b/jetty-annotations/pom.xml @@ -30,7 +30,7 @@ true - org.objectweb.asm;version="5",* + ${osgi.slf4j.import.packages},org.objectweb.asm;version="5",* osgi.serviceloader; filter:="(osgi.serviceloader=javax.servlet.ServletContainerInitializer)";resolution:=optional;cardinality:=multiple, osgi.extender; filter:="(osgi.extender=osgi.serviceloader.processor)";resolution:=optional diff --git a/jetty-jndi/pom.xml b/jetty-jndi/pom.xml index ef93ea29417..011a72d405c 100644 --- a/jetty-jndi/pom.xml +++ b/jetty-jndi/pom.xml @@ -31,7 +31,7 @@ true - javax.mail.*;resolution:=optional,* + ${osgi.slf4j.import.packages},javax.mail.*;resolution:=optional,* diff --git a/jetty-osgi/jetty-osgi-boot/pom.xml b/jetty-osgi/jetty-osgi-boot/pom.xml index c75a930bdf9..0b02f4c5736 100644 --- a/jetty-osgi/jetty-osgi-boot/pom.xml +++ b/jetty-osgi/jetty-osgi-boot/pom.xml @@ -71,30 +71,29 @@ org.eclipse.jetty.osgi.boot;singleton:=true org.eclipse.jetty.osgi.boot.JettyBootstrapActivator org.eclipse.jetty.*;version="[$(version;===;${parsedVersion.osgiVersion}),$(version;+;${parsedVersion.osgiVersion}))" - javax.mail;version="1.4.0";resolution:=optional, - javax.mail.event;version="1.4.0";resolution:=optional, - javax.mail.internet;version="1.4.0";resolution:=optional, - javax.mail.search;version="1.4.0";resolution:=optional, - javax.mail.util;version="1.4.0";resolution:=optional, - javax.servlet;version="[$(version;==;${servlet.api.version}),$(version;+;${servlet.api.version}))", - javax.servlet.http;version="[$(version;==;${servlet.api.version}),$(version;+;${servlet.api.version}))", - javax.transaction;version="1.1.0";resolution:=optional, - javax.transaction.xa;version="1.1.0";resolution:=optional, - org.objectweb.asm;version="$(version;=;${asm.version})";resolution:=optional, - org.osgi.framework, - org.osgi.service.cm;version="1.4.0", - org.osgi.service.event;version="1.4.0", - org.osgi.service.packageadmin, - org.osgi.service.startlevel;version="1.0.0", - org.osgi.service.url;version="1.0.0", - org.osgi.util.tracker;version="1.3.0", - org.slf4j;resolution:=optional, - org.slf4j.spi;resolution:=optional, - org.slf4j.helpers;resolution:=optional, - org.xml.sax, - org.xml.sax.helpers, - org.eclipse.jetty.annotations;resolution:=optional, - * + + ${osgi.slf4j.import.packages}, + javax.mail;version="1.4.0";resolution:=optional, + javax.mail.event;version="1.4.0";resolution:=optional, + javax.mail.internet;version="1.4.0";resolution:=optional, + javax.mail.search;version="1.4.0";resolution:=optional, + javax.mail.util;version="1.4.0";resolution:=optional, + javax.servlet;version="[$(version;==;${servlet.api.version}),$(version;+;${servlet.api.version}))", + javax.servlet.http;version="[$(version;==;${servlet.api.version}),$(version;+;${servlet.api.version}))", + javax.transaction;version="1.1.0";resolution:=optional, + javax.transaction.xa;version="1.1.0";resolution:=optional, + org.objectweb.asm;version="$(version;=;${asm.version})";resolution:=optional, + org.osgi.framework, + org.osgi.service.cm;version="1.4.0", + org.osgi.service.event;version="1.4.0", + org.osgi.service.packageadmin, + org.osgi.service.startlevel;version="1.0.0", + org.osgi.service.url;version="1.0.0", + org.osgi.util.tracker;version="1.3.0", + org.xml.sax, + org.xml.sax.helpers, + org.eclipse.jetty.annotations;resolution:=optional, + * osgi.extender; filter:="(osgi.extender=osgi.serviceloader.registrar)" diff --git a/jetty-osgi/test-jetty-osgi-context/pom.xml b/jetty-osgi/test-jetty-osgi-context/pom.xml index ee75b3a3d9c..ff9585fd152 100644 --- a/jetty-osgi/test-jetty-osgi-context/pom.xml +++ b/jetty-osgi/test-jetty-osgi-context/pom.xml @@ -74,7 +74,18 @@ compilation time. --> <_nouses>true - javax.servlet;version="[3.1,4.1)", javax.servlet.resources;version="[3.1,4.1)", org.osgi.framework, org.osgi.service.cm;version="1.2.0", org.osgi.service.packageadmin, org.osgi.service.startlevel;version="1.0.0", org.osgi.service.url;version="1.0.0", org.osgi.util.tracker;version="1.3.0", org.slf4j;resolution:=optional, org.slf4j.spi;resolution:=optional, org.slf4j.helpers;resolution:=optional, org.xml.sax, org.xml.sax.helpers, * + ${osgi.slf4j.import.packages}, + javax.servlet;version="[3.1,4.1)", + javax.servlet.resources;version="[3.1,4.1)", + org.osgi.framework, + org.osgi.service.cm;version="1.2.0", + org.osgi.service.packageadmin, + org.osgi.service.startlevel;version="1.0.0", + org.osgi.service.url;version="1.0.0", + org.osgi.util.tracker;version="1.3.0", + org.xml.sax, + org.xml.sax.helpers, + * org.eclipse.jetty.*;version="[$(version;==;${parsedVersion.osgiVersion}),$(version;+;${parsedVersion.osgiVersion}))" diff --git a/jetty-osgi/test-jetty-osgi-server/pom.xml b/jetty-osgi/test-jetty-osgi-server/pom.xml index b841cae9d5e..8b28b4a4c5f 100644 --- a/jetty-osgi/test-jetty-osgi-server/pom.xml +++ b/jetty-osgi/test-jetty-osgi-server/pom.xml @@ -66,7 +66,18 @@ compilation time. --> <_nouses>true - javax.servlet;version="[3.1,4.1)", javax.servlet.resources;version="[3.1,4.1)", org.osgi.framework, org.osgi.service.cm;version="1.2.0", org.osgi.service.packageadmin, org.osgi.service.startlevel;version="1.0.o", org.osgi.service.url;version="1.0.0", org.osgi.util.tracker;version="1.3.0", org.slf4j;resolution:=optional, org.slf4j.spi;resolution:=optional, org.slf4j.helpers;resolution:=optional, org.xml.sax, org.xml.sax.helpers, * + ${osgi.slf4j.import.packages}, + javax.servlet;version="[3.1,4.1)", + javax.servlet.resources;version="[3.1,4.1)", + org.osgi.framework, + org.osgi.service.cm;version="1.2.0", + org.osgi.service.packageadmin, + org.osgi.service.startlevel;version="1.0.0", + org.osgi.service.url;version="1.0.0", + org.osgi.util.tracker;version="1.3.0", + org.xml.sax, + org.xml.sax.helpers, + * org.eclipse.jetty.*;version="[$(version;==;${parsedVersion.osgiVersion}),$(version;+;${parsedVersion.osgiVersion}))" diff --git a/jetty-osgi/test-jetty-osgi/pom.xml b/jetty-osgi/test-jetty-osgi/pom.xml index 4f787c7d9ec..bdf15a204e8 100644 --- a/jetty-osgi/test-jetty-osgi/pom.xml +++ b/jetty-osgi/test-jetty-osgi/pom.xml @@ -135,6 +135,18 @@
+ + org.slf4j + slf4j-api + 1.7.30 + test + + + org.slf4j + slf4j-simple + 1.7.30 + test + org.eclipse.jetty jetty-slf4j-impl diff --git a/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestOSGiUtil.java b/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestOSGiUtil.java index daa3f96dc31..088ee4e308e 100644 --- a/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestOSGiUtil.java +++ b/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestOSGiUtil.java @@ -142,13 +142,36 @@ public class TestOSGiUtil res.add(systemProperty("org.ops4j.pax.url.mvn.settings").value(System.getProperty("settingsFilePath"))); } - //make src/test/resources/jetty-logging.properties visible to jetty in the osgi container + /* + * Jetty 10 uses slf4j 2.0.0 by default, however we want to test with slf4j 1.7.30 for backwards compatibility. + * To do that, we need to use slf4j-simple as the logging implementation. We make a simplelogger.properties + * file available so that jetty logging can be configured + */ + res.add(mavenBundle().groupId("org.slf4j").artifactId("slf4j-api").versionAsInProject().noStart()); + TinyBundle simpleLoggingPropertiesBundle = TinyBundles.bundle(); + simpleLoggingPropertiesBundle.add("simplelogger.properties", ClassLoader.getSystemResource("simplelogger.properties")); + simpleLoggingPropertiesBundle.set(Constants.BUNDLE_SYMBOLICNAME, "simple-logger-properties"); + simpleLoggingPropertiesBundle.set(Constants.FRAGMENT_HOST, "slf4j-simple"); + simpleLoggingPropertiesBundle.add(FragmentActivator.class); + res.add(CoreOptions.streamBundle(simpleLoggingPropertiesBundle.build()).noStart()); + res.add(mavenBundle().groupId("org.slf4j").artifactId("slf4j-simple").versionAsInProject().noStart()); + + /* + * NOTE: when running with slf4j >= 2.0.0, remove the slf4j simple logger above and uncomment the following lines + TinyBundle loggingPropertiesBundle = TinyBundles.bundle(); loggingPropertiesBundle.add("jetty-logging.properties", ClassLoader.getSystemResource("jetty-logging.properties")); loggingPropertiesBundle.set(Constants.BUNDLE_SYMBOLICNAME, "jetty-logging-properties"); loggingPropertiesBundle.set(Constants.FRAGMENT_HOST, "org.eclipse.jetty.logging"); loggingPropertiesBundle.add(FragmentActivator.class); res.add(CoreOptions.streamBundle(loggingPropertiesBundle.build()).noStart()); + //Fix missing ServiceLoader in slf4j-api 2.0.0 manifest + res.add(wrappedBundle(mavenBundle().groupId("org.slf4j").artifactId("slf4j-api").versionAsInProject() + .instructions("Require-Capability=osgi.serviceloader;filter:=\"(osgi.serviceloader=org.slf4j.spi.SLF4JServiceProvider)\",osgi.extender;filter:=\"(osgi.extender=osgi.serviceloader.processor)\"") + .overwriteManifest(OverwriteMode.MERGE) + .start()); + res.add(mavenBundle().groupId("org.eclipse.jetty").artifactId("jetty-slf4j-impl").versionAsInProject().start()); + */ res.add(mavenBundle().groupId("org.eclipse.jetty.toolchain").artifactId("jetty-servlet-api").versionAsInProject().start()); res.add(mavenBundle().groupId("org.eclipse.platform").artifactId("org.eclipse.osgi.util").versionAsInProject()); res.add(mavenBundle().groupId("org.eclipse.platform").artifactId("org.eclipse.osgi.services").versionAsInProject()); @@ -160,12 +183,8 @@ public class TestOSGiUtil res.add(mavenBundle().groupId("org.apache.aries.spifly").artifactId("org.apache.aries.spifly.dynamic.bundle").versionAsInProject().start()); res.add(mavenBundle().groupId("jakarta.annotation").artifactId("jakarta.annotation-api").versionAsInProject().start()); res.add(mavenBundle().groupId("org.apache.geronimo.specs").artifactId("geronimo-jta_1.1_spec").version("1.1.1").start()); - //the slf4j-api jar does not have support for ServiceLoader in osgi in its manifest, so add it now - res.add(wrappedBundle(mavenBundle().groupId("org.slf4j").artifactId("slf4j-api").versionAsInProject()) - .instructions("Require-Capability=osgi.serviceloader;filter:=\"(osgi.serviceloader=org.slf4j.spi.SLF4JServiceProvider)\",osgi.extender;filter:=\"(osgi.extender=osgi.serviceloader.processor)\"") - .overwriteManifest(OverwriteMode.MERGE) - .start()); - res.add(mavenBundle().groupId("org.eclipse.jetty").artifactId("jetty-slf4j-impl").versionAsInProject().start()); + + res.add(mavenBundle().groupId("org.eclipse.jetty").artifactId("jetty-util").versionAsInProject().start()); res.add(mavenBundle().groupId("org.eclipse.jetty").artifactId("jetty-deploy").versionAsInProject().start()); res.add(mavenBundle().groupId("org.eclipse.jetty").artifactId("jetty-server").versionAsInProject().start()); diff --git a/jetty-osgi/test-jetty-osgi/src/test/resources/simplelogger.properties b/jetty-osgi/test-jetty-osgi/src/test/resources/simplelogger.properties new file mode 100644 index 00000000000..d8dceb60cf2 --- /dev/null +++ b/jetty-osgi/test-jetty-osgi/src/test/resources/simplelogger.properties @@ -0,0 +1 @@ +org.slf4j.simpleLogger.defaultLogLevel=info diff --git a/jetty-plus/pom.xml b/jetty-plus/pom.xml index 63d03a59bef..294f5d8282f 100644 --- a/jetty-plus/pom.xml +++ b/jetty-plus/pom.xml @@ -23,7 +23,7 @@ true - javax.transaction.*;version="1.1",* + ${osgi.slf4j.import.packages},javax.transaction.*;version="1.1",* osgi.extender; filter:="(osgi.extender=osgi.serviceloader.registrar)" diff --git a/pom.xml b/pom.xml index 5486a2c07ac..abcd8950d52 100644 --- a/pom.xml +++ b/pom.xml @@ -45,6 +45,9 @@ 1.0.6 1.10.9 + + org.slf4j;version="[1.7,3.0)", org.slf4j.event;version="[1.7,3.0)", org.slf4j.helpers;version="[1.7,3.0)", org.slf4j.spi;version="[1.7,3.0)" + false -Dfile.encoding=UTF-8 -Duser.language=en -Duser.region=US -showversion -Xmx4g -Xms2g -Xlog:gc:stderr:time,level,tags @@ -742,7 +745,7 @@ . Copyright (c) 2008-2021 Mort Bay Consulting Pty Ltd and others. - org.slf4j.*;version="[1.7,3.0)", + ${osgi.slf4j.import.packages}, * <_provider-policy>]]> From 6dea0251c2a4948578092db1c81e0d33b06f95b9 Mon Sep 17 00:00:00 2001 From: Lachlan Date: Fri, 11 Jun 2021 00:05:54 +1000 Subject: [PATCH 2/3] Issue #4772 - support partial messages for Jetty WS API annotations (#6357) * Issue #4772 - support partial messages for Jetty WS API annotations Signed-off-by: Lachlan Roberts --- .../jetty/websocket/api/RemoteEndpoint.java | 1 + .../api/annotations/OnWebSocketMessage.java | 35 ++- .../JettyWebSocketFrameHandlerFactory.java | 95 ++++--- .../common/JettyWebSocketRemoteEndpoint.java | 1 + .../tests/AnnotatedPartialListenerTest.java | 240 ++++++++++++++++++ 5 files changed, 328 insertions(+), 44 deletions(-) create mode 100644 jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/AnnotatedPartialListenerTest.java diff --git a/jetty-websocket/websocket-jetty-api/src/main/java/org/eclipse/jetty/websocket/api/RemoteEndpoint.java b/jetty-websocket/websocket-jetty-api/src/main/java/org/eclipse/jetty/websocket/api/RemoteEndpoint.java index 7b12373a1db..ea3be5da329 100644 --- a/jetty-websocket/websocket-jetty-api/src/main/java/org/eclipse/jetty/websocket/api/RemoteEndpoint.java +++ b/jetty-websocket/websocket-jetty-api/src/main/java/org/eclipse/jetty/websocket/api/RemoteEndpoint.java @@ -103,6 +103,7 @@ public interface RemoteEndpoint * @param fragment the text being sent * @param isLast true if this is the last piece of the partial bytes * @param callback callback to notify of success or failure of the write operation + * @throws IOException this never throws IOException, it was a mistake to have this in the signature. */ void sendPartialString(String fragment, boolean isLast, WriteCallback callback) throws IOException; diff --git a/jetty-websocket/websocket-jetty-api/src/main/java/org/eclipse/jetty/websocket/api/annotations/OnWebSocketMessage.java b/jetty-websocket/websocket-jetty-api/src/main/java/org/eclipse/jetty/websocket/api/annotations/OnWebSocketMessage.java index 4fe5b3b9899..0ec8f1ece13 100644 --- a/jetty-websocket/websocket-jetty-api/src/main/java/org/eclipse/jetty/websocket/api/annotations/OnWebSocketMessage.java +++ b/jetty-websocket/websocket-jetty-api/src/main/java/org/eclipse/jetty/websocket/api/annotations/OnWebSocketMessage.java @@ -21,6 +21,7 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.eclipse.jetty.websocket.api.Session; +import org.eclipse.jetty.websocket.api.WebSocketPartialListener; /** * Annotation for tagging methods to receive Binary or Text Message events. @@ -30,23 +31,31 @@ import org.eclipse.jetty.websocket.api.Session; *

* Text Message Versions *

    - *
  1. public void methodName(String text)
  2. - *
  3. public void methodName({@link Session} session, String text)
  4. - *
  5. public void methodName(Reader reader)
  6. - *
  7. public void methodName({@link Session} session, Reader reader)
  8. + *
  9. {@code public void methodName(String text)}
  10. + *
  11. {@code public void methodName(Session session, String text)}
  12. + *
  13. {@code public void methodName(Reader reader)}
  14. + *
  15. {@code public void methodName(Session session, Reader reader)}
  16. *
- * Note: that the {@link Reader} in this case will always use UTF-8 encoding/charset (this is dictated by the RFC 6455 spec for Text Messages. If you need to - * use a non-UTF-8 encoding/charset, you are instructed to use the binary messaging techniques. - *

+ *

Note: that the {@link Reader} in this case will always use UTF-8 encoding/charset (this is dictated by the RFC 6455 spec for Text Messages. If you need to + * use a non-UTF-8 encoding/charset, you are instructed to use the binary messaging techniques.

* Binary Message Versions *
    - *
  1. public void methodName(ByteBuffer message)
  2. - *
  3. public void methodName({@link Session} session, ByteBuffer message)
  4. - *
  5. public void methodName(byte buf[], int offset, int length)
  6. - *
  7. public void methodName({@link Session} session, byte buf[], int offset, int length)
  8. - *
  9. public void methodName(InputStream stream)
  10. - *
  11. public void methodName({@link Session} session, InputStream stream)
  12. + *
  13. {@code public void methodName(ByteBuffer message)}
  14. + *
  15. {@code public void methodName(Session session, ByteBuffer message)}
  16. + *
  17. {@code public void methodName(byte[] buf, int offset, int length)}
  18. + *
  19. {@code public void methodName(Session session, byte[] buf, int offset, int length)}
  20. + *
  21. {@code public void methodName(InputStream stream)}
  22. + *
  23. {@code public void methodName(Session session, InputStream stream)}
  24. *
+ * Partial Message Variations + *

These are used to receive partial messages without aggregating them into a complete WebSocket message. Instead the a boolean + * argument is supplied to indicate whether this is the last segment of data of the message. See {@link WebSocketPartialListener} + * interface for more details on partial messages.

+ *
    + *
  1. {@code public void methodName(ByteBuffer payload, boolean last)}
  2. + *
  3. {@code public void methodName(String payload, boolean last)}
  4. + *
+ *

Note: Similar to the signatures above these can all be used with an optional first {@link Session} parameter.

*/ @Documented @Retention(RetentionPolicy.RUNTIME) diff --git a/jetty-websocket/websocket-jetty-common/src/main/java/org/eclipse/jetty/websocket/common/JettyWebSocketFrameHandlerFactory.java b/jetty-websocket/websocket-jetty-common/src/main/java/org/eclipse/jetty/websocket/common/JettyWebSocketFrameHandlerFactory.java index b16eba885ef..c18ad71b4d8 100644 --- a/jetty-websocket/websocket-jetty-common/src/main/java/org/eclipse/jetty/websocket/common/JettyWebSocketFrameHandlerFactory.java +++ b/jetty-websocket/websocket-jetty-common/src/main/java/org/eclipse/jetty/websocket/common/JettyWebSocketFrameHandlerFactory.java @@ -78,6 +78,51 @@ import org.eclipse.jetty.websocket.core.internal.util.ReflectUtils; */ public class JettyWebSocketFrameHandlerFactory extends ContainerLifeCycle { + private static final InvokerUtils.Arg[] textCallingArgs = new InvokerUtils.Arg[]{ + new InvokerUtils.Arg(Session.class), + new InvokerUtils.Arg(String.class).required() + }; + + private static final InvokerUtils.Arg[] binaryBufferCallingArgs = new InvokerUtils.Arg[]{ + new InvokerUtils.Arg(Session.class), + new InvokerUtils.Arg(ByteBuffer.class).required() + }; + + private static final InvokerUtils.Arg[] binaryArrayCallingArgs = new InvokerUtils.Arg[]{ + new InvokerUtils.Arg(Session.class), + new InvokerUtils.Arg(byte[].class).required(), + new InvokerUtils.Arg(int.class), // offset + new InvokerUtils.Arg(int.class) // length + }; + + private static final InvokerUtils.Arg[] inputStreamCallingArgs = new InvokerUtils.Arg[]{ + new InvokerUtils.Arg(Session.class), + new InvokerUtils.Arg(InputStream.class).required() + }; + + private static final InvokerUtils.Arg[] readerCallingArgs = new InvokerUtils.Arg[]{ + new InvokerUtils.Arg(Session.class), + new InvokerUtils.Arg(Reader.class).required() + }; + + private static final InvokerUtils.Arg[] textPartialCallingArgs = new InvokerUtils.Arg[]{ + new InvokerUtils.Arg(Session.class), + new InvokerUtils.Arg(String.class).required(), + new InvokerUtils.Arg(boolean.class).required() + }; + + private static final InvokerUtils.Arg[] binaryPartialBufferCallingArgs = new InvokerUtils.Arg[]{ + new InvokerUtils.Arg(Session.class), + new InvokerUtils.Arg(ByteBuffer.class).required(), + new InvokerUtils.Arg(boolean.class).required() + }; + + private static final InvokerUtils.Arg[] binaryPartialArrayCallingArgs = new InvokerUtils.Arg[]{ + new InvokerUtils.Arg(Session.class), + new InvokerUtils.Arg(byte[].class).required(), + new InvokerUtils.Arg(boolean.class).required() + }; + private final WebSocketContainer container; private final WebSocketComponents components; private final Map, JettyWebSocketFrameHandlerMetadata> metadataMap = new ConcurrentHashMap<>(); @@ -333,34 +378,6 @@ public class JettyWebSocketFrameHandlerFactory extends ContainerLifeCycle if (onMessages != null && onMessages.length > 0) { // The different kind of @OnWebSocketMessage method parameter signatures expected - - InvokerUtils.Arg[] textCallingArgs = new InvokerUtils.Arg[]{ - new InvokerUtils.Arg(Session.class), - new InvokerUtils.Arg(String.class).required() - }; - - InvokerUtils.Arg[] binaryBufferCallingArgs = new InvokerUtils.Arg[]{ - new InvokerUtils.Arg(Session.class), - new InvokerUtils.Arg(ByteBuffer.class).required() - }; - - InvokerUtils.Arg[] binaryArrayCallingArgs = new InvokerUtils.Arg[]{ - new InvokerUtils.Arg(Session.class), - new InvokerUtils.Arg(byte[].class).required(), - new InvokerUtils.Arg(int.class), // offset - new InvokerUtils.Arg(int.class) // length - }; - - InvokerUtils.Arg[] inputStreamCallingArgs = new InvokerUtils.Arg[]{ - new InvokerUtils.Arg(Session.class), - new InvokerUtils.Arg(InputStream.class).required() - }; - - InvokerUtils.Arg[] readerCallingArgs = new InvokerUtils.Arg[]{ - new InvokerUtils.Arg(Session.class), - new InvokerUtils.Arg(Reader.class).required() - }; - for (Method onMsg : onMessages) { assertSignatureValid(endpointClass, onMsg, OnWebSocketMessage.class); @@ -409,11 +426,27 @@ public class JettyWebSocketFrameHandlerFactory extends ContainerLifeCycle metadata.setTextHandler(ReaderMessageSink.class, methodHandle, onMsg); continue; } - else + + methodHandle = InvokerUtils.optionalMutatedInvoker(lookup, endpointClass, onMsg, textPartialCallingArgs); + if (methodHandle != null) { - // Not a valid @OnWebSocketMessage declaration signature - throw InvalidSignatureException.build(endpointClass, OnWebSocketMessage.class, onMsg); + // Partial Text Message + assertSignatureValid(endpointClass, onMsg, OnWebSocketMessage.class); + metadata.setTextHandler(PartialStringMessageSink.class, methodHandle, onMsg); + continue; } + + methodHandle = InvokerUtils.optionalMutatedInvoker(lookup, endpointClass, onMsg, binaryPartialBufferCallingArgs); + if (methodHandle != null) + { + // Partial ByteBuffer Message + assertSignatureValid(endpointClass, onMsg, OnWebSocketMessage.class); + metadata.setBinaryHandle(PartialByteBufferMessageSink.class, methodHandle, onMsg); + continue; + } + + // Not a valid @OnWebSocketMessage declaration signature + throw InvalidSignatureException.build(endpointClass, OnWebSocketMessage.class, onMsg); } } diff --git a/jetty-websocket/websocket-jetty-common/src/main/java/org/eclipse/jetty/websocket/common/JettyWebSocketRemoteEndpoint.java b/jetty-websocket/websocket-jetty-common/src/main/java/org/eclipse/jetty/websocket/common/JettyWebSocketRemoteEndpoint.java index bfb14b678b4..04e3ca4e814 100644 --- a/jetty-websocket/websocket-jetty-common/src/main/java/org/eclipse/jetty/websocket/common/JettyWebSocketRemoteEndpoint.java +++ b/jetty-websocket/websocket-jetty-common/src/main/java/org/eclipse/jetty/websocket/common/JettyWebSocketRemoteEndpoint.java @@ -156,6 +156,7 @@ public class JettyWebSocketRemoteEndpoint implements org.eclipse.jetty.websocket b.block(getBlockingTimeout(), TimeUnit.MILLISECONDS); } + // FIXME: Remove the throws IOException from API for this method in the next major release. @Override public void sendPartialString(String fragment, boolean isLast, WriteCallback callback) { diff --git a/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/AnnotatedPartialListenerTest.java b/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/AnnotatedPartialListenerTest.java new file mode 100644 index 00000000000..ef3909bc6e3 --- /dev/null +++ b/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/AnnotatedPartialListenerTest.java @@ -0,0 +1,240 @@ +// +// ======================================================================== +// Copyright (c) 1995-2021 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.websocket.tests; + +import java.io.IOException; +import java.net.URI; +import java.nio.ByteBuffer; +import java.util.Objects; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; + +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.servlet.ServletContextHandler; +import org.eclipse.jetty.util.BufferUtil; +import org.eclipse.jetty.websocket.api.Session; +import org.eclipse.jetty.websocket.api.WebSocketPartialListener; +import org.eclipse.jetty.websocket.api.WriteCallback; +import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage; +import org.eclipse.jetty.websocket.api.annotations.WebSocket; +import org.eclipse.jetty.websocket.api.exceptions.InvalidWebSocketException; +import org.eclipse.jetty.websocket.client.WebSocketClient; +import org.eclipse.jetty.websocket.server.config.JettyWebSocketServletContainerInitializer; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class AnnotatedPartialListenerTest +{ + public static class PartialEchoSocket implements WebSocketPartialListener + { + private Session session; + + @Override + public void onWebSocketConnect(Session session) + { + this.session = session; + } + + @Override + public void onWebSocketPartialBinary(ByteBuffer payload, boolean fin) + { + session.getRemote().sendPartialBytes(payload, fin, WriteCallback.NOOP); + } + + @Override + public void onWebSocketPartialText(String payload, boolean fin) + { + try + { + session.getRemote().sendPartialString(payload, fin, WriteCallback.NOOP); + } + catch (IOException e) + { + throw new IllegalStateException(e); + } + } + } + + @WebSocket + public static class PartialStringListener + { + public BlockingQueue messages = new LinkedBlockingQueue<>(); + + public static class MessageSegment + { + public String message; + public boolean last; + } + + @OnWebSocketMessage + public void onMessage(String message, boolean last) + { + MessageSegment messageSegment = new MessageSegment(); + messageSegment.message = message; + messageSegment.last = last; + messages.add(messageSegment); + } + } + + @WebSocket + public static class PartialByteBufferListener + { + public BlockingQueue messages = new LinkedBlockingQueue<>(); + + public static class MessageSegment + { + public ByteBuffer buffer; + public boolean last; + } + + @OnWebSocketMessage + public void onMessage(ByteBuffer buffer, boolean last) + { + MessageSegment messageSegment = new MessageSegment(); + messageSegment.buffer = BufferUtil.copy(buffer); + messageSegment.last = last; + messages.add(messageSegment); + } + } + + @WebSocket + public static class InvalidDoubleBinaryListener + { + @OnWebSocketMessage + public void onMessage(ByteBuffer bytes, boolean last) + { + } + + @OnWebSocketMessage + public void onMessage(ByteBuffer bytes) + { + } + } + + @WebSocket + public static class InvalidDoubleTextListener + { + @OnWebSocketMessage + public void onMessage(String content, boolean last) + { + } + + @OnWebSocketMessage + public void onMessage(String content) + { + } + } + + private Server server; + private URI serverUri; + private WebSocketClient client; + + @BeforeEach + public void before() throws Exception + { + server = new Server(); + ServerConnector connector = new ServerConnector(server); + server.addConnector(connector); + ServletContextHandler contextHandler = new ServletContextHandler(); + contextHandler.setContextPath("/"); + server.setHandler(contextHandler); + JettyWebSocketServletContainerInitializer.configure(contextHandler, ((servletContext, container) -> + { + container.setAutoFragment(false); + container.addMapping("/", PartialEchoSocket.class); + })); + server.start(); + serverUri = URI.create("ws://localhost:" + connector.getLocalPort() + "/"); + + client = new WebSocketClient(); + client.setAutoFragment(false); + client.start(); + } + + @AfterEach + public void after() throws Exception + { + client.stop(); + server.stop(); + } + + @Test + public void testAnnotatedPartialString() throws Exception + { + PartialStringListener endpoint = new PartialStringListener(); + try (Session session = client.connect(endpoint, serverUri).get(5, TimeUnit.SECONDS)) + { + session.getRemote().sendPartialString("hell", false); + session.getRemote().sendPartialString("o w", false); + session.getRemote().sendPartialString("orld", true); + } + + PartialStringListener.MessageSegment segment; + + segment = Objects.requireNonNull(endpoint.messages.poll(5, TimeUnit.SECONDS)); + assertThat(segment.message, is("hell")); + assertThat(segment.last, is(false)); + + segment = Objects.requireNonNull(endpoint.messages.poll(5, TimeUnit.SECONDS)); + assertThat(segment.message, is("o w")); + assertThat(segment.last, is(false)); + + segment = Objects.requireNonNull(endpoint.messages.poll(5, TimeUnit.SECONDS)); + assertThat(segment.message, is("orld")); + assertThat(segment.last, is(true)); + } + + @Test + public void testAnnotatedPartialByteBuffer() throws Exception + { + PartialByteBufferListener endpoint = new PartialByteBufferListener(); + try (Session session = client.connect(endpoint, serverUri).get(5, TimeUnit.SECONDS)) + { + session.getRemote().sendPartialBytes(BufferUtil.toBuffer("hell"), false); + session.getRemote().sendPartialBytes(BufferUtil.toBuffer("o w"), false); + session.getRemote().sendPartialBytes(BufferUtil.toBuffer("orld"), true); + } + + PartialByteBufferListener.MessageSegment segment; + + segment = Objects.requireNonNull(endpoint.messages.poll(5, TimeUnit.SECONDS)); + assertThat(segment.buffer, is(BufferUtil.toBuffer("hell"))); + assertThat(segment.last, is(false)); + + segment = Objects.requireNonNull(endpoint.messages.poll(5, TimeUnit.SECONDS)); + assertThat(segment.buffer, is(BufferUtil.toBuffer("o w"))); + assertThat(segment.last, is(false)); + + segment = Objects.requireNonNull(endpoint.messages.poll(5, TimeUnit.SECONDS)); + assertThat(segment.buffer, is(BufferUtil.toBuffer("orld"))); + assertThat(segment.last, is(true)); + } + + @Test + public void testDoubleOnMessageAnnotation() + { + InvalidDoubleBinaryListener doubleBinaryListener = new InvalidDoubleBinaryListener(); + assertThrows(InvalidWebSocketException.class, () -> client.connect(doubleBinaryListener, serverUri)); + + InvalidDoubleTextListener doubleTextListener = new InvalidDoubleTextListener(); + assertThrows(InvalidWebSocketException.class, () -> client.connect(doubleTextListener, serverUri)); + } +} From 1223bb50ec91fd3387ee7b7983c8e79c5c975318 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Thu, 10 Jun 2021 11:10:09 -0500 Subject: [PATCH 3/3] Issue #6354 - fix bad slf4j reference in jetty-osgi-boot-jsp Signed-off-by: Joakim Erdfelt --- jetty-osgi/jetty-osgi-boot-jsp/pom.xml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/jetty-osgi/jetty-osgi-boot-jsp/pom.xml b/jetty-osgi/jetty-osgi-boot-jsp/pom.xml index 02cfe24fd09..729488a5223 100644 --- a/jetty-osgi/jetty-osgi-boot-jsp/pom.xml +++ b/jetty-osgi/jetty-osgi-boot-jsp/pom.xml @@ -56,7 +56,9 @@ org.eclipse.jetty.osgi.boot !org.eclipse.jetty.osgi.boot.* - org.eclipse.jdt.*;resolution:=optional, + + ${osgi.slf4j.import.packages}, + org.eclipse.jdt.*;resolution:=optional, org.eclipse.jdt.core.compiler.*;resolution:=optional, com.sun.el;resolution:=optional, com.sun.el.lang;resolution:=optional,