On Thu, Sep 03, 2009 at 03:04:33PM -0700, Gerald Turner wrote:
> Oleg Kalnichevski <[email protected]> writes:
>
> > Gerald Turner wrote:
> >> ??? Releasing connections has increased the amount of code (finally
> >> block logic) that client applications need:
> >>
> >> HttpClient 3.x:
> >>
> >> HttpMethod method = null;
> >> try {
> >> ???
> >> }
> >> finally {
> >> if (method != null) {
> >> try {
> >> method.releaseConnection()
> >> }
> >> catch (Exception e) {
> >> log.warn("Failed to release connection: "
> >> + e.getMessage(), e);
> >> }
> >> }
> >> }
> >>
> >> HttpClient 4.0:
> >>
> >> HttpUriRequest request = null;
> >> HttpResponse response = null;
> >> try {
> >> ???
> >> }
> >> finally {
> >> boolean released = false;
> >>
> >> // Try consumeContent first, so that connection may be
> >> // kept-alive and reused. Note there is no response entity
> >> // for HTTP codes like 304
> >> if (response != null) {
> >> try {
> >> HttpEntity entity = response.getEntity();
> >> if (entity != null) {
> >> entity.consumeContent();
> >> released = true;
> >> }
> >> }
> >> catch (Exception e) {
> >> log.warn("Failed to consume HttpEntity: "
> >> + e.getMessage(), e);
> >> }
> >> }
> >>
> >> // Otherwise abort the connection, seems allow the
> >> // connection to be kept-alive and reused in some cases
> >> // (like when there was no response entity), this is a good
> >> // thing even if ???abort??? sounds like it's going to close.
> >> if (!released && request != null) {
> >> try {
> >> request.abort();
> >> }
> >> catch (Exception e) {
> >> log.warn("Failed to abort HttpUriRequest: "
> >> + e.getMessage(), e);
> >> }
> >> }
> >> }
> >> }
> >>
> >
> > Pretty much all this code is not necessary. (1) HttpClient will
> > automatically release the underlying connection as long as the entity
> > content is consumed to the end of stream; (2) HttpClient will
> > automatically release the underlying connection on any I/O exception
> > thrown while reading the response content. No special processing is
> > required in such as case.
> >
>
> Ah!, I should've mentioned that much of the code I've been working on
> fires off GETs/POSTs (even a Range-conditioned GET in one place) and
> doesn't bother reading the response content, it's happy with a 200 (or
> 206).
>
I do not think this makes any difference. If there is no response entity there
is no connection associated with that response that has to be released back to
the pool.
> Some of the code I've been working on expects 304's (If-Modified-Since)
> and 404's, there is response content (like a user-friendly File Not
> Found HTML page), but again it only cares about the status code.
>
> Anyway the heavy finally block is safe correct? For instance a 304 has
> no content, but I fall thru can call request.abort() even though it's
> already been recycled.
>
The finally block is okay but it is just plain ugly and the request.abort() bit
in it is completely useless, because there will never a connection to abort in
the first place.
> > In fact, this is perfectly sufficient to ensure proper release of
> > resources:
> >
> > HttpResponse rsp = httpclient.execute(target, req);
> > HttpEntity entity = rsp.getEntity();
> > if (entity != null) {
> > InputStream instream = entity.getContent();
> > try {
> > // process content
> > } finally {
> > instream.close();
> > // entity.consumeContent() would also do
> > }
> > }
> >
> > That is it.
> >
> >>
> >> ??? In addition to this complex finally-block, I have created a class
> >> which extends HttpEntity (CompressedEntity, does gzip
> >> Transfer-Encoding of requests), it's assigned to requests, it needs
> >> to release resources, HttpService.handleRequest does call
> >> consumeContent, however I am concerned with HTTP request executions
> >> that don't make it that far (connection manager timeout for
> >> instance), in that case my finally block has to call
> >> request.getEntity().consumeContent().
> >>
> >
> > See above. In case of an I/O error the connection will get
> > automatically closed and released back to the manager.
> >
>
> I'm not so sure about this - it's a request entity that begins life with
> some resource that needs to be cleaned. If HttpClient doesn't get as
> far as obtaining a connection from it's pool, wouldn't the request *not*
> have it's consumeContent called?
>
I see now. If a request entity allocates some resources at the _construction_
time, HttpClient will make no attempt to release them in case of an abnormal
termination of the request. The caller will have to ensure resources get
cleaned up.
> >> ??? Multi-part requests in HttpClient 3.x were a little easier to work
> >> with in a few special cases, the Part interface included the ???name???
> >> field, whereas the ???name??? field is now a parameter in the
> >> MultipartEntity.addPart(String,BodyContent) call.
> >>
> >> Before:
> >>
> >> Part[] parts = {
> >> new StringPart("Some-Field", "Some-Value"),
> >> new FilePart("Some-File", f)
> >> };
> >>
> >> MultipartRequestEntity entity =
> >> new MultipartRequestEntity(parts, method.getParams());
> >>
> >> After:
> >>
> >> LinkedHashMap<String,ContentBody> parts =
> >> new LinkedHashMap<String,ContentBody>();
> >> parts.put("Some-Field", new StringBody("Some-Value"));
> >> parts.put("Some-File", new FileBody(f));
> >>
> >> MultipartEntity entity = new MultipartEntity();
> >> for (Entry<String,ContentBody> part : parts)
> >> entity.addPart(part.getKey(), part.getValue());
> >>
> >> In some respects I like the API better, but I do have some code which
> >> needs to assemble the parts (including names) independent of the HTTP
> >> request execution, so in one application I wrote a Part wrapper:
> >>
> >> for (Part part : parts)
> >> entity.addPart(part.getName(), part.getContent());
> >>
> >
> > If you can think of a fix for the issue, raise a JIRA.
> >
>
> FormBodyPart does the trick (couples name and content).
>
> MultipartEntity.addPart looks like this:
>
> public void addPart(final String name, final ContentBody contentBody) {
> this.multipart.addBodyPart(new FormBodyPart(name, contentBody));
> this.dirty = true;
> }
>
> How about an addPart override that looks like:
>
> public void addPart(final BodyPart bodyPart) {
> this.multipart.addBodyPart(bodyPart);
> this.dirty = true;
> }
>
Raise a JIRA.
> > ConnPerRouteBean was a bug mistake on my part. I am thinking about
> > deprecating the damn thing in 4.1.
> >
>
> That's too bad, although it does seem useful to configure varying pool
> sizes by hostname.
>
> What do you think about the nested introspection thing?
>
> It could be done by having the bean classes reference each other, like
> ClientParamBean.getConnManagerParam (returns ConnManagerParamBean), but
> there is also the problem of complex types ??? maybe make a policy for the
> bean classes to deal with int/long/boolean/String types only (and
> getters for nested beans).
>
I have been thinking about using good ol' setters and getters on
the ThreadSafeClientConnManager class
> >> ??? The method DateUtils#formatDate(Date) is very handy and I use it in
> >> a lot of applications, nothing wrong here except that the package
> >> ??????.impl.cookie??? throws me off ??? having me rely on any external API
> >> implementation (???impl???) doesn't feel like a smart thing for me to do,
> >> also I don't use cookies at all, I use this method for handling
> >> Last-Modified and If-Modified-Since kinds of headers.
> >>
> >
> > We could deprecate DateUtils class in the impl package and move it to
> > another package within the 'public' API space.
> >
>
> I vote yes ???
>
If you feel very strongly about it, raise a JIRA.
Cheers
Oleg
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]