Paul,
I completely understand your point of view, but I do not see that it is
benefical in the end to concentrate solely on the 80:20 rule. The problem is
more complex and you need to take into account the Jave Ecosystem that
always was null-enabled unless JAX-RS hit the road: The average Java
programmer is aware of the fact that parameters can be null. Also the
InputStream/Reader-vs-JAXB comparison is odd: Reader and InputStream both
are always valid objects since they are no real data objects -- they are
just an interfaces / facades to the actual data object, and they can tell if
there is no object (like returning -1 for .read()) -- it makes no sense to
null them in case of a missing body. In the case of a JAXB elements like
"Book", "Car" or "Person" those are not an interface / facade to the data
object but those are the data object itself -- so it makes pretty sense to
null them! There is no interface that can be asked "is this a valid car or
just a fake car created to cover the problem of was passed?" (yes, I know
the awkward workaround of getting an InputStream and manually using JAXB in
case of Concent-Length is larger zero). If a car got passed, I will get a
car. If no car got passes, I will get null. It's just as simple as all the
rest of Java and I do not see that it is overly complex to check for null at
start if an application doesn't accept nulls. Also I do not see that it
would be a real problem in the real world to overhaul the existing JAX-RS
applications to add null checks -- most of the programmers propably do not
know that there is no null, since it is not documented clearly so most
programmers just expect null already. Moreover, it is not a JAX-RS problem
at all, but a Java Ecosystem problem: If there would be a @NotNull
expception in the JRE that checks for null and throws NullPointerException
this would be beneficial for ALL Java programs, while your proposal just
works in this particular JAX-RS case in exactly inverse logic: While
everywhere in the Java Ecosystem null is allowed and must be checked for
explicitly, and potentially a future @NotNull annotation would make it
easier to deal with that by adding THAT explicity, at the same time bodies
in JAX-RS will be never null but the programmer now must add the reversed
annotation. Your proposal is just totally distorted from the overall Java
view, while it is only correct inside of the JAX-RS world. So this
discussion should not be "what helps 80% of JAX-RS users" but more "what is
like Java ever had been and what is not". Or: MY 80% of programmers will
love @NotNull while YOUR 80% of programmers would love @NoContent -- but MY
80% of programmers is measured of all Java programmers, while YOUR
programmers are measured of all JAX-RS programmers. So mine are more. ;-)
BTW, what does mhadley think about this and why are we the only ones
discussing this? Is nobody else interested in contributing his opinion? ;-)
Regards
Markus
From: Paul.Sandoz_at_Sun.COM [mailto:Paul.Sandoz_at_Sun.COM]
Sent: Mittwoch, 17. Dezember 2008 13:42
To: dev_at_jersey.dev.java.net
Subject: Re: [Jersey] JAX-RS based WebDAV support
Hi,
I know it is valid HTTP, that is not in dispute :-) what we disagree on is
whether this is a common 80% case for HTTP *applications*.
If you get an InputStream from Servlet does it return null when no content
is present? What about a Reader? Thus in those cases it may make sense not
to return null to retain similar expectations (see later for more on this).
With respect to high-level more application specific processing say with XML
and JAXB i think it depends on the application specific aspects of what you
are doing. I have written many examples with JAXB and have as of yet not
been required to explicitly support no content in the request and am happy
to let the runtime deal with returning of a client error. WebDAV is the only
use case that i currently know of that supports no content or content. What
you are asking me to do is require that i do the checking myself as well as
change my existing applications to avoid NPEs when a client is in error.
This is why i prefer an annotated solution for what i consider the 20% case,
for example:
@Consumes("application/xml")
@POST
public void post(@NoContent JAXBBean bean) { ... }
and for the specific WebDAV we can tweak support for the JAXB processing to
support the semantics of common WebDAV usage. The @NoContent annotation (for
want of a better word) would result in the return of specific stuff for no
content dependent on the Java type and message body reader, thus the
semantics do not always have to be null e.g there could be a default value
for some Java types perhaps (e.g. for WebDav it could be an empty
PropPatch/Find JAXB bean, which means the client does not have to deal with
the null case, one less thing to deal with).
The current situation does not crash anything on the server application
directly with an NPE. With lower-level types like InputStream, Reader,
byte[] and String it can still be determined if there is no content, but! i
think we should change that so by default a 400 client error is returned for
byte[], String, File etc (this requires clarifications to the JAX-RS spec).
When using JAXB the current situation is a 400 Client Error is returned.
What i am proposing is a solution for what i consider that 20% case where
clients can PUT or POST no content or content and it is not a client error
to do so. The annotation documents this is the case for both existing and
new developers, additionally it means we can potentially support additional
information in the WADL and generated HTML for developers writing clients.
In summary i am proposing the opposite of what you are proposing as the
default case :-) if you can convince me that your proposed default case
covers the 80% use-case bracket, namely content and no content sent by the
client is the common case, i will change my mind :-)
Paul.
On Dec 16, 2008, at 7:46 AM, Markus Karg wrote:
Paul,
I see it differently. I think that stability of the default solution must be
the first target to match, and this includes the zero-body case, since it is
valid HTTP and it is a thing that the Microsoft Redirectory does with EVERY
single PUT (sending an empty PUT first). So it is not an edge case. I think
it would be like programming a "for good whether only" software if JAX-RS
imposes programmers targeting use of the Microsoft client to take extra care
of empty bodies. Dealing with null pointers is a totally common thing in the
Java area and I do not see why there should be any difference for this
single injected argument, while all the rest of variables still can be null.
I would agree with you if Java would have special language support for
pointers that never can be null (like C++ has in a limited way). But it
lacks this. And again, I am NOT talking about WebDAV so I have to disagree
with a WebDAV special reader. It is VALID HTTP and has nothing to do with
WebDAV. The case that Content-Type is provided but Content-Length is null
imposes no specialities: Content-Type selects the method to call, and
Content-Length imposes null to be used. It is plain old Java in my eyes that
a parameter can be null and imposes no special complexity for the average
programmer. Actually from my understanding of logic, it is the only valid
solution to set the argument to null if Content-Length is zero and I was
very surprised to get an empty String body when I changed the body type to
String. No joke. Using blank Strings is completely strange!
Let's face the consequences:
My Solution: Straightforward Java solution, works in all cases as Java
programmers are used to deal with NULLs.
Your Solution: Imposes an add "Java doesn't know NULLs and Strings can be
blank but never NULL" semantics which is not typical to Java, and will crash
if the JAX-RS programmer forgets to add an additional (!) annotation, or
will work with WebDAV solely while the problem is not related to WebDAV at
all.
At least from my view, I cannot see that the Pros of your idea would
outwight the Cons of your proposal. But maybe I did not get the point why
your proposal should be any better than mine.
Regards
Markus
From: Paul.Sandoz_at_Sun.COM [mailto:Paul.Sandoz_at_Sun.COM]
Sent: Montag, 15. Dezember 2008 12:47
To: dev_at_jersey.dev.java.net
Subject: Re: [Jersey] JAX-RS based WebDAV support
On Dec 11, 2008, at 7:45 PM, Markus KARG wrote:
Paul,
since the HTTP specification does not enforce that any request must have a
body with a size larger than zero bytes, JAX-RS should not enforce this. So
if JAX-RS wants to provide a means for applications to deal with this issue,
my proposal is that the default is to inject null. If there is an
application that is unable to deal with that, it must check for null. This
is straightforward interpretation of the HTTP specification. It is ok if you
want to add an optional annotation that will prevent calling a method in
case of a null body. But since that is the exception (from the HTTP
specification view) the default must be to inject null.
I think we agree we need to do something. But i disagree with the default
solution because i think it is poor from an ease of use perspective. I
expect the common case is that content is expected thus having the runtime
deal with the case when no content occurs is very useful rather than having
the developer deal with this case. What you are proposing is that the
developer remember an edge case which if they forgot introduces a potential
NPE in their application.
Having thought about it a little more a null value may also be specific to
the Java type and media type being processed so i am not sure we can assume
that null is utilized when the Content-Type is present and the content
length is 0 (where the length is derived from the Content-Length header or
chunked encoding) for all cases.
In this respect we could propose a solution where by the request entity is
annotated and declares what should be done if the Content-Type is present
and the content length is 0. Thus it requires explicit developer
intervention for JAXB to deal with 0 content length. I realize this makes it
a little harder for utilizing WebDAV support. So... another solution could
be to provide message body readers for the WebDAV explicit JAXB types that
support 0 content length. Such support can reuse the general JAXB readers. I
think i might prefer the latter solution because we are associating specific
semantics with the WebDAV content/media type and the Java type.
For the Content-Type: It is valid to provide a content type even with a
missing body. The HTTP specification does not say that Concent-Type may only
be sent if Content-Length is larger than zero. So I do not see the problem.
If a client does not send a Content-Type but sends an empty body it is
obvious that the client does not care about any content type specific
behaviour. But I do not see that this is related to the zero-bytes-content,
since a client today could send a non-empty body with missing Content-Type
and no mathing file extension, and then you also do not know which method to
call. So this problem is not releated to the zero-bytes-problem. Your
proposal of ConsumesNothing is great, but it is independent of the
content-length. It solves a different problem.
Agreed it is independent of the Content-Type being present with 0 content
length.
Paul.
Regards
Markus
From: Paul.Sandoz_at_Sun.COM [mailto:Paul.Sandoz_at_Sun.COM]
Sent: Donnerstag, 11. Dezember 2008 10:32
To: dev_at_jersey.dev.java.net
Subject: Re: [Jersey] JAX-RS based WebDAV support
On Dec 10, 2008, at 7:51 PM, Markus KARG wrote:
Paul,
I do not see that 400 would be a correct return code, since 400 means "The
request could not be understood by the server DUE TO MALFORMED SYNTAX.". The
syntax was not malformed, it was pretty correct!
It depends on what you want to do. The syntax of the request may well be
malformed because a request entity is absent but a request entity is
required. The malformed syntax applies to the whole request and not just to
the syntax of request entity.
As you point out it is correct for a WebDAV method (i presume it is allowed
in the specification?) but for another application a HTTP method may require
a request entity and in this case having the application have to check for
null would be annoying when the framework could do that.
So still I believe that nobody out there would say something against a
single solution of injecting null.
You mean a default solution or an optional solution?
Maybe we should let people vote? ;-)
:-) i think it requires more discussion/exploration of the best way to
provide such a solution.
Part of the difficultly i am having with arriving at a solution is a method
such as:
@POST
@Consumes("application/xml")
public void post(String xxx) {
}
states that it consumes something. Now if i have two method consuming
something:
@POST
@Consumes("application/xml")
public void post(String xxx) {
}
@POST
@Consumes("application/json")
public void post(String xxx) {
}
then if the Content-Type is absent then which method would get called? we
need a way to state that one of these method may be called if the
Content-Type is absent or another method that gets called when the
Content-Type is absent, for example:
@POST
@ConsumesNothing
public void post() {
}
And we need to define the case of what happens when the Content-Type is
present but the content length is 0.
In the WebDAV case is the Content-Type absent from the request?
Paul.
Regards
Markus
For the issue with the empty bodies: We need to find a solution since it's
really a black spot on the overall great API. I think it would be not much a
problem to convince everybody that it makes more sense to inject null
instead of throwing a NullPointerException? :-)
Yes, i can see it both ways. Sometimes you want a null value to be
propagated and sometimes you do not and want a 400 response instead (the
latter probably being the most common i suspect).
One solution is to have a separate resource method for a request that does
not contain a request entity. Another could be to annotation the request
entity or the resource method that declares if there is not content-type
then the entity should be assigned a null value. I think i prefer the latter
solution.
Paul.