users@jersey.java.net

[Jersey] Callable in Jersey causes method return without waiting for callable returns

From: Felix Gao <guohangao_at_gmail.com>
Date: Tue, 10 Sep 2013 20:23:21 -0700

I have a piece of code in Jersey that looks like


 @Metered
    @GET
    @Produces(MediaType.APPLICATION_JSON)
    @Path("search")
    public javax.ws.rs.core.Response search(@QueryParam("tags") final
String tags,
                                            @QueryParam("sw")final String
sw,
                                            @QueryParam("ne")final String
ne,
                                            @QueryParam("page")
@DefaultValue("1") final String page ) throws Exception {

        final Map<String, String> param = new HashMap<String, String>(){{
            put("sw", sw);
            put("ne", ne);
            put("page", page);
            put("tags", tags);
        }};

       final ExecutorService pool = Executors.newFixedThreadPool(2);
       final CompletionService<List<Images>> cs = new
ExecutorCompletionService<List<Images>>(pool);
       final List<Callable<List<Images>>> calls = new
ArrayList<Callable<List<Images>>>();
       List<Images> result = new LinkedList<Images>();

        for(BaseImageSearch search : this.searches){
            calls.add(new ImageSearchCallable(search, param));
        }


        for(Callable<List<Images>> call : calls){
            logger.debug("hitting the image service"+ call.toString());
            cs.submit(call);
        }


        try {
            for (Callable<List<Images>> call : calls) {
                Future<List<Images>> f = cs.take();
                result.addAll(f.get());
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            logger.error("thread interrupted", e);
        } catch (ExecutionException e) {
            Thread.currentThread().interrupt();
            logger.error("thread execution exception", e);
        } catch (Throwable t){
            logger.error("throwable is ",t);
        }finally {
            if (pool != null) {
                pool.shutdownNow();
            }
        }
       logger.info(String.format("ready to write out with %s images",
result.size()));

       javax.ws.rs.core.Response.ResponseBuilder rb =
javax.ws.rs.core.Response.status(javax.ws.rs.core.Response.Status.OK);
       rb.entity("this is the fake payload");

       return rb.build();
    }


 curl -v "
http://localhost:8180/api/images/search?sw=51.459134,-0.224028&ne=51.543732,-0.122985"*
About to connect() to localhost port 8180 (#0)
* Trying ::1...
* connected
* Connected to localhost (::1) port 8180 (#0)
> GET /api/images/search?sw=51.459134,-0.224028&ne=51.543732,-0.122985
HTTP/1.1
> User-Agent: curl/7.24.0 (x86_64-apple-darwin12.0) libcurl/7.24.0
OpenSSL/0.9.8r zlib/1.2.5
> Host: localhost:8180
> Accept: */*
>
* Empty reply from server
* Connection #0 to host localhost left intact
curl: (52) Empty reply from server
* Closing connection #0

however the log says
INFO [2013-09-11 03:05:48,642] com.test.resources.image.ImagesResource:
ready to write out with 100 images


My Image Search eventually calls this method

    @Inject
    public FlickrImages(Client client, FlickrConfiguration config){
        this.config = config;
        webResource = client.resource(URI);
    }
The client is injected in by Guice and it is a singleton that is shared by
many classes

    public List<Images> getBboxQuery(final String tags , final String
bounds, final String min, final String page){
        final DateTime min_taken = new DateTime().minusMonths(MONTH_AGO);

        MultivaluedMap<String, String> params = new
MultivaluedMapImpl(DEFAULT){{
            if (!StringUtils.isEmpty(tags))
                add("tags", tags);
            add("api_key", config.getApiKey());
            add("method", METHOD_GET_SEARCH);
            if (!StringUtils.isEmpty(min))
                add("min_upload_date", min);
            else
                add("min_upload_date", min_taken.getMillis()/1000);
            add("bbox", bounds);
            add("page", page);
        }};
        ClientResponse response =
this.webResource.queryParams(params).accept("application/json").get(ClientResponse.class);
        if (response.getStatus() != 200) {
            throw new RuntimeException("Failed : HTTP error code : " +
response.getStatus());
        }
        StringBuilder result = new
StringBuilder(response.getEntity(String.class));
        result.replace(0, 14, "");
        result.replace(result.lastIndexOf(")"),result.lastIndexOf(")")+1,
"");
        try {
            FlickrJsonSearch val = MAPPER.readValue(result.toString(),
FlickrJsonSearch.class);
            return val.toImagesPojo();
        } catch (IOException e) {
           LOGGER.error("unable to parse value result:
"+result.toString(),e);
        }

        return null;
    }



It Seems to me that each of my children thread's response somehow caused
the Get method to think it is the response from the parent and returned.

So what I don't understand is that why the method returned without waiting
for the response in the parent thread to complete?

I am using jersey 1.17.1