Bug #6747
TypeMarshaller.convertTypeFromType can't convert the v2.Log object to v1.Log object
100%
Description
In the getLogRecords method, we use TypeMarshaller.convertTypeFromType to convert a v2.Log object to v1.Log object. However I got an error:
metacat 20150107-12:52:58: [ERROR]: class java.lang.ClassCastException: org.dataone.service.types.v2.LogEntry cannot be cast to org.dataone.service.types.v1.LogEntry [edu.ucsb.nceas.metacat.restservice.D1ResourceHandler]
java.lang.ClassCastException: org.dataone.service.types.v2.LogEntry cannot be cast to org.dataone.service.types.v1.LogEntry
at org.dataone.service.JiBX_bindingMungeAdapter.JiBX_binding_marshal_1_31()
at org.dataone.service.JiBX_bindingMungeAdapter.JiBX_binding_marshal_1_32()
at org.dataone.service.JiBX_bindingLog_access.marshal()
at org.dataone.service.types.v1.Log.marshal(Log.java)
at org.jibx.runtime.impl.MarshallingContext.marshalRoot(MarshallingContext.java:1021)
at org.jibx.runtime.impl.MarshallingContext.marshalDocument(MarshallingContext.java:1041)
at org.dataone.service.util.TypeMarshaller.marshalTypeToOutputStream(TypeMarshaller.java:99)
at org.dataone.service.util.TypeMarshaller.marshalTypeToOutputStream(TypeMarshaller.java:78)
at edu.ucsb.nceas.metacat.restservice.v1.MNResourceHandler.getLog(MNResourceHandler.java:1056)
at edu.ucsb.nceas.metacat.restservice.v1.MNResourceHandler.handle(MNResourceHandler.java:279)
I looked the Log classes and found that the v2.Log contains a list of v2.LogEntry objects and v1.log contains a list of v1.LogEntry objects. So I changed the code to convert the v2.LogEntry object to v1.LogEntry object rather than convert the v2.Log to v1.Log directly.
Here is the code:
for(int i=0; i<log.getCount(); i++) {
org.dataone.service.types.v2.LogEntry v2Entry = log.getLogEntry(i);
LogEntry v1Entry = TypeMarshaller.convertTypeFromType(v2Entry, LogEntry.class);
retLog.addLogEntry(v1Entry);
}
However it still doesn't work:
Caused by: java.lang.IllegalArgumentException: Cannot invoke org.dataone.service.types.v1.LogEntry.setEvent - argument type mismatch
at org.apache.commons.beanutils.PropertyUtilsBean.invokeMethod(PropertyUtilsBean.java:1778)
at org.apache.commons.beanutils.PropertyUtilsBean.setSimpleProperty(PropertyUtilsBean.java:1759)
at org.apache.commons.beanutils.BeanUtilsBean.copyProperty(BeanUtilsBean.java:447)
at org.apache.commons.beanutils.BeanUtilsBean.copyProperties(BeanUtilsBean.java:261)
at org.apache.commons.beanutils.BeanUtils.copyProperties(BeanUtils.java:114)
at org.dataone.service.util.TypeMarshaller.convertTypeFromType(TypeMarshaller.java:219)
at edu.ucsb.nceas.metacat.dataone.v1.MNodeService.getLogRecords(MNodeService.java:454)
... 28 more
In v2.LogEntry, the parameter of the setEvent is the Event class; in v1.LogEntry class, the parameter of the setEvent is the String class. So they don't match.
Subtasks
Related issues
Associated revisions
fixes #6747. Refactored convertTypeFromType to not produce invalid v1.LogEntries or v1.Log due to incompatible event values.
fixes #6747. Refactored convertTypeFromType to not produce invalid v1.LogEntries or v1.Log due to incompatible event values.
History
#1 Updated by Jing Tao almost 10 years ago
If we add the new method setEvent with the String parameter, will the issue be fixed?
#2 Updated by Rob Nahf almost 10 years ago
- Tracker changed from Task to Bug
- translation missing: en.field_remaining_hours set to 0.0
- Start date deleted (
2015-01-07) - Assignee set to Rob Nahf
- Category set to d1_common_java
The convertTypeFromType method doesn't seem to universally work. (probably only works for cases where one type is derived from another and has extra fields, but not sure).
Propose to make a Factory / Builder class in d1_common_java for specific cases.
#3 Updated by Rob Nahf almost 10 years ago
- Target version set to CCI-2.0.0
- Start date set to 2014-12-01
- Due date set to 2015-01-07
#4 Updated by Ben Leinfelder almost 10 years ago
- Due date changed from 2015-01-07 to 2015-01-08
Please check our the BeanUtils Converter to see if this can be leveraged before developing a roll-your-own solution.
http://commons.apache.org/proper/commons-beanutils/apidocs/org/apache/commons/beanutils/Converter.html
#5 Updated by Jing Tao almost 10 years ago
Something need to be clarified : the V2 LogEntry class has the setEvent(String event) method; the v1 LogEntry class has the setEvent(Event event) method.
I tried a wrapper solution - create a setEvent(String event) method in V1LogEntryWrapper class:
public static class V1LogEntryWrapper extends org.dataone.service.types.v1.LogEntry{
public void setEvent(String event) { if(event != null) { super.setEvent(Event.convert(event)); } } }
Try to convert the V2 LogEntry object to the V1LogEntryWrapper object first, then cast the V1LogEntryWrapper to the v1 LogEntry:
public org.dataone.service.types.v1.LogEntry convert(LogEntry logEntryV2) throws InstantiationException, IllegalAccessException, InvocationTargetException, JiBXException, IOException {
org.dataone.service.types.v1.LogEntry logEntryV1 = null;;
if(logEntryV2 != null) {
//convert the v2 LogEntry object to the V1LogEntryWrapper object V1LogEntryWrapper wrapper = TypeMarshaller.convertTypeFromType(logEntryV2, V1LogEntryWrapper.class); //convert the V2LogEntryWrapper object to the v1 LogEntry object logEntryV1 = (org.dataone.service.types.v1.LogEntry) wrapper; } return logEntryV1;}
However it still doesn't work:
Cannot invoke org.dataone.service.types.v1.LogEntry.setEvent - argument type mismatch
I am going to use the getter and setter methods to convert it.
#6 Updated by Jing Tao almost 10 years ago
- Status changed from New to In Progress
Using the getter and setter works.
#7 Updated by Rob Nahf almost 10 years ago
- Project changed from Infrastructure to Java Client
- Category deleted (
d1_common_java) - Target version changed from CCI-2.0.0 to CLJ-2.0.0
#8 Updated by Rob Nahf over 9 years ago
Looking into the method, the errors have to do with BeanUtils.copyProperties, which assumes that setters with the same name correspond to the same type. We should still be able to use other BeanUtils methods (describe, in combination with copyProperty) and some targeted introspection to avoid type-mismatch exceptions.
Because the deep-copy preparatory step is the step related to Marshalling, this method should be moved to a dedicated builder or factory classes.
A single method for all conversions (as exists now) is seemingly more future-proof with regards to the API, but implementation might be easier with dedicated methods that know the idiosyncrasies of each conversion, such as:
public static convertSystemMetadata(Object original, Class targetClass);
public static convertLog(Object original, Class targetClass);
public static convertNodeList(Object original, Class targetClass);
etc.
Although it's tempting at this point to have version-to-version specific converter methods, it would lead to a multiplication of methods upon v3 and v4, needing v!/2 methods for each evolving type.
The shortcomings of BeanUtils.decribe, is that it cannot know the class of the setter's parameter before it is set.
#9 Updated by Rob Nahf over 9 years ago
- Status changed from In Progress to Testing
- % Done changed from 30 to 50
- Category set to d1_common_java
rewrote convertTypeFromType to handle all v2 -v1 conversions. The initial solution left incompatible LogEntries (where the v2.Event string doesn't map to a v1.Event Enum element) in the Log. However, we determined that this was not schema valid, so when serialized, they could not be deserialized.
The solution here is to omit the entire incompatible LogEntry. Added special handling to remove incompatibilities before returning the v1.Log or v1.LogEntry.
#10 Updated by Rob Nahf over 9 years ago
- % Done changed from 50 to 100
- Status changed from Testing to Closed
Applied in changeset d1client|r16256.