4

I am implementing an application which is to support multiple tenants (users) within it. Each tenant makes use of a 3rd party service (API), although with different authentication credentials. The approach I have in mind for this is that of initializing a custom @Scope for each tenant and simply injecting the appropriate authentication parameters according to the underlying scope.

My tenant implementation is currently very simplistic and bare-bones:

public class TenantContext {
    private static final ThreadLocal<String> CONTEXT = new ThreadLocal<>();

    public static void setTenant(Tenant tenant) {
        CONTEXT.set(tenant.getName());
    }

    public static void setTenantId(String tenantId) {
        CONTEXT.set(tenantId);
    }

    public static String getTenantId() {
        return CONTEXT.get();
    }

    public static void clear() {
        CONTEXT.remove();
    }
}

Whilst my custom TenantScope class looks like this:

public class TenantScope implements Scope {
    private Map<String, Object> scopedObjects = Collections.synchronizedMap(new HashMap<>());
    private Map<String, Runnable> destructionCallbacks = Collections.synchronizedMap(new HashMap<>());

    /* Standard get(), remove(), registerDestroyCallback(), etc. */

    @Override
    public String getConversationId() {
        return TenantContext.getTenantId();
    }
}

In my configuration, I simply declare a bean which handles communication with the external service:

@Scope(scopeName = "tenant")
public @Bean some_API someAPI() {
     /* Load tenant credentials.. */
     return new some_API(credentials);
}

However, the infrastructure described above does not seem to work. When loading the Bean declared above within different TenantContexts, the underlying object does not seem to change (e.g. the bean does not "sense" that the scope has changed).

        TenantContext.setTenantId("tenant-1");
        logger.info(some_API.toString());
        TenantContext.setTenantId("tenant-2");
        logger.info(some_API.toString());

returns the same object as if it were a singleton.

What would be the possible causes for this issue? Am I declaring the custom scope incorrectly? Any help would be greatly appreciated!

Kanghu
  • 561
  • 1
  • 10
  • 23

1 Answers1

1

I think what you are trying to achieve is create a custom scope to handle your scenario. The correct and recommended way of doing this is by extending CustomScopeConfigurer as mentioned in docs of Scope annotation as well.

In order to use custom scopes you must register the scope with Spring - something like below

@Configuration
public class ScopeConfig {

    @Bean
    public CustomScopeConfigurer customScopeConfigurer() {
        CustomScopeConfigurer configurer = new CustomScopeConfigurer();

        Map<String,Object> scopes = new HashMap<String,Object>();
        scope.put("scope-name",new CustomScope());

        configurer.setScopes(scopes);
        return configurer;
    }
}
Shailendra
  • 8,874
  • 2
  • 28
  • 37