jdaugherty opened a new issue, #16692:
URL: https://github.com/apache/grails-core/issues/16692
### Summary
A GORM data service method that returns an `Observable` of a scalar property
projection fails at runtime with a `GroovyCastException`: the single projected
value is cast to an `Iterable`. The same projection declared as a `Single`
works.
### Steps to reproduce
```groovy
@Entity
class Book {
String title
String author
}
@Service(Book)
interface BookSingleService {
Single<String> findBookAuthor(String title) // works
}
@Service(Book)
interface BookObservableService {
Observable<String> findBookAuthor(String title) // fails
}
```
```groovy
new Book(title: 'The Stand', author: 'Stephen King').save(flush: true)
datastore.getService(BookSingleService).findBookAuthor('The
Stand').blockingGet() // 'Stephen King'
datastore.getService(BookObservableService).findBookAuthor('The
Stand').toList().blockingGet() // throws
```
Run as a throwaway spec in `grails-datamapping-core-test` against a
`SimpleMapDatastore`, as `RxServiceImplSpec` does.
There is no spelling that works for an `Observable` of a scalar projection:
`Observable<String> findAllBookAuthor(String title)` is rejected at compile
time with `No implementations possible for method
'findAllBookAuthor(java.lang.String):io.reactivex.rxjava3.core.Observable'`, so
only the `find` form matches an implementer, and that one fails at runtime.
### Actual behaviour
```
org.codehaus.groovy.runtime.typehandling.GroovyCastException: Cannot cast
object 'Stephen King' with class 'java.lang.String' to class
'java.lang.Iterable'
at
grails.gorm.transactions.GrailsTransactionTemplate$2.doInTransaction(GrailsTransactionTemplate.groovy:139)
at
org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:137)
at
grails.gorm.transactions.GrailsTransactionTemplate.doExecute(GrailsTransactionTemplate.groovy:136)
at
grails.gorm.transactions.GrailsTransactionTemplate.execute_closure2(GrailsTransactionTemplate.groovy:131)
at
grails.gorm.transactions.GrailsTransactionTemplate.inConnectionScope(GrailsTransactionTemplate.groovy:96)
at
grails.gorm.transactions.GrailsTransactionTemplate.execute(GrailsTransactionTemplate.groovy:131)
at
org.grails.gorm.rx.services.support.RxServiceSupport.create_closure1(RxServiceSupport.groovy:62)
at
io.reactivex.rxjava3.internal.operators.observable.ObservableDefer.subscribeActual(ObservableDefer.java:33)
...
at io.reactivex.rxjava3.core.Single.blockingGet(Single.java:3644)
```
### Expected behaviour
An `Observable<String>` emits the projected value (or every projected value
for a multi-valued query) and completes, in the same way `Single<String>`
delivers one.
### Where it comes from
`ObservableResultAdapter` (`grails-datamapping-rx`,
`org.grails.gorm.rx.services.implementers`) adapts every
`IterableServiceImplementer` to an `Observable` return type. `doesImplement`
accepts the method for an `IterableProjectionServiceImplementer` when the
prefix matches (`find`) and the projection is compatible with
`Iterable<String>`, and `implement` then narrows the synchronous return type to
`Iterable<String>` before delegating, so the generated body is typed as
returning an `Iterable` while the adapted projection implementer returns the
single value. `RxServiceSupport.create` would handle a scalar
(`Observable.just`) if it ever received one, but the cast in the generated,
transactional body fails first.
### Affected versions
Reproduced on `9.0.x` (RxJava 3, at the head of #16619) and on `8.1.x`
(RxJava 1, `bd0c1394ce`'s successor at the branch head), with the same
`GroovyCastException` from the data service body in both.
`grails-datamapping-rx` is not part of the build on `7.0.x`, `7.2.x` or `8.0.x`
(`settings.gradle` has it commented out there), so the module, and with it this
bug, ships from 8.1 on.
Found by @codeconsole while reviewing #16619, where the data services moved
to RxJava 3; the failure is the same on RxJava 1, so it is not caused by that
change.
@borinquenkid, could you take a look at this one?
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]