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