From 322ab74a4d4c6d8d2875d0127fd4f3dab814d9a5 Mon Sep 17 00:00:00 2001 From: srosse <none@none> Date: Tue, 4 Feb 2014 09:42:55 +0100 Subject: [PATCH] OO-968: refactor the method to send notifications emails, use annotation for the hibernate mapping, --- .../org/olat/basesecurity/BaseSecurity.java | 8 + .../basesecurity/BaseSecurityManager.java | 12 + .../InfoMessageNotificationHandler.java | 20 +- .../persistence/_spring/core_persistence.xml | 4 +- .../notifications/NotificationsManager.java | 8 - .../model/InstantMessageImpl.java | 4 +- .../NotificationsManagerImpl.java | 452 +++++++----------- .../olat/notifications/PublisherImpl.hbm.xml | 59 --- .../org/olat/notifications/PublisherImpl.java | 88 +++- .../olat/notifications/SubscriberImpl.hbm.xml | 33 -- .../olat/notifications/SubscriberImpl.java | 70 ++- .../_spring/notificationsContext.xml | 3 + .../org/olat/upgrade/OLATUpgrade_8_1_0.java | 2 +- .../database/mysql/alter_9_3_0_to_9_4_0.sql | 16 +- .../database/oracle/alter_9_3_0_to_9_4_0.sql | 16 +- .../postgresql/alter_9_3_0_to_9_4_0.sql | 16 +- .../NotificationsManagerTest.java | 112 ++--- 17 files changed, 425 insertions(+), 498 deletions(-) delete mode 100644 src/main/java/org/olat/notifications/PublisherImpl.hbm.xml delete mode 100644 src/main/java/org/olat/notifications/SubscriberImpl.hbm.xml diff --git a/src/main/java/org/olat/basesecurity/BaseSecurity.java b/src/main/java/org/olat/basesecurity/BaseSecurity.java index 4a7cf9ec9d8..c8125dabf1a 100644 --- a/src/main/java/org/olat/basesecurity/BaseSecurity.java +++ b/src/main/java/org/olat/basesecurity/BaseSecurity.java @@ -268,6 +268,14 @@ public interface BaseSecurity { * @return the identity or null */ public Identity loadIdentityByKey(Long identityKey, boolean strict); + + /** + * Method to load all the identities. Paging is mandatory! + * @param firstResult + * @param maxResults + * @return + */ + public List<Identity> loadIdentities(int firstResult, int maxResults); /** * get number of users with last login greater than lastLoginLimit diff --git a/src/main/java/org/olat/basesecurity/BaseSecurityManager.java b/src/main/java/org/olat/basesecurity/BaseSecurityManager.java index 01b48d82fa9..fcf34ff99fd 100644 --- a/src/main/java/org/olat/basesecurity/BaseSecurityManager.java +++ b/src/main/java/org/olat/basesecurity/BaseSecurityManager.java @@ -1401,6 +1401,18 @@ public class BaseSecurityManager extends BasicManager implements BaseSecurity { .setParameter("keys", identityKeys) .getResultList(); } + + public List<Identity> loadIdentities(int firstResult, int maxResults) { + StringBuilder sb = new StringBuilder(); + sb.append("select ident from ").append(IdentityImpl.class.getName()).append(" as ident order by ident.key"); + + return DBFactory.getInstance().getCurrentEntityManager() + .createQuery(sb.toString(), Identity.class) + .setFirstResult(firstResult) + .setMaxResults(maxResults) + .getResultList(); + + } /** * diff --git a/src/main/java/org/olat/commons/info/notification/InfoMessageNotificationHandler.java b/src/main/java/org/olat/commons/info/notification/InfoMessageNotificationHandler.java index b54975c1268..f0348eff5c1 100644 --- a/src/main/java/org/olat/commons/info/notification/InfoMessageNotificationHandler.java +++ b/src/main/java/org/olat/commons/info/notification/InfoMessageNotificationHandler.java @@ -38,6 +38,7 @@ import org.olat.core.util.notifications.Subscriber; import org.olat.core.util.notifications.SubscriptionInfo; import org.olat.core.util.notifications.items.SubscriptionListItem; import org.olat.core.util.notifications.items.TitleItem; +import org.olat.core.util.resource.OresHelper; import org.olat.repository.RepositoryManager; /** @@ -57,6 +58,10 @@ public class InfoMessageNotificationHandler extends LogDelegator implements Noti SubscriptionInfo si = null; Publisher p = subscriber.getPublisher(); Date latestNews = p.getLatestNewsDate(); + + if(14745600 == p.getKey()) { + System.out.println(latestNews); + } // do not try to create a subscription info if state is deleted - results in // exceptions, course @@ -67,22 +72,11 @@ public class InfoMessageNotificationHandler extends LogDelegator implements Noti final Long resId = subscriber.getPublisher().getResId(); final String resName = subscriber.getPublisher().getResName(); String resSubPath = subscriber.getPublisher().getSubidentifier(); - String businessPath = subscriber.getPublisher().getBusinessPath(); String title = RepositoryManager.getInstance().lookupDisplayNameByOLATResourceableId(resId); si = new SubscriptionInfo(subscriber.getKey(), p.getType(), new TitleItem(title, CSS_CLASS_ICON), null); - OLATResourceable ores = new OLATResourceable() { - @Override - public String getResourceableTypeName() { - return resName; - } - @Override - public Long getResourceableId() { - return resId; - } - }; - - List<InfoMessage> infos = InfoMessageManager.getInstance().loadInfoMessageByResource(ores, resSubPath, businessPath, compareDate, null, 0, 0); + OLATResourceable ores = OresHelper.createOLATResourceableInstance(resName, resId); + List<InfoMessage> infos = InfoMessageManager.getInstance().loadInfoMessageByResource(ores, resSubPath, null, compareDate, null, 0, 0); for(InfoMessage info:infos) { String desc = info.getTitle(); String tooltip = info.getMessage(); diff --git a/src/main/java/org/olat/core/commons/persistence/_spring/core_persistence.xml b/src/main/java/org/olat/core/commons/persistence/_spring/core_persistence.xml index 1c43c1649e2..695f914eb11 100644 --- a/src/main/java/org/olat/core/commons/persistence/_spring/core_persistence.xml +++ b/src/main/java/org/olat/core/commons/persistence/_spring/core_persistence.xml @@ -54,8 +54,6 @@ <mapping-file>org/olat/modules/openmeetings/model/OpenMeetingsReference.hbm.xml</mapping-file> <mapping-file>org/olat/properties/Property.hbm.xml</mapping-file> <mapping-file>org/olat/catalog/CatalogEntryImpl.hbm.xml</mapping-file> - <mapping-file>org/olat/notifications/PublisherImpl.hbm.xml</mapping-file> - <mapping-file>org/olat/notifications/SubscriberImpl.hbm.xml</mapping-file> <mapping-file>org/olat/registration/TemporaryKeyImpl.hbm.xml</mapping-file> <mapping-file>org/olat/course/statistic/daily/DailyStat.hbm.xml</mapping-file> <mapping-file>org/olat/course/statistic/dayofweek/DayOfWeekStat.hbm.xml</mapping-file> @@ -125,6 +123,8 @@ <class>org.olat.modules.qpool.model.QEducationalContext</class> <class>org.olat.modules.qpool.model.QItemType</class> <class>org.olat.modules.qpool.model.QLicense</class> + <class>org.olat.notifications.SubscriberImpl</class> + <class>org.olat.notifications.PublisherImpl</class> <class>org.olat.ims.lti.model.LTIOutcomeImpl</class> <properties> <property name="hibernate.generate_statistics" value="true"/> diff --git a/src/main/java/org/olat/core/util/notifications/NotificationsManager.java b/src/main/java/org/olat/core/util/notifications/NotificationsManager.java index 401588b59c9..34035299518 100644 --- a/src/main/java/org/olat/core/util/notifications/NotificationsManager.java +++ b/src/main/java/org/olat/core/util/notifications/NotificationsManager.java @@ -111,14 +111,6 @@ public abstract class NotificationsManager extends BasicManager { */ public abstract Publisher getOrCreatePublisher(final SubscriptionContext scontext, final PublisherData pdata); - /** - * Modify the publisher - * @param oldContext - * @param newContext - * @return - */ - public abstract Publisher updatePublisher(SubscriptionContext oldContext, SubscriptionContext newContext); - public abstract List<Publisher> getAllPublisher(); /** diff --git a/src/main/java/org/olat/instantMessaging/model/InstantMessageImpl.java b/src/main/java/org/olat/instantMessaging/model/InstantMessageImpl.java index e103cf24702..b4d65a5a914 100644 --- a/src/main/java/org/olat/instantMessaging/model/InstantMessageImpl.java +++ b/src/main/java/org/olat/instantMessaging/model/InstantMessageImpl.java @@ -56,8 +56,8 @@ public class InstantMessageImpl implements InstantMessage, Persistable, CreateIn private static final long serialVersionUID = 1425964260797865080L; @Id - @GeneratedValue(generator = "system-uuid") - @GenericGenerator(name = "system-uuid", strategy = "hilo") + @GeneratedValue(generator = "system-uuid") + @GenericGenerator(name = "system-uuid", strategy = "hilo") @Column(name="id", nullable=false, unique=true, insertable=true, updatable=false) private Long key; diff --git a/src/main/java/org/olat/notifications/NotificationsManagerImpl.java b/src/main/java/org/olat/notifications/NotificationsManagerImpl.java index b4bcd7d3185..7ca5892090c 100644 --- a/src/main/java/org/olat/notifications/NotificationsManagerImpl.java +++ b/src/main/java/org/olat/notifications/NotificationsManagerImpl.java @@ -25,13 +25,11 @@ package org.olat.notifications; -import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Calendar; import java.util.Collection; import java.util.Collections; import java.util.Date; -import java.util.Deque; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -46,8 +44,9 @@ import javax.persistence.TypedQuery; import org.hibernate.FlushMode; import org.olat.ControllerFactory; +import org.olat.basesecurity.BaseSecurity; import org.olat.core.CoreSpringFactory; -import org.olat.core.commons.persistence.DBFactory; +import org.olat.core.commons.persistence.DB; import org.olat.core.commons.persistence.DBQuery; import org.olat.core.commons.persistence.PersistenceHelper; import org.olat.core.gui.translator.Translator; @@ -95,6 +94,7 @@ public class NotificationsManagerImpl extends NotificationsManager implements Us private static final int PUB_STATE_OK = 0; private static final int PUB_STATE_NOT_OK = 1; + private static final int BATCH_SIZE = 100; private static final String LATEST_EMAIL_USER_PROP = "noti_latest_email"; private final SubscriptionInfo NOSUBSINFO = new NoSubscriptionInfo(); @@ -107,6 +107,10 @@ public class NotificationsManagerImpl extends NotificationsManager implements Us private static final Map<String, Integer> INTERVAL_DEF_MAP = buildIntervalMap(); private Object lockObject = new Object(); + private DB dbInstance; + private BaseSecurity securityManager; + private PropertyManager propertyManager; + /** * [used by spring] * @param userDeletionManager @@ -116,6 +120,30 @@ public class NotificationsManagerImpl extends NotificationsManager implements Us INSTANCE = this; } + /** + * [used by Spring] + * @param dbInstance + */ + public void setDbInstance(DB dbInstance) { + this.dbInstance = dbInstance; + } + + /** + * [user by Spring] + * @param securityManager + */ + public void setSecurityManager(BaseSecurity securityManager) { + this.securityManager = securityManager; + } + + /** + * [used by Spring] + * @param propertyManager + */ + public void setPropertyManager(PropertyManager propertyManager) { + this.propertyManager = propertyManager; + } + /** * @param resName * @param resId @@ -134,7 +162,8 @@ public class NotificationsManagerImpl extends NotificationsManager implements Us businessPath = businessPath.substring(0, 230); } PublisherImpl pi = new PublisherImpl(resName, resId, subidentifier, type, data, businessPath, new Date(), PUB_STATE_OK); - DBFactory.getInstance().saveObject(pi); + pi.setCreationDate(new Date()); + dbInstance.getCurrentEntityManager().persist(pi); return pi; } @@ -146,9 +175,10 @@ public class NotificationsManagerImpl extends NotificationsManager implements Us */ protected Subscriber doCreateAndPersistSubscriber(Publisher persistedPublisher, Identity listener) { SubscriberImpl si = new SubscriberImpl(persistedPublisher, listener); + si.setCreationDate(new Date()); si.setLastModified(new Date()); si.setLatestEmailed(new Date()); - DBFactory.getInstance().saveObject(si); + dbInstance.getCurrentEntityManager().persist(si); return si; } @@ -173,13 +203,13 @@ public class NotificationsManagerImpl extends NotificationsManager implements Us @Override public List<Subscriber> getSubscribers(Identity identity, List<String> types) { StringBuilder sb = new StringBuilder(); - sb.append("select sub from ").append(SubscriberImpl.class.getName()).append(" as sub ") + sb.append("select sub from notisub as sub ") .append("inner join fetch sub.publisher as publisher ") .append("where sub.identity = :anIdentity"); if(types != null && !types.isEmpty()) { sb.append(" and publisher.type in (:types)"); } - TypedQuery<Subscriber> query = DBFactory.getInstance().getCurrentEntityManager().createQuery(sb.toString(), Subscriber.class); + TypedQuery<Subscriber> query = dbInstance.getCurrentEntityManager().createQuery(sb.toString(), Subscriber.class); query.setParameter("anIdentity", identity); if(types != null && !types.isEmpty()) { query.setParameter("types", types); @@ -195,11 +225,11 @@ public class NotificationsManagerImpl extends NotificationsManager implements Us @Override public List<Subscriber> getValidSubscribers(Identity identity) { StringBuilder q = new StringBuilder(); - q.append("select sub from ").append(SubscriberImpl.class.getName()).append(" sub ") + q.append("select sub from notisub sub ") .append(" inner join fetch sub.publisher as pub ") .append(" where sub.identity.key=:anIdentityKey and pub.state=").append(PUB_STATE_OK); - return DBFactory.getInstance().getCurrentEntityManager() + return dbInstance.getCurrentEntityManager() .createQuery(q.toString(), Subscriber.class) .setParameter("anIdentityKey", identity.getKey()) .getResultList(); @@ -211,54 +241,24 @@ public class NotificationsManagerImpl extends NotificationsManager implements Us @Override public List<Subscriber> getValidSubscribersOf(Publisher publisher) { StringBuilder q = new StringBuilder(); - q.append("select sub from ").append(SubscriberImpl.class.getName()).append(" sub ") + q.append("select sub from notisub sub ") .append(" inner join fetch sub.identity") .append(" where sub.publisher = :publisher and sub.publisher.state=").append(PUB_STATE_OK); - return DBFactory.getInstance().getCurrentEntityManager() + return dbInstance.getCurrentEntityManager() .createQuery(q.toString(), Subscriber.class) .setParameter("publisher", publisher) .getResultList(); } - /** - * @return a list of subscribers ordered by the name of the identity of the - * subscription - */ - protected List<Subscriber> getAllValidSubscribers() { - StringBuilder q = new StringBuilder(); - q.append("select sub from ").append(SubscriberImpl.class.getName()).append(" sub") - .append(" inner join fetch sub.publisher as pub") - .append(" where pub.state=").append(PUB_STATE_OK).append(" order by sub.identity.name"); - return DBFactory.getInstance().getCurrentEntityManager() - .createQuery(q.toString(), Subscriber.class) - .getResultList(); - } - - protected List<Subscriber> getAllValidSubscribers(int firstResult, int maxResults) { - StringBuilder q = new StringBuilder(); - q.append("select sub from ").append(SubscriberImpl.class.getName()).append(" sub") - .append(" inner join fetch sub.publisher as pub") - .append(" where pub.state=").append(PUB_STATE_OK).append(" order by sub.identity.name, sub.key"); - TypedQuery<Subscriber> query = DBFactory.getInstance().getCurrentEntityManager() - .createQuery(q.toString(), Subscriber.class); - if(firstResult >= 0) { - query.setFirstResult(firstResult); - } - if(maxResults > 0) { - query.setMaxResults(maxResults); - } - return query.getResultList(); - } - @Override public List<SubscriptionInfo> getSubscriptionInfos(Identity identity, String publisherType) { StringBuilder sb = new StringBuilder(); - sb.append("select sub from ").append(SubscriberImpl.class.getName()).append(" sub") + sb.append("select sub from notisub sub") .append(" inner join fetch sub.publisher as pub") .append(" where sub.identity=:identity and pub.type=:type and pub.state=:aState"); - List<Subscriber> subscribers = DBFactory.getInstance().getCurrentEntityManager() + List<Subscriber> subscribers = dbInstance.getCurrentEntityManager() .createQuery(sb.toString(), Subscriber.class) .setParameter("aState", PUB_STATE_OK) .setParameter("type", publisherType) @@ -284,114 +284,89 @@ public class NotificationsManagerImpl extends NotificationsManager implements Us } return sis; } - + public void notifyAllSubscribersByEmail() { - logAudit("starting notification cronjob for email sending", null); + logAudit("starting notification cronjob to send email", null); WorkThreadInformations.setLongRunningTask("sendNotifications"); - - List<SubscriptionItem> items = new ArrayList<SubscriptionItem>(); - List<Subscriber> subsToUpdate = null; - boolean veto = false; - Subscriber latestSub = null; - Identity ident = null; - Translator translator = null; - Locale locale = null; + int counter = 0; + List<Identity> identities; + do { + identities = securityManager.loadIdentities(counter, BATCH_SIZE); + for(Identity identity:identities) { + processSubscribersByEmail(identity); + } + counter += identities.size(); + dbInstance.commitAndCloseSession(); + } while(identities.size() == BATCH_SIZE); - Date defaultCompareDate = getDefaultCompareDate(); + // done, purge last entry + WorkThreadInformations.unsetLongRunningTask("sendNotifications"); + logAudit("end notification cronjob to send email", null); + } + + private void processSubscribersByEmail(Identity ident) { long start = System.currentTimeMillis(); + if(ident.getStatus().compareTo(Identity.STATUS_VISIBLE_LIMIT) >= 0) { + return;//send only to active user + } - Set<Long> subs = new HashSet<Long>(); - - // loop all subscriptions, as its ordered by identity, they get collected for each identity - for(AllSubscriberIterator subscriberIt= new AllSubscriberIterator(); subscriberIt.hasNext(); ){ - try { - Subscriber sub = subscriberIt.next(); - ident = sub.getIdentity(); - - if(subs.contains(sub.getKey())) { - System.out.println("ERROR"); - } else { - subs.add(sub.getKey()); - } + String userInterval = getUserIntervalOrDefault(ident); + if("never".equals(userInterval)) { + return; + } - if (latestSub == null || (!ident.equalsByPersistableKey(latestSub.getIdentity()))) { - // first time or next identity => prepare for a new user and send old data. - - // send a mail - notifySubscribersByEmail(latestSub, items, subsToUpdate, translator, start, veto); - - // prepare for new user - start = System.currentTimeMillis(); - locale = I18nManager.getInstance().getLocaleOrDefault(ident.getUser().getPreferences().getLanguage()); - translator = Util.createPackageTranslator(NotificationsManagerImpl.class, locale); - items = new ArrayList<SubscriptionItem>(); - subsToUpdate = new ArrayList<Subscriber>(); - latestSub = sub; - veto = false; - - PropertyManager pm = PropertyManager.getInstance(); - Property p = pm.findProperty(ident, null, null, null, LATEST_EMAIL_USER_PROP); - if(p != null) { - Date latestEmail = new Date(p.getLongValue()); - String userInterval = getUserIntervalOrDefault(ident); - Date compareDate = getCompareDateFromInterval(userInterval); - if(latestEmail.after(compareDate)) { - veto = true; - } - } - } - - if(veto) { - continue; - } - // only send notifications to active users - if(ident.getStatus().compareTo(Identity.STATUS_VISIBLE_LIMIT) >= 0) { - continue; - } - // this user doesn't want notifications - String userInterval = getUserIntervalOrDefault(ident); - if("never".equals(userInterval)) { - continue; - } - - // find out the info that happened since the date the last email was sent. Only those infos need to be emailed. - // mail is only sent if users interval is over. - Date compareDate = getCompareDateFromInterval(userInterval); - Date latestEmail = sub.getLatestEmailed(); - - SubscriptionItem subsitem = null; - if (latestEmail == null || compareDate.after(latestEmail)){ - // no notif. ever sent until now - if (latestEmail == null) { - latestEmail = defaultCompareDate; - } else if (latestEmail.before(defaultCompareDate)) { - //no notification older than a month - latestEmail = defaultCompareDate; - } - subsitem = createSubscriptionItem(sub, locale, SubscriptionInfo.MIME_PLAIN, SubscriptionInfo.MIME_PLAIN, latestEmail); - } else if(latestEmail != null && latestEmail.after(compareDate)) { - //already send an email within the user's settings interval - veto = true; - } - if (subsitem != null) { - items.add(subsitem); - subsToUpdate.add(sub); + Date compareDate = getCompareDateFromInterval(userInterval); + Property p = propertyManager.findProperty(ident, null, null, null, LATEST_EMAIL_USER_PROP); + if(p != null) { + Date latestEmail = new Date(p.getLongValue()); + if(latestEmail.after(compareDate)) { + return;//nothing to do + } + } + + Date defaultCompareDate = getDefaultCompareDate(); + List<Subscriber> subscribers = getSubscribers(ident); + if(subscribers.isEmpty()) { + return; + } + + String langPrefs = null; + if(ident.getUser() != null && ident.getUser().getPreferences() != null) { + langPrefs = ident.getUser().getPreferences().getLanguage(); + } + Locale locale = I18nManager.getInstance().getLocaleOrDefault(langPrefs); + + boolean veto = false; + Subscriber latestSub = null; + List<SubscriptionItem> items = new ArrayList<>(); + List<Subscriber> subsToUpdate = new ArrayList<>(); + for(Subscriber sub:subscribers) { + Date latestEmail = sub.getLatestEmailed(); + + SubscriptionItem subsitem = null; + if (latestEmail == null || compareDate.after(latestEmail)){ + // no notif. ever sent until now + if (latestEmail == null) { + latestEmail = defaultCompareDate; + } else if (latestEmail.before(defaultCompareDate)) { + //no notification older than a month + latestEmail = defaultCompareDate; } - } catch(Error er) { - logError("Error in NotificationsManagerImpl.notifyAllSubscribersByEmail, ", er); - throw er; - } catch(RuntimeException re) { - logError("RuntimeException in NotificationsManagerImpl.notifyAllSubscribersByEmail,", re); - throw re; - } catch(Throwable th) { - logError("Throwable in NotificationsManagerImpl.notifyAllSubscribersByEmail,", th); + subsitem = createSubscriptionItem(sub, locale, SubscriptionInfo.MIME_PLAIN, SubscriptionInfo.MIME_PLAIN, latestEmail); + } else if(latestEmail != null && latestEmail.after(compareDate)) { + //already send an email within the user's settings interval + veto = true; + } + if (subsitem != null) { + items.add(subsitem); + subsToUpdate.add(sub); } - } // for + latestSub = sub; + } - // done, purge last entry + Translator translator = Util.createPackageTranslator(NotificationsManagerImpl.class, locale); notifySubscribersByEmail(latestSub, items, subsToUpdate, translator, start, veto); - WorkThreadInformations.unsetLongRunningTask("sendNotifications"); } private void notifySubscribersByEmail(Subscriber latestSub, List<SubscriptionItem> items, List<Subscriber> subsToUpdate, Translator translator, long start, boolean veto) { @@ -403,26 +378,25 @@ public class NotificationsManagerImpl extends NotificationsManager implements Us Identity curIdent = latestSub.getIdentity(); boolean sentOk = sendMailToUserAndUpdateSubscriber(curIdent, items, translator, subsToUpdate); if (sentOk) { - PropertyManager pm = PropertyManager.getInstance(); - Property p = pm.findProperty(curIdent, null, null, null, LATEST_EMAIL_USER_PROP); - if(p == null) { - p = pm.createUserPropertyInstance(curIdent, null, LATEST_EMAIL_USER_PROP, null, null, null, null); - p.setLongValue(new Date().getTime()); - pm.saveProperty(p); - } else { - p.setLongValue(new Date().getTime()); - pm.updateProperty(p); - } + Property p = propertyManager.findProperty(curIdent, null, null, null, LATEST_EMAIL_USER_PROP); + if(p == null) { + p = propertyManager.createUserPropertyInstance(curIdent, null, LATEST_EMAIL_USER_PROP, null, null, null, null); + p.setLongValue(new Date().getTime()); + propertyManager.saveProperty(p); + } else { + p.setLongValue(new Date().getTime()); + propertyManager.updateProperty(p); + } - StringBuilder mailLog = new StringBuilder(); - mailLog.append("Notifications mailed for ").append(curIdent.getName()).append(' ').append(items.size()).append(' ').append((System.currentTimeMillis() - start)).append("ms"); - logAudit(mailLog.toString()); + StringBuilder mailLog = new StringBuilder(); + mailLog.append("Notifications mailed for ").append(curIdent.getName()).append(' ').append(items.size()).append(' ').append((System.currentTimeMillis() - start)).append("ms"); + logAudit(mailLog.toString()); } else { logAudit("Error sending notification email to : " + curIdent.getName()); } } //collecting the SubscriptionItem can potentially make a lot of DB calls - DBFactory.getInstance().intermediateCommit(); + dbInstance.intermediateCommit(); } /** @@ -458,6 +432,11 @@ public class NotificationsManagerImpl extends NotificationsManager implements Us * @see org.olat.core.util.notifications.NotificationsManager#getUserIntervalOrDefault(org.olat.core.id.Identity) */ public String getUserIntervalOrDefault(Identity ident){ + if(ident.getUser() == null || ident.getUser().getPreferences() == null) { + logWarn("User " + ident.getName() + " has no preferences invalid", null); + return getDefaultNotificationInterval(); + } + String userInterval = ident.getUser().getPreferences().getNotificationInterval(); if (!StringHelper.containsNonWhitespace(userInterval)) userInterval = getDefaultNotificationInterval(); List<String> avIntvls = getEnabledNotificationIntervals(); @@ -485,14 +464,13 @@ public class NotificationsManagerImpl extends NotificationsManager implements Us } StringBuilder q = new StringBuilder(); - q.append("select sub from ").append(SubscriberImpl.class.getName()).append(" sub ") + q.append("select sub from notisub sub ") .append(" inner join fetch sub.publisher where sub.key in (:aKey)"); - EntityManager em = DBFactory.getInstance().getCurrentEntityManager(); + EntityManager em = dbInstance.getCurrentEntityManager(); List<Long> keys = PersistenceHelper.toKeys(subscribersToUpdate); List<Subscriber> subscribers = em.createQuery(q.toString(), Subscriber.class) .setParameter("aKey", keys) - .setLockMode(LockModeType.PESSIMISTIC_WRITE) .getResultList(); for (Subscriber subscriber :subscribers) { @@ -546,11 +524,11 @@ public class NotificationsManagerImpl extends NotificationsManager implements Us @Override public Subscriber getSubscriber(Long key) { StringBuilder q = new StringBuilder(); - q.append("select sub from ").append(SubscriberImpl.class.getName()).append(" as sub") + q.append("select sub from notisub as sub") .append(" inner join fetch sub.publisher ") .append(" where sub.key=:aKey"); - List<Subscriber> res = DBFactory.getInstance().getCurrentEntityManager() + List<Subscriber> res = dbInstance.getCurrentEntityManager() .createQuery(q.toString(), Subscriber.class) .setParameter("aKey", key.longValue()) .getResultList(); @@ -602,7 +580,7 @@ public class NotificationsManagerImpl extends NotificationsManager implements Us @Override public Publisher getPublisher(SubscriptionContext subsContext) { StringBuilder q = new StringBuilder(); - q.append("select pub from ").append(PublisherImpl.class.getName()).append(" pub ") + q.append("select pub from notipublisher pub ") .append(" where pub.resName=:resName and pub.resId = :resId"); if(StringHelper.containsNonWhitespace(subsContext.getSubidentifier())) { q.append(" and pub.subidentifier=:subidentifier"); @@ -610,7 +588,7 @@ public class NotificationsManagerImpl extends NotificationsManager implements Us q.append(" and (pub.subidentifier='' or pub.subidentifier is null)"); } - TypedQuery<Publisher> query = DBFactory.getInstance().getCurrentEntityManager() + TypedQuery<Publisher> query = dbInstance.getCurrentEntityManager() .createQuery(q.toString(), Publisher.class) .setParameter("resName", subsContext.getResName()) .setParameter("resId", subsContext.getResId()); @@ -628,16 +606,17 @@ public class NotificationsManagerImpl extends NotificationsManager implements Us Publisher pub = getPublisher(subsContext); if(pub != null && pub.getKey() != null) { //prevent optimistic lock issue - DBFactory.getInstance().getCurrentEntityManager().detach(pub); - pub = DBFactory.getInstance().getCurrentEntityManager().find(PublisherImpl.class, pub.getKey(), LockModeType.PESSIMISTIC_WRITE); + dbInstance.getCurrentEntityManager().detach(pub); + pub = dbInstance.getCurrentEntityManager() + .find(PublisherImpl.class, pub.getKey(), LockModeType.PESSIMISTIC_WRITE); } return pub; } @Override public List<Publisher> getAllPublisher() { - String q = "select pub from org.olat.notifications.PublisherImpl pub"; - return DBFactory.getInstance().getCurrentEntityManager().createQuery(q, Publisher.class) + String q = "select pub from notipublisher pub"; + return dbInstance.getCurrentEntityManager().createQuery(q, Publisher.class) .getResultList(); } @@ -647,8 +626,8 @@ public class NotificationsManagerImpl extends NotificationsManager implements Us * @return a list of publishers belonging to the resource */ private List<Publisher> getPublishers(String resName, Long resId) { - String q = "select pub from org.olat.notifications.PublisherImpl pub" + " where pub.resName = :resName" + " and pub.resId = :resId"; - return DBFactory.getInstance().getCurrentEntityManager() + String q = "select pub from notipublisher pub where pub.resName=:resName and pub.resId= :resId"; + return dbInstance.getCurrentEntityManager() .createQuery(q, Publisher.class) .setParameter("resName", resName) .setParameter("resId", resId.longValue()) @@ -669,13 +648,13 @@ public class NotificationsManagerImpl extends NotificationsManager implements Us List<Publisher> pubs = getPublishers(type, id); if(pubs.isEmpty()) return; - String q1 = "delete from org.olat.notifications.SubscriberImpl sub where sub.publisher in (:publishers)"; - DBQuery query1 = DBFactory.getInstance().createQuery(q1); + String q1 = "delete from notisub sub where sub.publisher in (:publishers)"; + DBQuery query1 = dbInstance.createQuery(q1); query1.setParameterList("publishers", pubs); query1.executeUpdate(FlushMode.AUTO); - String q2 = "delete from org.olat.notifications.PublisherImpl pub where pub in (:publishers)"; - DBQuery query2 = DBFactory.getInstance().createQuery(q2); + String q2 = "delete from notipublisher pub where pub in (:publishers)"; + DBQuery query2 = dbInstance.createQuery(q2); query2.setParameterList("publishers", pubs); query2.executeUpdate(FlushMode.AUTO); } @@ -689,31 +668,13 @@ public class NotificationsManagerImpl extends NotificationsManager implements Us @Override public Subscriber getSubscriber(Identity identity, Publisher publisher) { StringBuilder q = new StringBuilder(); - q.append("select sub from ").append(SubscriberImpl.class.getName()).append(" as sub ") - .append(" where sub.publisher.key=:publisherKey and sub.identity.key=:identityKey"); - - List<Subscriber> res = DBFactory.getInstance().getCurrentEntityManager() - .createQuery(q.toString(), Subscriber.class) - .setParameter("publisherKey", publisher.getKey()) - .setParameter("identityKey", identity.getKey()) - .getResultList(); - - if (res.size() == 0) return null; - if (res.size() != 1) throw new AssertException("only one subscriber per person and publisher!!"); - Subscriber s = res.get(0); - return s; - } - - private Subscriber getSubscriberForUpdate(Identity identity, Publisher publisher) { - StringBuilder q = new StringBuilder(); - q.append("select sub from ").append(SubscriberImpl.class.getName()).append(" as sub ") + q.append("select sub from notisub as sub ") .append(" where sub.publisher.key=:publisherKey and sub.identity.key=:identityKey"); - List<Subscriber> res = DBFactory.getInstance().getCurrentEntityManager() + List<Subscriber> res = dbInstance.getCurrentEntityManager() .createQuery(q.toString(), Subscriber.class) .setParameter("publisherKey", publisher.getKey()) .setParameter("identityKey", identity.getKey()) - .setLockMode(LockModeType.PESSIMISTIC_WRITE) .getResultList(); if (res.size() == 0) return null; @@ -727,8 +688,8 @@ public class NotificationsManagerImpl extends NotificationsManager implements Us */ @Override public List<Subscriber> getSubscribers(Publisher publisher) { - String q = "select sub from org.olat.notifications.SubscriberImpl sub where sub.publisher = :publisher"; - return DBFactory.getInstance().getCurrentEntityManager() + String q = "select sub notisub sub where sub.publisher = :publisher"; + return dbInstance.getCurrentEntityManager() .createQuery(q, Subscriber.class) .setParameter("publisher", publisher) .getResultList(); @@ -740,8 +701,8 @@ public class NotificationsManagerImpl extends NotificationsManager implements Us */ @Override public List<Identity> getSubscriberIdentities(Publisher publisher) { - String q = "select sub.identity from org.olat.notifications.SubscriberImpl sub where sub.publisher = :publisher"; - return DBFactory.getInstance().getCurrentEntityManager() + String q = "select sub.identity from notisub sub where sub.publisher = :publisher"; + return dbInstance.getCurrentEntityManager() .createQuery(q, Identity.class) .setParameter("publisher", publisher) .getResultList(); @@ -767,23 +728,12 @@ public class NotificationsManagerImpl extends NotificationsManager implements Us } return notificationHandlers.get(type); } - - @Override - public Publisher updatePublisher(SubscriptionContext oldContext, SubscriptionContext newContext) { - Publisher p = getPublisherForUpdate(oldContext); - - p.setResId(newContext.getResId()); - p.setResName(newContext.getResName()); - p.setSubidentifier(newContext.getSubidentifier()); - - return DBFactory.getInstance().getCurrentEntityManager().merge(p); - } /** * @param subscriber */ private void deleteSubscriber(Subscriber subscriber) { - DBFactory.getInstance().deleteObject(subscriber); + dbInstance.deleteObject(subscriber); } /** @@ -795,19 +745,20 @@ public class NotificationsManagerImpl extends NotificationsManager implements Us */ @Override public void markSubscriberRead(Identity identity, SubscriptionContext subsContext) { - Publisher p = getPublisherForUpdate(subsContext); + Publisher p = getPublisher(subsContext); if (p == null) throw new AssertException("cannot markRead for identity " + identity.getName() + ", since the publisher for the given subscriptionContext does not exist: subscontext = " + subsContext); markSubscriberRead(identity, p); } - private void markSubscriberRead(Identity identity, Publisher p) { - Subscriber sub = getSubscriberForUpdate(identity, p); + private Subscriber markSubscriberRead(Identity identity, Publisher p) { + Subscriber sub = getSubscriber(identity, p); if(sub != null) { sub.setLastModified(new Date()); - DBFactory.getInstance().getCurrentEntityManager().merge(sub); + sub = dbInstance.getCurrentEntityManager().merge(sub); } + return sub; } /** @@ -833,6 +784,7 @@ public class NotificationsManagerImpl extends NotificationsManager implements Us // news after subscription time doCreateAndPersistSubscriber(toUpdate, identity); } + dbInstance.commit(); } /** @@ -852,7 +804,8 @@ public class NotificationsManagerImpl extends NotificationsManager implements Us return; } toUpdate.setLatestNewsDate(new Date()); - Publisher publisher = DBFactory.getInstance().getCurrentEntityManager().merge(toUpdate); + Publisher publisher = dbInstance.getCurrentEntityManager().merge(toUpdate); + dbInstance.commit();//commit the select for update // no need to sync, since there is only one gui thread at a time from one // user @@ -862,7 +815,7 @@ public class NotificationsManagerImpl extends NotificationsManager implements Us if(sendEvents) { //commit all things on the database - DBFactory.getInstance().commit(); + dbInstance.commit(); // channel-notify all interested listeners (e.g. the pnotificationsportletruncontroller) // 1. find all subscribers which can be affected @@ -903,13 +856,15 @@ public class NotificationsManagerImpl extends NotificationsManager implements Us Publisher p = getPublisherForUpdate(subscriptionContext); // if no publisher yet. //TODO: check race condition: can p be null at all? - if (p == null) return; - Subscriber s = getSubscriber(identity, p); - if (s != null) { - deleteSubscriber(s); - } else { - logWarn("could not unsubscribe " + identity.getName() + " from publisher:" + p.getResName() + "," + p.getResId() + "," + p.getSubidentifier(), null); + if (p != null) { + Subscriber s = getSubscriber(identity, p); + if (s != null) { + deleteSubscriber(s); + } else { + logWarn("could not unsubscribe " + identity.getName() + " from publisher:" + p.getResName() + "," + p.getResId() + "," + p.getSubidentifier(), null); + } } + dbInstance.commit(); } /** @@ -934,12 +889,12 @@ public class NotificationsManagerImpl extends NotificationsManager implements Us @Override public boolean isSubscribed(Identity identity, SubscriptionContext subscriptionContext) { StringBuilder q = new StringBuilder(); - q.append("select count(sub) from ").append(SubscriberImpl.class.getName()).append(" as sub ") + q.append("select count(sub) from notisub as sub ") .append(" inner join sub.publisher as pub ") .append(" where sub.identity.key=:anIdentityKey and pub.resName=:resName and pub.resId=:resId") .append(" and pub.subidentifier=:subidentifier"); - Number count = DBFactory.getInstance().getCurrentEntityManager() + Number count = dbInstance.getCurrentEntityManager() .createQuery(q.toString(), Number.class) .setParameter("anIdentityKey", identity.getKey()) .setParameter("resName", subscriptionContext.getResName()) @@ -969,7 +924,7 @@ public class NotificationsManagerImpl extends NotificationsManager implements Us deleteSubscriber(subscriber); } // else: - DBFactory.getInstance().deleteObject(p); + dbInstance.deleteObject(p); } /** @@ -979,11 +934,12 @@ public class NotificationsManagerImpl extends NotificationsManager implements Us */ @Override public void deactivate(Publisher publisher) { - EntityManager em = DBFactory.getInstance().getCurrentEntityManager(); + EntityManager em = dbInstance.getCurrentEntityManager(); PublisherImpl toDeactivate = em.find(PublisherImpl.class, publisher.getKey(), LockModeType.PESSIMISTIC_WRITE); toDeactivate.setState(PUB_STATE_NOT_OK); em.merge(toDeactivate); + dbInstance.commit(); } /** @@ -1033,13 +989,15 @@ public class NotificationsManagerImpl extends NotificationsManager implements Us if (latestEmailed == null) throw new AssertException("compareDate may not be null, use a date from history"); try { + boolean debug = isLogDebugEnabled(); + SubscriptionItem si = null; Publisher pub = subscriber.getPublisher(); NotificationsHandler notifHandler = getNotificationsHandler(pub); - if (isLogDebugEnabled()) logDebug("create subscription with handler: " + notifHandler.getClass().getName()); + if(debug) logDebug("create subscription with handler: " + notifHandler.getClass().getName()); // do not create subscription item when deleted if (isPublisherValid(pub)) { - if (isLogDebugEnabled()) logDebug("NotifHandler: " + notifHandler.getClass().getName() + " compareDate: " + latestEmailed.toString() + " now: " + new Date().toString(), null); + if(debug) logDebug("NotifHandler: " + notifHandler.getClass().getName() + " compareDate: " + latestEmailed.toString() + " now: " + new Date().toString(), null); SubscriptionInfo subsInfo = notifHandler.createSubscriptionInfo(subscriber, locale, latestEmailed); if (subsInfo.hasNews()) { si = createSubscriptionItem(subsInfo, subscriber, locale, mimeTypeTitle, mimeTypeContent); @@ -1157,46 +1115,4 @@ public class NotificationsManagerImpl extends NotificationsManager implements Us public List<String> getEnabledNotificationIntervals() { return notificationIntervals; } - - /** - * Iterate through the valid subscribers - * - * Initial date: 23.10.2013<br> - * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com - * - */ - private class AllSubscriberIterator implements Iterator<Subscriber> { - private final static int BATCH_SIZE = 500; - - private int count = 0; - private Deque<Subscriber> subscribers = new ArrayDeque<Subscriber>(BATCH_SIZE + 25); - - public void fillTheStack() { - List<Subscriber> batch = getAllValidSubscribers(count, BATCH_SIZE); - subscribers.addAll(batch); - count += batch.size(); - } - - @Override - public boolean hasNext() { - if(subscribers.size() == 0) { - fillTheStack(); - } - return subscribers.size() > 0; - } - - @Override - public Subscriber next() { - if(subscribers.size() == 0) { - fillTheStack(); - } - return subscribers.pollFirst(); - } - - @Override - public void remove() { - //not implemented - } - } -} - +} \ No newline at end of file diff --git a/src/main/java/org/olat/notifications/PublisherImpl.hbm.xml b/src/main/java/org/olat/notifications/PublisherImpl.hbm.xml deleted file mode 100644 index d7147d81771..00000000000 --- a/src/main/java/org/olat/notifications/PublisherImpl.hbm.xml +++ /dev/null @@ -1,59 +0,0 @@ -<?xml version="1.0"?> -<!DOCTYPE hibernate-mapping PUBLIC - "-//Hibernate/Hibernate Mapping DTD//EN" - "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> - -<hibernate-mapping default-lazy="false"> - <class name="org.olat.notifications.PublisherImpl" table="o_noti_pub"> - - <!-- - key (see Interface org.olat.core.commons.persistence.Persistable), - lastModified (see Interface org.olat.core.commons.persistence.Auditable) - and creationDate (see Interface org.olat.core.commons.persistence.Auditable) - are attributes inherited from the abstract class - org.olat.core.commons.persistence.PersistentObject - --> - <id name="key" type="long" column="publisher_id" unsaved-value="null"> - <generator class="hilo"/> - </id> - <version name="version" access="field" column="version" type="int"/> - <property name="creationDate" column="creationdate" type="timestamp" /> - - <!-- data from publisherdata --> - - <property name="type" type="string"> - <column name="publishertype" not-null="true" unique="false" length="50" /> - </property> - - <!-- data which is type specific. it can be whatever can be packed into a string. - We can use XStreams to wrap any object into the db in xml form - --> - <property name="data" unique="false" type="string" not-null="false"> - <column name="data" length="16777210"/> - </property> - - <!-- reference: ores and subidentifier (soft, since we would like to have notifications maybe even after deletion); - may be null in case it has been marked deleted - --> - <property name="resName" type="string" > - <column name="resname" not-null="false" unique="false" length="50" index="name_idx5" /> - </property> - - <property name="resId" type="long"> - <column name="resid" not-null="false" unique="false" index="name_idx6" /> - </property> - - <property name="subidentifier" type="string"> - <column name="subident" not-null="false" unique="false" length="128" index="name_idx7"/> - </property> - - <property name="state" unique="false" type="int" /> - - <!-- mandatory; if created, then the latestNewsDate is set to the current time --> - <property name="latestNewsDate" not-null="true" column="latestnews" type="timestamp" /> - - - <property name="businessPath" not-null="false" column="businesspath" type="string" /> - - </class> -</hibernate-mapping> \ No newline at end of file diff --git a/src/main/java/org/olat/notifications/PublisherImpl.java b/src/main/java/org/olat/notifications/PublisherImpl.java index e64bae0a9ef..70389e8eb20 100644 --- a/src/main/java/org/olat/notifications/PublisherImpl.java +++ b/src/main/java/org/olat/notifications/PublisherImpl.java @@ -27,30 +27,71 @@ package org.olat.notifications; import java.util.Date; -import org.olat.core.commons.persistence.PersistentObject; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.Table; +import javax.persistence.Temporal; +import javax.persistence.TemporalType; + +import org.hibernate.annotations.GenericGenerator; +import org.olat.core.id.CreateInfo; +import org.olat.core.id.Persistable; import org.olat.core.util.notifications.Publisher; /** - * Description: <br> - * TODO: Felix Jost Class Description for PublisherImpl - * <P> * * Initial Date: 21.10.2004 <br> * @author Felix Jost + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com */ -public class PublisherImpl extends PersistentObject implements Publisher { +@Entity(name="notipublisher") +@Table(name="o_noti_pub") +public class PublisherImpl implements Publisher, CreateInfo, Persistable { private static final long serialVersionUID = -7684628889607509977L; - private String type; // e.g. Forum - private String resName; // e.g. CourseModule - private Long resId; // e.g. 2343284327 - private String subidentifier; // e.g. 69680861018558 (for a node in - // a course) - private String data; // any additional data (depending on type) - private int state; // 0 = ok - private Date latestNewsDate; + @Id + @GeneratedValue(generator = "system-uuid") + @GenericGenerator(name = "system-uuid", strategy = "hilo") + @Column(name="publisher_id", nullable=false, unique=true, insertable=true, updatable=false) + private Long key; + + @Temporal(TemporalType.TIMESTAMP) + @Column(name="creationdate", nullable=false, insertable=true, updatable=false) + private Date creationDate; + + //for compatibility purpose + @Column(name="version", nullable=false, insertable=true, updatable=false) + private int version = 0; + + // e.g. Forum + + @Column(name="publishertype", nullable=false, insertable=true, updatable=false) + private String type; + // e.g. CourseModule + @Column(name="resname", nullable=false, insertable=true, updatable=false) + private String resName; + // e.g. 2343284327 + @Column(name="resid", nullable=false, insertable=true, updatable=false) + private Long resId; + // e.g. 69680861018558 (for a node in a course) + @Column(name="subident", nullable=true, insertable=true, updatable=false) + private String subidentifier; + @Column(name="businesspath", nullable=true, insertable=true, updatable=false) private String businessPath; + // any additional data (depending on type) + @Column(name="data", nullable=true, insertable=true, updatable=false) + private String data; + // 0 = ok + @Column(name="state", nullable=false, insertable=true, updatable=false) + private int state; + + @Temporal(TemporalType.TIMESTAMP) + @Column(name="latestnews", nullable=false, insertable=true, updatable=true) + private Date latestNewsDate; + /** * for hibernate only @@ -78,6 +119,22 @@ public class PublisherImpl extends PersistentObject implements Publisher { this.latestNewsDate = latestNewsDate; } + public Long getKey() { + return key; + } + + public void setKey(Long key) { + this.key = key; + } + + public Date getCreationDate() { + return creationDate; + } + + public void setCreationDate(Date creationDate) { + this.creationDate = creationDate; + } + /** * @return resId */ @@ -201,4 +258,9 @@ public class PublisherImpl extends PersistentObject implements Publisher { } return false; } + + @Override + public boolean equalsByPersistableKey(Persistable persistable) { + return equals(persistable); + } } \ No newline at end of file diff --git a/src/main/java/org/olat/notifications/SubscriberImpl.hbm.xml b/src/main/java/org/olat/notifications/SubscriberImpl.hbm.xml deleted file mode 100644 index 21f742ed230..00000000000 --- a/src/main/java/org/olat/notifications/SubscriberImpl.hbm.xml +++ /dev/null @@ -1,33 +0,0 @@ -<?xml version="1.0"?> -<!DOCTYPE hibernate-mapping PUBLIC - "-//Hibernate/Hibernate Mapping DTD//EN" - "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> - -<hibernate-mapping default-lazy="false"> - <class name="org.olat.notifications.SubscriberImpl" table="o_noti_sub"> - <!-- - key (see Interface org.olat.core.commons.persistence.Persistable), - lastModified (see Interface org.olat.core.commons.persistence.Auditable) - and creationDate (see Interface org.olat.core.commons.persistence.Auditable) - are attributes inherited from the abstract class - org.olat.core.commons.persistence.PersistentObject - --> - <id name="key" type="long" column="publisher_id" unsaved-value="null"> - <generator class="hilo"/> - </id> - <version name="version" access="field" column="version" type="int"/> - <property name="lastModified" column="lastmodified" type="timestamp" /> - <property name="creationDate" column="creationdate" type="timestamp" /> - - <many-to-one name="publisher" class="org.olat.notifications.PublisherImpl" outer-join="auto" cascade="none"> - <column name="fk_publisher" not-null="true" unique="false" unique-key="publisher_per_person"/> - </many-to-one> - - <many-to-one name="identity" class="org.olat.basesecurity.IdentityImpl" outer-join="auto" cascade="none"> - <column name="fk_identity" not-null="true" unique="false" unique-key="publisher_per_person" /> - </many-to-one> - - <!-- maybe null; timestamp when latest email sending has taken place --> - <property name="latestEmailed" not-null="false" column="latestemailed" type="timestamp" /> - </class> -</hibernate-mapping> \ No newline at end of file diff --git a/src/main/java/org/olat/notifications/SubscriberImpl.java b/src/main/java/org/olat/notifications/SubscriberImpl.java index e0516b10772..2315f2ca689 100644 --- a/src/main/java/org/olat/notifications/SubscriberImpl.java +++ b/src/main/java/org/olat/notifications/SubscriberImpl.java @@ -27,34 +27,70 @@ package org.olat.notifications; import java.util.Date; -import org.olat.core.commons.persistence.PersistentObject; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.Table; +import javax.persistence.Temporal; +import javax.persistence.TemporalType; + +import org.hibernate.annotations.GenericGenerator; +import org.olat.basesecurity.IdentityImpl; +import org.olat.core.id.CreateInfo; import org.olat.core.id.Identity; +import org.olat.core.id.Persistable; import org.olat.core.util.notifications.Publisher; import org.olat.core.util.notifications.Subscriber; /** * Description: <br> - * TODO: Felix Jost Class Description for Subscriber + * Relation between the publisher and the identity * <P> * * Initial Date: 21.10.2004 <br> * @author Felix Jost + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com */ -public class SubscriberImpl extends PersistentObject implements Subscriber { +@Entity(name="notisub") +@Table(name="o_noti_sub") +public class SubscriberImpl implements Subscriber, CreateInfo, Persistable { private static final long serialVersionUID = 6165097156137862263L; - + + @Id + @GeneratedValue(generator = "system-uuid") + @GenericGenerator(name = "system-uuid", strategy = "hilo") + @Column(name="publisher_id", nullable=false, unique=true, insertable=true, updatable=false) + private Long key; + + @Temporal(TemporalType.TIMESTAMP) + @Column(name="creationdate", nullable=false, insertable=true, updatable=false) + private Date creationDate; + @Temporal(TemporalType.TIMESTAMP) + @Column(name="lastmodified", nullable=false, insertable=true, updatable=true) + private Date lastModified; + //for compatibility purpose + @Column(name="version", nullable=false, insertable=true, updatable=false) + private int version = 0; + // reference to the subscribed publisher + @ManyToOne(targetEntity=PublisherImpl.class,fetch=FetchType.LAZY,optional=false) + @JoinColumn(name="fk_publisher", nullable=false, updatable=false) private Publisher publisher; // the user this subscription belongs to + @ManyToOne(targetEntity=IdentityImpl.class,fetch=FetchType.LAZY,optional=false) + @JoinColumn(name="fk_identity", nullable=false, updatable=false) private Identity identity; // when the user latest received an email concering this subscription; may be null if no email has been sent yet + @Temporal(TemporalType.TIMESTAMP) + @Column(name="latestemailed", nullable=false, insertable=true, updatable=true) private Date latestEmailed; - private Date lastModified; - - /** * for hibernate only */ @@ -70,6 +106,21 @@ public class SubscriberImpl extends PersistentObject implements Subscriber { identity = listener; } + public Long getKey() { + return key; + } + + public void setKey(Long key) { + this.key = key; + } + + public Date getCreationDate() { + return creationDate; + } + + public void setCreationDate(Date creationDate) { + this.creationDate = creationDate; + } /** * @return the identity */ @@ -143,4 +194,9 @@ public class SubscriberImpl extends PersistentObject implements Subscriber { } return false; } + + @Override + public boolean equalsByPersistableKey(Persistable persistable) { + return equals(persistable); + } } \ No newline at end of file diff --git a/src/main/java/org/olat/notifications/_spring/notificationsContext.xml b/src/main/java/org/olat/notifications/_spring/notificationsContext.xml index 55d3c863e4a..73c84fe8e2b 100644 --- a/src/main/java/org/olat/notifications/_spring/notificationsContext.xml +++ b/src/main/java/org/olat/notifications/_spring/notificationsContext.xml @@ -19,6 +19,9 @@ <bean id="org.olat.commons.info.notification.InfoMessageNotificationHandler" class="org.olat.commons.info.notification.InfoMessageNotificationHandler" /> <bean id="org.olat.core.util.notifications.NotificationsManager" class="org.olat.notifications.NotificationsManagerImpl" > + <property name="dbInstance" ref="database"/> + <property name="securityManager" ref="baseSecurityManager"/> + <property name="propertyManager" ref="propertyManager"/> <!-- Configure which notification intervals you want the user to choose from. Don't add other intervals unless you also implement them. But you can surely diff --git a/src/main/java/org/olat/upgrade/OLATUpgrade_8_1_0.java b/src/main/java/org/olat/upgrade/OLATUpgrade_8_1_0.java index 2454e945723..20f415fd188 100644 --- a/src/main/java/org/olat/upgrade/OLATUpgrade_8_1_0.java +++ b/src/main/java/org/olat/upgrade/OLATUpgrade_8_1_0.java @@ -134,7 +134,7 @@ public class OLATUpgrade_8_1_0 extends OLATUpgrade { private List<Publisher> getAssessmentPublishers() { StringBuilder sb = new StringBuilder(); - sb.append("select pub from ").append(PublisherImpl.class.getName()).append(" pub where pub.resName='AssessmentManager' and type='AssessmentManager'"); + sb.append("select pub from notipublisher pub where pub.resName='AssessmentManager' and type='AssessmentManager'"); DBQuery query = dbInstance.createQuery(sb.toString()); @SuppressWarnings("unchecked") List<Publisher> res = query.list(); diff --git a/src/main/resources/database/mysql/alter_9_3_0_to_9_4_0.sql b/src/main/resources/database/mysql/alter_9_3_0_to_9_4_0.sql index a193432dd01..b2076ac8f6c 100644 --- a/src/main/resources/database/mysql/alter_9_3_0_to_9_4_0.sql +++ b/src/main/resources/database/mysql/alter_9_3_0_to_9_4_0.sql @@ -6,7 +6,7 @@ alter table o_gp_business add column participantspublic bit not null default 0; alter table o_gp_business add column waitingpublic bit not null default 0; alter table o_gp_business add column downloadmembers bit not null default 0; -create or replace view o_gp_contact_participant_v as +create or replace view o_gp_contact_participant_v as ( select bg_part_member.id as membership_id, bgroup.group_id as bg_id, @@ -19,9 +19,9 @@ create or replace view o_gp_contact_participant_v as inner join o_bs_membership as bg_part_member on (bg_part_member.secgroup_id = bgroup.fk_partipiciantgroup) inner join o_bs_identity as ident on (bg_part_member.identity_id = ident.id) where bgroup.participantsintern=1 -; +); -create or replace view o_gp_contact_owner_v as +create or replace view o_gp_contact_owner_v as ( select bg_owner_member.id as membership_id, bgroup.group_id as bg_id, @@ -34,9 +34,9 @@ create or replace view o_gp_contact_owner_v as inner join o_bs_membership as bg_owner_member on (bg_owner_member.secgroup_id = bgroup.fk_ownergroup) inner join o_bs_identity as ident on (bg_owner_member.identity_id = ident.id) where bgroup.ownersintern=1 -; +); -create or replace view o_gp_contactkey_participant_v as +create or replace view o_gp_contactkey_participant_v as ( select bg_part_member.id as membership_id, bgroup.fk_partipiciantgroup as bg_part_sec_id, @@ -45,9 +45,9 @@ create or replace view o_gp_contactkey_participant_v as from o_gp_business as bgroup inner join o_bs_membership as bg_part_member on (bg_part_member.secgroup_id = bgroup.fk_partipiciantgroup) where bgroup.participantsintern=1 -; +); -create or replace view o_gp_contactkey_owner_v as +create or replace view o_gp_contactkey_owner_v as ( select bg_owner_member.id as membership_id, bgroup.fk_partipiciantgroup as bg_part_sec_id, @@ -56,5 +56,5 @@ create or replace view o_gp_contactkey_owner_v as from o_gp_business as bgroup inner join o_bs_membership as bg_owner_member on (bg_owner_member.secgroup_id = bgroup.fk_ownergroup) where bgroup.ownersintern=1 -; +); diff --git a/src/main/resources/database/oracle/alter_9_3_0_to_9_4_0.sql b/src/main/resources/database/oracle/alter_9_3_0_to_9_4_0.sql index d000fbe602d..a2629e9bb78 100644 --- a/src/main/resources/database/oracle/alter_9_3_0_to_9_4_0.sql +++ b/src/main/resources/database/oracle/alter_9_3_0_to_9_4_0.sql @@ -6,7 +6,7 @@ alter table o_gp_business add (participantspublic number default 0 not null); alter table o_gp_business add (waitingpublic number default 0 not null); alter table o_gp_business add (downloadmembers number default 0 not null); -create view o_gp_contact_participant_v as +create view o_gp_contact_participant_v as ( select bg_part_member.id as membership_id, bgroup.group_id as bg_id, @@ -19,9 +19,9 @@ create view o_gp_contact_participant_v as inner join o_bs_membership bg_part_member on (bg_part_member.secgroup_id = bgroup.fk_partipiciantgroup) inner join o_bs_identity ident on (bg_part_member.identity_id = ident.id) where bgroup.participantsintern=1 -; +); -create view o_gp_contact_owner_v as +create view o_gp_contact_owner_v as ( select bg_owner_member.id as membership_id, bgroup.group_id as bg_id, @@ -34,9 +34,9 @@ create view o_gp_contact_owner_v as inner join o_bs_membership bg_owner_member on (bg_owner_member.secgroup_id = bgroup.fk_ownergroup) inner join o_bs_identity ident on (bg_owner_member.identity_id = ident.id) where bgroup.ownersintern=1 -; +); -create view o_gp_contactkey_participant_v as +create view o_gp_contactkey_participant_v as ( select bg_part_member.id as membership_id, bgroup.fk_partipiciantgroup as bg_part_sec_id, @@ -45,9 +45,9 @@ create view o_gp_contactkey_participant_v as from o_gp_business bgroup inner join o_bs_membership bg_part_member on (bg_part_member.secgroup_id = bgroup.fk_partipiciantgroup) where bgroup.participantsintern=1 -; +); -create view o_gp_contactkey_owner_v as +create view o_gp_contactkey_owner_v as ( select bg_owner_member.id as membership_id, bgroup.fk_partipiciantgroup as bg_part_sec_id, @@ -56,4 +56,4 @@ create view o_gp_contactkey_owner_v as from o_gp_business bgroup inner join o_bs_membership bg_owner_member on (bg_owner_member.secgroup_id = bgroup.fk_ownergroup) where bgroup.ownersintern=1 -; +); diff --git a/src/main/resources/database/postgresql/alter_9_3_0_to_9_4_0.sql b/src/main/resources/database/postgresql/alter_9_3_0_to_9_4_0.sql index c07faa6a724..7c091b7b695 100644 --- a/src/main/resources/database/postgresql/alter_9_3_0_to_9_4_0.sql +++ b/src/main/resources/database/postgresql/alter_9_3_0_to_9_4_0.sql @@ -7,7 +7,7 @@ alter table o_gp_business add column waitingpublic bool not null default false; alter table o_gp_business add column downloadmembers bool not null default false; drop view o_gp_visible_participant_v; -create view o_gp_contact_participant_v as +create view o_gp_contact_participant_v as ( select bg_part_member.id as membership_id, bgroup.group_id as bg_id, @@ -20,10 +20,10 @@ create view o_gp_contact_participant_v as inner join o_bs_membership as bg_part_member on (bg_part_member.secgroup_id = bgroup.fk_partipiciantgroup) inner join o_bs_identity as ident on (bg_part_member.identity_id = ident.id) where bgroup.participantsintern=true -; +); drop view o_gp_visible_owner_v; -create view o_gp_contact_owner_v as +create view o_gp_contact_owner_v as ( select bg_owner_member.id as membership_id, bgroup.group_id as bg_id, @@ -36,9 +36,9 @@ create view o_gp_contact_owner_v as inner join o_bs_membership as bg_owner_member on (bg_owner_member.secgroup_id = bgroup.fk_ownergroup) inner join o_bs_identity as ident on (bg_owner_member.identity_id = ident.id) where bgroup.ownersintern=true -; +); -create view o_gp_contactkey_participant_v as +create view o_gp_contactkey_participant_v as ( select bg_part_member.id as membership_id, bgroup.fk_partipiciantgroup as bg_part_sec_id, @@ -47,9 +47,9 @@ create view o_gp_contactkey_participant_v as from o_gp_business as bgroup inner join o_bs_membership as bg_part_member on (bg_part_member.secgroup_id = bgroup.fk_partipiciantgroup) where bgroup.participantsintern=true -; +); -create view o_gp_contactkey_owner_v as +create view o_gp_contactkey_owner_v as ( select bg_owner_member.id as membership_id, bgroup.fk_partipiciantgroup as bg_part_sec_id, @@ -58,4 +58,4 @@ create view o_gp_contactkey_owner_v as from o_gp_business as bgroup inner join o_bs_membership as bg_owner_member on (bg_owner_member.secgroup_id = bgroup.fk_ownergroup) where bgroup.ownersintern=true -; +); diff --git a/src/test/java/org/olat/notifications/NotificationsManagerTest.java b/src/test/java/org/olat/notifications/NotificationsManagerTest.java index dcc0c6708e4..d83298488e9 100644 --- a/src/test/java/org/olat/notifications/NotificationsManagerTest.java +++ b/src/test/java/org/olat/notifications/NotificationsManagerTest.java @@ -98,32 +98,31 @@ public class NotificationsManagerTest extends OlatTestCase { } @Test - public void testUpdatePublisherContext() { + public void testCreateUpdatePublisher() { String identifier = UUID.randomUUID().toString().replace("-", ""); - SubscriptionContext context = new SubscriptionContext("PS", new Long(123), identifier); + SubscriptionContext context = new SubscriptionContext("PS2", new Long(124), identifier); PublisherData publisherData = new PublisherData("testPublisherSubscriber", "e.g. forumdata=keyofforum", null); Publisher publisher = notificationManager.getOrCreatePublisher(context, publisherData); dbInstance.commitAndCloseSession(); //check values Assert.assertNotNull(publisher); - Assert.assertEquals("PS", publisher.getResName()); - Assert.assertEquals(new Long(123), publisher.getResId()); - Assert.assertEquals(identifier, publisher.getSubidentifier()); + Assert.assertNotNull(publisher.getKey()); + Assert.assertNotNull(publisher.getCreationDate()); + Assert.assertNotNull(publisher.getLatestNewsDate()); + Assert.assertEquals("PS2", publisher.getResName()); + Assert.assertEquals(new Long(124), publisher.getResId()); + + sleep(2000); - //modify - String modifiedIdentifier = UUID.randomUUID().toString().replace("-", ""); - SubscriptionContext modifiedContext = new SubscriptionContext("PSModified", new Long(12300), modifiedIdentifier); - notificationManager.updatePublisher(context, modifiedContext); - dbInstance.commitAndCloseSession(); + //update the publisher + notificationManager.markPublisherNews(context, null, false); - //check if exists - Publisher reloadedPublisher = notificationManager.getPublisher(modifiedContext); + //check if exists and last news date is updated + Publisher reloadedPublisher = notificationManager.getPublisher(context); Assert.assertNotNull(reloadedPublisher); Assert.assertEquals(publisher, reloadedPublisher); - Assert.assertEquals("PSModified", reloadedPublisher.getResName()); - Assert.assertEquals(new Long(12300), reloadedPublisher.getResId()); - Assert.assertEquals(modifiedIdentifier, reloadedPublisher.getSubidentifier()); + Assert.assertTrue(publisher.getLatestNewsDate().before(reloadedPublisher.getLatestNewsDate())); } @Test @@ -173,6 +172,36 @@ public class NotificationsManagerTest extends OlatTestCase { Assert.assertEquals(subscriber, reloadedSubscriber); } + @Test + public void testMarkSubscriberRead() { + Identity id = JunitTestHelper.createAndPersistIdentityAsUser("subs-" + UUID.randomUUID().toString()); + //create a publisher + String identifier = UUID.randomUUID().toString().replace("-", ""); + SubscriptionContext context = new SubscriptionContext("All", new Long(123), identifier); + PublisherData publisherData = new PublisherData("testAllPublishers", "e.g. forumdata=keyofforum", null); + Publisher publisher = notificationManager.getOrCreatePublisher(context, publisherData); + dbInstance.commit(); + Assert.assertNotNull(publisher); + + //subscribe + notificationManager.subscribe(id, context, publisherData); + dbInstance.commit(); + //load the subscriber + Subscriber subscriber = notificationManager.getSubscriber(id, publisher); + Assert.assertNotNull(subscriber); + dbInstance.commitAndCloseSession(); + + sleep(2000); + + notificationManager.markSubscriberRead(id, context); + + //check the last modification date + Subscriber reloadedSubscriber = notificationManager.getSubscriber(subscriber.getKey()); + Assert.assertNotNull(reloadedSubscriber); + Assert.assertEquals(subscriber, reloadedSubscriber); + Assert.assertTrue(subscriber.getLastModified().before(reloadedSubscriber.getLastModified())); + } + @Test public void testUnsubscribe_v1() { Identity id = JunitTestHelper.createAndPersistIdentityAsUser("unsubs-" + UUID.randomUUID().toString()); @@ -279,59 +308,6 @@ public class NotificationsManagerTest extends OlatTestCase { Assert.assertEquals(publisher, subscribers.get(1).getPublisher()); } - @Test - public void testGetAllValidSubscribers() { - Identity id = JunitTestHelper.createAndPersistIdentityAsUser("valid1b-" + UUID.randomUUID().toString()); - //create a publisher - String identifier = UUID.randomUUID().toString().replace("-", ""); - SubscriptionContext context = new SubscriptionContext("AllSubs", new Long(130), identifier); - PublisherData publisherData = new PublisherData("testGetAllValidSubscribers", "e.g. forumdata=keyofforum", null); - Publisher publisher = notificationManager.getOrCreatePublisher(context, publisherData); - dbInstance.commitAndCloseSession(); - Assert.assertNotNull(publisher); - //add subscriber - notificationManager.subscribe(id, context, publisherData); - dbInstance.commitAndCloseSession(); - - //get all subscribers - List<Subscriber> allSubscribers = ((NotificationsManagerImpl)notificationManager).getAllValidSubscribers(); - Assert.assertNotNull(allSubscribers); - Assert.assertFalse(allSubscribers.isEmpty()); - - //get current subscriber - Subscriber thisSubscriber = notificationManager.getSubscriber(id, publisher); - Assert.assertNotNull(thisSubscriber); - Assert.assertTrue(allSubscribers.contains(thisSubscriber)); - } - - @Test - public void testGetAllValidSubscribers_paged() { - Identity id = JunitTestHelper.createAndPersistIdentityAsUser("valid1paged-" + UUID.randomUUID().toString()); - //create a publisher - for(int i=0; i<10; i++) { - String identifier = UUID.randomUUID().toString().replace("-", ""); - SubscriptionContext context = new SubscriptionContext("AllSubs", new Long(130 + i), identifier); - PublisherData publisherData = new PublisherData("testGetAllValidSubscribers", "e.g. forumdata=keyofforum", null); - Publisher publisher = notificationManager.getOrCreatePublisher(context, publisherData); - Assert.assertNotNull(publisher); - - dbInstance.commitAndCloseSession(); - //add subscriber - notificationManager.subscribe(id, context, publisherData); - dbInstance.commitAndCloseSession(); - } - - //get all subscribers - List<Subscriber> allSubscribers = ((NotificationsManagerImpl)notificationManager).getAllValidSubscribers(0, -1); - Assert.assertNotNull(allSubscribers); - Assert.assertFalse(allSubscribers.isEmpty()); - - //get all subcribers pages - List<Subscriber> partialSubscribers = ((NotificationsManagerImpl)notificationManager).getAllValidSubscribers(0, 8); - Assert.assertNotNull(partialSubscribers); - Assert.assertEquals(8, partialSubscribers.size()); - } - @Test public void testGetSubscriberIdentities() { Identity id1 = JunitTestHelper.createAndPersistIdentityAsUser("valid1b-" + UUID.randomUUID().toString()); -- GitLab