ClassCastException on SELECT NEW ... after UPDATE over Java RMI

#1

Hi,

we are using ObjectDB 2.5.4_04 on Linux. Our application, which uses ObjectDb as a DB, exposes a Java RMI interface which allows remote RMI clients to run JPQL queries on the DB.

The application internally runs SELECT NEW ... queries. For example:

SELECT NEW com.arunta.base.db.FileNameDTO(r.id, r.fileName) FROM RecordingMetaData AS r WHERE ...

A remote RMI client runs SELECT (but not SELECT NEW ...) and UPDATE JPQL queries on the DB via the RMI interface. What we noticed is that once we UPDATE a record from the client (via the RMI interface), all SELECT NEW ... queries, which the applications runs internally, start to fail with ClassCastException. The same exception is thrown by your DB Explorer tool when trying the same, or similar, SELECT NEW ... query:

Exception in thread "AWT-EventQueue-0" [ObjectDB 2.5.4_04] javax.persistence.PersistenceException              
Failed to build result of type 'NEW com.arunta.base.db.FileNameDTO(r.id, r.fileName)' (error 783)              
        at java.awt.AWTEventMulticaster.mouseMoved(AWTEventMulticaster.java:330)                               
        at java.awt.Component.processMouseMotionEvent(Component.java:6550)                                     
        at javax.swing.JComponent.processMouseMotionEvent(JComponent.java:3339)                                
        at java.awt.Component.processEvent(Component.java:6274)                                                
        at java.awt.Container.processEvent(Container.java:2229)                                                
        at java.awt.Component.dispatchEventImpl(Component.java:4861)                                           
        at java.awt.Container.dispatchEventImpl(Container.java:2287)                                           
        at java.awt.Component.dispatchEvent(Component.java:4687)                                               
        at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4832)                              
        at java.awt.LightweightDispatcher.processMouseEvent(Container.java:4505)                               
        at java.awt.LightweightDispatcher.dispatchEvent(Container.java:4422)                                   
        at java.awt.Container.dispatchEventImpl(Container.java:2273)                                           
        at java.awt.Window.dispatchEventImpl(Window.java:2719)                                                 
        at java.awt.Component.dispatchEvent(Component.java:4687)                                               
        at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:723)                                          
        at java.awt.EventQueue.access$200(EventQueue.java:103)                                                 
        at java.awt.EventQueue$3.run(EventQueue.java:682)                                                      
        at java.awt.EventQueue$3.run(EventQueue.java:680)                                                      
        at java.security.AccessController.doPrivileged(Native Method)                                          
        at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:76)                  
        at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:87)                  
        at java.awt.EventQueue$4.run(EventQueue.java:696)                                                      
        at java.awt.EventQueue$4.run(EventQueue.java:694)                                                      
        at java.security.AccessController.doPrivileged(Native Method)                                          
        at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:76)                  
        at java.awt.EventQueue.dispatchEvent(EventQueue.java:693)                                              
        at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:242)                   
        at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:161)                      
        at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:150)                   
        at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:146)                               
        at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:138)                               
        at java.awt.EventDispatchThread.run(EventDispatchThread.java:91)                                       
Caused by: com.objectdb.o.UserException: Failed to build result of type 'NEW com.arunta.base.db.FileNameDTO(r.id, r.fileName)'                                                                                                 
        at com.objectdb.o.MSG.d(MSG.java:74)                                                                   
        at com.objectdb.o.ORB.l(ORB.java:230)                                                                  
        at com.objectdb.o.RSL.r(RSL.java:312)                                                                  
        at com.objectdb.o.RSL.get(RSL.java:169)                                                                
        ... 44 more                                                                                            
Caused by: java.lang.IllegalArgumentException: java.lang.ClassCastException@13a7f4bd                           
        at sun.reflect.GeneratedConstructorAccessor2.newInstance(Unknown Source)                               
        at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
        at java.lang.reflect.Constructor.newInstance(Constructor.java:525)                                     
        at com.objectdb.o.ORB.l(ORB.java:195)                                                                  
        ... 46 more

For your information the FileNameDTO.java is (simple bean):

public class FileNameDTO {

    private Long id;
    private String fileName;

    public FileNameDTO(Long id, String fileName) {
        this.id = id;
        this.fileName = fileName;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getFileName() {
        return fileName;
    }

    public void setFileName(String fileName) {
        this.fileName = fileName;
    }
   
}

 

The attached DB file has 3 records which were updateted by a remote client (over RMI). They have IDs 9766, 9890 and 9897. These records cause all SELECT NEW ... type queries, which run internally, to fail with the ClassCastException.

Other SELECT queries (i.e. non SELECT NEW ... quereis) work fine inside the application and from remote clients via RMI after the update. For example SELECT COUNT(*) ..., or SELECT r FROM ... all work fine.

 

DB Doctor does not fix the problem.

We need guidance on how to debug and fix the problem.

Thank you

Emil

 

 

 

 

 

 

 

 

#2

The value of the id field in entity object RecordingMetaData#9897 is Integer rather than Long.

Therefore, if the constructor of FileNameDTO expects a Long value there is no match.

As a workaround you can change the query to:

SELECT NEW com.arunta.base.db.FileNameDTO((long)r.id, r.fileName)
FROM RecordingMetaData AS r ...

so a casting should solve the mismatch.

According to your report, it may be possible to set a value of a different type in an UPDATE query. Check your UPDATE queries and see if you can specify new values as long explicitly rather than as int to avoid this mismatch.

ObjectDB Support
#3

This is a bit confusing. We do not understand how the id field in RecordingMetaData can be Integer when it is defined as Long:

public class RecordingMetaData {

    private Long id;
    private Long version;

Just in case we introduced problems in the original attached DB, we are attaching here two DB files which were produced under controlled condition as follows:

1. We started the application without a DB. It creates a new DB file and then adds 5 records in it. The DB file is in trvr-good.zip. All works well with this DB file. We can run SELECT NEW ... queries both in the application and with DB Explorer.

2. Then we run the following update query from the remote client over RMI (extract from the application log including information from log4j):

INFO [RMI TCP Connection(7)-192.168.120.56] (TRDatabaseServiceImpl.java:286) - Remote user emil executing DB update:                                                                                                          
UPDATE RecordingMetaData AS r SET r.notes='#%# Emil~FE~FF~00~27s note' WHERE r.id IN (1)

The DB file after the query is in trvr-corrupt.zip. As you can see the update completes Ok. We see the problem with SELECT NEW ... queries after this update both in the application and with DB Explorer. As a matter of fact any update causes the problem. The above is just an example.

 

Each of the DB files shows the id field for all 5 records as Long. We do not update the id field in any way. Hence our confusion on how it can be Integer.

Thank you for your assistance.

 

 

#4

If you run the following query on both databases you can see the difference:

select r.id from RecordingMetaData r

For object #1 the id is Integer rather than Long in the second database.

You can fix the database with the following query that changes it back to Long.

UPDATE RecordingMetaData AS r SET r.notes=r.notes WHERE r.id IN (1L)

Obviously this is a bug. ObjectDB supports dynamic types (in order to support dynamic type languages in the future), but this is currently not in use, and anyway, the value from the WHERE clause should not replace the existing value.

We will fix the bug soon. But meanwhile as a workaround, make sure that the type in the IN expression matches the type of the field, and it should work.

ObjectDB Support
#5

Thank you. We look forward to the release that contains a fix for this problem.

Emil

#6

Build 2.5.5_06 includes a fix for this issue. Thank you for your report.

ObjectDB Support
#7

I can confirm that the issue does not exist in 2.5.5_14. Thank you.

#8

This issue has re-surfaced in 2.6.1_05. See attached screen capture.

The type of the IDs is changed to Integer from Long during queries.

Reards

Emil

#9

It could be another cause of the same issue.

We have a simple test code from your previous report, but we do not have the test database:

public final class T1401 {

    public static void main(String[] args)  {

        EntityManagerFactory emf = Persistence.createEntityManagerFactory(
                "C:\\trvr-bad.odb");
        EntityManager em = emf.createEntityManager();
       
        em.getMetamodel().embeddable(FileNameDTO.class);

        Query query = em.createQuery(
            "SELECT NEW com.arunta.base.db.FileNameDTO(r.id, r.fileName) " +
            "FROM RecordingMetaData AS r");
        List resultList = query.getResultList();
        System.out.println(resultList.size());
       
        em.close();
        emf.close();
    }
}

Could you please provide an up to date database and query that can demonstrate this issue?

 

ObjectDB Support

Reply