we're stuck a bit with TwilioChatClient and can't sort out what's wrong. In some specific case there is no callback from some method.
For example:
twilioChatClient.channelsList()?.channel(withSidOrUniqueName: someSid, completion: { result, channel
// this block is never called
}
But we can fetch this channel using:
twilioChatClient.channelsList()?.subscribedChannels().first(where: { $0.sid == someSid || $0.uniqueName == someSid })
We send POST request to our server, where we create new channel. So the app get SID from response, and trying to connect to it
In the log after calling twilioChatClient.channelsList()?.channel(withSidOrUniqueName: "CH4155de0b7d374f34b027b5885b207ff9", completion... we see:
2021-01-13 18:15:51.167642+0200 <<<<TwilioChatClient: 0x28119a700>>>> - | Chat IPM | channels: [api] get channel CH4155de0b7d374f34b027b5885b207ff9
2021-01-13 18:15:51.167875+0200 <<<<TwilioChatClient: 0x28119a700>>>> - | Chat IPM | [Yq7OB] got channel from cache CH4155de0b7d374f34b027b5885b207ff9
2021-01-13 18:15:51.167928+0200 <<<<TwilioChatClient: 0x28119a700>>>> - | Chat IPM | channels: got from cache CH4155de0b7d374f34b027b5885b207ff9
So definitely Twilio found the channel, but why completion block is not called ?
After that we found workaround to get Channel directly from subscribedChannels() but we stuck with another issues with this channel (that was fetched from subscribedChannels())
If we try to fetch last message from channel using:
guard channel.status == .joined, channel.synchronizationStatus == .all else {
return
}
channel.messages?.getLastWithCount(100, completion: { [weak self] resulte, twilioMessages in
// this block is never called also
})
It seems very strange... It happens only for new channels, if we restart app - everything is working as expected.
There is no errors in
- (void)chatClient:(nonnull TwilioChatClient *)client errorReceived:(nonnull TCHError *)error;
There is no updates here :
- (void)chatClient:(nonnull TwilioChatClient *)client connectionStateUpdated:(TCHClientConnectionState)state
OR
- (void)chatClient:(nonnull TwilioChatClient *)client synchronizationStatusUpdated:(TCHClientSynchronizationStatus)status;
We also check:
chatClient.connectionState == .connected and chatClient.synchronizationStatus == .completed
before calling channel(withSidOrUniqueName: and getLastWithCount(
We're using the latest version 4.0.2 and it works the same in 3.1.1 (Xcode 12.2)
A bit more information. We have controller that we use for showing AR (ARKit). And when the scene is recognized, twilio stops working.
On viewDidLoad we start the timer that each second perform two actions
timer = Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true, block: { [weak self] _ in
guard currentIndex < sids.count else { return }
guard isWorking == false else { return }
let sid = sids[currentIndex]
print("DEBUG: calling channelWithSidOrUniqueName \(sid)")
chatClient.channelsList()?.channel(withSidOrUniqueName: sid, completion: { result, channel in
print("DEBUG: get completion from channelWithSidOrUniqueName \(result.isSuccessful())")
guard let channel = channel else { return }
if channel.synchronizationStatus == .all {
print("DEBUG: synchronizationStatus == all")
}
if channel.status == .joined {
print("DEBUG: status == .joined")
}
lastChannel = channel
print("DEBUG: calling getLastWithCount \(channel.sid)")
channel.messages?.getLastWithCount(5, completion: { result, _ in
print("DEBUG: get completion from getLastWithCount \(channel.sid) \(result.isSuccessful())")
currentIndex += 1
isWorking = false
})
})
And if everything is okay we see in the logs like
2021-01-14 13:12:47.435842+0200 <<<<TwilioChatClient: 0x282715790>>>> - | Chat IPM | channels: [api] get user channels
DEBUG: calling channelWithSidOrUniqueName CH1425bfdb625945c1ab43bdb440e9ee2a
2021-01-14 13:12:47.437993+0200 <<<<TwilioChatClient: 0x282715790>>>> - | Chat IPM | channels: [api] get channel CH1425bfdb625945c1ab43bdb440e9ee2a
DEBUG: get completion from channelWithSidOrUniqueName true
DEBUG: synchronizationStatus == all
DEBUG: status == .joined
DEBUG: calling getLastWithCount Optional("CH1425bfdb625945c1ab43bdb440e9ee2a")
2021-01-14 13:12:47.438580+0200 <<<<TwilioChatClient: 0x282715790>>>> - | Chat IPM | [9HU8c] channel: [api] get messages
2021-01-14 13:12:47.438727+0200 <<<<TwilioChatClient: 0x282715790>>>> - | Chat IPM | [9HU8c] messages: [api] getLastMessages, count 5
2021-01-14 13:12:47.438798+0200 <<<<TwilioChatClient: 0x282715790>>>> - 8564 | Sync TSCollectionItemQueryActivity(3) | constructed
2021-01-14 13:12:47.438865+0200 <<<<TwilioChatClient: 0x282715790>>>> - 8564 | Sync TSCollectionItemQueryActivity(3) | start 2999
2021-01-14 13:12:47.438977+0200 <<<<TwilioChatClient: 0x282715790>>>> - 2827 | Sync List | query page id: 2999
2021-01-14 13:12:47.439049+0200 <<<<TwilioChatClient: 0x282715790>>>> - | Sync CoreDataActivity | query [1] /v3/Services/IS566142b988d74af6bba32abbdb4a7474/Lists/ES6bb26aae7efb48b49ec0b232e2b1ba65/Items?Order=desc
2021-01-14 13:12:47.439117+0200 <<<<TwilioChatClient: 0x282715790>>>> - | Twilsock Message::makePayload | Payload size 0
2021-01-14 13:12:47.439234+0200 <<<<TwilioChatClient: 0x282715790>>>> - 289 | Twilsock Client | sendUpstreamMessage, id = RQ8bfe33a7e9ac4507a060e1332a533eff
2021-01-14 13:12:47.648547+0200 <<<<TwilioChatClient: 0x282715790>>>> - 8564 | Sync TSCollectionItemQueryActivity(3) | response status 200 body 3349 bytes
2021-01-14 13:12:47.649263+0200 <<<<TwilioChatClient: 0x282715790>>>> - 8564 | Sync TSCollectionItemQueryActivity(3) | destructed
2021-01-14 13:12:47.649585+0200 <<<<TwilioChatClient: 0x282715790>>>> - | Chat IPM | [9HU8c] messages: query done, results count 3
DEBUG: get completion from getLastWithCount Optional("CH1425bfdb625945c1ab43bdb440e9ee2a") true
But after a while it stops working
2021-01-14 13:12:49.435863+0200 <<<<TwilioChatClient: 0x282715790>>>> - | Chat IPM | channels: [api] get user channels
DEBUG: calling channelWithSidOrUniqueName CHa109c68be1454e5ca8bfffa3eedd37f2
2021-01-14 13:12:49.437479+0200 <<<<TwilioChatClient: 0x282715790>>>> - | Chat IPM | channels: [api] get channel CHa109c68be1454e5ca8bfffa3eedd37f2
DEBUG: get completion from channelWithSidOrUniqueName true
DEBUG: synchronizationStatus == all
DEBUG: status == .joined
DEBUG: calling getLastWithCount Optional("CHa109c68be1454e5ca8bfffa3eedd37f2")
2021-01-14 13:12:49.438019+0200 <<<<TwilioChatClient: 0x282715790>>>> - | Chat IPM | [NRBeN] channel: [api] get messages
2021-01-14 13:12:49.438139+0200 <<<<TwilioChatClient: 0x282715790>>>> - | Chat IPM | [NRBeN] messages: [api] getLastMessages, count 5
2021-01-14 13:12:49.438248+0200 <<<<TwilioChatClient: 0x282715790>>>> - 8572 | Sync TSCollectionItemQueryActivity(3) | constructed
2021-01-14 13:12:49.438320+0200 <<<<TwilioChatClient: 0x282715790>>>> - 8572 | Sync TSCollectionItemQueryActivity(3) | start 3003
2021-01-14 13:12:49.438384+0200 <<<<TwilioChatClient: 0x282715790>>>> - 6750 | Sync List | query page id: 3003
2021-01-14 13:12:49.438451+0200 <<<<TwilioChatClient: 0x282715790>>>> - | Sync CoreDataActivity | query [1] /v3/Services/IS566142b988d74af6bba32abbdb4a7474/Lists/ES0ca1135ad73e45d9b0f9e482ea1884b1/Items?Order=desc
2021-01-14 13:12:49.438551+0200 <<<<TwilioChatClient: 0x282715790>>>> - | Twilsock Message::makePayload | Payload size 0
2021-01-14 13:12:49.438635+0200 <<<<TwilioChatClient: 0x282715790>>>> - 289 | Twilsock Client | sendUpstreamMessage, id = RQc3dafd4ae4a546fa8cebee15567d1d14
2021-01-14 13:12:49.629690+0200 <<<<TwilioChatClient: 0x282715790>>>> - 8572 | Sync TSCollectionItemQueryActivity(3) | response status 200 body 3349 bytes
2021-01-14 13:12:49.630136+0200 <<<<TwilioChatClient: 0x282715790>>>> - 8572 | Sync TSCollectionItemQueryActivity(3) | destructed
2021-01-14 13:12:49.630233+0200 <<<<TwilioChatClient: 0x282715790>>>> - | Chat IPM | [NRBeN] messages: query done, results count 3
2021-01-14 13:12:49.630302+0200 <<<<TwilioChatClient: 0x282715790>>>> - | Chat IPM | [NRBeN] messages: range 0:2
2021-01-14 13:12:49.630368+0200 <<<<TwilioChatClient: 0x282715790>>>> - | Chat IPM | [JUbw4] message: StateNoState->StateNotInitialized
2021-01-14 13:12:49.630433+0200 <<<<TwilioChatClient: 0x282715790>>>> - | Chat IPM | [JUbw4] message: StateNotInitialized->StateInitialized
2021-01-14 13:12:49.630498+0200 <<<<TwilioChatClient: 0x282715790>>>> - | Chat IPM | [v2zHz] message: StateNoState->StateNotInitialized
2021-01-14 13:12:49.630562+0200 <<<<TwilioChatClient: 0x282715790>>>> - | Chat IPM | [v2zHz] message: StateNotInitialized->StateInitialized
2021-01-14 13:12:49.630797+0200 <<<<TwilioChatClient: 0x282715790>>>> - | Chat IPM | [nPcGj] message: StateNoState->StateNotInitialized
2021-01-14 13:12:49.630863+0200 <<<<TwilioChatClient: 0x282715790>>>> - | Chat IPM | [nPcGj] message: StateNotInitialized->StateInitialized
And after that my method is waiting forever callback from getLastWithCount
I would be very grateful for any advice or suggestions
Twilio team confirmed that there is some issue when we use ARKit on iOS 14.3... 14.4
https://github.com/twilio/twilio-chat-ios/issues/22
Related
I am doing my firsts steps in Grails (version 3.1.7), and I am doing an application which needs user authentication. This app is a web application that also provide some REST functionality, so I need a web login and a "rest" login with token.
I am using spring-security-core:3.1.0 and spring-security-rest:2.0.0.M2 for these propose and both logins are working properly.
Now I am having some troubles when I try to create a new user via the CRUD generated with grails generate-all package.User, I generated the view properly (I have a client class that the user could have or not so if the user has it I provide the client fields in the same create section too because a client can have only one user). When I save it I got a internal server error 500:
URI /web/webUser/save, Class java.lang.NullPointerException, Message null
in the file: \web\WebUserController.groovy
and the line is: webUser.save flush:true
the trace is:
Line | Method
80 | doFilter in grails.plugin.springsecurity.rest.RestLogoutFilter
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
| 64 | doFilter in grails.plugin.springsecurity.web.UpdateRequestContextHolderExceptionTranslationFilter
| 53 | doFilter in grails.plugin.springsecurity.web.filter.GrailsAnonymousAuthenticationFilter
| 143 | doFilter in grails.plugin.springsecurity.rest.RestAuthenticationFilter
| 62 | doFilter in grails.plugin.springsecurity.web.authentication.logout.MutableLogoutFilter
| 58 | doFilter in grails.plugin.springsecurity.web.SecurityRequestHolderFilter
| 1145 | runWorker in java.util.concurrent.ThreadPoolExecutor
| 615 | run in java.util.concurrent.ThreadPoolExecutor$Worker
^ 745 | run . . . in java.lang.Thread
Caused by NullPointerException: null
->> 47 | $tt__save in WebUserController.groovy
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
| 96 | doInTransaction in grails.transaction.GrailsTransactionTemplate$2
| 93 | execute . in grails.transaction.GrailsTransactionTemplate
| 96 | doInTransaction in grails.transaction.GrailsTransactionTemplate$2
| 93 | execute . in grails.transaction.GrailsTransactionTemplate
| 80 | doFilter in grails.plugin.springsecurity.rest.RestLogoutFilter
| 64 | doFilter in grails.plugin.springsecurity.web.UpdateRequestContextHolderExceptionTranslationFilter
| 53 | doFilter in grails.plugin.springsecurity.web.filter.GrailsAnonymousAuthenticationFilter
| 143 | doFilter in grails.plugin.springsecurity.rest.RestAuthenticationFilter
| 62 | doFilter in grails.plugin.springsecurity.web.authentication.logout.MutableLogoutFilter
| 58 | doFilter in grails.plugin.springsecurity.web.SecurityRequestHolderFilter
| 1145 | runWorker in java.util.concurrent.ThreadPoolExecutor
| 615 | run . . . in java.util.concurrent.ThreadPoolExecutor$Worker
^ 745 | run in java.lang.Thread
I am not sure if I have something wrong in the security plugin configuration or in the user and client domain class, or in the userController.
Edit: Updete info!
#Transactional(readOnly = true)
class WebUserController {
static allowedMethods = [save: "POST", update: "PUT", delete: "DELETE"]
static namespace = 'web'
def springSecurityService
#Transactional
def save(WebUser webUser) {
if (webUser == null) {
transactionStatus.setRollbackOnly()
notFound()
return
}
if (webUser.hasErrors()) {
transactionStatus.setRollbackOnly()
respond webUser.errors, view:'create'
return
}
webUser.save flush:true // line where the NullPointerException is threw
if (webUser.isAdmin){
UserRole.create webUser, Role.findByAuthority('ROLE_ADMIN')
} else {
UserRole.create webUser, Role.findByAuthority('ROLE_CLIENT')
}
request.withFormat {
form multipartForm {
flash.message = message(code: 'default.created.message', args: [message(code: 'webUser.label', default: 'WebUser'), webUser.id])
redirect webUser
}
'*' { respond webUser, [status: CREATED] }
}
}
And the code in the domin:
transient springSecurityService
String username
String password
boolean enabled = true
boolean accountExpired
boolean accountLocked
boolean passwordExpired
static transients = ['isAdmin', 'springSecurityService']
boolean isAdmin
Client client
String name
String email
Date lastVisit
static hasMany = [orders: BOrder]
static constraints = {
username size: 5..15, blank: false, unique: true
password size: 5..15, blank: false, password: true
email email: true, blank: false, unique: true
name size: 0..50, nullable: true
lastVisit nullable: true
client nullable: true
}
The error happens when the client is either null or not. And debugging if I add a afterInsert in the WebUser domain class, I can see the Client id generated if it is not null, but the WebUser id is not generated, and I do not see any error message at that point.
Well I found where was the issue.
The problem was that I added a constraint to the password size, and the validate were executed before the password was hashed, so the validate pass, then the password was hashed and it doesn't have less than the 15 characters, so it failed when it try to persist the user in the database and then it threw the null pointer exception.
Removing or changing the max length restriction to the password, the problem were solved.
The only weird thing to me is the error message that I got, which didn't tell nothing relevant in my opinion.
I get a NullPointerException as shown below when i run my job (who calling a service class):
Caused by NullPointerException: null
->> 184 | doCall in GrailsMelodyGrailsPlugin$_closure4_closure16_closure17
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
| 25 | doCall in ppf.ExportFichierService$_tt__lancerTraitements_closure3_closure4
| 23 | doCall in ppf.ExportFichierService$_$tt__lancerTraitements_closure3
| 12 | $tt__lancerTraitements in ppf.ExportFichierService
| 184 | doCall in GrailsMelodyGrailsPlugin$_closure4_closure16_closure17
| 13 | execute in ppf.ExportFichierJob
| 104 | execute in grails.plugins.quartz.GrailsJobFactory$GrailsJob
| 27 | execute in grails.plugins.quartz.QuartzDisplayJob
| 202 | run . . in org.quartz.core.JobRunShell
^ 573 | run in org.quartz.simpl.SimpleThreadPool$WorkerThread
package ppf
import grails.transaction.Transactional
#Transactional
class ExportFichierService extends FichierService {
def getPaiementsAExporter(def creancier) {}
def lancerTraitements() {
log.info "Entree dans la methode lancer traitement"
Creancier.fichierExportables.each { creancier ->
def instanceTypeFichier = [ new ExportFichierPaiementsOrdreService() ]
log.info "Instance de fichier:${instanceTypeFichier}"
if (creancier.deleted == false) {
log.info "${creancier.deleted}"
instanceTypeFichier.each { it ->
log.info "Instance: ${it}"
it.exporter(creancier)
}
}
}
}
}
def exporter(def creancier) {
def paiements = getPaiementsAExporter(creancier)
if (paiements.count() > 0) {
def fichierPaiementsOrdre = creationFichier(paiements, creancier)
exporterPaiements(fichierPaiementsOrdre, paiements)
sauvegarderFichier (fichierPaiementsOrdre, paiements, creancier)
}
}
Please help me to know why it can't call the method exporter properly
Finally i found the solution instead of putting instances like this [ new xxxService] i injected it and it works
I have an action that receives a file correctly and saves it to a destination folder without problems.
When the destination folder has a file with the same name, the method transferTo, first deletes the existing file, then copies the new one (http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/multipart/commons/CommonsMultipartFile.html#transferTo-java.io.File-).
But if the destination file exists, Grails is throwing this error, and the existing file is deleted but the uploaded one is not copied.
I'm working in WinXP, so I don't think it is a permissions issues (the file is deleted so I guess has nothing to do with permissions).
| Error 2015-05-24 23:47:58,199 [http-bio-8090-exec-3] ERROR errors.GrailsExceptionResolver - FileNotFoundException occurred when processing request: [POST] /ehr/test/upload - parameters:
doit: upload
overwrite: true
SYNCHRONIZER_TOKEN: 8deaf46b-b6ff-4362-ac70-7223f37ae806
SYNCHRONIZER_URI: /ehr/test/upload
opts\Signos.opt (Access is denied). Stacktrace follows:
Message: opts\Signos.opt (Access is denied)
Line | Method
->> 221 | <init> in java.io.FileOutputStream
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
| 171 | <init> in ''
| 417 | write . . in org.apache.commons.fileupload.disk.DiskFileItem
| 85 | upload in test.TestController
| 198 | doFilter in grails.plugin.cache.web.filter.PageFragmentCachingFilter
| 63 | doFilter in grails.plugin.cache.web.filter.AbstractFilter
| 1145 | runWorker in java.util.concurrent.ThreadPoolExecutor
| 615 | run in java.util.concurrent.ThreadPoolExecutor$Worker
^ 744 | run . . . in java.lang.Thread
The upload action looks like:
def upload(boolean overwrite)
{
if (params.doit)
{
def errors = []
def f = request.getFile('opt')
def xml = new String( f.getBytes() )
def destination = config.opt_repo + f.getOriginalFilename()
File fileDest = new File( destination )
if (!overwrite && fileDest.exists())
{
errors << "The OPT already exists, do you want to overwrite?"
return [errors: errors, ask_overwrite: true]
}
// Some validation logic here ...
if (errors.size() == 0)
{
// http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/multipart/commons/CommonsMultipartFile.html#transferTo-java.io.File-
// If the file exists, it will be deleted first
f.transferTo(fileDest)
}
}
}
Inside my Applicant domain class I have the following:
static hasMany = [recommendationFiles:ApplicantFile]
static mapping = {recommendationFiles joinTable: [name:"LETTER_FILES", key: "APPLICANT_ID", column: "LETTER_ID"]}
When I do the following:
def applicant = Applicant.findByENumber(session.user.eNumber)
def applicantFiles = applicant.recommendationFiles
println applicantFiles
applicantFiles.each {
applicant.removeFromRecommendationFiles(it)
}
applicant.save(flush:true)
I get this as an error which makes no sense to me:
| Error 2015-04-08 10:41:59,570 [http-bio-8080-exec-10] ERROR errors.GrailsExceptionResolver - ConcurrentModificationException occurred when processi
ng request: [POST] /scholarshipsystem/specialized/index - parameters:
_action_reUpload: Re-Upload
Stacktrace follows:
Message: null
Line | Method
->> 793 | nextEntry in java.util.HashMap$HashIterator
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
| 828 | next in java.util.HashMap$KeyIterator
| 106 | reUpload in scholarshipSystem.SpecializedController$$EP9HMKbd
| 195 | doFilter in grails.plugin.cache.web.filter.PageFragmentCachingFilter
| 63 | doFilter in grails.plugin.cache.web.filter.AbstractFilter
| 895 | runTask in java.util.concurrent.ThreadPoolExecutor$Worker
| 918 | run . . . in ''
^ 662 | run in java.lang.Thread
There's a few ways to make this work. One way is to convert the list to an array and iterate over it.
def list = applicantFiles.toArray()
list.each {
applicant.removeFromRecommendationFiles(it)
}
Another way, if you're just removing the entire collection would be to...
applicant.recommendationFiles*.delete()
applicant.recommendationFiles.clear()
I see that this error message has been posted several times already in the context of hibernate.
I am getting this error while using grails service and a domain class, any help will be really appreciated
Domain class
class Coupon {
Date dateCreated
Date lastUpdated
String code
String email
String address
String state
String city
String zip
def couponCodeGeneratorService
def beforeValidate() {
println code+"---------8-"
code = couponCodeGeneratorService.generate()
println code+"----------"
}
static constraints = {
email blank:false,email:true
address blank:false
state blank:false
city blank:false
zip blank:false
}
}
Service
class CouponCodeGeneratorService {
Random randomGenerator = new Random()
def serviceMethod() {
}
def generate(){
def group1 = randomGenerator.nextInt(9999)+"";
def group2 = randomGenerator.nextInt(9999)+"";
def group3 = randomGenerator.nextInt(9999)+"";
def group4 = randomGenerator.nextInt(9999)+"";
return group1.padLeft(4,"0") +group2.padLeft(4,"0")+group3.padLeft(4,"0")+group4.padLeft(4,"0")
}
}
The error I am getting is
---------8-
4844634041715590----------
4844634041715590---------8-
| Error 2012-09-10 11:32:54,938 [http-bio-8080-exec-7] ERROR hibernate.AssertionFailure - an assertion failure occured (this may indicate a bug in Hibernate, but is more likely due to unsafe use of the session)
Message: null id in com.easytha.Coupon entry (don't flush the Session after an exception occurs)
Line | Method
->> 19 | beforeValidate in com.easytha.Coupon
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
| 46 | onApplicationEvent in org.grails.datastore.mapping.engine.event.AbstractPersistenceEventListener
| 24 | save . . . . . . . in com.easytha.CouponController
| 186 | doFilter in grails.plugin.cache.web.filter.PageFragmentCachingFilter
| 63 | doFilter . . . . . in grails.plugin.cache.web.filter.AbstractFilter
| 886 | runTask in java.util.concurrent.ThreadPoolExecutor$Worker
| 908 | run . . . . . . . in ''
^ 662 | run in java.lang.Thread
| Error 2012-09-10 11:32:54,944 [http-bio-8080-exec-7] ERROR errors.GrailsExceptionResolver - AssertionFailure occurred when processing request: [POST] /EasyTha/coupon/save - parameters:
zip: asdf
address: asd
email: s.s#s.xom
state: asd
code:
create: Create
city: asdf
null id in com.easytha.Coupon entry (don't flush the Session after an exception occurs). Stacktrace follows:
Message: null id in com.easytha.Coupon entry (don't flush the Session after an exception occurs)
Line | Method
->> 19 | beforeValidate in com.easytha.Coupon
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
| 46 | onApplicationEvent in org.grails.datastore.mapping.engine.event.AbstractPersistenceEventListener
| 24 | save . . . . . . . in com.easytha.CouponController
| 186 | doFilter in grails.plugin.cache.web.filter.PageFragmentCachingFilter
| 63 | doFilter . . . . . in grails.plugin.cache.web.filter.AbstractFilter
| 886 | runTask in java.util.concurrent.ThreadPoolExecutor$Worker
| 908 | run . . . . . . . in ''
^ 662 | run in java.lang.Thread
I am not very familiar with Hibernate, also is this a correct way to create a coupon code that looks like a credit card number?
I suspect the problem may be that the CouponCodeGeneratorService is transactional. Therefore, when you call the service method from inside your beforeValidate you're opening and closing a transaction (even though you don't touch the database inside the method), which among other things will cause another flush of the session.
Try making the service non-transactional:
static transactional = false