Hello, We're trying to build a POC on SCIM APIs on top of Syncope.Problem is when we're using some basic SCIM APIs to update the groups membership of a given user, we got a OptimisticLockException. This is due to the fact that SCIM group membership can be updated only from the Group endpoint (not from the User endpoint).From https://tools.ietf.org/html/rfc7643#page-24 groups A list of groups to which the user belongs,... Since this attribute has a mutability of "readOnly", group membership changes MUST be applied via the "Group" Resource (Section 4.2). This attribute has a mutability of "readOnly". So, we have the following scenario : * we have a user1 member of 2 groups (group1 and group2). * we want to remove the user1 from both groups from a UI console. * the UI then needs to send 2 HTTP PUT on SCIM /Groups endpoint (one for each group). /PUT Groups/group1 /PUT Groups/group2 * we get a OptimisticLockException since both calls are made for a relation on the same user - because on the SCIM side for the Group endpoint, we must call userService.update(userTO) to update a user <-> group relation. i.e. MembershipTO membershipTO = new MembershipTO.Builder().group(userTO.getKey(), "USER").group(groupTO.getKey(), groupTO.getName()).build(); userTO.getMemberships().add(membershipTO); try { userService.update(userTO); } catch (SyncopeClientException e) { throw new SCIMException(String.format("User %s was not added to the group %s", userId, groupTO.getName()), e); }
Is there an API to update user or group membership without testing @Version field ? (i.e like a syncope REST API on top of a jpql update ?) Perhaps we're not using the good API here (is there an API to handle membership from the group's side ? Do you see another possible solution (besides updating membership from the Group side/screen) ? Thanks,Adrian Sample stacktrace on client side :2016-09-27 09:31:46.065 DEBUG 1 --- [tp1754926770-22] o.s.web.client.RestTemplate : PUT request for "http://scim:7777/Groups/2564b3a7-0f5d-424d-a4b3-a70f5d624d80" resulted in 500 (Server Error); invoking error handler at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1061) [jetty-server-9.2.14.v20151106.jar!/:9.2.14.v20151106] at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141) [jetty-server-9.2.14.v20151106.jar!/:9.2.14.v20151106] at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:97) [jetty-server-9.2.14.v20151106.jar!/:9.2.14.v20151106] at org.eclipse.jetty.server.Server.handle(Server.java:499) [jetty-server-9.2.14.v20151106.jar!/:9.2.14.v20151106] at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:311) [jetty-server-9.2.14.v20151106.jar!/:9.2.14.v20151106] at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:257) [jetty-server-9.2.14.v20151106.jar!/:9.2.14.v20151106] at org.eclipse.jetty.io.AbstractConnection$2.run(AbstractConnection.java:544) [jetty-io-9.2.14.v20151106.jar!/:9.2.14.v20151106] at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:635) [jetty-util-9.2.14.v20151106.jar!/:9.2.14.v20151106] at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:555) [jetty-util-9.2.14.v20151106.jar!/:9.2.14.v20151106] at java.lang.Thread.run(Thread.java:745) [na:1.8.0_92-internal] Caused by: org.apache.syncope.common.lib.SyncopeClientException: GenericPersistence [OptimisticLockException: An optimistic lock violation was detected when flushing object instance "org.apache.syncope.core.persistence.jpa.entity.user.JPAUPlainAttrValue-62f5f8bf-b390-4354-b5f8-bfb390a354e8" to the data store. This indicates that the object was concurrently modified in another transaction.] at org.apache.syncope.common.lib.SyncopeClientException.build(SyncopeClientException.java:37) ~[syncope-common-lib-2.0.0-SNAPSHOT.jar!/:2.0.0-SNAPSHOT] at org.apache.syncope.client.lib.RestClientExceptionMapper.checkSyncopeClientCompositeException(RestClientExceptionMapper.java:147) ~[syncope-client-lib-2.0.0-SNAPSHOT.jar!/:2.0.0-SNAPSHOT] at org.apache.syncope.client.lib.RestClientExceptionMapper.fromResponse(RestClientExceptionMapper.java:58) ~[syncope-client-lib-2.0.0-SNAPSHOT.jar!/:2.0.0-SNAPSHOT] at org.apache.syncope.client.lib.RestClientExceptionMapper.fromResponse(RestClientExceptionMapper.java:42) ~[syncope-client-lib-2.0.0-SNAPSHOT.jar!/:2.0.0-SNAPSHOT] at org.apache.cxf.jaxrs.client.ClientProxyImpl.checkResponse(ClientProxyImpl.java:306) ~[cxf-rt-rs-client-3.1.7.jar!/:3.1.7] 2016-09-27 09:31:46.068 DEBUG 1 --- [tp1754926770-22] .m.m.a.ExceptionHandlerExceptionResolver : Resolving exception from handler [public void org.mycompany.iam.server.controller.GroupController.updateGroup(java.lang.String,org.mycompany.iam.common.model.Group) throws org.mycompany.iam.server.exception.IamServerException]: org.mycompany.iam.server.exception.IamServerException: Can't update group at org.apache.cxf.jaxrs.client.ClientProxyImpl.handleResponse(ClientProxyImpl.java:838) ~[cxf-rt-rs-client-3.1.7.jar!/:3.1.7] 2016-09-27 09:31:46.071 DEBUG 1 --- [tp1754926770-22] .m.m.a.ExceptionHandlerExceptionResolver : Invoking @ExceptionHandler method: public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.mycompany.iam.server.exception.IamServerExceptionHandler.handleIamException(org.mycompany.iam.server.exception.IamServerException) at org.apache.cxf.jaxrs.client.ClientProxyImpl.doChainedInvocation(ClientProxyImpl.java:752) ~[cxf-rt-rs-client-3.1.7.jar!/:3.1.7] at org.apache.cxf.jaxrs.client.ClientProxyImpl.invoke(ClientProxyImpl.java:231) ~[cxf-rt-rs-client-3.1.7.jar!/:3.1.7] at com.sun.proxy.$Proxy131.update(Unknown Source) ~[na:na] at org.mycompany.iam.scim.provisioning.SCIMGroupProvisioning.addUserToGroup(SCIMGroupProvisioning.java:201) ~[iam-scim_tpsvc-1011-update-group-in-user-1-20160927092425-g1ca9982.jar!/:na] ... 57 common frames omitted Sample stacktrace on Syncope side :09:31:45.914 ERROR org.apache.syncope.core.rest.cxf.RestServiceExceptionMapper - Exception thrown org.apache.openjpa.persistence.OptimisticLockException: Optimistic locking errors were detected when flushing to the data store. The following objects may have been concurrently modified in another transaction: [org.apache.syncope.core.persistence.jpa.entity.user.JPAUPlainAttrValue-62f5f8bf-b390-4354-b5f8-bfb390a354e8, org.apache.syncope.core.persistence.jpa.entity.user.JPAUPlainAttrValue-314a1e53-0235-41ff-8a1e-53023571ff42, org.apache.syncope.core.persistence.jpa.entity.user.JPAUPlainAttrValue-c4c32ec3-0312-4f00-832e-c30312af0045, org.apache.syncope.core.persistence.jpa.entity.user.JPAUPlainAttrValue-9ce613a2-5b42-4bce-a613-a25b425bce86, org.apache.syncope.core.persistence.jpa.entity.user.JPAUPlainAttrValue-32fb4c5b-995f-45f2-bb4c-5b995f05f255, org.apache.syncope.core.persistence.jpa.entity.user.JPAUPlainAttrValue-7117ce5e-4f87-43da-97ce-5e4f8773da92] at org.apache.openjpa.kernel.BrokerImpl.newFlushException(BrokerImpl.java:2357) ~[openjpa-kernel-2.4.1.jar:2.4.1] enjpa-kernel-2.4.1.jar:2.4.1].kernel.BrokerImpl.flush(BrokerImpl.java:2205) ~[op--More-- at org.apache.openjpa.kernel.BrokerImpl.flushSafe(BrokerImpl.java:2103) ~[openjpa-kernel-2.4.1.jar:2.4.1] at org.apache.openjpa.kernel.BrokerImpl.flush(BrokerImpl.java:1874) ~[openjpa-kernel-2.4.1.jar:2.4.1] at org.apache.openjpa.kernel.DelegatingBroker.flush(DelegatingBroker.java:1044) ~[openjpa-kernel-2.4.1.jar:2.4.1] at org.apache.openjpa.persistence.EntityManagerImpl.flush(EntityManagerImpl.java:664) ~[openjpa-persistence-2.4.1.jar:2.4.1] at org.apache.syncope.core.persistence.jpa.dao.JPAUserDAO.save(JPAUserDAO.java:399) ~[syncope-core-persistence-jpa-2.0.0-SNAPSHOT.jar:2.0.0-SNAPSHOT] at org.apache.syncope.core.persistence.jpa.dao.JPAUserDAO.save(JPAUserDAO.java:83) ~[syncope-core-persistence-jpa-2.0.0-SNAPSHOT.jar:2.0.0-SNAPSHOT] at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.8.0_91] at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:1.8.0_91] at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.8.0_91] at java.lang.reflect.Method.invoke(Method.java:498) ~[?:1.8.0_91] at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:333) ~[spring-aop-4.3.2.RELEASE.jar:4.3.2.RELEASE] at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207) ~[spring-aop-4.3.2.RELEASE.jar:4.3.2.RELEASE] at com.sun.proxy.$Proxy153.save(Unknown Source) ~[?:?] at org.apache.syncope.core.workflow.java.DefaultUserWorkflowAdapter.doUpdate(DefaultUserWorkflowAdapter.java:115) ~[syncope-core-workflow-java-2.0.0-SNAPSHOT.jar:2.0.0-SNAPSHOT] at org.apache.syncope.core.workflow.java.AbstractUserWorkflowAdapter.update(AbstractUserWorkflowAdapter.java:78) ~[syncope-core-workflow-java-2.0.0-SNAPSHOT.jar:2.0.0-SNAPSHOT] at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.8.0_91] at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:1.8.0_91] at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.8.0_91] at java.lang.reflect.Method.invoke(Method.java:498) ~[?:1.8.0_91] at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:333) ~[spring-aop-4.3.2.RELEASE.jar:4.3.2.RELEASE] at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190) ~[spring-aop-4.3.2.RELEASE.jar:4.3.2.RELEASE] at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) ~[spring-aop-4.3.2.RELEASE.jar:4.3.2.RELEASE] at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99) ~[spring-tx-4.3.2.RELEASE.jar:4.3.2.RELEASE] at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:280) ~[spring-tx-4.3.2.RELEASE.jar:4.3.2.RELEASE] at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96) ~[spring-tx-4.3.2.RELEASE.jar:4.3.2.RELEASE] at org.apache.syncope.core.persistence.jpa.spring.DomainTransactionInterceptor.invoke(DomainTransactionInterceptor.java:64) ~[syncope-core-persistence-jpa-2.0.0-SNAPSHOT.jar:2.0.0-SNAPSHOT] at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.3.2.RELEASE.jar:4.3.2.RELEASE] at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213) ~[spring-aop-4.3.2.RELEASE.jar:4.3.2.RELEASE] at com.sun.proxy.$Proxy164.update(Unknown Source) ~[?:?] at org.apache.syncope.core.provisioning.java.DefaultUserProvisioningManager.update(DefaultUserProvisioningManager.java:121) ~[syncope-core-provisioning-java-2.0.0-SNAPSHOT.jar:2.0.0-SNAPSHOT] at org.apache.syncope.core.provisioning.java.DefaultUserProvisioningManager.update(DefaultUserProvisioningManager.java:57) ~[syncope-core-provisioning-java-2.0.0-SNAPSHOT.jar:2.0.0-SNAPSHOT] at org.apache.syncope.core.logic.UserLogic.doUpdate(UserLogic.java:232) ~[syncope-core-logic-2.0.0-SNAPSHOT.jar:2.0.0-SNAPSHOT] at org.apache.syncope.core.logic.UserLogic.update(UserLogic.java:213) ~[syncope-core-logic-2.0.0-SNAPSHOT.jar:2.0.0-SNAPSHOT] at org.apache.syncope.core.logic.UserLogic.update(UserLogic.java:68) ~[syncope-core-logic-2.0.0-SNAPSHOT.jar:2.0.0-SNAPSHOT] at org.apache.syncope.core.logic.UserLogic$$FastClassBySpringCGLIB$$67b1988f.invoke(<generated>) ~[syncope-core-logic-2.0.0-SNAPSHOT.jar:2.0.0-SNAPSHOT] at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204) ~[spring-core-4.3.2.RELEASE.jar:4.3.2.RELEASE] at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:720) ~[spring-aop-4.3.2.RELEASE.jar:4.3.2.RELEASE] at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) ~[spring-aop-4.3.2.RELEASE.jar:4.3.2.RELEASE] at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:85) ~[spring-aop-4.3.2.RELEASE.jar:4.3.2.RELEASE] at org.apache.syncope.core.logic.LogicInvocationHandler.around(LogicInvocationHandler.java:72) ~[syncope-core-logic-2.0.0-SNAPSHOT.jar:2.0.0-SNAPSHOT] at sun.reflect.GeneratedMethodAccessor130.invoke(Unknown Source) ~[?:?] at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.8.0_91] at java.lang.reflect.Method.invoke(Method.java:498) ~[?:1.8.0_91] at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:629) ~[spring-aop-4.3.2.RELEASE.jar:4.3.2.RELEASE] at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:618) ~[spring-aop-4.3.2.RELEASE.jar:4.3.2.RELEASE] at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:70) ~[spring-aop-4.3.2.RELEASE.jar:4.3.2.RELEASE] at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.3.2.RELEASE.jar:4.3.2.RELEASE] at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92) ~[spring-aop-4.3.2.RELEASE.jar:4.3.2.RELEASE] at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.3.2.RELEASE.jar:4.3.2.RELEASE] at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:655) ~[spring-aop-4.3.2.RELEASE.jar:4.3.2.RELEASE] at org.apache.syncope.core.logic.UserLogic$$EnhancerBySpringCGLIB$$134f19c3.update(<generated>) ~[syncope-core-logic-2.0.0-SNAPSHOT.jar:2.0.0-SNAPSHOT] at org.apache.syncope.core.rest.cxf.service.AbstractAnyService.update(AbstractAnyService.java:200) ~[syncope-core-rest-cxf-2.0.0-SNAPSHOT.jar:2.0.0-SNAPSHOT] at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.8.0_91] at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:1.8.0_91] at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.8.0_91] at java.lang.reflect.Method.invoke(Method.java:498) ~[?:1.8.0_91] at org.apache.cxf.service.invoker.AbstractInvoker.performInvocation(AbstractInvoker.java:180) ~[cxf-core-3.1.7.jar:3.1.7] at org.apache.cxf.service.invoker.AbstractInvoker.invoke(AbstractInvoker.java:96) ~[cxf-core-3.1.7.jar:3.1.7] at org.apache.cxf.jaxrs.JAXRSInvoker.invoke(JAXRSInvoker.java:189) ~[cxf-rt-frontend-jaxrs-3.1.7.jar:3.1.7] at org.apache.cxf.jaxrs.JAXRSInvoker.invoke(JAXRSInvoker.java:99) ~[cxf-rt-frontend-jaxrs-3.1.7.jar:3.1.7] Caused by: org.apache.openjpa.persistence.OptimisticLockException: An optimistic lock violation was detected when flushing object instance "org.apache.syncope.core.persistence.jpa.entity.user.JPAUPlainAttrValue-62f5f8bf-b390-4354-b5f8-bfb390a354e8" to the data store. This indicates that the object was concurrently modified in another transaction. at org.apache.openjpa.jdbc.kernel.PreparedStatementManagerImpl.flushAndUpdate(PreparedStatementManagerImpl.java:124) ~[openjpa-jdbc-2.4.1.jar:2.4.1] at org.apache.openjpa.jdbc.kernel.BatchingPreparedStatementManagerImpl.flushAndUpdate(BatchingPreparedStatementManagerImpl.java:79) ~[openjpa-jdbc-2.4.1.jar:2.4.1] at org.apache.openjpa.jdbc.kernel.PreparedStatementManagerImpl.flushInternal(PreparedStatementManagerImpl.java:100) ~[openjpa-jdbc-2.4.1.jar:2.4.1] at org.apache.openjpa.jdbc.kernel.PreparedStatementManagerImpl.flush(PreparedStatementManagerImpl.java:88) ~[openjpa-jdbc-2.4.1.jar:2.4.1] at org.apache.openjpa.jdbc.kernel.ConstraintUpdateManager.flush(ConstraintUpdateManager.java:550) ~[openjpa-jdbc-2.4.1.jar:2.4.1] at org.apache.openjpa.jdbc.kernel.ConstraintUpdateManager.flush(ConstraintUpdateManager.java:120) ~[openjpa-jdbc-2.4.1.jar:2.4.1] at org.apache.openjpa.jdbc.kernel.BatchingConstraintUpdateManager.flush(BatchingConstraintUpdateManager.java:59) ~[openjpa-jdbc-2.4.1.jar:2.4.1] at org.apache.openjpa.jdbc.kernel.AbstractUpdateManager.flush(AbstractUpdateManager.java:104) ~[openjpa-jdbc-2.4.1.jar:2.4.1] at org.apache.openjpa.jdbc.kernel.AbstractUpdateManager.flush(AbstractUpdateManager.java:77) ~[openjpa-jdbc-2.4.1.jar:2.4.1] at org.apache.openjpa.jdbc.kernel.JDBCStoreManager.flush(JDBCStoreManager.java:731) ~[openjpa-jdbc-2.4.1.jar:2.4.1] at org.apache.openjpa.kernel.DelegatingStoreManager.flush(DelegatingStoreManager.java:131) ~[openjpa-kernel-2.4.1.jar:2.4.1] at org.apache.openjpa.datacache.DataCacheStoreManager.flush(DataCacheStoreManager.java:668) ~[openjpa-kernel-2.4.1.jar:2.4.1] at org.apache.openjpa.kernel.DelegatingStoreManager.flush(DelegatingStoreManager.java:131) ~[openjpa-kernel-2.4.1.jar:2.4.1] ... 131 more