rest icon indicating copy to clipboard operation
rest copied to clipboard

Clarify publication of a JAX-RS application as a servlet

Open ronsigal opened this issue 4 years ago • 18 comments

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

ronsigal avatar Jan 16 '21 21:01 ronsigal

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.

ronsigal avatar Jan 16 '21 21:01 ronsigal

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

ronsigal avatar Jan 16 '21 21:01 ronsigal

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?

mkarg avatar Jan 18 '21 22:01 mkarg

@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?

spericas avatar Jan 19 '21 15:01 spericas

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

ronsigal avatar Jan 19 '21 22:01 ronsigal

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 avatar Jan 19 '21 22:01 ronsigal

@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 org.example.MyApplication 7 8 9 org.example.MyApplication 10 /myresources/* 11 12


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. 

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.

spericas avatar Jan 20 '21 19:01 spericas

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).

spericas avatar Jan 20 '21 19:01 spericas

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. ;-)

ronsigal avatar Jan 20 '21 23:01 ronsigal

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.

ronsigal avatar Jan 20 '21 23:01 ronsigal

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 the Application subclass.

For each servlet that an Application is associated with, a path is determined by the servlet's associated element, if one exists. If there is none, then the 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 element for "org.example.MyApplication", then 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.

ronsigal avatar Jan 20 '21 23:01 ronsigal

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".

ronsigal avatar Jan 20 '21 23:01 ronsigal

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 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.

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? :)

spericas avatar Jan 21 '21 20:01 spericas

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 to 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.

spericas avatar Jan 21 '21 20:01 spericas

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. ;-)

ronsigal avatar Jan 22 '21 01:01 ronsigal

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?

ronsigal avatar Jan 22 '21 01:01 ronsigal

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.

spericas avatar Jan 22 '21 15:01 spericas

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.

spericas avatar Jan 22 '21 15:01 spericas