Strange CodecNotFoundException

1,066 views
Skip to first unread message

Thai Ngo

unread,
Feb 28, 2018, 11:07:22 AM2/28/18
to DataStax Java Driver for Apache Cassandra User Mailing List
Hi everyone,

I am facing a strange CodecNotFoundException and looking for your help:

I am using:
- datastax.cassandra 3..4.0
- Java 8_152
- cassandra-unit 3.3.0.2
- google guava 24.0.jre

public class EmployeeTest extends AbstractTest {
   
@Autowired
    private CassandraSessionProvider cassandraSessionProvider;

   
@Test
    public void weirdCodecIssue() throws Exception {
       
String clusterName = "Test Cluster";
       
String contactPoints = "127.0.0.1:9042,127.0.0.2:9042,127.0.0.3:9042";
       
String keyspace = "mykeyspace";
       
try (final Session session = cassandraSessionProvider.getSession(
clusterName, contactPoints, keyspace)) {
            session.execute("USE " + keyspace);

           
// create Employee table
            session.execute(
                   
"CREATE TABLE mykeyspace.my_table ("
                            + "  name TEXT,"
                            + "  password BLOB,"
                            + "  expires_in_days INT,"
                            + "  password_reset_date TIMESTAMP,"
                            + "  PRIMARY KEY (name)"
                            + ");");

           
// populate employee data
            String employeeName = "foo";
           
BoundStatement employeeStatement =
                    session
.prepare("INSERT INTO mykeyspace.my_table (name, password, expires_in_days, password_reset_date) VALUES (?, ?, ?, ?)").bind();
            employeeStatement
.setString("name", employeeName);
            employeeStatement
.setBytes("password", ByteBuffer.wrap("123abc".getBytes("UTF-8")));
            employeeStatement
.setInt("expires_in_days", 30);
            employeeStatement
.setTimestamp("password_reset_date", new Date());
            session
.execute(employeeStatement);

           
// retrieve data
            MappingManager mappingManager = new MappingManager(session);
           
Mapper<EmployeeEntity> employeeEntityMapper = mappingManager.mapper(EmployeeEntity.class);
           
Statement employeeQuery = employeeEntityMapper.getQuery(employeeName);
           
ResultSet resultSet = session.execute(employeeQuery);
           
Row employeeRow = resultSet.one();
           
if (employeeRow == null) {
               
throw new Exception();
           
}

           
byte[] employeePassword = Bytes.getArray(employeeRow.getBytes(1));
           
int expiresInDays = employeeRow.getInt(2);
           
Date passwordResetOn = employeeRow.getTimestamp(3);

           
System.out.println(employeePassword);
           
System.out.println(expiresInDays);
           
System.out.println(passwordResetOn);
       
}
   
}
}

When running the test, I encounter either an exception of:
com.datastax.driver.core.exceptions.CodecNotFoundException: Codec not found for requested operation: [int <-> java.nio.ByteBuffer]

or:
com.datastax.driver.core.exceptions.CodecNotFoundException: Codec not found for requested operation: [timestamp <-> java.nio.ByteBuffer]



Further error from debug log:

22:18:29.132 [Test Cluster-worker-3] DEBUG c.d.driver.core.ControlConnection - [Control connection] Refreshing schema for mykeyspace.my_table (TABLE)
22:18:29.139 [Native-Transport-Requests-3] DEBUG o.apache.cassandra.db.SystemKeyspace - stored prepared statement for logged keyspace 'mykeyspace': 'INSERT INTO mykeyspace.my_table (name, password, expires_in_days, password_reset_on) VALUES (?, ?, ?, ?)'
22:28:37.808 [main] DEBUG com.datastax.driver.mapping.Mapper - Preparing query SELECT name AS col1,password_reset_on AS col2,password AS col3,expires_in_days AS col4 FROM mykeyspace.my_table WHERE name=?;
22:28:37.822 [Native-Transport-Requests-1] DEBUG o.apache.cassandra.db.SystemKeyspace - stored prepared statement for logged keyspace 'mykeyspace': 'SELECT name AS col1,password_reset_date AS col2,password AS col3,expires_in_days AS col4 FROM mykeyspace.my_table WHERE name=?;'
22:28:37.832 [main] DEBUG com.datastax.driver.core.Connection - Connection[/127.0.0.1:9142-4, inFlight=0, closed=true] closing connection
22:28:37.832 [main] DEBUG com.datastax.driver.core.Host.STATES - [/127.0.0.1:9142] Connection[/127.0.0.1:9142-4, inFlight=0, closed=true] closed, remaining = 1
.............................................................................................................
com.datastax.driver.core.exceptions.CodecNotFoundException: Codec not found for requested operation: [timestamp <-> java.nio.ByteBuffer]

at com.datastax.driver.core.CodecRegistry.notFound(CodecRegistry.java:741)
at com.datastax.driver.core.CodecRegistry.createCodec(CodecRegistry.java:588)
at com.datastax.driver.core.CodecRegistry.access$500(CodecRegistry.java:137)
at com.datastax.driver.core.CodecRegistry$TypeCodecCacheLoader.load(CodecRegistry.java:246)
at com.datastax.driver.core.CodecRegistry$TypeCodecCacheLoader.load(CodecRegistry.java:232)
at com.google.common.cache.LocalCache$LoadingValueReference.loadFuture(LocalCache.java:3524)
at com.google.common.cache.LocalCache$Segment.loadSync(LocalCache.java:2250)
at com.google.common.cache.LocalCache$Segment.lockedGetOrLoad(LocalCache.java:2133)
at com.google.common.cache.LocalCache$Segment.get(LocalCache.java:2046)
at com.google.common.cache.LocalCache.get(LocalCache.java:3963)
at com.google.common.cache.LocalCache.getOrLoad(LocalCache.java:3967)
at com.google.common.cache.LocalCache$LocalLoadingCache.get(LocalCache.java:4952)
at com.datastax.driver.core.CodecRegistry.lookupCodec(CodecRegistry.java:522)
at com.datastax.driver.core.CodecRegistry.codecFor(CodecRegistry.java:485)
at com.datastax.driver.core.CodecRegistry.codecFor(CodecRegistry.java:467)
at com.datastax.driver.core.AbstractGettableByIndexData.codecFor(AbstractGettableByIndexData.java:69)
at com.datastax.driver.core.AbstractGettableByIndexData.getBytes(AbstractGettableByIndexData.java:233)
at com.datastax.driver.core.AbstractGettableData.getBytes(AbstractGettableData.java:26)
at com.abc.provisioner.sample.EmployeeTest.weirdCodecIssue(EmployeeTest.java:61)

The EmployeeTest.java:61 is actually referred to this line:
final byte[] employeePassword = Bytes.getArray(employeeRow.getBytes(1));

any thoughts?

Thanks,
Thai

Andy Tolbert

unread,
Feb 28, 2018, 11:19:29 AM2/28/18
to DataStax Java Driver for Apache Cassandra User Mailing List
Hi Thai,

It looks like you are using the Mapper to create a query for you and then executing that and expecting the columns in the resulting row to be in the same order as how you specified the table.

There is no documented positional ordering from mapper queries, so you should retrieve the values by column names, i.e.: employeeRow.getBytes("password").

Alternatively, since you are already using the mapper, why not just use it to map your results to the EmployeeEntity pojo?  I.e.:

EmployeeEntity employee = employeeEntityMapper.get(employeeName);
byte[] employeePassword = Bytes.getArray(employee.getPassword());

Thanks,
Andy

--
You received this message because you are subscribed to the Google Groups "DataStax Java Driver for Apache Cassandra User Mailing List" group.
To unsubscribe from this group and stop receiving emails from it, send an email to java-driver-user+unsubscribe@lists.datastax.com.

Thai Ngo

unread,
Mar 1, 2018, 12:05:39 AM3/1/18
to DataStax Java Driver for Apache Cassandra User Mailing List
Hi Andy,

Thanks for your help and appreciate your time. However, still facing some other issue as below:

1. Retrieve the values by column names:

byte[] employeePassword = Bytes.getArray(employeeRow.getBytes("password"));
int expiresInDays = employeeRow.getInt("expires_in_days");
Date passwordResetOn = employeeRow.getTimestamp("password_reset_on");

When running the test, get this error:
java.lang.IllegalArgumentException: password is not a column defined in this metadata

at com.datastax.driver.core.ColumnDefinitions.getAllIdx(ColumnDefinitions.java:274)
at com.datastax.driver.core.ColumnDefinitions.getFirstIdx(ColumnDefinitions.java:280)
at com.datastax.driver.core.ArrayBackedRow.getIndexOf(ArrayBackedRow.java:81)
at com.datastax.driver.core.AbstractGettableData.getBytes(AbstractGettableData.java:151)
at com.abc.provisioner.sample.EmployeeTest.weirdCodecIssue(EmployeeTest.java:66)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)

Given that I can see the table with the columns and the query are created.

2. Use mapper to map the results to the EmployeeEntity pojo:

EmployeeEntity employee = employeeEntityMapper.get(employeeName);
byte[] employeePassword = Bytes.getArray(ByteBuffer.wrap(employee.getPassword()));
int expiresInDays = employee.getExpiresInDays();
Date passwordResetOn = employee.getPasswordResetOn();


When running the test, encounter this:
com.datastax.driver.core.exceptions.CodecNotFoundException: Codec not found for requested operation: [blob <-> [B]

at com.datastax.driver.core.exceptions.CodecNotFoundException.copy(CodecNotFoundException.java:56)
at com.datastax.driver.core.exceptions.CodecNotFoundException.copy(CodecNotFoundException.java:25)
at com.datastax.driver.mapping.DriverThrowables.propagateCause(DriverThrowables.java:41)
at com.datastax.driver.mapping.Mapper.get(Mapper.java:453)
at com.abc.provisioner.sample.EmployeeTest.weirdCodecIssue(EmployeeTest.java:58)

The EmployeeTest.java:58 is referring to:
EmployeeEntity employee = employeeEntityMapper.get(employeeName);


your thoughts?

Thanks,
Thai
To unsubscribe from this group and stop receiving emails from it, send an email to java-driver-us...@lists.datastax.com.

Jacques-Henri Berthemet

unread,
Mar 1, 2018, 3:43:27 AM3/1/18
to java-dri...@lists.datastax.com

Hi,

 

I think the problem is that you cant assume the position of a column in the response if you didnt make the select statement yourself. Even if you did, its always better to access columns by name, just in case. The fix would be:

 

            byte[] employeePassword = Bytes.getArray(employeeRow.getBytes("password"));
           
int expiresInDays = employeeRow.getInt("expires_in_days");


           
Date passwordResetOn = employeeRow.getTimestamp("password_reset_date");

 

 

Regards,

--

Jacques-Henri Berthemet

--

Thai Ngo

unread,
Mar 1, 2018, 4:19:55 AM3/1/18
to java-dri...@lists.datastax.com
Hi Jacques-Henri,

Thanks for your help. I have done that way but still facing some other issue as per stated in the previous post.

Thanks,
Thai

To unsubscribe from this group and stop receiving emails from it, send an email to java-driver-user+unsubscribe@lists.datastax.com.

--
You received this message because you are subscribed to the Google Groups "DataStax Java Driver for Apache Cassandra User Mailing List" group.
To unsubscribe from this group and stop receiving emails from it, send an email to java-driver-user+unsubscribe@lists.datastax.com.

Jacques-Henri Berthemet

unread,
Mar 1, 2018, 4:27:48 AM3/1/18
to java-dri...@lists.datastax.com

I see in the execution log that columns have been renamed by the mapper, it looks like youre not using the mapper correctly, if you dont want to use it you should make the select statement yourself:

SELECT * FROM mykeyspace.my_table WHERE name=?;

 

Or only use the mapper to access the data so that it takes care of column names, according to the below article:

http://techblog.poppulo.com/cassandra-object-mapping/

 

It should be something like:

            MappingManager mappingManager = new MappingManager(session);
           
Mapper<EmployeeEntity> employeeEntityMapper = mappingManager.mapper(EmployeeEntity.class);


           
EmployeeEntity employee = employeeEntityMapper.get(employeeName);

 

Regards,

--

Jacques-Henri Berthemet

To unsubscribe from this group and stop receiving emails from it, send an email to java-driver-us...@lists.datastax.com.

--
You received this message because you are subscribed to the Google Groups "DataStax Java Driver for Apache Cassandra User Mailing List" group.

To unsubscribe from this group and stop receiving emails from it, send an email to java-driver-us...@lists.datastax.com.

 

--
You received this message because you are subscribed to the Google Groups "DataStax Java Driver for Apache Cassandra User Mailing List" group.

To unsubscribe from this group and stop receiving emails from it, send an email to java-driver-us...@lists.datastax.com.

Andy Tolbert

unread,
Mar 1, 2018, 10:08:02 AM3/1/18
to DataStax Java Driver for Apache Cassandra User Mailing List
Hi Thai,

Could you share your EmployeeEntity code and confirm that your table schema is the same as your first message?

Thanks,
Andy

To unsubscribe from this group and stop receiving emails from it, send an email to java-driver-user+unsubscribe@lists.datastax.com.

Thai Ngo

unread,
Mar 1, 2018, 11:08:26 AM3/1/18
to java-dri...@lists.datastax.com
Hi Andy,


Also, to isolate the issue for better understanding of it, I am going to make a test project for this and will keep you posted if any finding.

Thanks,
Thai

Andy Tolbert

unread,
Mar 1, 2018, 11:12:56 AM3/1/18
to DataStax Java Driver for Apache Cassandra User Mailing List
Hi Thai,

Thanks for sharing your code.  I believe what is causing 'Codec not found for requested operation: [blob <-> [B]' is that the password field is a byte[].  It should be a ByteBuffer to map to the drivers designed type for the cql blob column (cql <-> java type mappings can be found here).

Thanks,
Andy

Thai Ngo

unread,
Mar 2, 2018, 6:14:29 AM3/2/18
to java-dri...@lists.datastax.com
Hi Andy,

Yes, it is because the password field is a byte[]. I am considering a change for it but it is a big change for me.

Alternatively, I made a quick look into the custom codecs available out there but it is likely that I have to write a custom codec for this. Appreciate any suggestion or sharing if similar thing has been around.

Thanks,
Thai

Kevin Gallardo

unread,
Mar 6, 2018, 10:29:58 AM3/6/18
to java-dri...@lists.datastax.com
Hi, 

As Andy mentioned, the driver requires the Java object class to match a specific class for each data types, the types are defined here: https://docs.datastax.com/en/developer/java-driver/3.4/manual/#cql-to-java-type-mapping. So for a Blob, the mapped object should be of class ByteBuffer.

However, there is a way to workaround this if you are using the mapper properly. In newer driver versions, the mapper allows to define annotations on getters and setters rather than fields, which would allow you to do the following:
  • keep your existing logic for your existing POJO with the byte[] object
  • create a new setter and getter for this field, annotate them with @Column(name = "nameOfBlobColumnInTable") and their signature would both use ByteBuffer instead of byte[]
  • use the mapper as defined with mapper.get() and so on.
  • no need to modify the codecRegistry
Here's an example:


public class MapperTestSetter {

    @Test
    public void test() {
        Cluster cluster = Cluster.builder().addContactPoint("127.0.0.1").build();

        try {
            Session session = cluster.connect();
            session.execute("CREATE TABLE mappertest.withblob (pkey INT PRIMARY KEY, blobcol BLOB)");
            session.execute("INSERT INTO mappertest.withblob (pkey, blobcol) VALUES (1, textAsBlob('snernern'))");

            MappingManager manager = new MappingManager(session);
            Mapper<WithBlob> mapperWithBlob = manager.mapper(WithBlob.class);

            WithBlob blob = mapperWithBlob.get(1);
            System.out.println("new String(blob.getMyBlobArray()) = " + new String(blob.getMyBlobArray().array()));

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            cluster.close();
        }
    }

    @Table(keyspace = "mappertest", name = "withblob")
    public static class WithBlob {
        private byte[] myBlobArray;

        @PartitionKey
        private int pkey;

        @Column(name = "blobcol")
        public ByteBuffer getMyBlobArray() {
            return ByteBuffer.wrap(myBlobArray);
        }

        @Column(name = "blobcol")
        public void setMyBlobArray(ByteBuffer bb) {
            this.myBlobArray = bb.array();
        }

        public int getPkey() {
            return pkey;
        }

        public void setPkey(int pkey) {
            this.pkey = pkey;
        }
    }
}

I just looked at the code you linked and basically I would suggest adding a getter and setter for your EmployeEntity class that uses ByteBuffers and annotate them with the mapping annotations as in the example above.

Hope that helps.
Thanks.
Kévin Gallardo.
Software Developer in Drivers and Tools Team,
DataStax.

Thai Ngo

unread,
Mar 6, 2018, 10:54:20 AM3/6/18
to java-dri...@lists.datastax.com
Hi Kevin,

Really appreciate your kindly support and your time. I made the change successfully and the issue is fixed. Sorry for not updating you on this as I assumed that the root-cause for the original issue has been found. The workaround is very similar to the one you are suggesting. In fact, I did not prefer another custom codec.

Cheers,
Thai
Reply all
Reply to author
Forward
0 new messages