I'm implementing notifications using Spring Boot and notifying users in a different thread using @Async.
Without this annotation, everything works well, but for when I put it on the method I use to notify, in only one observable entity, the observers don't get notified and I get this stack trace:
Unexpected exception occurred invoking async method: public void pt.ulisboa.tecnico.socialsoftware.tutor.notifications.NotificationServic e.notifyObservers(package.notifications.Observable,package.notifications.domain.Notification,ppackage.user.User) org.hibernate.exception.GenericJDBCException: could not initialize a collection: [package.course.CourseExecution.users#11] at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:47) ~[hibernate-core-5.4.9.Final.jar:5.4.9.Final] at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:113) ~[hibernate-core-5.4.9.Final.jar:5.4.9.Final] at org.hibernate.loader.collection.plan.AbstractLoadPlanBasedCollectionInitializer.initialize(AbstractLoadPlanBasedCollectionInitializer.java:97) ~[hibernate-core-5.4.9.Final.jar:5.4.9.Final] at org.hibernate.persister.collection.AbstractCollectionPersister.initialize(AbstractCollectionPersister.java:707) ~[hibernate-core-5.4.9.Final.jar:5.4.9.Final] at org.hibernate.event.internal.DefaultInitializeCollectionEventListener.onInitializeCollection(DefaultInitializeCollectionEventListener.java:76) ~[hibernate-core-5.4.9.Final.jar:5.4.9.Final] at org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:108) ~[hibernate-core-5.4.9.Final.jar:5.4.9.Final] at org.hibernate.internal.SessionImpl.initializeCollection(SessionImpl.java:2145) ~[hibernate-core-5.4.9.Final.jar:5.4.9.Final] at org.hibernate.collection.internal.AbstractPersistentCollection$4.doWork(AbstractPersistentCollection.java:589) ~[hibernate-core-5.4.9.Final.jar:5.4.9.Final] at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:264) ~[hibernate-core-5.4.9.Final.jar:5.4.9.Final] at org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:585) ~[hibernate-core-5.4.9.Final.jar:5.4.9.Final] at org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:149) ~[hibernate-core-5.4.9.Final.jar:5.4.9.Final] at org.hibernate.collection.internal.PersistentSet.toString(PersistentSet.java:327) ~[hibernate-core-5.4.9.Final.jar:5.4.9.Final] at java.base/java.lang.String.valueOf(String.java:2951) ~[na:na] at java.base/java.io.PrintStream.println(PrintStream.java:897) ~[na:na] at package.course.CourseExecution.Notify(CourseExecution.java:210) ~[classes/:na] at package.notifications.NotificationService.notifyObservers(NotificationService.java:82) ~[classes/:na] at package.notifications.NotificationService$$FastClassBySpringCGLIB$$d43e740c.invoke(<generated>) ~[classes/:na] at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) ~[spring-core-5.2.2.RELEASE.jar:5.2.2.RELEASE] at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:769) ~[spring-aop-5.2.2.RELEASE.jar:5.2.2.RELEASE] at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-5.2.2.RELEASE.jar:5.2.2.RELEASE] at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:747) ~[spring-aop-5.2.2.RELEASE.jar:5.2.2.RELEASE] at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:366) ~[spring-tx-5.2.2.RELEASE.jar:5.2.2.RELEASE] at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:99) ~[spring-tx-5.2.2.RELEASE.jar:5.2.2.RELEASE] at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.2.2.RELEASE.jar:5.2.2.RELEASE] at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:747) ~[spring-aop-5.2.2.RELEASE.jar:5.2.2.RELEASE] at org.springframework.aop.interceptor.AsyncExecutionInterceptor.lambda$invoke$0(AsyncExecutionInterceptor.java:115) ~[spring-aop-5.2.2.RELEASE.jar:5.2.2.RELEASE] at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264) ~[na:na] at java.base/java.lang.Thread.run(Thread.java:834) ~[na:na] Caused by: org.postgresql.util.PSQLException: This statement has been closed. at org.postgresql.jdbc.PgStatement.checkClosed(PgStatement.java:705) ~[postgresql-42.2.8.jar:42.2.8] at org.postgresql.jdbc.PgPreparedStatement.setInt(PgPreparedStatement.java:270) ~[postgresql-42.2.8.jar:42.2.8] at com.zaxxer.hikari.pool.HikariProxyPreparedStatement.setInt(HikariProxyPreparedStatement.java) ~[HikariCP-3.4.1.jar:na] at org.hibernate.type.descriptor.sql.IntegerTypeDescriptor$1.doBind(IntegerTypeDescriptor.java:46) ~[hibernate-core-5.4.9.Final.jar:5.4.9.Final] at org.hibernate.type.descriptor.sql.BasicBinder.bind(BasicBinder.java:73) ~[hibernate-core-5.4.9.Final.jar:5.4.9.Final] at org.hibernate.type.AbstractStandardBasicType.nullSafeSet(AbstractStandardBasicType.java:276) ~[hibernate-core-5.4.9.Final.jar:5.4.9.Final] at org.hibernate.type.AbstractStandardBasicType.nullSafeSet(AbstractStandardBasicType.java:271) ~[hibernate-core-5.4.9.Final.jar:5.4.9.Final] at org.hibernate.loader.plan.exec.internal.AbstractLoadPlanBasedLoader.bindPositionalParameters(AbstractLoadPlanBasedLoader.java:320) ~[hibernate-core-5.4.9.Final.jar:5.4.9.Final] at org.hibernate.loader.plan.exec.internal.AbstractLoadPlanBasedLoader.bindParameterValues(AbstractLoadPlanBasedLoader.java:291) ~[hibernate-core-5.4.9.Final.jar:5.4.9.Final] at org.hibernate.loader.plan.exec.internal.AbstractLoadPlanBasedLoader.prepareQueryStatement(AbstractLoadPlanBasedLoader.java:210) ~[hibernate-core-5.4.9.Final.jar:5.4.9.Final] at org.hibernate.loader.plan.exec.internal.AbstractLoadPlanBasedLoader.executeQueryStatement(AbstractLoadPlanBasedLoader.java:162) ~[hibernate-core-5.4.9.Final.jar:5.4.9.Final] at org.hibernate.loader.plan.exec.internal.AbstractLoadPlanBasedLoader.executeLoad(AbstractLoadPlanBasedLoader.java:104) ~[hibernate-core-5.4.9.Final.jar:5.4.9.Final] at org.hibernate.loader.collection.plan.AbstractLoadPlanBasedCollectionInitializer.initialize(AbstractLoadPlanBasedCollectionInitializer.java:87) ~[hibernate-core-5.4.9.Final.jar:5.4.9.Final] ... 25 common frames omitted The Caused by exception message is always the same, the other one, not always
The methods mentioned in the trace are:
- The Notifier (Async method)
@Async("notifyExecutor") // Even with the default executor, the error occurs @Retryable( value = { SQLException.class }, backoff = @Backoff(delay = 5000)) @Transactional(isolation = Isolation.REPEATABLE_READ) public void notifyObservers(Observable observable, Notification notification, User exclude) { observable.Notify(notification, exclude); } - The method calling notifier (needed services are
@Autowired)
@Retryable(value = { SQLException.class }, backoff = @Backoff(delay = 5000)) @Transactional(isolation = Isolation.REPEATABLE_READ) public AnnouncementDto createAnnouncement(AnnouncementDto announcementDto) { checkIfConsistentAnnouncement(announcementDto); User user = getTeacher(announcementDto.getUserId()); CourseExecution courseExecution = getCourseExecution(announcementDto.getCourseExecutionId()); if (announcementDto.getCreationDate() == null) { announcementDto .setCreationDate(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"))); } // Announcement has a CourseExecution as attribute Announcement announcement = new Announcement(user, courseExecution, announcementDto); entityManager.persist(announcement); NotificationDto notification = NotificationsCreation.create(ADD_ANNOUNCEMENT_TITLE, List.of(announcement.getUser().getName()), ADD_ANNOUNCEMENT_CONTENT, List.of(announcement.getTitle(), user.getName()), Notification.Type.ANNOUNCEMENT); this.notify(courseExecution, notification, user); return new AnnouncementDto(announcement); } // Calls the Async method private void notify(CourseExecution course, NotificationDto notification, User user) { notificationService.notifyObservers(course, notificationService.createNotification(notification), user); } - The method where the error occurs (only when accessing elements)
@Override public void Notify(Notification notification, User user) { for (Observer observer : this.users) { // Error occurs here, doesn't get inside the loop if (((User) observer).getId() == user.getId()) { continue; } observer.update(this, notification); } } I've seen answers for these, but they don't apply here. What I think is very weird is the fact that this only occurs with Announcement and not with the other observables. Since I want the announcements for all observers in CourseExecution, I made CourseExecution observable and when there is a new announcement we notify all CourseExecution observers.
Can someone help me please?
this.usersfield, which is not present in the code snippets. Could you please describe more info about this field?CourseExecution(the observable in question) and some other stuff that isn't needed here, implements the methodupdatethat adds the Notification to a listDataSourceconfiguration look like?