rest
rest copied to clipboard
Clarify publication of a JAX-RS application as a servlet
This issue arose in a longish conversation (https://github.com/resteasy/Resteasy/pull/2474) between @andymc12 and me in response to https://issues.redhat.com/browse/RESTEASY-2650. By the end, I came to believe that section 2.3.2 "Servlet" of the spec doesn't make sense. My confusion comes from not really understanding what it means for a servlet to "handle" an application. Also, it seems backwards for the value of @ApplicationPath to override the path in web.xml, so I changed that.
Here's my last substantive comment on the pull request:
My sense of it, at least tonight, is that the section in the spec just doesn't make sense. Or, actually, I think there's a way to make sense of it, but it's pretty twisted. In particular, the text says,
* If there is already a servlet that handles this application. That is, a servlet that has an initialization
parameter named javax.ws.rs.Application whose value is the fully qualified name of the Application subclass ...
If we take that as the definition of a servlet handling an Application, then
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<servlet>
<servlet-name>org.example.MyApplication</servlet-name>
</servlet>
<servlet-mapping>
<servlet-name>org.example.MyApplication</servlet-name>
<url-pattern>/myresources/*</url-pattern>
</servlet-mapping>
</web-app>
doesn't handle MyApplication!! And then the section
If no servlet handles this application, ... For example, if org.example.MyApplication is the name of the
Application subclass, a sample web.xml would be:
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<servlet>
<servlet-name>org.example.MyApplication</servlet-name>
</servlet>
<servlet-mapping>
<servlet-name>org.example.MyApplication</servlet-name>
<url-pattern>/myresources/*</url-pattern>
</servlet-mapping>
</web-app>
makes sense. Like you said, they're saying that web.xml has to have <servlet> and <servlet-mapping>. But I think this interpretation is weird, and, based on the way you wrote getServletsForApplication(), I think you would agree with me
I've re-written the section in a way that makes sense to me. If there's some agreement, I'll turn it into a pull request.
I don't see how to attach a file to this issue, but the document is here: https://issues.redhat.com/secure/attachment/12505596/_publication.adoc
Ron, honestly I cannot see where there should be anything wrong or weird in the spec. Can you please clearly outline what bothers you here in detail?
@ronsigal I'm not able to access the Redhat issues. Seems like more background information would be needed here. Like @mkarg, I don't understand the point you are trying to make.
Seems like the use case in question is that for which an implementation needs to dynamically add a servlet as none of the other conditions are met?
Hey @mkarg, @spericas,
Sorry, I should have been more explict. Here's what's bothering me. In the section
If no servlet handles this application, JAX-RS implementations are REQUIRED to dynami-
cally add a servlet whose fully qualified name must be that of the Application subclass. If
the Application subclass is annotated with @ApplicationPath, implementations are RE-
QUIRED to use the value of this annotation appended with ”/*” to define a mapping for the added
server. Otherwise, the application MUST be packaged with a web.xml that specifies a servlet
mapping. For example, if org.example.MyApplication is the name of the Application
subclass, a sample web.xml would be:
1 <web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
2 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3 xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
4 http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
5 <servlet>
6 <servlet-name>org.example.MyApplication</servlet-name>
7 </servlet>
8 <servlet-mapping>
9 <servlet-name>org.example.MyApplication</servlet-name>
10 <url-pattern>/myresources/*</url-pattern>
11 </servlet-mapping>
12 </web-app>
On the one hand, it's saying "no servlet handles this application". On the other hand, it's saying that if there's no @ApplicationPath annotation on the Application, then web.xml must have a servlet mapping. Then it gives the example web.xml that not only has a <servlet-mapping> but also has
5 <servlet>
6 <servlet-name>org.example.MyApplication</servlet-name>
7 </servlet>
Given the assumption that "no servlet handles this application", then the "org.example.MyApplication" servlet must not handle the org.example.MyApplication Application. That's what I think is "weird". So, I started by explictly defining the meaning of a servlet "handling" an Application:
There are two ways in which an `Application` subclass is _associated_ with
one or more servlets:
* It is associated with a servlet if that servlet's name is the
fully qualified classname of the `Application` subclass.
* It is associated with one or more servlets that have an initialization
parameter named
+
`jakarta.ws.rs.Application`
+
whose value is the fully qualified name of the `Application` subclass.
@spericas, I don't know why you can't see that file, but you should be able to see the JIRA issue https://issues.redhat.com/browse/RESTEASY-2650, from which you can download the file. Does that work?
-Ron
One other somewhat unusual thing, as @andymc12 pointed out, is that the value of @ApplicationPath, if it exists, is supposed to override the value of a <servlet-path>. Wouldn't it be more conventional for a value in a file to override a value in the code? I turned that around my rewritten version.
@ronsigal Let's analyze this step by step ...
If no servlet handles this application, JAX-RS implementations are REQUIRED to dynami- cally add a servlet whose fully qualified name must be that of the Application subclass. If the Application subclass is annotated with @ApplicationPath, implementations are RE- QUIRED to use the value of this annotation appended with ”/*” to define a mapping for the added server.
Up to this point it states that if no servlet handles the app and @ApplicationPath
is present, then implementations need to dynamically add servlet as described.
Otherwise,
If no @ApplicationPath
is defined, then an implementation simply does not have all the necessary information (i.e. mapping) to dynamically add the servlet, so it needs a deployment descriptor from the user from which this info can be obtained --this can only come from Servlet spec since JAX-RS does not define any descriptors.
the application MUST be packaged with a web.xml that specifies a servlet mapping. For example, if org.example.MyApplication is the name of the Application subclass, a sample web.xml would be:
1 <web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" 2 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 3 xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 4 http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"> 5
6 8org.example.MyApplication 79 12org.example.MyApplication 10/myresources/* 11On the one hand, it's saying "no servlet handles this application". On the other hand, it's saying that if there's no @ApplicationPath annotation on the Application, then web.xml must have a servlet mapping.
That's correct.
Then it gives the example web.xml that not only has a
but also has 5 <servlet> 6 <servlet-name>org.example.MyApplication</servlet-name> 7 </servlet>
Yes, I think this is because in the <servlet-mapping>
section we need to reference servlets by name. If you don't have this, then the descriptor would be incomplete. Note that <servlet-class>
is not specified as this is handled by the implementation. In most cases, @ApplicationPath
would be specified, so this is a bit of an odd case.
Given the assumption that "no servlet handles this application", then the "org.example.MyApplication" servlet must not handle the org.example.MyApplication Application. That's what I think is "weird".
Again, it's workaround to be able to specify the value of @ApplicationPath
when absent. You may be reading too much into it here perhaps?
@spericas, I don't know why you can't see that file, but you should be able to see the JIRA issue https://issues.redhat.com/browse/RESTEASY-2650, from which you can download the file. Does that work?
No, because I don't have a RH account.
One other somewhat unusual thing, as @andymc12 pointed out, is that the value of @ApplicationPath, if it exists, is supposed to override the value of a
. Wouldn't it be more conventional for a value in a file to override a value in the code? I turned that around my rewritten version.
That makes sense in override situations, but this is not one of them. This is an integration point between Servlets and Jakarta REST, two separate specs. In other words, web.xml
is not used to configure anything directly in Jakarta REST (no such descriptor exists).
Hey @spericas,
Thank you for the careful response. I follow you most of the way. The point at which I get stuck is, given web.xml
1 <web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
2 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3 xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
4 http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
5 <servlet>
6 <servlet-name>org.example.MyApplication</servlet-name>
7 </servlet>
8 <servlet-mapping>
9 <servlet-name>org.example.MyApplication</servlet-name>
10 <url-pattern>/myresources/*</url-pattern>
11 </servlet-mapping>
12 </web-app>
I look at that and think, "there's a servlet, named org.example.MyApplication, which handles MyApplication
". It's true that there's no <servlet-class> element, but RESTEasy, and I assume other implementations, has its own implementation. But to make sense of the text, servlet org.example.MyApplication doesn't handle MyApplication
, which seemed counterintuitive to me.
My attempt at a solution was to start with a definition:
There are two ways in which an `Application` subclass is _associated_ with
one or more servlets:
* It is associated with a servlet if that servlet's name is the
fully qualified classname of the `Application` subclass.
* It is associated with one or more servlets that have an initialization
parameter named
+
`jakarta.ws.rs.Application`
+
whose value is the fully qualified name of the `Application` subclass.
On the other hand, the current text defines "associated" just by the second clause. If that's the intended definition, then my intuition about the web.xml (above) is just wrong, and the current text makes sense. Maybe I'm the only one who has had that reaction, in which case, I would close the issue. If anyone else has stumbled at that point, then a little clarification might help.
re: "I don't have a RH account"
You want one? I know people. ;-)
re: "web.xml is not used to configure anything directly in Jakarta REST"
I'm not following. In the absence of @ApplicationPath
, aren't we, in effect, using <servlet-mapping> to configure a JAX-RS application? I think your point is to subtle for me.
Here's my rewrite of the section, somewhat poorly translated from asciidoc:
[[servlet]] ==== Servlet
A JAX-RS application is packaged as a Web application in a .war
file.
The application classes are packaged in WEB-INF/classes
or
WEB-INF/lib
and required libraries are packaged in WEB-INF/lib
. See
the Servlet specification for full details on packaging of web
applications.
It is RECOMMENDED that implementations support the Servlet 3 framework pluggability mechanism to enable portability between containers and to avail themselves of container-supplied class scanning facilities. When using the pluggability mechanism then the behavior described here must apply.
There are two ways in which an Application
subclass is associated with
one or more servlets:
-
It is associated with a servlet if that servlet's name is the fully qualified classname of the
Application
subclass. -
It is associated with one or more servlets that have an initialization parameter named
jakarta.ws.rs.Application
whose value is the fully qualified name of theApplication
subclass.
For each servlet that an Application
is associated with, a path
is determined by the servlet's associated Application
subclass is REQUIRED to have
an @ApplicationPath
annotation, and its path is determined by the annotation's
value appended with "/*". It follows that an Application
subclass may
be associated with at most one servlet that lacks a
For example, if
org.example.MyApplication
is the name of the Application
subclass, a
sample web.xml
could be:
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<servlet>
<servlet-name>org.example.MyApplication</servlet-name>
</servlet>
<servlet-mapping>
<servlet-name>org.example.MyApplication</servlet-name>
<url-pattern>/myresources/*</url-pattern>
</servlet-mapping>
</web-app>
If an Application
subclass is associated with no servlets, then
JAX-RS implementations are REQUIRED to dynamically add a servlet whose
name must be the fully qualified name of the Application
subclass. The path is
determined just as if the servlet had been defined explicitly.
For example, if
org.example.MyApplication
is the name of the Application
subclass, a
sample web.xml
in this case could be:
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<servlet-mapping>
<servlet-name>org.example.MyApplication</servlet-name>
<url-pattern>/myresources/*</url-pattern>
</servlet-mapping>
</web-app>
If there is no org.example.MyApplication
MUST have an @ApplicationPath
annotation.
Finally, if a .war
file has one or more JAX-RS resources and no
Application
subclasses, then JAX-RS implementations are REQUIRED to use
the jakarta.ws.rs.core.Application
class itself and to automatically
discover all root resource classes and providers, which MUST be packaged
with the application. If jakarta.ws.rs.core Application
is not associated
with any explicitly defined servlet,
then JAX-RS implementations are REQUIRED to dynamically add a servlet whose
name must be "jakarta.ws.rs.core.Application", and the web.xml
file MUST have a servlet mapping for "jakarta.ws.rs.core.Application". For example,
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<servlet-mapping>
<servlet-name>jakarta.ws.rs.core.Application</servlet-name>
<url-pattern>/myresources/*</url-pattern>
</servlet-mapping>
</web-app>
When an Application
subclass is present in the archive, if both
Application.getClasses
and Application.getSingletons
return an empty
collection then all root resource classes and providers packaged in the
web application MUST be included and the JAX-RS implementation is
REQUIRED to discover them automatically by scanning a .war
file as
described above. If either getClasses
or getSingletons
returns a
non-empty collection then only those classes or singletons returned MUST
be included in the published JAX-RS application.
The following table summarizes the Servlet 3 framework pluggability mechanism:
[id="Table-Summary-of-Servlet-3-framework-pluggability-cases", cols="24,14,39,31"]
.Summary of Servlet 3 framework pluggability cases
|==================================
|*Condition* |*Action* |*Servlet Name* |*web.xml*
|`Application` subclass handled by existing servlet |(none) |(already defined)
|Servlet mapping not required if the subclass has an `@ApplicationPath` annotation
|`Application` subclass _not_ handled by existing servlet |Add servlet
|Subclass name |Servlet mapping not required if the subclass has an `@ApplicationPath` annotation
|No `Application` subclass |Use or create servlet named "jakarta.ws.rs.core.Application"
|jakarta.ws.rs.core.Application |Required for servlet mapping
|==================================
If not using the Servlet 3 framework pluggability mechanism (e.g. in a
pre-Servlet 3.0 container), the servlet-class
or filter-class
element of the web.xml
descriptor SHOULD name the
JAX-RS implementation-supplied servlet or filter class respectively. The
Application
subclass SHOULD be identified using an init-param
with a
param-name
of jakarta.ws.rs.Application
.
Note that the Servlet 3 framework pluggability mechanism described above is based on servlets and not filters. Applications that prefer to use an implementation-supplied filter class must use the pre-Servlet 3.0 configuration mechanism.
One last thing. @andymc12 suggested renaming the init param "javax.ws.rs.Application" to "javax.ws.rs.core.Application", maybe starting by deprecating "javax.ws.rs.Application".
Hey @spericas,
Thank you for the careful response. I follow you most of the way. The point at which I get stuck is, given web.xml
1 <web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" 2 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 3 xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 4 http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"> 5 <servlet> 6 <servlet-name>org.example.MyApplication</servlet-name> 7 </servlet> 8 <servlet-mapping> 9 <servlet-name>org.example.MyApplication</servlet-name> 10 <url-pattern>/myresources/*</url-pattern> 11 </servlet-mapping> 12 </web-app>
I look at that and think, "there's a servlet, named org.example.MyApplication, which handles
MyApplication
". It's true that there's noelement, but RESTEasy, and I assume other implementations, has its own implementation. But to make sense of the text, servlet org.example.MyApplication doesn't handle MyApplication
, which seemed counterintuitive to me.
It does not. As stated before in the spec, that would be a servlet defined with an <init-param>
. For example,
<servlet>
<servlet-name>MyRestServlet</servlet-name>
<servlet-class>foo.bar.MyRestServletClass</servlet-class>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>foo.bar.MyApplication</param-value>
</init-param>
</servlet>
My attempt at a solution was to start with a definition:
There are two ways in which an `Application` subclass is _associated_ with one or more servlets: * It is associated with a servlet if that servlet's name is the fully qualified classname of the `Application` subclass. * It is associated with one or more servlets that have an initialization parameter named + `jakarta.ws.rs.Application` + whose value is the fully qualified name of the `Application` subclass.
On the other hand, the current text defines "associated" just by the second clause.
That's right. The first clause is only used by implementations to dynamically add servlets --with the aid of a "simplified" web.xml when the path is not in the annotation.
If that's the intended definition, then my intuition about the web.xml (above) is just wrong, and the current text makes sense. Maybe I'm the only one who has had that reaction, in which case, I would close the issue. If anyone else has stumbled at that point, then a little clarification might help.
Clarifications are always welcome. This text was changed when Servlets added their "pluggability feature"; some of the rationale for all this comes from even before that --and I'm sure I'm forgetting some details. Perhaps when we are allowed to break backward compatibility, we should simplify this and reduce the number of cases.
re: "I don't have a RH account"
You want one? I know people. ;-)
You know a guy that knows a guy? :)
re: "web.xml is not used to configure anything directly in Jakarta REST"
I'm not following. In the absence of
@ApplicationPath
, aren't we, in effect, usingto configure a JAX-RS application? I think your point is to subtle for me.
Yes, but only when it relates to the Servlet integration layer and the logic to dynamically add a servlet and when @ApplicationPath
is not specified (Jakarta REST does not require Servlets to run). I suppose we could have said that if the user provides such a web.xml anyway, we should use the pattern to override the annotation --but I don't recall if that was on the table at the time.
Ok, so, basically, the text makes logical sense as it stands, and my confusion came from a misunderstanding. That suggests, but doesn't prove, that there might be room for some clarification. Like I mentioned, @andymc12 and I had a long discussion about the meaning, but it's possible that I just led him down the path of confusion where none previously existed. ;-)
Before I just close this issue, I'll see if there's some minor addition that would have made things clearer to me. Couldn't hurt.
You know a guy that knows a guy? :)
Oh, I know lots of guys that know lots of guys. Don't know if any of them are dangerous, though. ;-)
I suppose we could have said that if the user provides such a web.xml anyway, we should use the pattern to override the annotation
That makes sense to me. Anyone else?
Ok, so, basically, the text makes logical sense as it stands, and my confusion came from a misunderstanding. That suggests, but doesn't prove, that there might be room for some clarification. Like I mentioned, @andymc12 and I had a long discussion about the meaning, but it's possible that I just led him down the path of confusion where none previously existed. ;-)
Before I just close this issue, I'll see if there's some minor addition that would have made things clearer to me. Couldn't hurt.
Yes, thanks.
I suppose we could have said that if the user provides such a web.xml anyway, we should use the pattern to override the annotation
That makes sense to me. Anyone else?
One problem with such a change is backward compatibility.