cumulocity

Caused by: java.lang.IllegalStateException: Not within any context! in Cumulocity Java SDK


I want to get the device details for each tenant present in the subscription in a multi threading environment using Executors service to fetch the device details using the Platform Service provided by the Cumulocity Micro-service SDK but getting the Exception :

Error creating bean with name 'scopedTarget.tenantPlatform': Scope 'tenant' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton; nested exception is java.lang.IllegalStateException: Not within any context!!!

So currently i tried by Autowiring both the MicroserviceSubscriptionsService and Platform services provided by cumulocity platform as :

 @Autowired
 private MicroserviceSubscriptionsService subscriptionsService;

 @Autowired
 private Platform platform;

I used Executors service for calling the device report for all the devices ( approx 200k devices for all tenants included).

  subscriptionsService.runForEachTenant( () -> {
     // did something for filtering and pagination 
       ManagedObjectCollectionRepresentation managedObjCol = managedObjectCollection.get(500, new QueryParam(PagingParam.WITH_TOTAL_PAGES, "true"));

    for (int i = 1; i <= managedObjCol.getPageStatistics().getTotalPages(); i++) {
            List<ManagedObjectRepresentation> managedObjList = managedObjectCollection.getPage(managedObjCol, i).getManagedObjects();
      
       ExecutorService es = Executors.newFixedThreadPool(20);
       int threadNo = 1;
       for (int j = 0; j < 20; j++) {
        int finalCurrentThreadNo = threadNo;
        final String tenantId = subscriptionsService.getTenant();
        es.submit(() -> {
             try {
                List<ManagedObjectRepresentation> sublist = getSubList(managedObjList,finalCurrentThreadNo, 20);
                for (ManagedObjectRepresentation mObjRep : sublist) {
                      OperationFilter operPendingFilter = new OperationFilter().byStatus(OperationStatus.PENDING).byDevice(mObjRep.getId().getValue());
            Iterable<OperationRepresentation> devCtr = platform.getDeviceControlApi().getOperationsByFilter(operPendingFilter).get(2000).allPages();
            List<OperationRepresentation> pendeingOperRep = StreamSupport.stream(devCtr.spliterator(), false).collect(Collectors.toList());
                    }
                  } catch (Exception e) {
                      e.printStackTrace();
                   }
              });
             threadNo++;
                  }
   }
}
});

public List<ManagedObjectRepresentation> getSubList(List<ManagedObjectRepresentation> list, int currentThreadNo, int totalThreads) {

        int j = list.size()/totalThreads;

        if(currentThreadNo ==1){
            return  list.subList(0,j);
        }else if (currentThreadNo == totalThreads){
            return list.subList(((currentThreadNo-1)*j), list.size());
        }else{
            return list.subList(((currentThreadNo-1)*j),currentThreadNo*j);
        }
    }

When i am using this platform.getDeviceControlApi().getOperationsByFilter() call to get the pending operations , I am getting this no context error.I am stuck here.Appreciate your help here


Solution

  • Access to any API via the SDK requires a context to be set. You can imagine the context as credentials for a specific tenant. If this context is not present you'll receive exactly this error message.

    Context is tied to the thread your code is executed in. There are a few cases where the context is automatically managed. This includes request handler threads. In any other threads, like the ones that are used when you create an ExecutorService you'll need to manage this context yourself. This means you'll have to hand the context into that thread and enter it.

    The easiest way for your code example is to inject the context service ContextService<MicroserviceCredentials>, get the context from it using the getContext() method and then inside your Runnable run your code in the context using contextService.runWithinContext()