Syntax highlighter header

Tuesday 17 August 2021

Unable to make private java.lang.reflect.Proxy() accessible with JDK-17 in JUnit test using Ant

Recently I encountered the following error while executing Junit test case using Ant. My testcase uses EJBs from a wildfly 24 server. I am using jboss-client.jar file from same server in my testcase.

2021-08-17 16:39:30,450 2428  ERROR [com.tk20.ejb.api.util.EJBUtils] (main:[]) Unable to make private java.lang.reflect.Proxy() accessible: module java.base does not "opens java.lang.reflect" to unnamed module @
394df057
java.lang.reflect.InaccessibleObjectException: Unable to make private java.lang.reflect.Proxy() accessible: module java.base does not "opens java.lang.reflect" to unnamed module @394df057
        at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:354) ~[?:?]
        at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:297) ~[?:?]
        at java.base/java.lang.reflect.Constructor.checkCanSetAccessible(Constructor.java:188) ~[?:?]
        at java.base/java.lang.reflect.Constructor.setAccessible(Constructor.java:181) ~[?:?]
        at org.jboss.marshalling.reflect.JDKSpecific$SerMethods.<init>(JDKSpecific.java:147) ~[jboss-client.jar:24.0.0.Final]
        at org.jboss.marshalling.reflect.SerializableClass.<init>(SerializableClass.java:84) ~[jboss-client.jar:24.0.0.Final]
        at org.jboss.marshalling.reflect.SerializableClassRegistry$1.computeValue(SerializableClassRegistry.java:62) ~[jboss-client.jar:24.0.0.Final]
        at org.jboss.marshalling.reflect.SerializableClassRegistry$1.computeValue(SerializableClassRegistry.java:59) ~[jboss-client.jar:24.0.0.Final]
        at java.base/java.lang.ClassValue.getFromHashMap(ClassValue.java:228) ~[?:?]
        at java.base/java.lang.ClassValue.getFromBackup(ClassValue.java:210) ~[?:?]
        at java.base/java.lang.ClassValue.get(ClassValue.java:116) ~[?:?]
        at org.jboss.marshalling.reflect.SerializableClassRegistry.lookup(SerializableClassRegistry.java:83) ~[jboss-client.jar:24.0.0.Final]
        at org.jboss.marshalling.reflect.SerializableClass.<init>(SerializableClass.java:76) ~[jboss-client.jar:24.0.0.Final]
        at org.jboss.marshalling.reflect.SerializableClassRegistry$1.computeValue(SerializableClassRegistry.java:62) ~[jboss-client.jar:24.0.0.Final]
        at org.jboss.marshalling.reflect.SerializableClassRegistry$1.computeValue(SerializableClassRegistry.java:59) ~[jboss-client.jar:24.0.0.Final]
        at java.base/java.lang.ClassValue.getFromHashMap(ClassValue.java:228) ~[?:?]
        at java.base/java.lang.ClassValue.getFromBackup(ClassValue.java:210) ~[?:?]
        at java.base/java.lang.ClassValue.get(ClassValue.java:116) ~[?:?]
        at org.jboss.marshalling.reflect.SerializableClassRegistry.lookup(SerializableClassRegistry.java:83) ~[jboss-client.jar:24.0.0.Final]
        at org.jboss.marshalling.river.RiverUnmarshaller.doReadNewObject(RiverUnmarshaller.java:1394) ~[jboss-client.jar:24.0.0.Final]
        at org.jboss.marshalling.river.RiverUnmarshaller.doReadObject(RiverUnmarshaller.java:298) ~[jboss-client.jar:24.0.0.Final]
        at org.jboss.marshalling.river.RiverUnmarshaller.doReadObject(RiverUnmarshaller.java:231) ~[jboss-client.jar:24.0.0.Final]
        at org.jboss.marshalling.AbstractObjectInput.readObject(AbstractObjectInput.java:41) ~[jboss-client.jar:24.0.0.Final]
        at org.wildfly.naming.client.remote.RemoteClientTransport.lookup(RemoteClientTransport.java:271) ~[jboss-client.jar:24.0.0.Final]
        at org.wildfly.naming.client.remote.RemoteContext.lambda$lookupNative$0(RemoteContext.java:190) ~[jboss-client.jar:24.0.0.Final]
        at org.wildfly.naming.client.NamingProvider.performExceptionAction(NamingProvider.java:222) ~[jboss-client.jar:24.0.0.Final]
        at org.wildfly.naming.client.remote.RemoteContext.performWithRetry(RemoteContext.java:100) ~[jboss-client.jar:24.0.0.Final]
        at org.wildfly.naming.client.remote.RemoteContext.lookupNative(RemoteContext.java:188) ~[jboss-client.jar:24.0.0.Final]
        at org.wildfly.naming.client.AbstractFederatingContext.lookup(AbstractFederatingContext.java:74) ~[jboss-client.jar:24.0.0.Final]
        at org.wildfly.naming.client.store.RelativeFederatingContext.lookupNative(RelativeFederatingContext.java:58) ~[jboss-client.jar:24.0.0.Final]
        at org.wildfly.naming.client.AbstractFederatingContext.lookup(AbstractFederatingContext.java:74) ~[jboss-client.jar:24.0.0.Final]
        at org.wildfly.naming.client.AbstractFederatingContext.lookup(AbstractFederatingContext.java:60) ~[jboss-client.jar:24.0.0.Final]
        at org.wildfly.naming.client.AbstractFederatingContext.lookup(AbstractFederatingContext.java:66) ~[jboss-client.jar:24.0.0.Final]
        at org.wildfly.naming.client.AbstractFederatingContext.lookup(AbstractFederatingContext.java:66) ~[jboss-client.jar:24.0.0.Final]
        at org.wildfly.naming.client.AbstractFederatingContext.lookup(AbstractFederatingContext.java:66) ~[jboss-client.jar:24.0.0.Final]
        at org.wildfly.naming.client.WildFlyRootContext.lookup(WildFlyRootContext.java:144) ~[jboss-client.jar:24.0.0.Final]

The reason of failure was traced to version of Ant which I was using version 1.9.15 of Ant. After upgrading Ant to version 1.10.11 the error was removed. Due to some reason with older version of Ant JDKSpecific class which was being used was compatible with JDK8 and not the one which is compatible with JDK9 or higher. After upgrading Ant to 1.10.11 it started picking right version of class and problem was fixed.

Please refer to my earlier post regarding similar  error faced while accessing EJB in wildfly 24 without involvement of Ant and JUnit.  https://blog.bigdatawithjasvant.com/2021/07/javalangreflectinaccessibleobjectexcept.html

Friday 13 August 2021

Implementing rate limit on an API

Here I am going to discuss an interesting programming problem. How to implement rate limit for an API per customer. You need to implement a rate limit of 10 requests per second with allowing a bust of 50 API calls. It means if user was silent for some time then tokens will accumulate up to 50 tokens and after that they will get expired. Every call will consume one token. One token will get accumulated every 100 milliseconds. 

Following is the implementation of code with limit of 10 API calls per second and allowing a bust of 50 API calls.


import java.util.HashMap;

public class RateLimitting {
	
	HashMap<String, Bucket> rateLimits = new HashMap<>();
	
	public static class Bucket {
		int tokens;
		long lastAccessTime;
		
		public Bucket() {
			tokens = 50;
			lastAccessTime = System.currentTimeMillis();
		}
	}
	
	public String api(String user, String req) {
		Bucket r = rateLimits.get(user);
		if(r==null) {
			r = new Bucket();
			rateLimits.put(user, r);
		} else {
			long current = System.currentTimeMillis();
			if((current - r.lastAccessTime)/100 >=1) {
				r.tokens = (int) (r.tokens + (current - r.lastAccessTime)/100);
				if(r.tokens>50) {
					r.tokens = 50;
				}
				r.lastAccessTime = current;
			}
		}
		
		if(r.tokens<0) {
			// deny the request
			return null;
		}
		
		r.tokens--;
		
		// Process the request
		return "Success";
		
	}
} 

Implement power function without using multiplication

In this post I am going to discuss one interesting  programming question. You are supposed to write power function without using multiply operator. You are allowed to use division operator but not multiply.

This is a good question to show your skill at optimizing code and showing that you don't miss the corner cases. You can use multiple additions to simulate multiplication. You need to make sure that you use lest number of additions. For example if you are multiplying 5 with 5 then you don't add 5 four time, you can do it by adding three times. You add 5 with 5 to get 10 in one addtion, You add 10 with 10 in second addition and after that you add 5 in third addition to get answer as 25.

You can implement one function for multiplying two numbers and use that function in your power function. Here again you need to make least number of calls to multiply function. For example if you are calculating 5^5 then we can do it with three calls to multiply functions instead of 4 calls. You multiply 5 with 5 in first call to get 25. You multiply 25 with 25 in second call and multiply 625 with 5 in third call.

Following is the java code for this question:


public class PowerWithoutMul {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		
		int m = 4;
		int n = 4;
		
		System.out.println(pow(m, n));
		

	}
	
	static int pow(int m, int n) {
		int res = 1;
		if(n/2>0) {
			int temp = pow(m, n/2);
			res = mul(temp, temp);
		}
		if(n%2==1) {
			res = mul(res, m);
		}
		return res;		
	}
	
	static int mul (int m, int k) {
		int res = 0;
		if(k/2>0) {
			int temp = mul(m, k/2);
			res =  temp + temp;
		}
		
		if(k%2 == 1) {
			res = res + m;
		}
		return res;
	}

}

Tuesday 10 August 2021

Minimum number of swaps required to sort an array

I was reading one interesting post on GeeksForGeeks at https://www.geeksforgeeks.org/minimum-number-swaps-required-sort-array/

I found it very informative. I thought of explaining this problem in my words.

Problem statement:

Given an array of n distinct elements, find the minimum number of swaps required to sort the array.

Example:


Input: {4, 3, 2, 1}
Output: 2
Explanation: Swap index 0 with 3 and 1 with 2 to 
              form the sorted array {1, 2, 3, 4}.

Input: {1, 5, 4, 3, 2}
Output: 2

Solution to this problem lies in sorting this array and observing how many elements have changed their position. For the elements who has not changed their position no swap is needed. For the elements who have changed their positions there will be cycles like following image(from GeeksForGeeks.com)

In the above image there are two cycles of two elements each. To sort this array we need two swaps, one for each cycle. Now look at following image (from GeeksForGeeks.com) The above array contains two cycles one with 2 elements and one with 3 elements. We need one swap for correcting two element cycle and two swaps for correcting 3 element cycle. 
We need N-1 swaps for correcting a N element cycle. If we have a cycle a->b->c->d->e->a then this 5 element cycle can be corrected by 4 swaps:

  1. (a, b)
  2. (b, c) note b will be at a's original place
  3. (c, d) note c will be at a's original place
  4. (d, e) note d will be at a's original place

After 4th swap you notice e is at a's original place already so no swap is needed.

For counting number of swap we can calculate cycle size either forward or backward. If we have a cycle a->b->c->d->e->a .We can start at a's original place in sorted array and see that element e is present there, now we see which element it present at e's original place ,we find d there then we look at d's original place then we find c there. If we continue this way we find a at b's original place when we look for a's original place we find that it was already visited so we break out of the loop. This gives us size of the cycle. We mark all elements of this cycle visited so we don't get into this cycle one again.

Following is the code from GeeksForGeeks:

import java.util.*;
import java.io.*;

class GfG
{
	// Function returns the
	// minimum number of swaps
	// required to sort the array
	public static int minSwaps(int[] nums)
	{
		int len = nums.length;
		HashMap<Integer, Integer> map = new HashMap<>();
		for(int i=0;i<len;i++)
			map.put(nums[i], i);
			
		Arrays.sort(nums);
		
		// To keep track of visited elements. Initialize
		// all elements as not visited or false.
		boolean[] visited = new boolean[len];
		Arrays.fill(visited, false);
		
		// Initialize result
		int ans = 0;
		for(int i=0;i<len;i++) {
		
			// already swapped and corrected or
			// already present at correct pos
			if(visited[i] || map.get(nums[i]) == i)
				continue;
				
			int j = i, cycle_size = 0;
			while(!visited[j]) {
				visited[j] = true;
				
				// move to next node
				j = map.get(nums[j]);
				cycle_size++;
			}
			
			// Update answer by adding current cycle.
			if(cycle_size > 0) {
				ans += (cycle_size - 1);
			}
		}
		return ans;
	}
}

// Driver class
class MinSwaps
{
	// Driver program to test the above function
	public static void main(String[] args)
	{
		int []a = {1, 5, 4, 3, 2};
		GfG g = new GfG();
		System.out.println(g.minSwaps(a));
	}
}
// This code is contributed by Saurabh Johari

Monday 9 August 2021

replacement of com.sun.jersey.spi.container.ContainerRequestFilters in jersey 2

Recently I was upgrading my application from Jersey 1 to Jersey 2. Following is my web.xml file in Jercey 1.


<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" 
            xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
  <display-name>myapplication</display-name>
  <servlet>
    <servlet-name>jersey-serlvet</servlet-name>
    <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
    <init-param>
      <param-name>com.sun.jersey.config.property.packages</param-name>
      <param-value>myapplication</param-value>
    </init-param>
    <init-param>
	  <param-name>com.sun.jersey.spi.container.ContainerRequestFilters</param-name>
	  <param-value>common.SecurityFilter</param-value>
	</init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>jersey-serlvet</servlet-name>
    <url-pattern>/rest/*</url-pattern>
  </servlet-mapping> 
</web-app>

I found that in Jersey 2 replacement for com.sun.jersey.spi.container.servlet.ServletContainer is org.glassfish.jersey.servlet.ServletContainer . From documentation of org.glassfish.jersey.servlet.ServletContainer I found that replacement of com.sun.jersey.config.property.packages is  jersey.config.server.provider.packages . But I was unable to find replacement for init-param parameter com.sun.jersey.spi.container.ContainerRequestFilters .  It was big struggle to find a replacement for this.

After struggling for a long time I found that we don't need to put filter in web.xml file. It will be picked by package scanning. Following is my filter code. I changed package names according to jersey 2.


package common;

import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.Provider;

import java.util.Base64;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.ContainerRequestContext;

@Provider
public class SecurityFilter implements ContainerRequestFilter {

	private static final String X_API_KEY = "x-api-key";
	private static final String X_API_SECRET = "1234567890";
	
	@Override
	public void filter(ContainerRequestContext requestContext) {
		String authapikey = requestContext.getHeaderString(X_API_KEY);
		if(authapikey!=null && !authapikey.isEmpty()) {
			String decodedKey = new String(Base64.getDecoder().decode(authapikey)) ;
			if(X_API_SECRET.equals(decodedKey)) {
				return ;
			}
			
		}
		
		Response unauthorizedResp = Response
						.status(Response.Status.UNAUTHORIZED)
						.entity("User cannot access the resource")
						.build();
		throw new WebApplicationException(unauthorizedResp);
	}
}

The annotation @Provider is required. Without this annotation this filter will not be invoked. Following is my web.xml with Jersey 2. 


<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" 
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" 
    id="WebApp_ID" version="3.0">
  <display-name>myapplication</display-name>
  <servlet>
    <servlet-name>jersey-serlvet</servlet-name>
    <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
    <init-param>
      <param-name>jersey.config.server.provider.packages</param-name>
      <param-value>myapplication;common</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>jersey-serlvet</servlet-name>
    <url-pattern>/rest/*</url-pattern>
  </servlet-mapping>  
</web-app>

Now my common.SecurityFilter get invoked with Jersey 2. Now my application is working in Jersey 2 as it was working with Jersey 1. Jersey 1 does not work with JDK-17 so you need Jersey 2 for working with JDK-17.

Tuesday 3 August 2021

Using CompletableFuture in Java

 Recently I was exploring CompletableFuture in Java and trying to find correct use case for it. This is what I found.

We have a service which need to combine data from multiple http calls. We submit http requests to a ExecutorService for doing that. We have a deadline that our service need to return in 2 seconds. So we set a deadline or 1 second for all http calls to return the response. If some http call does not return by this time we return timeout for that resource. Following is the code:


import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionService;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;

import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;

public class HttpClientTest4 {
	private static String apiKey="your_key_here";
	
	public static class RunnableRequest implements Runnable {
		String url;
		private CompletableFuture<String> cf;
				
		public RunnableRequest(String url, CompletableFuture<String> cf) {
			this.url = url;
			this.cf = cf;
		}
		
		@Override
		public void run() {
			try {
				//System.out.println("calling the task for movieId "+ url);
				CloseableHttpClient client = HttpClients.createDefault();
				HttpGet request = new HttpGet(url);
				CloseableHttpResponse response = client.execute(request);
				if(response.getStatusLine().getStatusCode()!=200) {
					cf.completeExceptionally(new RuntimeException("Issue getting response"));
					return;
				}
				String respString = EntityUtils.toString(response.getEntity());
				client.close();
				cf.complete(respString);
			} catch(IOException exp) {
				cf.completeExceptionally(exp);
			}
		}
		
	}
	
	public static class MyCompletebaleFuture<K,V> extends CompletableFuture<V> {
		K k;
		public MyCompletebaleFuture(K k) {
			super();
			this.k = k;
		}
		K getKey() {
			return k;
		}
	}

	public static void main(String[] args)  {

		ExecutorService executorSerivce = Executors.newFixedThreadPool(5);
		
		List<String> movieIds  = Arrays.asList("100","125","133","134","156","101","107","108","120","220","221","225","234","254","256");
		List<MyCompletebaleFuture<String,String>> futures = new ArrayList<>();
		for (String movieId : movieIds) {
			String url = "https://api.themoviedb.org/3/movie/"+movieId+"?api_key="+apiKey;
			MyCompletebaleFuture<String,String> cf = new MyCompletebaleFuture<>(movieId);
 			RunnableRequest req = new RunnableRequest(url, cf);			
			//System.out.println("submitting the task for movieId "+ movieId);			
			executorSerivce.submit(req);
			futures.add(cf);
		}
		
		ScheduledExecutorService ses = Executors.newScheduledThreadPool(1);
		Runnable timeoutTask = new Runnable() {
			@Override
			public void run() {
				RuntimeException re = new RuntimeException("Timeout");
				for (CompletableFuture<String> future : futures) {
					future.completeExceptionally(re);
				}				
			}			
		};
		ses.schedule(timeoutTask, 1500, TimeUnit.MILLISECONDS);
		
		for (MyCompletebaleFuture<String,String> future : futures) {
			String resp=null;
			Throwable failureCause=null;
			try {
				resp = future.get();
			} catch (InterruptedException e) {
				failureCause = e;
			} catch (ExecutionException e) {
				failureCause = e.getCause();
			}
			if(failureCause==null) {
				System.out.println("MovieID="+future.getKey() +" res="+resp);
			} else {
				System.out.println("MovieID="+future.getKey() + " exception="+failureCause.toString());
			}
		}
		executorSerivce.shutdown();
		ses.shutdown();
	}

}

For me the output was:


MovieID=100 res={"adult":false,"backdrop_path":"/tY6zVyt0OubPgCapbXFJLKhQqSu.jpg","belongs_to_collection":null,"budget":1350000,"genres":[{"id":35,"name":"Comedy"},{"id":80,"name":"Crime"}],"homepage":"http://www.universalstudiosentertainment.com/lock-stock-and-two-smoking-barrels/","id":100,"imdb_id":"tt0120735","original_language":"en","original_title":"Lock, Stock and Two Smoking Barrels","overview":"A card shark and his unwillingly-enlisted friends need to make a lot of cash quick after losing a sketchy poker match. To do this they decide to pull a heist on a small-time gang who happen to be operating out of the flat next door.","popularity":8.102,"poster_path":"/8kSerJrhrJWKLk1LViesGcnrUPE.jpg","production_companies":[{"id":491,"logo_path":"/rUp0lLKa1pr4UsPm8fgzmnNGxtq.png","name":"Summit Entertainment","origin_country":"US"},{"id":21920,"logo_path":null,"name":"The Steve Tisch Company","origin_country":""},{"id":13419,"logo_path":null,"name":"SKA Films","origin_country":""},{"id":1382,"logo_path":"/sOg7LGESPH5vCTOIdbMhLuypoLL.png","name":"PolyGram Filmed Entertainment","origin_country":"US"},{"id":20076,"logo_path":"/i9qXGJIP9fGN22PP5jXUVENbyHi.png","name":"HandMade Films","origin_country":"GB"}],"production_countries":[{"iso_3166_1":"GB","name":"United Kingdom"}],"release_date":"1998-03-05","revenue":28356188,"runtime":105,"spoken_languages":[{"english_name":"English","iso_639_1":"en","name":"English"}],"status":"Released","tagline":"A Disgrace to Criminals Everywhere.","title":"Lock, Stock and Two Smoking Barrels","video":false,"vote_average":8.2,"vote_count":4932}
MovieID=125 res={"adult":false,"backdrop_path":null,"belongs_to_collection":null,"budget":0,"genres":[{"id":99,"name":"Documentary"}],"homepage":"","id":125,"imdb_id":"tt0080668","original_language":"pl","original_title":"Dworzec","overview":"Kieslowski’s later film Dworzec (Station, 1980) portrays the atmosphere at Central Station in Warsaw after the rush hour.","popularity":1.94,"poster_path":"/c4xG9QoCx2zXc8iP3jQVLYDHiMi.jpg","production_companies":[{"id":2466,"logo_path":null,"name":"WFD","origin_country":""}],"production_countries":[{"iso_3166_1":"PL","name":"Poland"}],"release_date":"1980-01-01","revenue":0,"runtime":13,"spoken_languages":[{"english_name":"Polish","iso_639_1":"pl","name":"Polski"}],"status":"Released","tagline":"","title":"Railway Station","video":false,"vote_average":5.9,"vote_count":16}
MovieID=133 res={"adult":false,"backdrop_path":"/e6IPjjDzZDkgdkvxQtCjaVCMYJF.jpg","belongs_to_collection":null,"budget":0,"genres":[{"id":99,"name":"Documentary"}],"homepage":"","id":133,"imdb_id":"tt0054205","original_language":"en","original_title":"Primary","overview":"Primary is a documentary film about the primary elections between John F. Kennedy and Hubert Humphrey in 1960. Primary is the first documentary to use light equipment in order to follow their subjects in a more intimate filmmaking style. This unconventional way of filming created a new look for documentary films where the camera’s lens was right in the middle of what ever drama was occuring.","popularity":3.616,"poster_path":"/gVloWsthHRlw719vzRWRpBqDM20.jpg","production_companies":[{"id":2401,"logo_path":null,"name":"Drew Associates","origin_country":""},{"id":2402,"logo_path":null,"name":"Time","origin_country":""}],"production_countries":[{"iso_3166_1":"US","name":"United States of America"}],"release_date":"1960-01-01","revenue":0,"runtime":60,"spoken_languages":[{"english_name":"English","iso_639_1":"en","name":"English"}],"status":"Released","tagline":"","title":"Primary","video":false,"vote_average":6.5,"vote_count":24}
MovieID=134 res={"adult":false,"backdrop_path":"/rbsm633lnt3hKHEpk7FOt3dy82a.jpg","belongs_to_collection":null,"budget":26000000,"genres":[{"id":12,"name":"Adventure"},{"id":35,"name":"Comedy"},{"id":80,"name":"Crime"}],"homepage":"","id":134,"imdb_id":"tt0190590","original_language":"en","original_title":"O Brother, Where Art Thou?","overview":"In the deep south during the 1930s, three escaped convicts search for hidden treasure while a relentless lawman pursues them. On their journey they come across many comical characters and incredible situations. Based upon Homer's 'Odyssey'.","popularity":12.979,"poster_path":"/2YztYilviFCYcEtDAnrOstUWGie.jpg","production_companies":[{"id":2092,"logo_path":null,"name":"Mike Zoss Productions","origin_country":"US"},{"id":33,"logo_path":"/8lvHyhjr8oUKOOy2dKXoALWKdp0.png","name":"Universal Pictures","origin_country":"US"},{"id":9195,"logo_path":"/ou5BUbtulr6tIt699q6xJiEQTR9.png","name":"Touchstone Pictures","origin_country":"US"},{"id":10163,"logo_path":"/16KWBMmfPX0aJzDExDrPxSLj0Pg.png","name":"Working Title Films","origin_country":"GB"},{"id":694,"logo_path":"/5LEHONGkZBIoWvp1ygHOF8iyi1M.png","name":"StudioCanal","origin_country":"FR"}],"production_countries":[{"iso_3166_1":"FR","name":"France"},{"iso_3166_1":"GB","name":"United Kingdom"},{"iso_3166_1":"US","name":"United States of America"}],"release_date":"2000-08-30","revenue":71868327,"runtime":107,"spoken_languages":[{"english_name":"English","iso_639_1":"en","name":"English"}],"status":"Released","tagline":"They have a plan... but not a clue.","title":"O Brother, Where Art Thou?","video":false,"vote_average":7.3,"vote_count":3022}
MovieID=156 res={"adult":false,"backdrop_path":null,"belongs_to_collection":null,"budget":0,"genres":[{"id":35,"name":"Comedy"},{"id":18,"name":"Drama"},{"id":10749,"name":"Romance"}],"homepage":"","id":156,"imdb_id":"tt0329767","original_language":"da","original_title":"Wilbur begår selvmord","overview":"The strange comedy film of two close brothers; one, Wilbur, who wants to kill himself, and the other, Harbour, who tries to prevent this. When their father dies leaving them his bookstore they meet a woman who makes their lives a bit better yet with a bit more trouble as well.","popularity":3.117,"poster_path":"/f2nq2ZLqKPRaWb4HeOwxULKkSNk.jpg","production_companies":[{"id":223,"logo_path":"/b9Icqi8qGm7gJ6jVs8bauUo5N5Q.png","name":"Les Films du Losange","origin_country":"FR"},{"id":235,"logo_path":"/obOynoztBEhDNfHsDXBqOuQLAZk.png","name":"Nordisk Film","origin_country":"DK"},{"id":698,"logo_path":null,"name":"Scottish Screen","origin_country":""},{"id":758,"logo_path":"/fft3jrctrZNcgAxgN1w5Givtzip.png","name":"TV 2","origin_country":"DK"},{"id":980,"logo_path":null,"name":"The Glasgow Film Fund","origin_country":""}],"production_countries":[{"iso_3166_1":"DK","name":"Denmark"},{"iso_3166_1":"FR","name":"France"},{"iso_3166_1":"GB","name":"United Kingdom"},{"iso_3166_1":"SE","name":"Sweden"}],"release_date":"2002-11-08","revenue":0,"runtime":111,"spoken_languages":[{"english_name":"English","iso_639_1":"en","name":"English"}],"status":"Released","tagline":"Meet a man dying to live","title":"Wilbur Wants to Kill Himself","video":false,"vote_average":6.5,"vote_count":61}
MovieID=101 res={"adult":false,"backdrop_path":"/jRJrQ72VLyEnVsvwfep8Xjlvu8c.jpg","belongs_to_collection":null,"budget":16000000,"genres":[{"id":80,"name":"Crime"},{"id":18,"name":"Drama"},{"id":28,"name":"Action"}],"homepage":"","id":101,"imdb_id":"tt0110413","original_language":"en","original_title":"Léon: The Professional","overview":"Léon, the top hit man in New York, has earned a rep as an effective \"cleaner\". But when his next-door neighbors are wiped out by a loose-cannon DEA agent, he becomes the unwilling custodian of 12-year-old Mathilda. Before long, Mathilda's thoughts turn to revenge, and she considers following in Léon's footsteps.","popularity":37.287,"poster_path":"/wHqGb8J6tXBVwjqWooGMtNEjs2A.jpg","production_companies":[{"id":9,"logo_path":"/nda3dTUYdDrJ6rZqBpYvY865aDv.png","name":"Gaumont","origin_country":"FR"},{"id":66743,"logo_path":null,"name":"Les Films du Dauphin","origin_country":"FR"},{"id":5,"logo_path":"/71BqEFAF4V3qjjMPCpLuyJFB9A.png","name":"Columbia Pictures","origin_country":"US"}],"production_countries":[{"iso_3166_1":"US","name":"United States of America"},{"iso_3166_1":"FR","name":"France"}],"release_date":"1994-09-14","revenue":45284974,"runtime":111,"spoken_languages":[{"english_name":"English","iso_639_1":"en","name":"English"},{"english_name":"French","iso_639_1":"fr","name":"Français"},{"english_name":"Italian","iso_639_1":"it","name":"Italiano"}],"status":"Released","tagline":"If you want a job done well, hire a professional.","title":"Léon: The Professional","video":false,"vote_average":8.3,"vote_count":11096}
MovieID=107 res={"adult":false,"backdrop_path":"/32V4uHVRU2fbDsQl1dsYsZm3uba.jpg","belongs_to_collection":null,"budget":10000000,"genres":[{"id":80,"name":"Crime"},{"id":35,"name":"Comedy"}],"homepage":"","id":107,"imdb_id":"tt0208092","original_language":"en","original_title":"Snatch","overview":"Unscrupulous boxing promoters, violent bookmakers, a Russian gangster, incompetent amateur robbers and supposedly Jewish jewelers fight to track down a priceless stolen diamond.","popularity":17.959,"poster_path":"/56mOJth6DJ6JhgoE2jtpilVqJO.jpg","production_companies":[{"id":3287,"logo_path":"/bz6GbCQQXGNE56LTW9dwgksW0Iw.png","name":"Screen Gems","origin_country":"US"},{"id":13419,"logo_path":null,"name":"SKA Films","origin_country":""},{"id":5,"logo_path":"/71BqEFAF4V3qjjMPCpLuyJFB9A.png","name":"Columbia Pictures","origin_country":"US"}],"production_countries":[{"iso_3166_1":"GB","name":"United Kingdom"},{"iso_3166_1":"US","name":"United States of America"}],"release_date":"2000-09-01","revenue":83557872,"runtime":104,"spoken_languages":[{"english_name":"English","iso_639_1":"en","name":"English"},{"english_name":"Russian","iso_639_1":"ru","name":"Pусский"}],"status":"Released","tagline":"Stealin' stones and breakin' bones.","title":"Snatch","video":false,"vote_average":7.8,"vote_count":6747}
MovieID=108 res={"adult":false,"backdrop_path":"/s9rWmLANsVlSV4XmuC0IUcseGzO.jpg","belongs_to_collection":{"id":131,"name":"Three Colors Collection","poster_path":"/ph9rRSTG4xL8nubxNrK00VUMn5l.jpg","backdrop_path":"/AeHExfHIl70SZCea907KfEoSkfJ.jpg"},"budget":0,"genres":[{"id":18,"name":"Drama"}],"homepage":"","id":108,"imdb_id":"tt0108394","original_language":"fr","original_title":"Trois couleurs : Bleu","overview":"Julie is haunted by her grief after living through a tragic auto wreck that claimed the life of her composer husband and young daughter. Her initial reaction is to withdraw from her relationships, lock herself in her apartment and suppress her pain. But avoiding human interactions on the bustling streets of Paris proves impossible, and she eventually meets up with Olivier, an old friend who harbors a secret love for her, and who could draw her back to reality.","popularity":7.918,"poster_path":"/teDQRqy5STEhSvLERfGERBX25Br.jpg","production_companies":[{"id":591,"logo_path":"/q5I5RDwMEiqoNmfaJgd2LraEOJY.png","name":"France 3 Cinéma","origin_country":"FR"},{"id":1610,"logo_path":null,"name":"CED Productions","origin_country":""}],"production_countries":[{"iso_3166_1":"FR","name":"France"},{"iso_3166_1":"PL","name":"Poland"},{"iso_3166_1":"CH","name":"Switzerland"}],"release_date":"1993-09-08","revenue":0,"runtime":98,"spoken_languages":[{"english_name":"French","iso_639_1":"fr","name":"Français"},{"english_name":"Polish","iso_639_1":"pl","name":"Polski"}],"status":"Released","tagline":"","title":"Three Colors: Blue","video":false,"vote_average":7.7,"vote_count":1093}
MovieID=120 res={"adult":false,"backdrop_path":"/vRQnzOn4HjIMX4LBq9nHhFXbsSu.jpg","belongs_to_collection":{"id":119,"name":"The Lord of the Rings Collection","poster_path":"/nSNle6UJNNuEbglNvXt67m1a1Yn.jpg","backdrop_path":"/bccR2CGTWVVSZAG0yqmy3DIvhTX.jpg"},"budget":93000000,"genres":[{"id":12,"name":"Adventure"},{"id":14,"name":"Fantasy"},{"id":28,"name":"Action"}],"homepage":"http://www.lordoftherings.net/","id":120,"imdb_id":"tt0120737","original_language":"en","original_title":"The Lord of the Rings: The Fellowship of the Ring","overview":"Young hobbit Frodo Baggins, after inheriting a mysterious ring from his uncle Bilbo, must leave his home in order to keep it from falling into the hands of its evil creator. Along the way, a fellowship is formed to protect the ringbearer and make sure that the ring arrives at its final destination: Mt. Doom, the only place where it can be destroyed.","popularity":78.395,"poster_path":"/6oom5QYQ2yQTMJIbnvbkBL9cHo6.jpg","production_companies":[{"id":12,"logo_path":"/iaYpEp3LQmb8AfAtmTvpqd4149c.png","name":"New Line Cinema","origin_country":"US"},{"id":11,"logo_path":"/6FAuASQHybRkZUk08p9PzSs9ezM.png","name":"WingNut Films","origin_country":"NZ"},{"id":5237,"logo_path":null,"name":"The Saul Zaentz Company","origin_country":"US"},{"id":174,"logo_path":"/IuAlhI9eVC9Z8UQWOIDdWRKSEJ.png","name":"Warner Bros. Pictures","origin_country":"US"}],"production_countries":[{"iso_3166_1":"NZ","name":"New Zealand"},{"iso_3166_1":"US","name":"United States of America"}],"release_date":"2001-12-18","revenue":871368364,"runtime":179,"spoken_languages":[{"english_name":"English","iso_639_1":"en","name":"English"}],"status":"Released","tagline":"One ring to rule them all","title":"The Lord of the Rings: The Fellowship of the Ring","video":false,"vote_average":8.4,"vote_count":19453}
MovieID=220 res={"adult":false,"backdrop_path":"/u7i1XtbOubq0xwRy2Zbgmox6g9W.jpg","belongs_to_collection":null,"budget":1,"genres":[{"id":18,"name":"Drama"},{"id":10749,"name":"Romance"}],"homepage":"","id":220,"imdb_id":"tt0048028","original_language":"en","original_title":"East of Eden","overview":"In the Salinas Valley in and around World War I, Cal Trask feels he must compete against overwhelming odds with his brother for the love of their father. Cal is frustrated at every turn, from his reaction to the war, how to get ahead in business and in life, and how to relate to his estranged mother.","popularity":8.989,"poster_path":"/xv1MZVIop0SQqwLUymgE5eb2LFl.jpg","production_companies":[{"id":174,"logo_path":"/IuAlhI9eVC9Z8UQWOIDdWRKSEJ.png","name":"Warner Bros. Pictures","origin_country":"US"}],"production_countries":[{"iso_3166_1":"US","name":"United States of America"}],"release_date":"1955-03-09","revenue":5,"runtime":115,"spoken_languages":[{"english_name":"English","iso_639_1":"en","name":"English"}],"status":"Released","tagline":"The searing classic of paradise lost!","title":"East of Eden","video":false,"vote_average":7.6,"vote_count":496}
MovieID=221 res={"adult":false,"backdrop_path":"/jPXsPciwlPTG756CHkZt2jYpnNM.jpg","belongs_to_collection":null,"budget":1500000,"genres":[{"id":18,"name":"Drama"}],"homepage":"","id":221,"imdb_id":"tt0048545","original_language":"en","original_title":"Rebel Without a Cause","overview":"After moving to a new town, troublemaking teen Jim Stark is supposed to have a clean slate, although being the new kid in town brings its own problems. While searching for some stability, Stark forms a bond with a disturbed classmate, Plato, and falls for local girl Judy. However, Judy is the girlfriend of neighborhood tough, Buzz. When Buzz violently confronts Jim and challenges him to a drag race, the new kid's real troubles begin.","popularity":7.809,"poster_path":"/qNmYLRadv106IhG6T2wSFH3Bc7W.jpg","production_companies":[{"id":174,"logo_path":"/IuAlhI9eVC9Z8UQWOIDdWRKSEJ.png","name":"Warner Bros. Pictures","origin_country":"US"}],"production_countries":[{"iso_3166_1":"US","name":"United States of America"}],"release_date":"1955-10-29","revenue":199963,"runtime":111,"spoken_languages":[{"english_name":"English","iso_639_1":"en","name":"English"}],"status":"Released","tagline":"The bad boy from a good family.","title":"Rebel Without a Cause","video":false,"vote_average":7.6,"vote_count":1126}
MovieID=225 res={"adult":false,"backdrop_path":"/row4sMw5mVzKCWRbyrAQ39zvyLX.jpg","belongs_to_collection":{"id":457305,"name":"Solidarity Trilogy","poster_path":null,"backdrop_path":null},"budget":0,"genres":[{"id":18,"name":"Drama"},{"id":36,"name":"History"}],"homepage":"","id":225,"imdb_id":"tt0082222","original_language":"pl","original_title":"Człowiek z żelaza","overview":"In Warsaw in 1980, the Communist Party sends Winkel, a weak, alcoholic TV hack, to Gdansk to dig up dirt on the shipyard strikers, particularly on Maciek Tomczyk, an articulate worker whose father was killed in the December 1970 protests. Posing as sympathetic, Winkel interviews the people surrounding Tomczyk, including his detained wife, Agnieszka.","popularity":3.552,"poster_path":"/22wNUqKyz2m6wzAt31f26H8Y433.jpg","production_companies":[{"id":12838,"logo_path":null,"name":"Film Polski","origin_country":"PL"},{"id":6804,"logo_path":null,"name":"Zespól Filmowy \"X\"","origin_country":""}],"production_countries":[{"iso_3166_1":"PL","name":"Poland"}],"release_date":"1981-07-27","revenue":0,"runtime":147,"spoken_languages":[{"english_name":"Polish","iso_639_1":"pl","name":"Polski"},{"english_name":"Hungarian","iso_639_1":"hu","name":"Magyar"}],"status":"Released","tagline":"","title":"Man of Iron","video":false,"vote_average":6.9,"vote_count":37}
MovieID=234 exception=java.lang.RuntimeException: Timeout
MovieID=254 exception=java.lang.RuntimeException: Timeout
MovieID=256 exception=java.lang.RuntimeException: Timeout

You can see some requests are completed and some have timed out. We can combine result and send it as response.

Please comment if you need any clarification.