Reminder: Create & find trunk-related issues here. Legacy Trac tickets are still available. Problems? Workflow feedback? Need a module project? Open a support ticket.

OpenMRS Trunk

RequiredDataHandler throws ClassCastException while saving ReportSchema

Details

  • Type: Bug Bug
  • Status: Closed Closed
  • Priority: Must Must
  • Resolution: Fixed
  • Affects Version/s: None
  • Fix Version/s: OpenMRS 1.5.0
  • Component/s: None
  • Severity:
    3
  • Keywords:
  • Description:

    RequiredDataHandler throws ClassCastException while checking for Collection of OpenmrsObjects during save operation on ReportSchema.

  1. 1613.patch
    (2 kB)
    Burke Mamlin
    2009-07-03 14:19:46 EDT
  2. requiredDataAdvice.patch
    (3 kB)
    Burke Mamlin
    2009-08-13 03:22:17 EDT
  3. stacktrace-ClassCastException-on-saving-report-form.txt
    (10 kB)
    Burke Mamlin
    2009-07-03 14:15:44 EDT

Activity

Hide
Justin Miranda added a comment - 2009-07-03 14:19:46 EDT

partial solution to prevent ClassCastException

Show
Justin Miranda added a comment - 2009-07-03 14:19:46 EDT partial solution to prevent ClassCastException
Hide
Justin Miranda added a comment - 2009-07-03 14:38:17 EDT

The attached patch removes the ClassCastException but I'm not sure if it's

The exception occurred on ReportSchema.getDataSetDefinitions(). This method returns Mapped<? extends DataSetDefinition>. DataSetDefinition is of type OpenmrsMetadata, so the RequiredDataHandler should return false on the isOpenmrsObjectCollection() call.

However, what should be the case if we had something like NewPatientListModel.getPatients() which returns a Collection of SomeParameterizedType<? extends Patient>. I assume we would want to handle that, right? Right now, the first check ("Is the object a collection?") would pass, but then the second check ("Are the objects in the collection of type OpenmrsObject?") would fail.

For this we probably need to do something like:

if (type instanceof Class) \{ 
   // Same as current code
\}
else if (type instanceof ParameterizedType) \{ 
   // TODO Need to inspect to see if <? extends DataSetDefinition> is an OpenmrsObject
\}
else \{ 
   throw new APIException("Encountered unknown type " + type.getClass().getName());
\}

How do we inspect the Type <? extends DataSetDefinition> to figure out if DataSetDefinition is an OpenmrsObject?

Show
Justin Miranda added a comment - 2009-07-03 14:38:17 EDT The attached patch removes the ClassCastException but I'm not sure if it's The exception occurred on ReportSchema.getDataSetDefinitions(). This method returns Mapped<? extends DataSetDefinition>. DataSetDefinition is of type OpenmrsMetadata, so the RequiredDataHandler should return false on the isOpenmrsObjectCollection() call. However, what should be the case if we had something like NewPatientListModel.getPatients() which returns a Collection of SomeParameterizedType<? extends Patient>. I assume we would want to handle that, right? Right now, the first check ("Is the object a collection?") would pass, but then the second check ("Are the objects in the collection of type OpenmrsObject?") would fail. For this we probably need to do something like:
if (type instanceof Class) \{ 
   // Same as current code
\}
else if (type instanceof ParameterizedType) \{ 
   // TODO Need to inspect to see if <? extends DataSetDefinition> is an OpenmrsObject
\}
else \{ 
   throw new APIException("Encountered unknown type " + type.getClass().getName());
\}
How do we inspect the Type <? extends DataSetDefinition> to figure out if DataSetDefinition is an OpenmrsObject?
Hide
Ben Wolfe added a comment - 2009-07-06 14:03:17 EDT

Does this throw an error on a zero element list?

Show
Ben Wolfe added a comment - 2009-07-06 14:03:17 EDT Does this throw an error on a zero element list?
Hide
Mike Seaton added a comment - 2009-08-13 03:21:00 EDT

Hi guys,

Looking into this, the actual issue is that the method will fail whenever we have nested ParameterizedTypes. For example,

<pre>
List<List<String>> thisPropertyWillFail.
</pre>

This is due to the fact that we try to cast the first parameterized type to a Class (in the above case, this is the inner List<String> on line 333 of RequiredDataAdvice. Since List<String> is another ParameterizedType and not directly a class, this throws an Exception.

This has reared it's head in the Reporting module as Justin indicates because we have properties like this on our objects (i.e. List<Mapped<CohortDefinition>>)

Given the @should annotations on the method, I think we basically just need to account for such errors, and return false if they occur (a List<List<String>> is not a Collection of OpenmrsObjects after all.

See my attached patch as a proposed solution.

This should be put in 1.5 if at all possible, since it is a bug fix. I have no problem with RequiredDataAdvice not handling this type of property, but it should at least do those that it can without throwing an error so that we can create additional RequiredDataAdvice handlers in our module to account for the unhandled cases.

Show
Mike Seaton added a comment - 2009-08-13 03:21:00 EDT Hi guys, Looking into this, the actual issue is that the method will fail whenever we have nested ParameterizedTypes. For example, <pre> List<List<String>> thisPropertyWillFail. </pre> This is due to the fact that we try to cast the first parameterized type to a Class (in the above case, this is the inner List<String> on line 333 of RequiredDataAdvice. Since List<String> is another ParameterizedType and not directly a class, this throws an Exception. This has reared it's head in the Reporting module as Justin indicates because we have properties like this on our objects (i.e. List<Mapped<CohortDefinition>>) Given the @should annotations on the method, I think we basically just need to account for such errors, and return false if they occur (a List<List<String>> is not a Collection of OpenmrsObjects after all. See my attached patch as a proposed solution. This should be put in 1.5 if at all possible, since it is a bug fix. I have no problem with RequiredDataAdvice not handling this type of property, but it should at least do those that it can without throwing an error so that we can create additional RequiredDataAdvice handlers in our module to account for the unhandled cases.
Hide
Darius Jazayeri added a comment - 2009-08-13 03:36:57 EDT

Looks good to me, and more minimal than what I was going to do.

I think this is fair to commit to 1.5 before release. Ben, please comment on that.

Show
Darius Jazayeri added a comment - 2009-08-13 03:36:57 EDT Looks good to me, and more minimal than what I was going to do. I think this is fair to commit to 1.5 before release. Ben, please comment on that.
Hide
Ben Wolfe added a comment - 2009-08-13 18:46:32 EDT

Looks cool. Gold star for you for also making the unit test. I would prefer it be in its own test though. I did a poor job of naming that test you adding your assert to. Just make a new @should and unit test for the assert you did there.

I agree that this should be a 1.5 fix, can you commit it to trunk and 1.5?

Show
Ben Wolfe added a comment - 2009-08-13 18:46:32 EDT Looks cool. Gold star for you for also making the unit test. I would prefer it be in its own test though. I did a poor job of naming that test you adding your assert to. Just make a new @should and unit test for the assert you did there. I agree that this should be a 1.5 fix, can you commit it to trunk and 1.5?
Hide
Mike Seaton added a comment - 2009-08-13 20:06:06 EDT

Fixed in 1.5.x in rev:9780.
Fixed in trunk in rev:9781.

Show
Mike Seaton added a comment - 2009-08-13 20:06:06 EDT Fixed in 1.5.x in rev:9780. Fixed in trunk in rev:9781.

People

Dates

  • Created:
    2009-07-03 14:15:26 EDT
    Updated:
    2010-07-08 16:20:34 EDT
    Resolved:
    2010-07-01 22:44:23 EDT