Upgrade from grails 2.5.4 to 3.1.8, GORM error - grails

I am trying to upgrade project from grails 2.5.4 to 3.1.8 and now I am stuck on the following error.
org.springframework.dao.DataAccessResourceFailureException: Could not obtain current Hibernate Session; nested exception is org.hibernate.HibernateException: No Session found for current thread
org.grails.orm.hibernate.GrailsHibernateTemplate.getSession(GrailsHibernateTemplate.java:227)
org.grails.orm.hibernate.GrailsHibernateTemplate.doExecute(GrailsHibernateTemplate.java:183)
org.grails.orm.hibernate.GrailsHibernateTemplate.execute(GrailsHibernateTemplate.java:140)
org.grails.orm.hibernate.GrailsHibernateTemplate.execute(GrailsHibernateTemplate.java:110)
org.grails.orm.hibernate.AbstractHibernateGormStaticApi.findWhere(AbstractHibernateGormStaticApi.groovy:335)
org.grails.datastore.gorm.GormStaticApi.findWhere(GormStaticApi.groovy:658)
org.grails.datastore.gorm.GormEntity$Trait$Helper.findWhere(GormEntity.groovy:841)
org.grails.datastore.gorm.GormEntity$Trait$Helper$findWhere$4.call(Unknown Source)
com.etherapia.portal.security.User.findWhere(User.groovy)
com.etherapia.portal.security.User$findWhere$0.call(Unknown Source)
grails.plugin.springsecurity.userdetails.GormUserDetailsService.$tt__loadUserByUsername(GormUserDetailsService.groovy:60)
grails.plugin.springsecurity.userdetails.GormUserDetailsService$_loadUserByUsername_closure1.doCall(GormUserDetailsService.groovy)
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
java.lang.reflect.Method.invoke(Method.java:498)
org.springsource.loaded.ri.ReflectiveInterceptor.jlrMethodInvoke(ReflectiveInterceptor.java:1426)
org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:93)
groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:325)
org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:294)
groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1021)
groovy.lang.Closure.call(Closure.java:426)
groovy.lang.Closure.call(Closure.java:442)
grails.transaction.GrailsTransactionTemplate$2.doInTransaction(GrailsTransactionTemplate.groovy:96)
org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:133)
grails.transaction.GrailsTransactionTemplate.execute(GrailsTransactionTemplate.groovy:93)
grails.plugin.springsecurity.userdetails.GormUserDetailsService.loadUserByUsername(GormUserDetailsService.groovy)
grails.plugin.springsecurity.userdetails.GormUserDetailsService.loadUserByUsername(GormUserDetailsService.groovy:71)
org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices.processAutoLoginCookie(TokenBasedRememberMeServices.java:123)
org.springframework.security.web.authentication.rememberme.AbstractRememberMeServices.autoLogin(AbstractRememberMeServices.java:113)
org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter.doFilter(RememberMeAuthenticationFilter.java:97)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:169)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:205)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
grails.plugin.springsecurity.web.authentication.logout.MutableLogoutFilter.doFilter(MutableLogoutFilter.groovy:62)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:91)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
grails.plugin.springsecurity.web.SecurityRequestHolderFilter.doFilter(SecurityRequestHolderFilter.groovy:58)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:213)
org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:176)
org.grails.web.servlet.mvc.GrailsWebRequestFilter.doFilterInternal(GrailsWebRequestFilter.java:75)
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
org.grails.web.filters.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:67)
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:121)
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
org.springframework.boot.actuate.autoconfigure.MetricsFilter.doFilterInternal(MetricsFilter.java:103)
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
root cause
org.hibernate.HibernateException: No Session found for current thread
org.grails.orm.hibernate.GrailsSessionContext.currentSession(GrailsSessionContext.java:117)
org.hibernate.internal.SessionFactoryImpl.getCurrentSession(SessionFactoryImpl.java:1014)
org.grails.orm.hibernate.SessionFactoryProxy.getCurrentSession(SessionFactoryProxy.java:148)
org.grails.orm.hibernate.GrailsHibernateTemplate.getSession(GrailsHibernateTemplate.java:225)
org.grails.orm.hibernate.GrailsHibernateTemplate.doExecute(GrailsHibernateTemplate.java:183)
org.grails.orm.hibernate.GrailsHibernateTemplate.execute(GrailsHibernateTemplate.java:140)
org.grails.orm.hibernate.GrailsHibernateTemplate.execute(GrailsHibernateTemplate.java:110)
org.grails.orm.hibernate.AbstractHibernateGormStaticApi.findWhere(AbstractHibernateGormStaticApi.groovy:335)
org.grails.datastore.gorm.GormStaticApi.findWhere(GormStaticApi.groovy:658)
org.grails.datastore.gorm.GormEntity$Trait$Helper.findWhere(GormEntity.groovy:841)
org.grails.datastore.gorm.GormEntity$Trait$Helper$findWhere$4.call(Unknown Source)
com.etherapia.portal.security.User.findWhere(User.groovy)
com.etherapia.portal.security.User$findWhere$0.call(Unknown Source)
grails.plugin.springsecurity.userdetails.GormUserDetailsService.$tt__loadUserByUsername(GormUserDetailsService.groovy:60)
grails.plugin.springsecurity.userdetails.GormUserDetailsService$_loadUserByUsername_closure1.doCall(GormUserDetailsService.groovy)
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
The error shows up only in mozzila firefox, while chroome loads the page without assests. Images,css,js are not loaded properly in chroome.
Do you know where could be an issue?

In version 3.1.1 of the spring security core plugin the method generating the exception is annotated as #Transactional (see below). Maybe you are using an older version?
class GormUserDetailsService implements GrailsUserDetailsService {
protected Logger log = LoggerFactory.getLogger(getClass())
/**
* Some Spring Security classes (e.g. RoleHierarchyVoter) expect at least one role, so
* we give a user with no granted roles this one which gets past that restriction but
* doesn't grant anything.
*/
static final GrantedAuthority NO_ROLE = new SimpleGrantedAuthority(SpringSecurityUtils.NO_ROLE)
/** Dependency injection for the application. */
GrailsApplication grailsApplication
#Transactional(readOnly=true, noRollbackFor=[IllegalArgumentException, UsernameNotFoundException])
UserDetails loadUserByUsername(String username, boolean loadRoles) throws UsernameNotFoundException {
def conf = SpringSecurityUtils.securityConfig
String userClassName = conf.userLookup.userDomainClassName
def dc = grailsApplication.getDomainClass(userClassName)
if (!dc) {
throw new IllegalArgumentException("The specified user domain class '$userClassName' is not a domain class")
}
Class<?> User = dc.clazz
def user = User.findWhere((conf.userLookup.usernamePropertyName): username)
if (!user) {
log.warn 'User not found: {}', username
throw new NoStackUsernameNotFoundException()
}
Collection<GrantedAuthority> authorities = loadAuthorities(user, username, loadRoles)
createUserDetails user, authorities
}
...
}

Related

Spring Controller argument `#AuthenticationPrincipal Jwt principal` causes Exception: tokenValue can not be empty

The controller method looks like this
QuotaChangeResponseDTO memoryQuotaIncreaseRequest(
#Valid #RequestBody QuotaIncreaseRequestDTO requestDTO,
#AuthenticationPrincipal Jwt principal) {
and then I get the following exception pasted below.
But, if I remove the 2nd argument, and use SecurityContextHolder instead, it works.
This method does require authentication.
Update (a day later): I just changed the argument to: Principal principal and this works.
java.lang.IllegalArgumentException: tokenValue cannot be empty
at org.springframework.util.Assert.hasText(Assert.java:284) ~[spring-core-5.2.6.RELEASE.jar:5.2.6.RELEASE]
at org.springframework.security.oauth2.core.AbstractOAuth2Token.<init>(AbstractOAuth2Token.java:55) ~[spring-security-oauth2-core-5.3.2.RELEASE.jar:5.3.2.RELEASE]
at org.springframework.security.oauth2.jwt.Jwt.<init>(Jwt.java:69) ~[spring-security-oauth2-jose-5.3.2.RELEASE.jar:5.3.2.RELEASE]
at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) ~[na:na]
at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) ~[na:na]
at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:500) ~[na:na]
at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:481) ~[na:na]
at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:204) ~[spring-beans-5.2.6.RELEASE.jar:5.2.6.RELEASE]
at ...
Just as an example for deriving values from JwtAuthenticationToken via the
#AuthenticationPrincipal does not work (Thanks #Matt F.), so I used this instead:
public ResponseEntity<xxResponse> performXXXRequest (final #AuthenticationPrincipal Jwt principal, final #RequestBody XXDTO xxDTO){
.....
}
This works:
public ResponseEntity<xxResponse> performXXXRequest (final Principal principal, #RequestBody final XXDTO xxDTO){
.....
}
//To derive Claims or other JWT details like this:
private String resolveCallerIdFromJwtPrincipal(final Principal principal) {
JwtAuthenticationToken jwtAuthToken = (JwtAuthenticationToken) principal;
Jwt jwtPrincipal = (Jwt) jwtAuthToken.getPrincipal();
return jwtPrincipal.getClaimAsString("name");
}

Bug in Grails / Spring Security when using user groups and roles - can't authenticate

I think I have found a bug in Grails Spring Security 3.1.1, and latest Grails 3.2.6.
I have installed the Spring Security plugin.
From the command line console I did the following:
grails s2-quickstart org.softwood.security User Role --groupClassName=UserGroup
to create a user, role, and UserGroup table as I want to use the allocate roles to groups feature. I then configured the domain classes, and added a few users in the bootstrap to test it out like this:
def loadSecurityUserAndRoles () {
//plugin requires ROLE_ prefix see section 4.2/p18
Role adminRole = new Role(authority: 'ROLE_ADMIN').save(failOnError:true)
Role userRole = new Role(authority: 'ROLE_USER').save(failOnError:true)
Role xtraRole = new Role(authority: 'ROLE_XTRA').save(failOnError:true)
UserGroup adminGroup = new UserGroup (name:"GROUP_ADMIN").save(failOnError:true)
UserGroup userGroup = new UserGroup (name:"GROUP_USERS").save(failOnError:true)
User userWill = new User(username: 'will', password: 'password').save(failOnError:true)
User userMaz = new User(username: 'maz', password: 'password').save(failOnError:true)
User userMeg = new User(username: 'meg', password: 'password').save(failOnError:true)
//give adminGroup admin and user roles
UserGroupToRole sgr = UserGroupToRole.create(adminGroup, adminRole)
sgr = UserGroupToRole.create(adminGroup, userRole)
sgr = UserGroupToRole.create(userGroup, userRole)
assert UserGroupToRole.count() == 3
def auth2 = adminGroup.getAuthorities()
println "adminGroup authorities returned $auth2 "
//assign test user to adminGroup, and maz+meg to user group, inherit all group roles
UserToUserGroup su2g = UserToUserGroup.create (userWill, adminGroup, true)
su2g = UserToUserGroup.create (userMaz, userGroup, true)
su2g = UserToUserGroup.create (userMeg, userGroup, true)
//assign individual 'xtra' role to user
UserToRole sxtra = UserToRole.create(userWill, xtraRole, true)
assert UserToRole.count() == 1
def auth = userWill.getAuthorities()
assert auth.collect{it.authority}.sort() == ['ROLE_ADMIN', 'ROLE_USER', 'ROLE_XTRA']
println "userWill authorities returned $auth "
def mazAuth = userMaz.getAuthorities()
def megAuth = userMeg.getAuthorities()
println "user authorities returned maz: '$mazAuth', and meg: '$megAuth' "
def groups = userWill.getUserGroups()
assert groups.collect{it.name}.sort() == ['GROUP_ADMIN']
assert UserGroup.count() == 2
assert User.count() == 3
assert Role.count() == 3
assert UserToUserGroup.count() == 3
assert UserGroupToRole.count() == 3
assert UserToRole.count() == 1
}
This all seems to work as id expect and the basic asserts return the right numbers of roles for each user when I assert the <userInst>.getAuthorities():
I then setup a controller secureTest with open action and secured one
class SecureTestController {
def index() {
render "hello Will you passed the permit_any"
}
#Secured ('ROLE_ADMIN')
def secure () {
render "hello Will you passed the ROLE_ADMIN"
}
}
I run the app - it starts, I point the browser in secureTest/index - works fine as open url.
When I point the browser at secureTest/secure, it throws default login page. I fill in will/password at it throws stacktrace and fails to login.
The key part of that trace is here I think:
Caused by: groovy.lang.MissingPropertyException: No such property: authorities for class: org.softwood.security.Role
Possible solutions: authority
at org.grails.datastore.gorm.GormInstanceApi.propertyMissing(GormInstanceApi.groovy:55)
at org.grails.datastore.gorm.GormEntity$Trait$Helper.propertyMissing(GormEntity.groovy:57)
at org.grails.datastore.gorm.GormEntity$Trait$Helper$propertyMissing$9.call(Unknown Source)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:48)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:113)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:133)
at org.softwood.security.Role.propertyMissing(Role.groovy)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:93)
at groovy.lang.MetaClassImpl.invokeMissingProperty(MetaClassImpl.java:880)
at groovy.lang.MetaClassImpl.getProperty(MetaClassImpl.java:1861)
at groovy.lang.MetaClassImpl.getProperty(MetaClassImpl.java:3735)
at org.softwood.security.Role.getProperty(Role.groovy)
at org.codehaus.groovy.runtime.InvokerHelper.getProperty(InvokerHelper.java:172)
at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.getProperty(ScriptBytecodeAdapter.java:456)
at grails.plugin.springsecurity.userdetails.GormUserDetailsService$_loadAuthorities_closure2.doCall(GormUserDetailsService.groovy:92)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:93)
at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:325)
at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:294)
at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1024)
at groovy.lang.Closure.call(Closure.java:414)
at groovy.lang.Closure.call(Closure.java:430)
The method fails really here I think (GormUserDetailsService.groovy:92).
When you click that link the editor takes you to this in the plugin.
protected Collection<GrantedAuthority> loadAuthorities(user, String username, boolean loadRoles) {
if (!loadRoles) {
return []
}
def conf = SpringSecurityUtils.securityConfig
String authoritiesPropertyName = conf.userLookup.authoritiesPropertyName
String authorityPropertyName = conf.authority.nameField
boolean useGroups = conf.useRoleGroups
String authorityGroupPropertyName = conf.authority.groupAuthorityNameField
Collection<?> userAuthorities = user."$authoritiesPropertyName"
def authorities
if (useGroups) {
if (authorityGroupPropertyName) {
authorities = userAuthorities.collect { it."$authorityGroupPropertyName" }.flatten().unique().collect { new SimpleGrantedAuthority(it."$authorityPropertyName") }
}
else {
log.warn 'Attempted to use group authorities, but the authority name field for the group class has not been defined.'
}
}
else {
authorities = userAuthorities.collect { new SimpleGrantedAuthority(it."$authorityPropertyName") }
}
authorities ?: [NO_ROLE]
}
The key part here is this call sequence:
if (useGroups) {
if (authorityGroupPropertyName) {
authorities = userAuthorities.collect { it."$authorityGroupPropertyName" }.flatten().unique().collect { new SimpleGrantedAuthority(it."$authorityPropertyName") }
}
useGroups is true. I have a authorityGroupPropertyName that was set in application.groovy file by quick install script:
grails.plugin.springsecurity.authority.groupAuthorityNameField = 'authorities'
so this code line above calls:
userAuthorities.collect { it."$authorityGroupPropertyName" }.flatten().unique()
this returns a hashSet of role.authority names as string and the flatten/unique just makes sure there are no nested structure and strings are unique. So far so good.
The last bit is the bug I think.
<hashSet of role Names>.collect { new SimpleGrantedAuthority(it."$authorityPropertyName") }
In this bit the collect method is called on the set of strings but the string passed to 'SimpleGrantedAuthority' should just be the string. instead its calling
it."$authorityPropertyName"
where it is a string and has no such property.
The key bits set up in application.groovy are:
grails.plugin.springsecurity.userLookup.userDomainClassName = 'org.softwood.security.User'
grails.plugin.springsecurity.userLookup.authoritiesPropertyName = 'authorities'
grails.plugin.springsecurity.userLookup.authorityJoinClassName = 'org.softwood.security.UserToUserGroup'
grails.plugin.springsecurity.authority.className = 'org.softwood.security.Role'
grails.plugin.springsecurity.authority.groupAuthorityNameField = 'authorities' //'authority'
grails.plugin.springsecurity.useRoleGroups = true
As you can see I tried to change authorities to 'authority' as that's the property name in the role class. That fails with missing property message also.
I think this is a bug and the code should just have passed 'it':
.collect {new SimpleGrantedAuthority(it)}
to generate the hashSet of <SimpleGrantedAuthority> types.
Has any one else had this problem with Spring Security? I can't believe I'm the first to have fallen over it, or maybe no one is trying to use groups?
I have configured users role and role group too, this is how mine is working:
In user class:
Set<RoleGroup> getAuthorities() {
UserRoleGroup.findAllByUser(this)*.roleGroup
}
//not used
// Set<RoleGroup> getAuthoritiesNames() {
// UserRoleGroup.findAllByUser(this)*.roleGroup?.name
// }
Set<Role> getAuthority() {
UserRole.findAllByUser(this).collect { it.role } as Set
}
In RoleGroup.groovy I have: (think I changed these unsure)
Set<Role> getAuthorities() {
RoleGroupRole.findAllByRoleGroup(this)*.role
}
def getAuthority() {
RoleGroupRole.withTransaction {
RoleGroupRole.findAllByRoleGroup(this)*.role.authority[0]
}
}
In my bootstrap something like this creates default admin account bound to role and role group:
private void addAdminUser(String username) {
User adminUser = User.findByUsername(username)
if (!adminUser) {
User.withTransaction {
adminUser = new User(username: username, password: 'PASSWORD'
)
adminUser.save(flush: true)
}
}
def adminRole
Role.withTransaction {
adminRole= Role.findByAuthority('ROLE_ADMIN')
if (!adminRole) {
adminRole = new Role(authority: 'ROLE_ADMIN').save(flush: true)
UserRole.create adminUser, adminRole,true
}
}
def adminRoleGroup
RoleGroup.withTransaction {
adminRoleGroup = RoleGroup.findByName('ADMINS')
if (!adminRoleGroup) {
adminRoleGroup = new RoleGroup(name: 'ADMINS').save(flush: true)
}
}
UserRoleGroup.withTransaction {
def adminRoleGroupRole = RoleGroupRole.findByRole(adminRole)
if (!adminRoleGroupRole) {
adminRoleGroupRole = new RoleGroupRole(role: adminRole, roleGroup: adminRoleGroup).save(flush: true)
new UserRoleGroup(user: adminUser, roleGroup: adminRoleGroup).save(flush: true)
}
}
}
I think I've unpeeled the onion and found the issue.
Step 1. I went back to beginning and created a new project from scratch and added the grails-security plugin on empty project. From Grails console used this as per oneline guide grails s2-quickstart org.softwood User Role --groupClassName=RoleGroup.
This worked, so I compared what it had generated with what I'd got myself into and I think I understand where the problem for me was.
My role class was the same as empty test one - no difference.
Then I got to the User class, and somehow I'd got the getAuthorities() returning Set<Role> (this seemed sensible as the getAuthorites() in my UserGroup (aka RoleGroup in fresh start) returns Set<Role>. And basic User/Role returns Set<Role>.
If you generate a project with just User and Role, then User.getAuthorities() returns set<Role>. But when using a group the template is changed and Set<UserGroup> was being returned.
That difference is crucial, as the code that adjusts its de-referencing strategy is in code in GormUserDetailsService.loadAuthorities() where it can't be seen - see my 'tweaked version I'd been trying to fix.
if (useGroups) {
if (authorityGroupPropertyName) {
//authorities = userAuthorities.collect { it."$authorityGroupPropertyName" }.flatten().unique().collect { new SimpleGrantedAuthority(it."$authorityPropertyName") }
//ww edit to stop gpf.. userAuthorities is Set<Role>, so first collect gets the names and produces ArrayList of string
//second collect builds the authorities as Set<SimpleGrantedAuthority>
authorities = userAuthorities.collect { it."$authorityGroupPropertyName" }.flatten().unique().collect { new SimpleGrantedAuthority(it) }
}
else {
log.warn 'Attempted to use group authorities, but the authority name field for the group class has not been defined.'
}
}
else {
authorities = userAuthorities.collect { new SimpleGrantedAuthority(it."$authorityPropertyName") }
}
The problem with this is that the def authorities variable changes type depending on whether using user/role or user/group/role model.
I fell bang into the middle of this; I was trying to use groups (default allocation model) but permit individual Role assignments (individual granted Roles, as override facility).
When I read the User.authorities property - I assumed this would return the Roles it was linked with (via groups, and via my individual assignment override) (like in single user/role model). I'd sorted this out in my domain class code like this in class user.
Set<Role> getAuthorities() {
//orig UserUserGroupBroken.findAllByUser(this)*.userGroup
Set<Role> individualRoles = UserToRole.findAllByUser(this)*.role
Set<UserGroup> groups = UserToUserGroup.findAllByUser(this)*.group
Set<Role> groupRoles = groups.collect{it.getAuthorities() }
Set<Role> aggregateRoles = new HashSet()
aggregateRoles.addAll (groupRoles.flatten())
aggregateRoles.addAll (individualRoles.flatten())
aggregateRoles
}
So that all seemed sensible and did what I wanted (my tests showed the the correct Roles assigned via the superposition). However the GormUserDetailsService wasn't expecting that and broke as if useGroups is true it de-references two sets to get a Set<Role>, and use that to build the SimpleGrantedAuthority Set from.
The upshot of this is my purpose is defeated: it's too far a deviation from the assumed model.
I'll make it as a suggestion on the Git site, as it would just be so much cleaner and less opaque, and I think you intuitively assume that the authorities property on users will give you a Set<Role>.
For now just going to have to stick with just the basic user/group/role model and live with that.
I copied the code out of the GormUserDetailsService into my bootstrap so I could expand expand/play in my own file space.
I modified the if block and expanded in out like this:
if (useGroups) {
if (authorityGroupPropertyName) {
//userAuthorities returns Set<Role>
println """ debug
authoritiesPropertyName = $authoritiesPropertyName
authorityPropertyName = $authorityPropertyName
authorityGroupPropertyName = $authorityGroupPropertyName
userAuthorities returns $userAuthorities of type ${userAuthorities.getClass()}
"""
def roles = userAuthorities.collect { it."$authorityGroupPropertyName" }.flatten().unique()
authorities = roles.collect { new SimpleGrantedAuthority(it."$authorityPropertyName") }
my debug string shows this on the console
debug
authoritiesPropertyName = authorities
authorityPropertyName = authority
authorityGroupPropertyName = authority
userAuthorities returns [Role(authority:ROLE_XTRA), Role(authority:ROLE_USER), Role(authority:ROLE_ADMIN)] of type class java.util.HashSet
The first result returned into variable 'roles' is an ArrayList of String (each of the role.authority name instances. with correct values for this user as in earlier bootstrap setup for userWill.
The next of code now fails as I have an ArrayList of string, and it tries to access a property:
authorities = roles.collect { new SimpleGrantedAuthority(it."$authorityPropertyName") }
which fails with:
groovy.lang.MissingPropertyException: No such property: authority for class: java.lang.String
at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.unwrap(ScriptBytecodeAdapter.java:53)
at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.getProperty(ScriptBytecodeAdapter.java:458)
at coffeeshopapp.BootStrap$_loadSecurityUserAndRoles_closure6.doCall(BootStrap.groovy:115)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:93)
at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:325)
at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:294)
at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1024)
at groovy.lang.Closure.call(Closure.java:414)
at groovy.lang.Closure.call(Closure.java:430)
at org.codehaus.groovy.runtime.DefaultGroovyMethods.collect(DefaultGroovyMethods.java:3170)
at org.codehaus.groovy.runtime.DefaultGroovyMethods.collect(DefaultGroovyMethods.java:3140)
at org.codehaus.groovy.runtime.dgm$66.invoke(Unknown Source)
at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite$PojoMetaMethodSiteNoUnwrapNoCoerce.invoke(PojoMetaMethodSite.java:274)
at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite.call(PojoMetaMethodSite.java:56)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:48)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:113)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:125)
at coffeeshopapp.BootStrap.loadSecurityUserAndRoles(BootStrap.groovy:115)
Whichever way I cut this, this single line in the original source just doesn't work. Original line reads again:
authorities = userAuthorities.collect { it."$authorityGroupPropertyName" }.flatten().unique().collect { new SimpleGrantedAuthority(it."$authorityPropertyName") }
If it had read instead like this - the code would work:
authorities = userAuthorities.collect { it."$authorityGroupPropertyName" }.flatten().unique().collect { new SimpleGrantedAuthority(it) }
Surely the code in the v3.1.1 plugin can't work?

Spring Boot Application with Hazelcast Backed Spring Session Serialization Exception on Active Directory Login Failure

We have a Spring Boot application using a Hazecast-backed Spring Session. The application authenicates with Active Directory using Spring Security. If a user attempts to log in with invalid credentials, a serialization error is thrown:
com.hazelcast.nio.serialization.HazelcastSerializationException: java.io.NotSerializableException: com.sun.jndi.ldap.LdapCtx
at com.hazelcast.nio.serialization.SerializationServiceImpl.handleException(SerializationServiceImpl.java:380)
at com.hazelcast.nio.serialization.SerializationServiceImpl.toData(SerializationServiceImpl.java:235)
at com.hazelcast.nio.serialization.SerializationServiceImpl.toData(SerializationServiceImpl.java:207)
at com.hazelcast.map.impl.MapServiceContextImpl.toData(MapServiceContextImpl.java:338)
at com.hazelcast.map.impl.proxy.MapProxySupport.toData(MapProxySupport.java:1160)
at com.hazelcast.map.impl.proxy.MapProxyImpl.put(MapProxyImpl.java:96)
at org.springframework.session.hazelcast.config.annotation.web.http.HazelcastHttpSessionConfiguration$ExpiringSessionMap.put(HazelcastHttpSessionConfiguration.java:112)
at org.springframework.session.hazelcast.config.annotation.web.http.HazelcastHttpSessionConfiguration$ExpiringSessionMap.put(HazelcastHttpSessionConfiguration.java:102)
at org.springframework.session.MapSessionRepository.save(MapSessionRepository.java:72)
at org.springframework.session.MapSessionRepository.save(MapSessionRepository.java:36)
at org.springframework.session.web.http.SessionRepositoryFilter$SessionRepositoryRequestWrapper.commitSession(SessionRepositoryFilter.java:194)
at org.springframework.session.web.http.SessionRepositoryFilter$SessionRepositoryRequestWrapper.access$100(SessionRepositoryFilter.java:170)
at org.springframework.session.web.http.SessionRepositoryFilter.doFilterInternal(SessionRepositoryFilter.java:128)
at org.springframework.session.web.http.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:65)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:121)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207)
at org.springframework.boot.actuate.autoconfigure.MetricsFilter.doFilterInternal(MetricsFilter.java:103)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:212)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:141)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:522)
at org.apache.coyote.ajp.AbstractAjpProcessor.process(AbstractAjpProcessor.java:868)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:672)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1502)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1458)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:745)
This appears to be the identical to another issue (Spring Boot with Session/Redis Serialization Error with Bad Active Directory Ldap Credentials) with Redis, however there doesn't appear to be a similar mechanism to control serialization in the Hazelcast session mapping that there is for Redis in Spring Session.
We've come up with a workaround (below), but it seems less than ideal as HazelcastHttpSessionConfiguration doesn't really seem to lend itself to extension, so it seems like there should be a cleaner way that we aren't seeing.
We are extending the HazelcastHttpSessionConfiguration to get at the ExpiringSessionMap to remove the LdapCtx before serialization is attempted. This doesn't seem ideal as the HazelcastHttpSessionConfiguration doesn't really lend it self to extension, requiring duplication of code.
Is there a better solution that we're missing?
#Configuration
public class CustomHazelcastHttpSessionMapConfiguration extends HazelcastHttpSessionConfiguration{
private String sessionMapName = "spring:session:sessions";
private int maxInactiveIntervalInSeconds = 1800;
#Bean
public SessionRepository<ExpiringSession> sessionRepository(
HazelcastInstance hazelcastInstance, SessionEntryListener sessionListener) {
super.sessionRepository(hazelcastInstance, sessionListener);
MapSessionRepository sessionRepository = new MapSessionRepository(
new CustomExpiringSessionMap(hazelcastInstance.getMap(this.sessionMapName)));
sessionRepository
.setDefaultMaxInactiveInterval(this.maxInactiveIntervalInSeconds);
return sessionRepository;
}
#Override
public void setSessionMapName(String sessionMapName) {
this.sessionMapName = sessionMapName;
super.setSessionMapName(sessionMapName);
}
#Override
public void setMaxInactiveIntervalInSeconds(int maxInactiveIntervalInSeconds) {
this.maxInactiveIntervalInSeconds = maxInactiveIntervalInSeconds;
super.setMaxInactiveIntervalInSeconds(maxInactiveIntervalInSeconds);
}
static class CustomExpiringSessionMap implements Map<String, ExpiringSession> {
private IMap<String, ExpiringSession> delegate;
CustomExpiringSessionMap(IMap<String, ExpiringSession> delegate) {
this.delegate = delegate;
}
public ExpiringSession put(String key, ExpiringSession value) {
if (value == null) {
return this.delegate.put(key, value);
}
for (String attrName : value.getAttributeNames()) {
Object attrVal = value.getAttribute(attrName);
// Don't serialize LdapCtx in a BadCredentialsException
if (attrVal instanceof BadCredentialsException &&
((BadCredentialsException) attrVal).getCause() != null &&
((BadCredentialsException) attrVal).getCause() instanceof ActiveDirectoryAuthenticationException &&
((BadCredentialsException) attrVal).getCause().getCause() != null &&
((BadCredentialsException) attrVal).getCause().getCause() instanceof javax.naming.AuthenticationException) {
((javax.naming.AuthenticationException) ((BadCredentialsException) attrVal).getCause().getCause()).setResolvedObj(null);
}
}
return this.delegate.put(key, value, value.getMaxInactiveIntervalInSeconds(),
TimeUnit.SECONDS);
}
/*... copy and paste of the rest of ExpiringSessionMap */
}
}
You should configure a custom serialization for object(s) you're having issues with.
This way you would address your problem in Hazelcast configuration without extending/duplicating Spring Session's Hazelcast configuration.
A cleaner solution would be transient-attributes.
If you have a web filter, you can pass it a list of properties to control the behaviour, and this one is a comma separated list of attribute names to exclude from serialization.
DM me if you need more info.

Cannot set model for Listbox from EventQueue.subscribe method after refreshing page

Cannot set model from EventQueue.subscribe method after refreshing page.
I have two pages - my main .zul and included .zul files.There are separate controllers for each zul. I publish event from included page's controller when a user clicks on the listbox on the included page and pass customer object.
eq = EventQueues.lookup("CLIENTS", EventQueues.DESKTOP, true);
eq.publish(new Event("onClick", null, customer));
In my main .zul page's controller I receive event and retrieve customer object. Then, based on its id I provide main listbox with corresponding data.
eq = EventQueues.lookup("CLIENTS", EventQueues.DESKTOP, true);
eq.subscribe(new EventListener() {
public void onEvent(Event event) throws Exception {
if (!Executions.getCurrent().getDesktop().isAlive()) {
eq.unsubscribe(this);
return;
}
Customer customer = (Customer) event.getData();
if (customer != null){
id = customer.getId();// Need to identify what data to retrieve from database
crm_div.setVisible(false); // Listbox from included page
dataListbox.setVisible(true); // Listbox on main page
dataListbox.setModel(new DataListboxModel());// Go to database and extract relevant data
}
else{
alert("No client");
}
}
});
First time, it works fine. I receive event, get the object and successfuly provide listbox with model. However, when I go to another page and return I get NullPointerException. In log file, I noticed that session is the same, page was destroyed, but desktop is alive. I am using ZK 5.0.10.
at
org.zkoss.zk.ui.AbstractComponent.getAttachedUiEngine(AbstractComponent.java:387) at org.zkoss.zk.ui.AbstractComponent.smartUpdate(AbstractComponent.java:1487) at org.zkoss.zk.ui.AbstractComponent.smartUpdate(AbstractComponent.java:1462) at org.zkoss.zk.ui.AbstractComponent.smartUpdate(AbstractComponent.java:1495) at org.zkoss.zul.Listbox.resetDataLoader(Listbox.java:2982) at org.zkoss.zul.Listbox.setModel(Listbox.java:2377) at com.is.sdbooks.controller.ComposerTest.refreshModel(ComposerTest.java:169) at com.is.sdbooks.controller.ComposerTest.onDoubleClick$dataGrid(ComposerTest.java:180) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) at java.lang.reflect.Method.invoke(Unknown Source) at org.zkoss.zk.ui.event.GenericEventListener.onEvent(GenericEventListener.java:81) at org.zkoss.zk.ui.impl.EventProcessor.process0(EventProcessor.java:192) at org.zkoss.zk.ui.impl.EventProcessor.process(EventProcessor.java:138) at org.zkoss.zk.ui.event.Events.sendEvent(Events.java:306) at org.zkoss.zk.ui.event.Events.sendEvent(Events.java:329) at org.zkoss.zk.ui.AbstractComponent$ForwardListener.onEvent(AbstractComponent.java:3052) at org.zkoss.zk.ui.impl.EventProcessor.process0(EventProcessor.java:192) at org.zkoss.zk.ui.impl.EventProcessor.process(EventProcessor.java:138) at org.zkoss.zk.ui.impl.UiEngineImpl.processEvent(UiEngineImpl.java:1626) at org.zkoss.zk.ui.impl.UiEngineImpl.process(UiEngineImpl.java:1410) at org.zkoss.zk.ui.impl.UiEngineImpl.execUpdate(UiEngineImpl.java:1134) at org.zkoss.zk.au.http.DHtmlUpdateServlet.process(DHtmlUpdateServlet.java:562) at org.zkoss.zk.au.http.DHtmlUpdateServlet.doGet(DHtmlUpdateServlet.java:457) at org.zkoss.zk.au.http.DHtmlUpdateServlet.doPost(DHtmlUpdateServlet.java:465) at javax.servlet.http.HttpServlet.service(HttpServlet.java:637) at javax.servlet.http.HttpServlet.service(HttpServlet.java:717) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127) at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:298) at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:852) at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:588) at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:489) at java.lang.Thread.run(Unknown Source)
Problem solved. Just added condition to check if current page is alive
if (!Executions.getCurrent().getDesktop().isAlive()) {
eq.unsubscribe(this);
return;
}
if(!self.getPage().isAlive()){
eq.unsubscribe(this);
return;
}
Customer customer = (Customer) event.getData();

Grails and Quartz: Bad value for type long

I'm trying to save quartz jobs into the database. I've set up the tables, created quartz.properties files, but when I try to run the app, this exception shows up:
2012-02-01 17:36:23,708 [main] ERROR context.GrailsContextLoader - Error executing bootstraps: org.quartz.JobPersistenceException: Couldn't store trigger 'expirationTrigger' for 'com.pldtglobal.svngateway.ExpirationCheckerJob' job:Bad value for type long : \254\355\000\005sr\000\025org.quartz.JobDataMap\237\260\203\350\277\251\260\313\002\000\000xr\000&org.quartz.utils.StringKeyDirtyFlagMap\202\010\350\303\373\305](\002\000\001Z\000\023allowsTransientDataxr\000\035org.quartz.utils.DirtyFlagMap\023\346.\255(v\012\316\002\000\002Z\000\005dirtyL\000\003mapt\000\017Ljava/util/Map;xp\001sr\000\021java.util.HashMap\005\007\332\301\303\026`\321\003\000\002F\000\012loadFactorI\000\011thresholdxp?#\000\000\000\000\000\014w\010\000\000\000\020\000\000\000\001t\000'org.grails.plugins.quartz.grailsJobNamet\000.com.pldtglobal.svngateway.ExpirationCheckerJobx\000 [See nested exception: org.postgresql.util.PSQLException: Bad value for type long : \254\355\000\005sr\000\025org.quartz.JobDataMap\237\260\203\350\277\251\260\313\002\000\000xr\000&org.quartz.utils.StringKeyDirtyFlagMap\202\010\350\303\373\305](\002\000\001Z\000\023allowsTransientDataxr\000\035org.quartz.utils.DirtyFlagMap\023\346.\255(v\012\316\002\000\002Z\000\005dirtyL\000\003mapt\000\017Ljava/util/Map;xp\001sr\000\021java.util.HashMap\005\007\332\301\303\026`\321\003\000\002F\000\012loadFactorI\000\011thresholdxp?#\000\000\000\000\000\014w\010\000\000\000\020\000\000\000\001t\000'org.grails.plugins.quartz.grailsJobNamet\000.com.pldtglobal.svngateway.ExpirationCheckerJobx\000]
org.codehaus.groovy.runtime.InvokerInvocationException: org.quartz.JobPersistenceException: Couldn't store trigger 'expirationTrigger' for 'com.pldtglobal.svngateway.ExpirationCheckerJob' job:Bad value for type long : \254\355\000\005sr\000\025org.quartz.JobDataMap\237\260\203\350\277\251\260\313\002\000\000xr\000&org.quartz.utils.StringKeyDirtyFlagMap\202\010\350\303\373\305](\002\000\001Z\000\023allowsTransientDataxr\000\035org.quartz.utils.DirtyFlagMap\023\346.\255(v\012\316\002\000\002Z\000\005dirtyL\000\003mapt\000\017Ljava/util/Map;xp\001sr\000\021java.util.HashMap\005\007\332\301\303\026`\321\003\000\002F\000\012loadFactorI\000\011thresholdxp?#\000\000\000\000\000\014w\010\000\000\000\020\000\000\000\001t\000'org.grails.plugins.quartz.grailsJobNamet\000.com.pldtglobal.svngateway.ExpirationCheckerJobx\000 [See nested exception: org.postgresql.util.PSQLException: Bad value for type long : \254\355\000\005sr\000\025org.quartz.JobDataMap\237\260\203\350\277\251\260\313\002\000\000xr\000&org.quartz.utils.StringKeyDirtyFlagMap\202\010\350\303\373\305](\002\000\001Z\000\023allowsTransientDataxr\000\035org.quartz.utils.DirtyFlagMap\023\346.\255(v\012\316\002\000\002Z\000\005dirtyL\000\003mapt\000\017Ljava/util/Map;xp\001sr\000\021java.util.HashMap\005\007\332\301\303\026`\321\003\000\002F\000\012loadFactorI\000\011thresholdxp?#\000\000\000\000\000\014w\010\000\000\000\020\000\000\000\001t\000'org.grails.plugins.quartz.grailsJobNamet\000.com.pldtglobal.svngateway.ExpirationCheckerJobx\000]
at org.grails.tomcat.TomcatServer.start(TomcatServer.groovy:212)
at grails.web.container.EmbeddableServer$start.call(Unknown Source)
at _GrailsRun_groovy$_run_closure5_closure12.doCall(_GrailsRun_groovy:158)
at _GrailsRun_groovy$_run_closure5_closure12.doCall(_GrailsRun_groovy)
at _GrailsSettings_groovy$_run_closure10.doCall(_GrailsSettings_groovy:280)
at _GrailsSettings_groovy$_run_closure10.call(_GrailsSettings_groovy)
at _GrailsRun_groovy$_run_closure5.doCall(_GrailsRun_groovy:149)
at _GrailsRun_groovy$_run_closure5.call(_GrailsRun_groovy)
at _GrailsRun_groovy.runInline(_GrailsRun_groovy:116)
at _GrailsRun_groovy.this$4$runInline(_GrailsRun_groovy)
at _GrailsRun_groovy$_run_closure1.doCall(_GrailsRun_groovy:59)
at RunApp$_run_closure1.doCall(RunApp:33)
at gant.Gant$_dispatch_closure5.doCall(Gant.groovy:381)
at gant.Gant$_dispatch_closure7.doCall(Gant.groovy:415)
at gant.Gant$_dispatch_closure7.doCall(Gant.groovy)
at gant.Gant.withBuildListeners(Gant.groovy:427)
at gant.Gant.this$2$withBuildListeners(Gant.groovy)
at gant.Gant$this$2$withBuildListeners.callCurrent(Unknown Source)
at gant.Gant.dispatch(Gant.groovy:415)
at gant.Gant.this$2$dispatch(Gant.groovy)
at gant.Gant.invokeMethod(Gant.groovy)
at gant.Gant.executeTargets(Gant.groovy:590)
at gant.Gant.executeTargets(Gant.groovy:589)
Caused by: org.quartz.JobPersistenceException: Couldn't store trigger 'expirationTrigger' for 'com.pldtglobal.svngateway.ExpirationCheckerJob' job:Bad value for type long : \254\355\000\005sr\000\025org.quartz.JobDataMap\237\260\203\350\277\251\260\313\002\000\000xr\000&org.quartz.utils.StringKeyDirtyFlagMap\202\010\350\303\373\305](\002\000\001Z\000\023allowsTransientDataxr\000\035org.quartz.utils.DirtyFlagMap\023\346.\255(v\012\316\002\000\002Z\000\005dirtyL\000\003mapt\000\017Ljava/util/Map;xp\001sr\000\021java.util.HashMap\005\007\332\301\303\026`\321\003\000\002F\000\012loadFactorI\000\011thresholdxp?#\000\000\000\000\000\014w\010\000\000\000\020\000\000\000\001t\000'org.grails.plugins.quartz.grailsJobNamet\000.com.pldtglobal.svngateway.ExpirationCheckerJobx\000 [See nested exception: org.postgresql.util.PSQLException: Bad value for type long : \254\355\000\005sr\000\025org.quartz.JobDataMap\237\260\203\350\277\251\260\313\002\000\000xr\000&org.quartz.utils.StringKeyDirtyFlagMap\202\010\350\303\373\305](\002\000\001Z\000\023allowsTransientDataxr\000\035org.quartz.utils.DirtyFlagMap\023\346.\255(v\012\316\002\000\002Z\000\005dirtyL\000\003mapt\000\017Ljava/util/Map;xp\001sr\000\021java.util.HashMap\005\007\332\301\303\026`\321\003\000\002F\000\012loadFactorI\000\011thresholdxp?#\000\000\000\000\000\014w\010\000\000\000\020\000\000\000\001t\000'org.grails.plugins.quartz.grailsJobNamet\000.com.pldtglobal.svngateway.ExpirationCheckerJobx\000]
at org.quartz.impl.jdbcjobstore.JobStoreSupport.storeTrigger(JobStoreSupport.java:1241)
at org.quartz.impl.jdbcjobstore.JobStoreSupport$5.execute(JobStoreSupport.java:1147)
at org.quartz.impl.jdbcjobstore.JobStoreSupport$40.execute(JobStoreSupport.java:3670)
at org.quartz.impl.jdbcjobstore.JobStoreCMT.executeInLock(JobStoreCMT.java:242)
at org.quartz.impl.jdbcjobstore.JobStoreSupport.executeInLock(JobStoreSupport.java:3666)
at org.quartz.impl.jdbcjobstore.JobStoreSupport.storeTrigger(JobStoreSupport.java:1143)
at org.quartz.core.QuartzScheduler.scheduleJob(QuartzScheduler.java:790)
at org.quartz.impl.StdScheduler.scheduleJob(StdScheduler.java:254)
at org.quartz.Scheduler$scheduleJob.call(Unknown Source)
at QuartzGrailsPlugin$_closure5_closure24.doCall(QuartzGrailsPlugin.groovy:223)
at QuartzGrailsPlugin$_closure5.doCall(QuartzGrailsPlugin.groovy:218)
at QuartzGrailsPlugin.invokeMethod(QuartzGrailsPlugin.groovy)
at QuartzGrailsPlugin$_closure3_closure21.doCall(QuartzGrailsPlugin.groovy:169)
at QuartzGrailsPlugin$_closure3.doCall(QuartzGrailsPlugin.groovy:167)
... 23 more
Caused by: org.postgresql.util.PSQLException: Bad value for type long : \254\355\000\005sr\000\025org.quartz.JobDataMap\237\260\203\350\277\251\260\313\002\000\000xr\000&org.quartz.utils.StringKeyDirtyFlagMap\202\010\350\303\373\305](\002\000\001Z\000\023allowsTransientDataxr\000\035org.quartz.utils.DirtyFlagMap\023\346.\255(v\012\316\002\000\002Z\000\005dirtyL\000\003mapt\000\017Ljava/util/Map;xp\001sr\000\021java.util.HashMap\005\007\332\301\303\026`\321\003\000\002F\000\012loadFactorI\000\011thresholdxp?#\000\000\000\000\000\014w\010\000\000\000\020\000\000\000\001t\000'org.grails.plugins.quartz.grailsJobNamet\000.com.pldtglobal.svngateway.ExpirationCheckerJobx\000
at org.postgresql.jdbc2.AbstractJdbc2ResultSet.toLong(AbstractJdbc2ResultSet.java:2796)
at org.postgresql.jdbc2.AbstractJdbc2ResultSet.getLong(AbstractJdbc2ResultSet.java:2019)
at org.postgresql.jdbc4.Jdbc4ResultSet.getBlob(Jdbc4ResultSet.java:52)
at org.postgresql.jdbc2.AbstractJdbc2ResultSet.getBlob(AbstractJdbc2ResultSet.java:335)
at org.quartz.impl.jdbcjobstore.StdJDBCDelegate.getObjectFromBlob(StdJDBCDelegate.java:3462)
at org.quartz.impl.jdbcjobstore.StdJDBCDelegate.selectJobDetail(StdJDBCDelegate.java:904)
at org.quartz.impl.jdbcjobstore.JobStoreSupport.storeTrigger(JobStoreSupport.java:1197)
... 36 more
Application context shutting down...
Application context shutdown.
I don't have any idea on where the actual problem is. The code is alright and running when the jobs weren't saved in the database.
In your grails-app/conf/quartz.properties, replace
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
with
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.PostgreSQLDelegate
I'm getting the same error even using the correct delegate, so no promises.
For spring boot, you can also specify the PG driver using the following property in application.properties -
spring.quartz.properties.org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.PostgreSQLDelegate
For anyone using Quartz and Spring Boot, I had the same problem after migrating from using Quartz in Tomcat to Spring Boot. In Tomcat, we were using a quartz properties file and manually loading it when creating the Scheduler. One of those properties was:
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.PostgreSQLDelegate
In Spring Boot, the scheduler is created automatically through an auto config, and therefore our properties weren't being applied.
Our solution was to use a SchedulerFactoryBeanCustomizer and set the Quartz properties. This customizer is applied before the scheduler is created so it's a good place to configure Quartz.
#Bean
public SchedulerFactoryBeanCustomizer schedulerFactoryBeanCustomizer()
{
return new SchedulerFactoryBeanCustomizer()
{
#Override
public void customize(SchedulerFactoryBean bean)
{
bean.setQuartzProperties(createQuartzProperties());
}
};
}
private Properties createQuartzProperties()
{
// Could also load from a file
Properties props = new Properties();
props.put("org.quartz.jobStore.driverDelegateClass", "org.quartz.impl.jdbcjobstore.PostgreSQLDelegate");
return props;
}
And for reference here is the full quartz.properties we migrated from:
org.quartz.scheduler.instanceName=ProcessAutomation
org.quartz.scheduler.instanceId=AUTO
org.quartz.scheduler.jmx.export=true
org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount=10
org.quartz.threadPool.threadPriority=5
org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreCMT
org.quartz.jobStore.dataSource=QuartzDS
org.quartz.jobStore.nonManagedTXDataSource=springNonTxDataSource.ProcessAutomation
org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.PostgreSQLDelegate
org.quartz.jobStore.misfireThreshold=60000
org.quartz.jobStore.isClustered=true
org.quartz.jobStore.clusterCheckinInterval=20000
#Bean
public Properties quartzProperties() throws IOException {
PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
propertiesFactoryBean.setLocation(new ClassPathResource("application.properties"));
Properties props = new Properties();
props.put("org.quartz.jobStore.driverDelegateClass", "org.quartz.impl.jdbcjobstore.PostgreSQLDelegate");
propertiesFactoryBean.setProperties(props);
propertiesFactoryBean.afterPropertiesSet();
return propertiesFactoryBean.getObject();
}
Alternately if you want to set all quartz properties like clustered, thread-pool etc.. Instead of typing them here in this method, create a quartz.properties file and use below;
#Autowired
private QuartzProperties quartzProperties;
#Autowired
DataSource dataSource;
#Bean
public SchedulerFactoryBean schedulerFactoryBean() throws IOException {
SchedulerFactoryBean factory = new SchedulerFactoryBean();
factory.setOverwriteExistingJobs(true);
factory.setDataSource(dataSource);
factory.setQuartzProperties(quartzProperties());
AutowiringSpringBeanJobFactory jobFactory = new AutowiringSpringBeanJobFactory();
jobFactory.setApplicationContext(applicationContext);
factory.setJobFactory(jobFactory);
return factory;
}
#Bean
public Properties quartzProperties() throws IOException {
PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
propertiesFactoryBean.setLocation(new ClassPathResource("/application.properties"));
Properties props = new Properties();
props.putAll(quartzProperties.getProperties());
propertiesFactoryBean.setProperties(props);
propertiesFactoryBean.afterPropertiesSet(); //it's important
return propertiesFactoryBean.getObject();
}
quartz.properties file example below:-
org.quartz.scheduler.instanceName=springBootQuartzApp
org.quartz.scheduler.instanceId=AUTO
org.quartz.threadPool.threadCount=50
org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.PostgreSQLDelegate
org.quartz.jobStore.useProperties=true
#org.quartz.jobStore.misfireThreshold=60000
org.quartz.jobStore.isClustered=true
org.quartz.plugin.shutdownHook.class=org.quartz.plugins.management.ShutdownHookPlugin
org.quartz.plugin.shutdownHook.cleanShutdown=TRUE
I also face this issue and I just add :
properties.put("org.quartz.jobStore.driverDelegateClass", "org.quartz.impl.jdbcjobstore.PostgreSQLDelegate");
full bean configuration below
#Bean
public SchedulerFactoryBean scheduler(Trigger... triggers) {
SchedulerFactoryBean schedulerFactory = new SchedulerFactoryBean();
Properties properties = new Properties();
properties.setProperty("org.quartz.scheduler.instanceName", "MY_INSTANCE_NAME");
properties.setProperty("org.quartz.scheduler.instanceId", "INSTANCE_ID_01");
properties.put("org.quartz.jobStore.driverDelegateClass", "org.quartz.impl.jdbcjobstore.PostgreSQLDelegate");
schedulerFactory.setOverwriteExistingJobs(true);
schedulerFactory.setAutoStartup(true);
schedulerFactory.setQuartzProperties(properties);
schedulerFactory.setDataSource(dataSource);
schedulerFactory.setJobFactory(springBeanJobFactory());
schedulerFactory.setWaitForJobsToCompleteOnShutdown(true);
if (ArrayUtils.isNotEmpty(triggers)) {
schedulerFactory.setTriggers(triggers);
}
return schedulerFactory;
}

Resources