Spring Couchbase - DynamicProxyable not working as expected

Hi.
I am using DynamicProxyable interface provided by the spring data couchbase to dynamically select scope for different tenant. I am facing some issues when using DynamicProxyable interface.

I am using:

  • Spring boot 3.4.1
  • Java 21
  • Spring Data Couchbase (non-reactive)
  • multimodule setup

Problems

  1. Not able to use Page<T> findAll(Pageable pageable)
    Seems while executing count query it is executing in _default scope. The plain findAll() works fine.

  2. using variable in repository with custom method, throws error.

public interface AttributeRepository
       extends CouchbaseRepository<Attribute, String>, DynamicProxyable<AttributeRepository> {

    @Query("""
            #{#n1ql.selectEntity}
            WHERE attribute_group_id = $groupCode
            OFFSET $offset
            LIMIT $limit""")
    List<Attribute> getAllAttributes(String groupCode, Integer offset, Integer limit);
}
AttributeDaoImpl.class 

    @Override
    public List<Attribute> getAllContextTenantAttributes(String attributeGroupCode, int offset, int limit) {
        return attributeRepository
                .withScope(getThreadContextTenantScope())
                .getAllAttributes(attributeGroupCode, offset, limit);
    }
Stacktrace for the problem 2
java.lang.NullPointerException: null
	at java.base/java.lang.Class.isAssignableFrom(Native Method) ~[na:na]
	at org.springframework.data.couchbase.repository.support.FindMethod.internalFind(FindMethod.java:77) ~[spring-data-couchbase-5.4.1.jar:5.4.1]
	at org.springframework.data.couchbase.repository.support.FindMethod.findMethod(FindMethod.java:33) ~[spring-data-couchbase-5.4.1.jar:5.4.1]
	at org.springframework.data.couchbase.repository.support.DynamicInvocationHandler.invoke(DynamicInvocationHandler.java:119) ~[spring-data-couchbase-5.4.1.jar:5.4.1]
	at jdk.proxy3/jdk.proxy3.$Proxy148.getAllAttributes(Unknown Source) ~[na:na]
	
	at o.c.n.p.AttributeDaoImpl.getAllContextTenantAttributes(AttributeDaoImpl.java:63) ~[data.jar:na]
	
	at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) ~[na:na]
	at java.base/java.lang.reflect.Method.invoke(Method.java:580) ~[na:na]
	at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:359) ~[spring-aop-6.2.1.jar:6.2.1]
	at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:196) ~[spring-aop-6.2.1.jar:6.2.1]
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-6.2.1.jar:6.2.1]
	at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:138) ~[spring-tx-6.2.1.jar:6.2.1]
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) ~[spring-aop-6.2.1.jar:6.2.1]
	at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:727) ~[spring-aop-6.2.1.jar:6.2.1]
	
	at o.c.n.p.data.dao.impl.AttributeDaoImpl$$SpringCGLIB$$0.getAllContextTenantAttributes(<generated>) ~[data.jar:na]
	at o.c.n.p.core.service.impl.AttributeServiceImpl.getAllAttributes(AttributeServiceImpl.java:57) ~[core.jar:na]
	at o.c.n.p.api.handler.impl.AttributeHandlerImpl.getAllAttributes(AttributeHandlerImpl.java:61) ~[main/:na]
	at o.c.n.p.api.controller.AttributeController.getAttributes(AttributeController.java:139) ~[main/:na]

Surprisingly, it stops throwing error, if I remove the attributeGroupCode from repository method call or change it to string literal instead of passing value in variable. Like:

AttributeDaoImpl.class 

    @Override
    public List<Attribute> getAllContextTenantAttributes(String attributeGroupCode, int offset, int limit) {
        return attributeRepository
                .withScope(getThreadContextTenantScope())
                .getAllAttributes("attributeGroupCode", offset, limit);
    }

I could not generate the second problem. Any hint would be a great help.

Thanks. I’ll investigate after the holidays.

Hi
This is an update for the Second Problem.

I was going through the implementation in the library. It seems the problem is occurring because the null values are currently not supported for the repository method. The code piece that finds that actual method for invocation is missing null check for the parameter types.

In this case when passing null in groupCode for getAllAttributes method of repository, the internalFind method in org.springframework.data.couchbase.repository.support.FindMethod.java is throwing NPE. This is because the first arg value (0th index value) is null which results in parameterTypes[0] value to be null.

Below I have added the method causing the error.

org.springframework.data.couchbase.repository.support.FindMethod.java

private static Method internalFind(Method[] toTest, String name, Class[] parameterTypes)
            throws NoSuchMethodException {
    ... 
    // This loop contains the issue. 
    for (j = 0; j < l; j++) {
         if(params[j] == int.class && parameterTypes[j] == Integer.class )
             continue;
         if(params[j] == long.class && parameterTypes[j] == Long.class )
             continue;
         if(params[j] == double.class && parameterTypes[j] == Double.class )
             continue;
         if(params[j] == boolean.class && parameterTypes[j] == Boolean.class )
             continue;
         // Missing null check for the `parameterTypes` values. 
         // In my case the 0th index value was passed null, 
         // so no class value is set for the first parameter of the method
         if (!params[j].isAssignableFrom(parameterTypes[j]))
              break;
         }
    ...
}

I hope this helps is fixing the issue.

1 Like

Hi.
Is there any update on the issue ?

I opened a ticket

The next release date is February 14, I should be able to get to it by then.

1 Like

Hello Michael
It is been sometime, is there any update on the issue ?

Fixed. Will be in the release scheduled for April 22. https://spring.io/projects#release-calendar

1 Like

Hello MIchael,

Is there any way, I can use this feature now until next release ? Like any snapshot version.