diff --git a/src/main/java/org/olat/admin/user/course/CourseOverviewController.java b/src/main/java/org/olat/admin/user/course/CourseOverviewController.java
index 103f540bd5480aacd80305d28a503f2df9271f3b..417b41071bf6aaf625d972157a530c317404eb0b 100644
--- a/src/main/java/org/olat/admin/user/course/CourseOverviewController.java
+++ b/src/main/java/org/olat/admin/user/course/CourseOverviewController.java
@@ -399,7 +399,7 @@ public class CourseOverviewController extends BasicController  {
 		MailPackage mailing = new MailPackage(sendMail);
 		if(re != null) {
 			List<RepositoryEntryPermissionChangeEvent> changes = Collections.singletonList((RepositoryEntryPermissionChangeEvent)e);
-			repositoryManager.updateRepositoryEntryMembership(getIdentity(), ureq.getUserSession().getRoles(), re, changes, mailing);
+			repositoryManager.updateRepositoryEntryMemberships(getIdentity(), ureq.getUserSession().getRoles(), re, changes, mailing);
 		}
 
 		businessGroupService.updateMemberships(getIdentity(), e.getGroupChanges(), mailing);
@@ -448,7 +448,7 @@ public class CourseOverviewController extends BasicController  {
 		}
 		List<RepositoryEntryPermissionChangeEvent> repoChanges = Collections.singletonList(changeEvent);
 		for(RepositoryEntry repoEntry:res) {
-			repositoryManager.updateRepositoryEntryMembership(getIdentity(), ureq.getUserSession().getRoles(), repoEntry, repoChanges, reMailing);
+			repositoryManager.updateRepositoryEntryMemberships(getIdentity(), ureq.getUserSession().getRoles(), repoEntry, repoChanges, reMailing);
 		}
 		
 		//make sure all is committed before loading the model again (I see issues without)
diff --git a/src/main/java/org/olat/basesecurity/model/IdentityRefImpl.java b/src/main/java/org/olat/basesecurity/model/IdentityRefImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..8d86fee6c06e0de60d4bbad201a5b208714db313
--- /dev/null
+++ b/src/main/java/org/olat/basesecurity/model/IdentityRefImpl.java
@@ -0,0 +1,58 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); <br>
+ * you may not use this file except in compliance with the License.<br>
+ * You may obtain a copy of the License at the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <p>
+ * Unless required by applicable law or agreed to in writing,<br>
+ * software distributed under the License is distributed on an "AS IS" BASIS, <br>
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
+ * See the License for the specific language governing permissions and <br>
+ * limitations under the License.
+ * <p>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+package org.olat.basesecurity.model;
+
+import org.olat.basesecurity.IdentityRef;
+
+/**
+ * 
+ * Initial date: 09.02.2015<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class IdentityRefImpl implements IdentityRef {
+	
+	private final Long identityKey;
+	
+	public IdentityRefImpl(Long identityKey) {
+		this.identityKey = identityKey;
+	}
+
+	@Override
+	public Long getKey() {
+		return identityKey;
+	}
+
+	@Override
+	public int hashCode() {
+		return identityKey == null ? 86583206 : identityKey.hashCode();
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if (this == obj) {
+			return true;
+		} else if(obj instanceof IdentityRefImpl) {
+			IdentityRefImpl ref = (IdentityRefImpl)obj;
+			return identityKey != null && identityKey.equals(ref.identityKey);
+		}
+		return false;
+	}
+}
diff --git a/src/main/java/org/olat/commons/coordinate/cluster/jms/ClusterEventBus.java b/src/main/java/org/olat/commons/coordinate/cluster/jms/ClusterEventBus.java
index 2a7e4d88596d8c445a50cefba29f6a76157a2709..6a374464e8f23d0e76c3c0f5ba1d51419ea25505 100644
--- a/src/main/java/org/olat/commons/coordinate/cluster/jms/ClusterEventBus.java
+++ b/src/main/java/org/olat/commons/coordinate/cluster/jms/ClusterEventBus.java
@@ -222,6 +222,11 @@ public class ClusterEventBus extends AbstractEventBus implements MessageListener
 		final long msgId = ++latestSentMsgId;
 		final Integer nodeId = clusterConfig.getNodeId();
 		
+		if(ores != null && ores.getResourceableId() != null
+				&& ores.getResourceableId().equals(0l) && "BusinessGroup".equals(ores.getResourceableTypeName())) {
+			System.out.println();
+		}
+		
 		jmsExecutor.execute(new Runnable() {
 			public void run() {
 				try {
diff --git a/src/main/java/org/olat/core/commons/services/notifications/NotificationsManager.java b/src/main/java/org/olat/core/commons/services/notifications/NotificationsManager.java
index c1fca403a7ed2ad56c4c8183c875cf4cc8528078..4c38b37448c3f864943abff7a0d6b12351926095 100644
--- a/src/main/java/org/olat/core/commons/services/notifications/NotificationsManager.java
+++ b/src/main/java/org/olat/core/commons/services/notifications/NotificationsManager.java
@@ -28,6 +28,7 @@ import java.util.Date;
 import java.util.List;
 import java.util.Locale;
 
+import org.olat.basesecurity.IdentityRef;
 import org.olat.core.gui.translator.Translator;
 import org.olat.core.id.Identity;
 import org.olat.core.id.OLATResourceable;
@@ -275,7 +276,16 @@ public abstract class NotificationsManager extends BasicManager {
 	 * @param types
 	 * @return
 	 */
-	public abstract List<Subscriber> getSubscribers(Identity identity, List<String> types);
+	public abstract List<Subscriber> getSubscribers(IdentityRef identity, List<String> types);
+
+	/**
+	 * subscribers for ONE person (e.g. subscribed to 5 forums -> 5 subscribers
+	 * belonging to this person) restricted to the specified types
+	 * 
+	 * @param identity
+	 * @return List of Subscriber Objects which belong to the identity
+	 */
+	public abstract List<Subscriber> getSubscribers(IdentityRef identity, long resId);
 	
 	/**
 	 * @param identity
@@ -299,6 +309,8 @@ public abstract class NotificationsManager extends BasicManager {
 
 	public abstract void unsubscribe(Subscriber s);
 	
+	public abstract void unsubscribeAllForIdentityAndResId(IdentityRef identity, Long resId);
+
 	/**
 	 * @param identity
 	 * @param subscriptionContext
diff --git a/src/main/java/org/olat/core/commons/services/notifications/manager/NotificationsManagerImpl.java b/src/main/java/org/olat/core/commons/services/notifications/manager/NotificationsManagerImpl.java
index 7150ff1a632087c6f0ed130d9d39717bb6613901..2a11526cda0a5fae1e1767c757405d45d76d3ea2 100644
--- a/src/main/java/org/olat/core/commons/services/notifications/manager/NotificationsManagerImpl.java
+++ b/src/main/java/org/olat/core/commons/services/notifications/manager/NotificationsManagerImpl.java
@@ -45,6 +45,7 @@ import javax.persistence.TypedQuery;
 import org.hibernate.FlushMode;
 import org.olat.NewControllerFactory;
 import org.olat.basesecurity.BaseSecurity;
+import org.olat.basesecurity.IdentityRef;
 import org.olat.core.CoreSpringFactory;
 import org.olat.core.commons.persistence.DB;
 import org.olat.core.commons.persistence.DBQuery;
@@ -194,6 +195,7 @@ public class NotificationsManagerImpl extends NotificationsManager implements Us
 	 * @param identity
 	 * @return List of Subscriber Objects which belong to the identity
 	 */
+	@Override
 	public List<Subscriber> getSubscribers(Identity identity) {
 		return getSubscribers(identity, Collections.<String>emptyList());
 	}
@@ -206,22 +208,45 @@ public class NotificationsManagerImpl extends NotificationsManager implements Us
 	 * @return List of Subscriber Objects which belong to the identity
 	 */
 	@Override
-	public List<Subscriber> getSubscribers(Identity identity, List<String> types) {
+	public List<Subscriber> getSubscribers(IdentityRef identity, List<String> types) {
 		StringBuilder sb = new StringBuilder();
 		sb.append("select sub from notisub as sub ")
 		  .append("inner join fetch sub.publisher as publisher ")
-		  .append("where sub.identity = :anIdentity");
+		  .append("where sub.identity.key = :identityKey");
 		if(types != null && !types.isEmpty()) {
 			sb.append(" and publisher.type in (:types)");
 		}
-		TypedQuery<Subscriber> query = dbInstance.getCurrentEntityManager().createQuery(sb.toString(), Subscriber.class);
-		query.setParameter("anIdentity", identity);
+		TypedQuery<Subscriber> query = dbInstance.getCurrentEntityManager()
+				.createQuery(sb.toString(), Subscriber.class)
+				.setParameter("identityKey", identity.getKey());
 		if(types != null && !types.isEmpty()) {
 			query.setParameter("types", types);
 		}
 		return query.getResultList();
 	}
 
+	/**
+	 * subscribers for ONE person (e.g. subscribed to 5 forums -> 5 subscribers
+	 * belonging to this person) restricted to the specified Olat resourceable id
+	 * 
+	 * @param identity
+	 * @param resId
+	 * @return List of Subscriber Objects which belong to the identity
+	 */
+	@Override
+	public List<Subscriber> getSubscribers(IdentityRef identity, long resId) {
+		StringBuilder sb = new StringBuilder();
+		sb.append("select sub from notisub as sub ")
+		  .append("inner join fetch sub.publisher as publisher ")
+		  .append("where sub.identity.key = :identityKey")
+		  .append(" and publisher.resId = :resId)");
+		return dbInstance.getCurrentEntityManager()
+			.createQuery(sb.toString(), Subscriber.class)
+			.setParameter("identityKey", identity.getKey())
+			.setParameter("resId", resId)
+			.getResultList();
+	}
+
 	/**
 	 * @param identity
 	 * @return a list of all subscribers which belong to the identity and which
@@ -893,6 +918,14 @@ public class NotificationsManagerImpl extends NotificationsManager implements Us
 		}
 	}
 
+	@Override
+	public void unsubscribeAllForIdentityAndResId(IdentityRef identity, Long resId) {
+		List<Subscriber> subscribers = getSubscribers(identity, resId.longValue());
+		for (Subscriber sub:subscribers) {
+			unsubscribe (sub);
+		}
+	}
+
 	/**
 	 * @param identity
 	 * @param subscriptionContext
diff --git a/src/main/java/org/olat/course/assessment/manager/AssessmentModeCoordinationServiceImpl.java b/src/main/java/org/olat/course/assessment/manager/AssessmentModeCoordinationServiceImpl.java
index 240aa15002f0f4dc41c56958f914bb396172bc58..b6ec61df1b0b99e927f387b1660ef8f8fd27850d 100644
--- a/src/main/java/org/olat/course/assessment/manager/AssessmentModeCoordinationServiceImpl.java
+++ b/src/main/java/org/olat/course/assessment/manager/AssessmentModeCoordinationServiceImpl.java
@@ -27,7 +27,7 @@ import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 
-import org.olat.basesecurity.IdentityRef;
+import org.olat.basesecurity.model.IdentityRefImpl;
 import org.olat.core.commons.persistence.DB;
 import org.olat.core.gui.control.Event;
 import org.olat.core.logging.OLog;
@@ -157,12 +157,7 @@ public class AssessmentModeCoordinationServiceImpl implements AssessmentModeCoor
 	}
 	
 	private void sendEventAfterMembershipChange(final Long identityKey) {
-		List<AssessmentMode> modes = assessmentModeManager.getAssessmentModeFor(new IdentityRef() {
-			@Override
-			public Long getKey() {
-				return identityKey;
-			}
-		});
+		List<AssessmentMode> modes = assessmentModeManager.getAssessmentModeFor(new IdentityRefImpl(identityKey));
 		for(AssessmentMode mode:modes) {
 			Status status = mode.getStatus();
 			if(status == Status.leadtime ) {
diff --git a/src/main/java/org/olat/course/assessment/ui/AssessmentModeAdminSettingsController.java b/src/main/java/org/olat/course/assessment/ui/AssessmentModeAdminSettingsController.java
index 17438d64e3ab6e10a087aba118fe5529f9749ab0..7b604ae5501a6af2ed849c040ed836a622bef815 100644
--- a/src/main/java/org/olat/course/assessment/ui/AssessmentModeAdminSettingsController.java
+++ b/src/main/java/org/olat/course/assessment/ui/AssessmentModeAdminSettingsController.java
@@ -1,3 +1,22 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); <br>
+ * you may not use this file except in compliance with the License.<br>
+ * You may obtain a copy of the License at the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <p>
+ * Unless required by applicable law or agreed to in writing,<br>
+ * software distributed under the License is distributed on an "AS IS" BASIS, <br>
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
+ * See the License for the specific language governing permissions and <br>
+ * limitations under the License.
+ * <p>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
 package org.olat.course.assessment.ui;
 
 import org.olat.core.gui.UserRequest;
diff --git a/src/main/java/org/olat/course/member/MembersOverviewController.java b/src/main/java/org/olat/course/member/MembersOverviewController.java
index 92f6bd6baabf7703b2729c17715be2c90c4c197e..81733df69f196ecb213dd8121c82419e3970e63a 100644
--- a/src/main/java/org/olat/course/member/MembersOverviewController.java
+++ b/src/main/java/org/olat/course/member/MembersOverviewController.java
@@ -312,7 +312,7 @@ public class MembersOverviewController extends BasicController implements Activa
 		MailPackage reMailing = new MailPackage(template, result, getWindowControl().getBusinessControl().getAsString(), template != null);
 		
 		List<RepositoryEntryPermissionChangeEvent> repoChanges = changes.generateRepositoryChanges(members);
-		repositoryManager.updateRepositoryEntryMembership(getIdentity(), ureq.getUserSession().getRoles(), repoEntry, repoChanges, reMailing);
+		repositoryManager.updateRepositoryEntryMemberships(getIdentity(), ureq.getUserSession().getRoles(), repoEntry, repoChanges, reMailing);
 
 		//commit all changes to the group memberships
 		List<BusinessGroupMembershipChange> allModifications = changes.generateBusinessGroupMembershipChange(members);
diff --git a/src/main/java/org/olat/course/run/CourseRuntimeController.java b/src/main/java/org/olat/course/run/CourseRuntimeController.java
index 28122cbd662f847b69697e1096066d6adce6b652..10ca9f9786e4d1fd268da35ed31fcd5878d8c5bb 100644
--- a/src/main/java/org/olat/course/run/CourseRuntimeController.java
+++ b/src/main/java/org/olat/course/run/CourseRuntimeController.java
@@ -32,7 +32,6 @@ import org.olat.core.commons.fullWebApp.LayoutMain3ColsController;
 import org.olat.core.commons.fullWebApp.popup.BaseFullWebappPopupLayoutFactory;
 import org.olat.core.commons.modules.bc.FolderRunController;
 import org.olat.core.commons.modules.glossary.GlossaryMainController;
-import org.olat.core.commons.persistence.DBFactory;
 import org.olat.core.commons.persistence.PersistenceHelper;
 import org.olat.core.commons.services.mark.Mark;
 import org.olat.core.gui.UserRequest;
@@ -995,8 +994,6 @@ public class CourseRuntimeController extends RepositoryEntryRuntimeController im
 		repositoryManager.leave(getIdentity(), getRepositoryEntry(), status, reMailing);
 		//leave groups
 		businessGroupService.leave(getIdentity(), getRepositoryEntry(), status, reMailing);
-		//reload this but make sure all changes are committed before reload
-		DBFactory.getInstance().commit();
 		
 		if(status.isWarningManagedGroup() || status.isWarningManagedCourse()) {
 			showWarning("sign.out.warning.managed");
diff --git a/src/main/java/org/olat/group/BusinessGroupService.java b/src/main/java/org/olat/group/BusinessGroupService.java
index 27eeee4fee7a83e09ab1491afbbca820dc877a9f..67d43fd7d11dfd4389ea2f122df713b10ecf6ee3 100644
--- a/src/main/java/org/olat/group/BusinessGroupService.java
+++ b/src/main/java/org/olat/group/BusinessGroupService.java
@@ -296,7 +296,7 @@ public interface BusinessGroupService {
 	
 	public void addResourcesTo(List<BusinessGroup> groups, List<RepositoryEntry> resources);
 	
-	public void removeResourceFrom(List<BusinessGroup> group, RepositoryEntry re);
+	public void removeResourceFrom(List<BusinessGroup> group, RepositoryEntryRef re);
 	
 	public void removeResource(RepositoryEntryRef resource);
 	
diff --git a/src/main/java/org/olat/group/manager/BusinessGroupMembershipProcessor.java b/src/main/java/org/olat/group/manager/BusinessGroupMembershipProcessor.java
new file mode 100644
index 0000000000000000000000000000000000000000..1f6bcbdeda2ba0e715d48bacedaa1421404efc48
--- /dev/null
+++ b/src/main/java/org/olat/group/manager/BusinessGroupMembershipProcessor.java
@@ -0,0 +1,126 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); <br>
+ * you may not use this file except in compliance with the License.<br>
+ * You may obtain a copy of the License at the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <p>
+ * Unless required by applicable law or agreed to in writing,<br>
+ * software distributed under the License is distributed on an "AS IS" BASIS, <br>
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
+ * See the License for the specific language governing permissions and <br>
+ * limitations under the License.
+ * <p>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+package org.olat.group.manager;
+
+import java.util.Collections;
+import java.util.List;
+
+import org.olat.basesecurity.GroupRoles;
+import org.olat.basesecurity.IdentityRef;
+import org.olat.basesecurity.model.IdentityRefImpl;
+import org.olat.core.commons.services.notifications.NotificationsManager;
+import org.olat.core.gui.control.Event;
+import org.olat.core.util.coordinate.CoordinatorManager;
+import org.olat.core.util.event.GenericEventListener;
+import org.olat.core.util.resource.OresHelper;
+import org.olat.group.BusinessGroup;
+import org.olat.group.BusinessGroupRef;
+import org.olat.group.model.BGRepositoryEntryRelation;
+import org.olat.group.model.BusinessGroupRefImpl;
+import org.olat.group.model.BusinessGroupRelationModified;
+import org.olat.group.ui.edit.BusinessGroupModifiedEvent;
+import org.olat.repository.RepositoryEntryRef;
+import org.olat.repository.RepositoryManager;
+import org.olat.repository.manager.RepositoryEntryRelationDAO;
+import org.olat.repository.model.RepositoryEntryRefImpl;
+import org.olat.resource.OLATResource;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+/**
+ * process event related to membership removed from groups.
+ * 
+ * 
+ * Initial date: 09.02.2015<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+@Service
+public class BusinessGroupMembershipProcessor implements InitializingBean, GenericEventListener {
+	
+	@Autowired
+	private CoordinatorManager coordinator;
+	@Autowired
+	private NotificationsManager notificationsManager;
+	@Autowired
+	private RepositoryManager repositoryManager;
+	@Autowired
+	private BusinessGroupRelationDAO businessGroupRelationDao;
+	@Autowired
+	private RepositoryEntryRelationDAO repositoryEntryRelationDao;
+
+	@Override
+	public void afterPropertiesSet() throws Exception {
+		coordinator.getCoordinator().getEventBus().registerFor(this, null, OresHelper.lookupType(BusinessGroup.class));
+	}
+
+	@Override
+	public void event(Event event) {
+		if(event instanceof BusinessGroupModifiedEvent) {
+			BusinessGroupModifiedEvent e = (BusinessGroupModifiedEvent)event;
+			if(BusinessGroupModifiedEvent.IDENTITY_REMOVED_EVENT.equals(e.getCommand())) {
+				processIdentityRemoved(e.getModifiedGroupKey(), e.getAffectedIdentityKey());
+			}
+		} else if(event instanceof BusinessGroupRelationModified) {
+			BusinessGroupRelationModified e = (BusinessGroupRelationModified)event;
+			if(BusinessGroupRelationModified.RESOURCE_REMOVED_EVENT.equals(e.getCommand())) {
+				processResourceRemoved(e.getGroupKey(), e.getRepositoryEntryKey());
+			}
+		}
+	}
+	
+	private void processResourceRemoved(Long groupKey, Long repoKey) {
+		BusinessGroupRef groupRef = new BusinessGroupRefImpl(groupKey);
+		RepositoryEntryRef entryRef = new RepositoryEntryRefImpl(repoKey);
+		OLATResource resource = repositoryManager.lookupRepositoryEntryResource(entryRef.getKey());
+
+		List<Long> memberKeys = businessGroupRelationDao
+			.getMemberKeys(Collections.singletonList(groupRef), GroupRoles.coach.name(), GroupRoles.participant.name());
+		for(Long memberKey:memberKeys) {
+			IdentityRef member = new IdentityRefImpl(memberKey);
+			List<String> remaingRoles = repositoryEntryRelationDao.getRoles(member, entryRef);
+			if(remaingRoles.isEmpty()) {
+				notificationsManager.unsubscribeAllForIdentityAndResId(member, resource.getResourceableId());
+			}
+		}
+	}
+	
+	private void processIdentityRemoved(Long groupKey, Long identityKey) {
+		IdentityRef identityRef = new IdentityRefImpl(identityKey);
+		BusinessGroupRef groupRef = new BusinessGroupRefImpl(groupKey);
+		
+		if(!businessGroupRelationDao.hasAnyRole(identityRef, groupRef)) {
+			notificationsManager.unsubscribeAllForIdentityAndResId(identityRef, groupRef.getKey());
+			
+			List<BGRepositoryEntryRelation> relations = businessGroupRelationDao
+					.findRelationToRepositoryEntries(Collections.singletonList(groupKey), 0, -1);
+			for(BGRepositoryEntryRelation relation:relations) {
+				Long repositoryEntryKey = relation.getRepositoryEntryKey();
+				RepositoryEntryRef entryRef = new RepositoryEntryRefImpl(repositoryEntryKey);
+				List<String> remaingRoles = repositoryEntryRelationDao.getRoles(identityRef, entryRef);
+				if(remaingRoles.isEmpty()) {
+					OLATResource resource = repositoryManager.lookupRepositoryEntryResource(entryRef.getKey());
+					notificationsManager.unsubscribeAllForIdentityAndResId(identityRef, resource.getResourceableId());
+				}
+			}
+		}
+	}
+}
diff --git a/src/main/java/org/olat/group/manager/BusinessGroupRelationDAO.java b/src/main/java/org/olat/group/manager/BusinessGroupRelationDAO.java
index 2ab194a79680556f7b803530c12e72ae4781ba05..3bc0fd280553298fd7f8580d4270e7da914cc10f 100644
--- a/src/main/java/org/olat/group/manager/BusinessGroupRelationDAO.java
+++ b/src/main/java/org/olat/group/manager/BusinessGroupRelationDAO.java
@@ -117,7 +117,7 @@ public class BusinessGroupRelationDAO {
 				.getResultList();
 	}
 	
-	public int countRoles(BusinessGroup group, String... role) {
+	public int countRoles(BusinessGroupRef group, String... role) {
 		StringBuilder sb = new StringBuilder();
 		sb.append("select count(membership) from ").append(BusinessGroupImpl.class.getName()).append(" as bgroup ")
 		  .append(" inner join bgroup.baseGroup as baseGroup")
@@ -183,6 +183,20 @@ public class BusinessGroupRelationDAO {
 		return count == null ? false : count.intValue() > 0;
 	}
 	
+	public boolean hasAnyRole(IdentityRef identity, BusinessGroupRef group) {
+		StringBuilder sb = new StringBuilder();
+		sb.append("select count(membership) from ").append(BusinessGroupImpl.class.getName()).append(" as bgroup ")
+		  .append(" inner join bgroup.baseGroup as baseGroup")
+		  .append(" inner join baseGroup.members as membership")
+		  .append(" where bgroup.key=:businessGroupKey and membership.identity.key=:identityKey");
+
+		Number count = dbInstance.getCurrentEntityManager().createQuery(sb.toString(), Number.class)
+				.setParameter("businessGroupKey", group.getKey())
+				.setParameter("identityKey", identity.getKey())
+				.getSingleResult();
+		return count == null ? false : count.intValue() > 0;
+	}
+	
 	public void touchMembership(IdentityRef identity, BusinessGroupRef group) {
 		StringBuilder sb = new StringBuilder();
 		sb.append("select membership from ").append(BusinessGroupImpl.class.getName()).append(" as bgroup ")
@@ -252,7 +266,7 @@ public class BusinessGroupRelationDAO {
 		return members;
 	}
 	
-	public void deleteRelation(BusinessGroup group, RepositoryEntry entry) {
+	public void deleteRelation(BusinessGroup group, RepositoryEntryRef entry) {
 		repositoryEntryRelationDao.removeRelation(group.getBaseGroup(), entry);
 	}
 	
diff --git a/src/main/java/org/olat/group/manager/BusinessGroupServiceImpl.java b/src/main/java/org/olat/group/manager/BusinessGroupServiceImpl.java
index 8164dc6b136fa35b51154ee43982626dd695c7ab..ecb3cd915968336d05ce4f4aada8f8b1870d78c3 100644
--- a/src/main/java/org/olat/group/manager/BusinessGroupServiceImpl.java
+++ b/src/main/java/org/olat/group/manager/BusinessGroupServiceImpl.java
@@ -48,7 +48,6 @@ import org.olat.collaboration.CollaborationToolsFactory;
 import org.olat.core.CoreSpringFactory;
 import org.olat.core.commons.persistence.DB;
 import org.olat.core.commons.services.notifications.NotificationsManager;
-import org.olat.core.commons.services.notifications.Subscriber;
 import org.olat.core.id.Identity;
 import org.olat.core.id.Roles;
 import org.olat.core.logging.DBRuntimeException;
@@ -71,7 +70,6 @@ import org.olat.core.util.resource.OLATResourceableJustBeforeDeletedEvent;
 import org.olat.core.util.resource.OresHelper;
 import org.olat.group.BusinessGroup;
 import org.olat.group.BusinessGroupAddResponse;
-import org.olat.group.BusinessGroupImpl;
 import org.olat.group.BusinessGroupManagedFlag;
 import org.olat.group.BusinessGroupMembership;
 import org.olat.group.BusinessGroupModule;
@@ -92,6 +90,7 @@ import org.olat.group.model.BusinessGroupMembershipChange;
 import org.olat.group.model.BusinessGroupMembershipImpl;
 import org.olat.group.model.BusinessGroupMembershipViewImpl;
 import org.olat.group.model.BusinessGroupMembershipsChanges;
+import org.olat.group.model.BusinessGroupRelationModified;
 import org.olat.group.model.EnrollState;
 import org.olat.group.model.IdentityGroupKey;
 import org.olat.group.model.LeaveOption;
@@ -810,6 +809,7 @@ public class BusinessGroupServiceImpl implements BusinessGroupService, UserDataD
 		params.setIdentity(identity);
 		params.setAttendee(true);
 		List<BusinessGroup> groups = businessGroupDAO.findBusinessGroups(params, entry, 0, -1);
+		List<BusinessGroupModifiedEvent.Deferred> events = new ArrayList<BusinessGroupModifiedEvent.Deferred>();
 		for(BusinessGroup group:groups) {
 			if(BusinessGroupManagedFlag.isManaged(group, BusinessGroupManagedFlag.membersmanagement)) {
 				status.setWarningManagedGroup(true);
@@ -819,6 +819,8 @@ public class BusinessGroupServiceImpl implements BusinessGroupService, UserDataD
 				removeParticipant(identity, identity, group, mailing, null);
 			}
 		}
+		dbInstance.commit();
+		BusinessGroupModifiedEvent.fireDeferredEvents(events);
 	}
 
 	@Override
@@ -1012,9 +1014,6 @@ public class BusinessGroupServiceImpl implements BusinessGroupService, UserDataD
 
 		boolean removed = businessGroupRelationDAO.removeRole(identity, group, GroupRoles.participant.name());
 		if(removed) {
-			//remove subscriptions if user gets removed
-			removeSubscriptions(identity, group);
-			
 			// notify currently active users of this business group
 			BusinessGroupModifiedEvent.Deferred event = BusinessGroupModifiedEvent.createDeferredEvent(BusinessGroupModifiedEvent.IDENTITY_REMOVED_EVENT, group, identity);
 			if(events != null) {
@@ -1459,15 +1458,12 @@ public class BusinessGroupServiceImpl implements BusinessGroupService, UserDataD
 		
 		businessGroupRelationDAO.removeRole(identityToRemove, group, GroupRoles.coach.name());
 		
-		//remove subsciptions if user gets removed
-		removeSubscriptions(identityToRemove, group);
-		
 		// notify currently active users of this business group
 		BusinessGroupModifiedEvent.Deferred event;
 		if (identityToRemove.getKey().equals(ureqIdentity.getKey()) ) {
 			event = BusinessGroupModifiedEvent.createDeferredEvent(BusinessGroupModifiedEvent.MYSELF_ASOWNER_REMOVED_EVENT, group, identityToRemove);
 		} else {
-  		event = BusinessGroupModifiedEvent.createDeferredEvent(BusinessGroupModifiedEvent.IDENTITY_REMOVED_EVENT, group, identityToRemove);
+			event = BusinessGroupModifiedEvent.createDeferredEvent(BusinessGroupModifiedEvent.IDENTITY_REMOVED_EVENT, group, identityToRemove);
 		}
 		if(events != null) {
 			events.add(event);
@@ -1488,19 +1484,6 @@ public class BusinessGroupServiceImpl implements BusinessGroupService, UserDataD
 		BusinessGroupModifiedEvent.fireDeferredEvents(events);
 	}
 	
-	private void removeSubscriptions(Identity identity, BusinessGroup group) {
-		NotificationsManager notiMgr = NotificationsManager.getInstance();
-		List<Subscriber> l = notiMgr.getSubscribers(identity);
-		for (Iterator<Subscriber> iterator = l.iterator(); iterator.hasNext();) {
-			Subscriber subscriber = iterator.next();
-			Long resId = subscriber.getPublisher().getResId();
-			Long groupKey = group.getKey();
-			if (resId != null && groupKey != null && resId.equals(groupKey)) {
-				notiMgr.unsubscribe(subscriber);
-			}
-		}
-	}
-	
 	@Override
 	public boolean hasResources(BusinessGroup group) {
 		return businessGroupRelationDAO.countResources(group) > 0;
@@ -1636,29 +1619,34 @@ public class BusinessGroupServiceImpl implements BusinessGroupService, UserDataD
 	}
 
 	@Override
-	public void removeResourceFrom(List<BusinessGroup> groups, RepositoryEntry re) {
+	public void removeResourceFrom(List<BusinessGroup> groups, RepositoryEntryRef re) {
 		if(groups == null || groups.isEmpty()) {
 			return; // nothing to do
 		}
 		
+		List<BusinessGroupRelationModified> events = new ArrayList<BusinessGroupRelationModified>();
+		
 		int count = 0;
 		for(BusinessGroup group:groups) {
 			businessGroupRelationDAO.deleteRelation(group, re);
+			events.add(new BusinessGroupRelationModified(BusinessGroupRelationModified.RESOURCE_REMOVED_EVENT, group.getKey(), re.getKey()));
 			if(count++ % 20 == 0) {
 				dbInstance.commit();
 			}
 		}
 		dbInstance.commit();
+		
+		for(BusinessGroupRelationModified event:events) {
+			CoordinatorManager.getInstance().getCoordinator().getEventBus()
+				.fireEventToListenersOf(event, OresHelper.lookupType(BusinessGroup.class));
+		}
 	}
 	
 	@Override
-	public void removeResource(RepositoryEntryRef resource) {
+	public void removeResource(RepositoryEntryRef re) {
 		SearchBusinessGroupParams params = new SearchBusinessGroupParams();
-		List<BusinessGroup> groups = findBusinessGroups(params, resource, 0, -1);
-		for(BusinessGroup group:groups) {
-			repositoryEntryRelationDao.removeRelation(((BusinessGroupImpl)group).getBaseGroup(), resource);
-		}
-		dbInstance.commit();
+		List<BusinessGroup> groups = findBusinessGroups(params, re, 0, -1);
+		removeResourceFrom(groups, re);
 	}
 
 	@Override
diff --git a/src/main/java/org/olat/group/model/BusinessGroupRefImpl.java b/src/main/java/org/olat/group/model/BusinessGroupRefImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..5c1214e4bca0d55d047ad3d4b7ed4bf70659f909
--- /dev/null
+++ b/src/main/java/org/olat/group/model/BusinessGroupRefImpl.java
@@ -0,0 +1,58 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); <br>
+ * you may not use this file except in compliance with the License.<br>
+ * You may obtain a copy of the License at the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <p>
+ * Unless required by applicable law or agreed to in writing,<br>
+ * software distributed under the License is distributed on an "AS IS" BASIS, <br>
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
+ * See the License for the specific language governing permissions and <br>
+ * limitations under the License.
+ * <p>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+package org.olat.group.model;
+
+import org.olat.group.BusinessGroupRef;
+
+/**
+ * 
+ * Initial date: 09.02.2015<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public final class BusinessGroupRefImpl implements BusinessGroupRef {
+	
+	private final Long groupKey;
+	
+	public BusinessGroupRefImpl(Long groupKey) {
+		this.groupKey = groupKey;
+	}
+
+	@Override
+	public Long getKey() {
+		return groupKey;
+	}
+
+	@Override
+	public int hashCode() {
+		return groupKey == null ? 98376802 : groupKey.hashCode();
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if (this == obj) {
+			return true;
+		} else if(obj instanceof BusinessGroupRefImpl) {
+			BusinessGroupRefImpl ref = (BusinessGroupRefImpl)obj;
+			return groupKey != null && groupKey.equals(ref.groupKey);
+		}
+		return false;
+	}
+}
diff --git a/src/main/java/org/olat/group/model/BusinessGroupRelationModified.java b/src/main/java/org/olat/group/model/BusinessGroupRelationModified.java
new file mode 100644
index 0000000000000000000000000000000000000000..bedc18df7cdc2fd42aed5b2946e850dd26a89571
--- /dev/null
+++ b/src/main/java/org/olat/group/model/BusinessGroupRelationModified.java
@@ -0,0 +1,52 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); <br>
+ * you may not use this file except in compliance with the License.<br>
+ * You may obtain a copy of the License at the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <p>
+ * Unless required by applicable law or agreed to in writing,<br>
+ * software distributed under the License is distributed on an "AS IS" BASIS, <br>
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
+ * See the License for the specific language governing permissions and <br>
+ * limitations under the License.
+ * <p>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+package org.olat.group.model;
+
+import org.olat.core.util.event.MultiUserEvent;
+
+/**
+ * 
+ * Initial date: 09.02.2015<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class BusinessGroupRelationModified extends MultiUserEvent {
+
+	private static final long serialVersionUID = 4694938528412441148L;
+	
+	public static final String RESOURCE_REMOVED_EVENT = "resource.removed.event";
+	
+	private Long groupKey;
+	private Long repositoryEntryKey;
+	
+	public BusinessGroupRelationModified(String cmd, Long groupKey, Long repositoryEntryKey) {
+		super(cmd);
+		this.groupKey = groupKey;
+		this.repositoryEntryKey = repositoryEntryKey;
+	}
+
+	public Long getGroupKey() {
+		return groupKey;
+	}
+
+	public Long getRepositoryEntryKey() {
+		return repositoryEntryKey;
+	}
+}
diff --git a/src/main/java/org/olat/group/ui/main/AbstractMemberListController.java b/src/main/java/org/olat/group/ui/main/AbstractMemberListController.java
index 33e39333f92b6f586b539905fd201953961829a3..2fcb1cec311002599d59e72ef246a911e561ca76 100644
--- a/src/main/java/org/olat/group/ui/main/AbstractMemberListController.java
+++ b/src/main/java/org/olat/group/ui/main/AbstractMemberListController.java
@@ -462,7 +462,7 @@ public abstract class AbstractMemberListController extends BasicController imple
 		MailPackage mailing = new MailPackage(sendMail);
 		if(repoEntry != null) {
 			List<RepositoryEntryPermissionChangeEvent> changes = Collections.singletonList((RepositoryEntryPermissionChangeEvent)e);
-			repositoryManager.updateRepositoryEntryMembership(getIdentity(), ureq.getUserSession().getRoles(), repoEntry, changes, mailing);
+			repositoryManager.updateRepositoryEntryMemberships(getIdentity(), ureq.getUserSession().getRoles(), repoEntry, changes, mailing);
 		}
 
 		businessGroupService.updateMemberships(getIdentity(), e.getGroupChanges(), mailing);
@@ -475,7 +475,7 @@ public abstract class AbstractMemberListController extends BasicController imple
 		MailPackage mailing = new MailPackage(sendMail);
 		if(repoEntry != null) {
 			List<RepositoryEntryPermissionChangeEvent> repoChanges = changes.generateRepositoryChanges(members);
-			repositoryManager.updateRepositoryEntryMembership(getIdentity(), ureq.getUserSession().getRoles(), repoEntry, repoChanges, mailing);
+			repositoryManager.updateRepositoryEntryMemberships(getIdentity(), ureq.getUserSession().getRoles(), repoEntry, repoChanges, mailing);
 		}
 
 		//commit all changes to the group memberships
diff --git a/src/main/java/org/olat/repository/RepositoryManager.java b/src/main/java/org/olat/repository/RepositoryManager.java
index 87b558ec2dad0939c46feecdd8d17ddc5dae3618..8cfadd46551881a590b757705adaa04669b8b77b 100644
--- a/src/main/java/org/olat/repository/RepositoryManager.java
+++ b/src/main/java/org/olat/repository/RepositoryManager.java
@@ -54,6 +54,7 @@ import org.olat.core.commons.persistence.PersistenceHelper;
 import org.olat.core.commons.services.image.ImageService;
 import org.olat.core.commons.services.image.Size;
 import org.olat.core.commons.services.mark.impl.MarkImpl;
+import org.olat.core.commons.services.notifications.NotificationsManager;
 import org.olat.core.gui.UserRequest;
 import org.olat.core.id.Identity;
 import org.olat.core.id.OLATResourceable;
@@ -66,7 +67,11 @@ import org.olat.core.logging.activity.ThreadLocalUserActivityLogger;
 import org.olat.core.manager.BasicManager;
 import org.olat.core.util.FileUtils;
 import org.olat.core.util.StringHelper;
+import org.olat.core.util.coordinate.CoordinatorManager;
+import org.olat.core.util.event.EventBus;
+import org.olat.core.util.event.MultiUserEvent;
 import org.olat.core.util.mail.MailPackage;
+import org.olat.core.util.resource.OresHelper;
 import org.olat.core.util.vfs.LocalFolderImpl;
 import org.olat.core.util.vfs.VFSContainer;
 import org.olat.core.util.vfs.VFSItem;
@@ -77,6 +82,7 @@ import org.olat.group.GroupLoggingAction;
 import org.olat.repository.manager.RepositoryEntryRelationDAO;
 import org.olat.repository.model.RepositoryEntryLifecycle;
 import org.olat.repository.model.RepositoryEntryMembership;
+import org.olat.repository.model.RepositoryEntryMembershipModifiedEvent;
 import org.olat.repository.model.RepositoryEntryPermissionChangeEvent;
 import org.olat.repository.model.RepositoryEntrySecurity;
 import org.olat.repository.model.RepositoryEntryShortImpl;
@@ -123,6 +129,8 @@ public class RepositoryManager extends BasicManager {
 	private ACReservationDAO reservationDao;
 	@Autowired
 	private LifeFullIndexer lifeIndexer;
+	@Autowired
+	private NotificationsManager notificationsManager;
 
 	/**
 	 * @return Singleton.
@@ -821,21 +829,6 @@ public class RepositoryManager extends BasicManager {
 		     .append(" where v.access > 0 and (")
 		     .append("   membership.identity.key=:editorKey and membership.role='").append(GroupRoles.owner.name()).append("'")
 		     .append(" )");
-		/*
-		 //TODO groups match policy
-		     .append(" and ((")
-		     .append("  ownerGroup in (select ownerSgmsi.securityGroup from ").append(SecurityGroupMembershipImpl.class.getName()).append(" ownerSgmsi where ownerSgmsi.identity.key=:editorKey)")
-		     .append(" ) or (")
-		     .append("  reResource in (select groupRelation.resource from ").append(BGResourceRelation.class.getName()).append(" as groupRelation, ")
-		     .append("    ").append(SecurityGroupMembershipImpl.class.getName()).append(" as sgmsi,")
-		     .append("    ").append(PolicyImpl.class.getName()).append(" as poi,")
-		     .append("    ").append(OLATResourceImpl.class.getName()).append(" as ori")
-		     .append("     where sgmsi.identity.key = :editorKey and sgmsi.securityGroup = poi.securityGroup")
-		     .append("     and poi.permission = 'bgr.editor' and poi.olatResource = ori")
-		     .append("     and groupRelation.resource=ori")
-		     .append("  )")
-		     .append(" ))");
-		*/
 		
 		if(resourceTypes != null && resourceTypes.length > 0) {
 			query.append(" and reResource.resName in (:resnames)");
@@ -1430,12 +1423,23 @@ public class RepositoryManager extends BasicManager {
 		return dbQuery;
 	}
 	
+	/**
+	 * Leave the course, commit to the database and send events
+	 * 
+	 * @param identity
+	 * @param re
+	 * @param status
+	 * @param mailing
+	 */
 	public void leave(Identity identity, RepositoryEntry re, LeavingStatusList status, MailPackage mailing) {
 		if(RepositoryEntryManagedFlag.isManaged(re, RepositoryEntryManagedFlag.membersmanagement)) {
 			status.setWarningManagedCourse(true);
 		} else {
-			List<Identity> removeIdentities = Collections.singletonList(identity);
-			removeParticipants(identity, removeIdentities, re, mailing, true);
+			List<RepositoryEntryMembershipModifiedEvent> deferredEvents = new ArrayList<>();
+			removeParticipant(identity, identity, re, mailing, true);
+			deferredEvents.add(RepositoryEntryMembershipModifiedEvent.removed(identity, re));
+			dbInstance.commit();
+			sendDeferredEvents(deferredEvents, re);
 		}
 	}
 	
@@ -1477,20 +1481,39 @@ public class RepositoryManager extends BasicManager {
 	 * @param logger
 	 */
 	public void removeOwners(Identity ureqIdentity, List<Identity> removeIdentities, RepositoryEntry re){
-    for (Identity identity : removeIdentities) {
-    	repositoryEntryRelationDao.removeRole(identity, re, GroupRoles.owner.name());
+		List<RepositoryEntryMembershipModifiedEvent> deferredEvents = new ArrayList<>();
+		
+		for (Identity identity : removeIdentities) {
+			removeOwner(ureqIdentity, identity, re);
+			deferredEvents.add(RepositoryEntryMembershipModifiedEvent.removed(identity, re));
+		}
+		
+		dbInstance.commit();
+		sendDeferredEvents(deferredEvents, re);
+	}
+	
+	private void sendDeferredEvents(List<? extends MultiUserEvent> events, OLATResourceable ores) {
+		EventBus eventBus = CoordinatorManager.getInstance().getCoordinator().getEventBus();
+		for(MultiUserEvent event:events) {
+			eventBus.fireEventToListenersOf(event, ores);
+			eventBus.fireEventToListenersOf(event, OresHelper.lookupType(RepositoryEntry.class));
+		}
+	}
+	
+	private void removeOwner(Identity ureqIdentity, Identity identity, RepositoryEntry re) {
+		repositoryEntryRelationDao.removeRole(identity, re, GroupRoles.owner.name());
 
-			ActionType actionType = ThreadLocalUserActivityLogger.getStickyActionType();
-			ThreadLocalUserActivityLogger.setStickyActionType(ActionType.admin);
-			try{
-				ThreadLocalUserActivityLogger.log(GroupLoggingAction.GROUP_OWNER_REMOVED, getClass(),
-						LoggingResourceable.wrap(re, OlatResourceableType.genRepoEntry), LoggingResourceable.wrap(identity));
-			} finally {
-				ThreadLocalUserActivityLogger.setStickyActionType(actionType);
-			}
-			logAudit("Identity(.key):" + ureqIdentity.getKey() + " removed identity '" + identity.getName()
-					+ "' from repositoryentry with key " + re.getKey());
-    }
+
+		ActionType actionType = ThreadLocalUserActivityLogger.getStickyActionType();
+		ThreadLocalUserActivityLogger.setStickyActionType(ActionType.admin);
+		try{
+			ThreadLocalUserActivityLogger.log(GroupLoggingAction.GROUP_OWNER_REMOVED, getClass(),
+					LoggingResourceable.wrap(re, OlatResourceableType.genRepoEntry), LoggingResourceable.wrap(identity));
+		} finally {
+			ThreadLocalUserActivityLogger.setStickyActionType(actionType);
+		}
+		logAudit("Identity(.key):" + ureqIdentity.getKey() + " removed identity '" + identity.getName()
+				+ "' from repositoryentry with key " + re.getKey());
 	}
 	
 	public void acceptPendingParticipation(Identity ureqIdentity, Identity identityToAdd, OLATResource resource, ResourceReservation reservation) {
@@ -1583,21 +1606,29 @@ public class RepositoryManager extends BasicManager {
 	 * @param re
 	 * @param logger
 	 */
-	public void removeTutors(Identity ureqIdentity, List<Identity> removeIdentities, RepositoryEntry re){
+	public void removeTutors(Identity ureqIdentity, List<Identity> removeIdentities, RepositoryEntry re) {
+		List<RepositoryEntryMembershipModifiedEvent> deferredEvents = new ArrayList<>();
 		for (Identity identity : removeIdentities) {
-			repositoryEntryRelationDao.removeRole(identity, re, GroupRoles.coach.name());
-    	
-			ActionType actionType = ThreadLocalUserActivityLogger.getStickyActionType();
-			ThreadLocalUserActivityLogger.setStickyActionType(ActionType.admin);
-			try{
-				ThreadLocalUserActivityLogger.log(GroupLoggingAction.GROUP_OWNER_REMOVED, getClass(),
-						LoggingResourceable.wrap(re, OlatResourceableType.genRepoEntry), LoggingResourceable.wrap(identity));
-			} finally {
-				ThreadLocalUserActivityLogger.setStickyActionType(actionType);
-			}
-			logAudit("Identity(.key):" + ureqIdentity.getKey() + " removed identity '" + identity.getName()
-					+ "' from repositoryentry with key " + re.getKey());
+			removeTutor(ureqIdentity, identity, re);
+			deferredEvents.add(RepositoryEntryMembershipModifiedEvent.removed(identity, re));
+		}
+		dbInstance.commit();
+		sendDeferredEvents(deferredEvents, re);
+	}
+	
+	private void removeTutor(Identity ureqIdentity, Identity identity, RepositoryEntry re) {
+		repositoryEntryRelationDao.removeRole(identity, re, GroupRoles.coach.name());
+		
+		ActionType actionType = ThreadLocalUserActivityLogger.getStickyActionType();
+		ThreadLocalUserActivityLogger.setStickyActionType(ActionType.admin);
+		try{
+			ThreadLocalUserActivityLogger.log(GroupLoggingAction.GROUP_OWNER_REMOVED, getClass(),
+					LoggingResourceable.wrap(re, OlatResourceableType.genRepoEntry), LoggingResourceable.wrap(identity));
+		} finally {
+			ThreadLocalUserActivityLogger.setStickyActionType(actionType);
 		}
+		logAudit("Identity(.key):" + ureqIdentity.getKey() + " removed identity '" + identity.getName()
+				+ "' from repositoryentry with key " + re.getKey());
 	}
 	
 	/**
@@ -1674,24 +1705,32 @@ public class RepositoryManager extends BasicManager {
 	 * @param logger
 	 */
 	public void removeParticipants(Identity ureqIdentity, List<Identity> removeIdentities, RepositoryEntry re, MailPackage mailing, boolean sendMail) {
+		List<RepositoryEntryMembershipModifiedEvent> deferredEvents = new ArrayList<>();
 		for (Identity identity : removeIdentities) {
-			repositoryEntryRelationDao.removeRole(identity, re, GroupRoles.participant.name());
-
-			if(sendMail) {
-				RepositoryMailing.sendEmail(ureqIdentity, identity, re, RepositoryMailing.Type.removeParticipant, mailing);
-			}
+			removeParticipant(ureqIdentity, identity, re, mailing, sendMail);
+			deferredEvents.add(RepositoryEntryMembershipModifiedEvent.removed(identity, re));
+		}
+		dbInstance.commit();
+		sendDeferredEvents(deferredEvents, re);
+	}
+	
+	private void removeParticipant(Identity ureqIdentity, Identity identity, RepositoryEntry re, MailPackage mailing, boolean sendMail) {
+		repositoryEntryRelationDao.removeRole(identity, re, GroupRoles.participant.name());
+		
+		if(sendMail) {
+			RepositoryMailing.sendEmail(ureqIdentity, identity, re, RepositoryMailing.Type.removeParticipant, mailing);
+		}
 
-			ActionType actionType = ThreadLocalUserActivityLogger.getStickyActionType();
-			ThreadLocalUserActivityLogger.setStickyActionType(ActionType.admin);
-			try{
-				ThreadLocalUserActivityLogger.log(GroupLoggingAction.GROUP_OWNER_REMOVED, getClass(),
-						LoggingResourceable.wrap(re, OlatResourceableType.genRepoEntry), LoggingResourceable.wrap(identity));
-			} finally {
-				ThreadLocalUserActivityLogger.setStickyActionType(actionType);
-			}
-			logAudit("Identity(.key):" + ureqIdentity.getKey() + " removed identity '" + identity.getName()
-					+ "' from repositoryentry with key " + re.getKey());
+		ActionType actionType = ThreadLocalUserActivityLogger.getStickyActionType();
+		ThreadLocalUserActivityLogger.setStickyActionType(ActionType.admin);
+		try{
+			ThreadLocalUserActivityLogger.log(GroupLoggingAction.GROUP_OWNER_REMOVED, getClass(),
+					LoggingResourceable.wrap(re, OlatResourceableType.genRepoEntry), LoggingResourceable.wrap(identity));
+		} finally {
+			ThreadLocalUserActivityLogger.setStickyActionType(actionType);
 		}
+		logAudit("Identity(.key):" + ureqIdentity.getKey() + " removed identity '" + identity.getName()
+				+ "' from repositoryentry with key " + re.getKey());
 	}
 	
 	/**
@@ -1722,6 +1761,14 @@ public class RepositoryManager extends BasicManager {
 		}
 
 		boolean allOk = repositoryEntryRelationDao.removeMembers(re, members);
+		if (allOk) {
+			List<RepositoryEntryMembershipModifiedEvent> deferredEvents = new ArrayList<>();
+			for(Identity identity:members) {
+				deferredEvents.add(RepositoryEntryMembershipModifiedEvent.removed(identity, re));
+			}
+			dbInstance.commit();
+			sendDeferredEvents(deferredEvents, re);
+		}
 		if (allOk) {
 			// do logging - not optimal but 
 			StringBuilder sb = new StringBuilder();
@@ -2137,31 +2184,46 @@ public class RepositoryManager extends BasicManager {
 		return entries;
 	}
 	
-	public void updateRepositoryEntryMembership(Identity ureqIdentity, Roles ureqRoles, RepositoryEntry re,
+	public void updateRepositoryEntryMemberships(Identity ureqIdentity, Roles ureqRoles, RepositoryEntry re,
 			List<RepositoryEntryPermissionChangeEvent> changes, MailPackage mailing) {
+
+		List<RepositoryEntryMembershipModifiedEvent> deferredEvents = new ArrayList<>();
 		for(RepositoryEntryPermissionChangeEvent e:changes) {
-			if(e.getRepoOwner() != null) {
-				if(e.getRepoOwner().booleanValue()) {
-					addOwners(ureqIdentity, new IdentitiesAddEvent(e.getMember()), re);
-				} else {
-					removeOwners(ureqIdentity, Collections.singletonList(e.getMember()), re);
-				}
+			updateRepositoryEntryMembership(ureqIdentity, ureqRoles, re, e, mailing, deferredEvents);
+		}
+
+		dbInstance.commit();
+		sendDeferredEvents(deferredEvents, re);
+	}
+	
+	private void updateRepositoryEntryMembership(Identity ureqIdentity, Roles ureqRoles, RepositoryEntry re,
+			RepositoryEntryPermissionChangeEvent changes, MailPackage mailing,
+			List<RepositoryEntryMembershipModifiedEvent> deferredEvents) {
+		
+		if(changes.getRepoOwner() != null) {
+			if(changes.getRepoOwner().booleanValue()) {
+				addOwners(ureqIdentity, new IdentitiesAddEvent(changes.getMember()), re);
+			} else {
+				removeOwner(ureqIdentity, changes.getMember(), re);
+				deferredEvents.add(RepositoryEntryMembershipModifiedEvent.removed(changes.getMember(), re));
 			}
-			
-			if(e.getRepoTutor() != null) {
-				if(e.getRepoTutor().booleanValue()) {
-					addTutors(ureqIdentity, ureqRoles, new IdentitiesAddEvent(e.getMember()), re, mailing);
-				} else {
-					removeTutors(ureqIdentity, Collections.singletonList(e.getMember()), re);
-				}
+		}
+		
+		if(changes.getRepoTutor() != null) {
+			if(changes.getRepoTutor().booleanValue()) {
+				addTutors(ureqIdentity, ureqRoles, new IdentitiesAddEvent(changes.getMember()), re, mailing);
+			} else {
+				removeTutor(ureqIdentity, changes.getMember(), re);
+				deferredEvents.add(RepositoryEntryMembershipModifiedEvent.removed(changes.getMember(), re));
 			}
-			
-			if(e.getRepoParticipant() != null) {
-				if(e.getRepoParticipant().booleanValue()) {
-					addParticipants(ureqIdentity, ureqRoles, new IdentitiesAddEvent(e.getMember()), re, mailing);
-				} else {
-					removeParticipants(ureqIdentity, Collections.singletonList(e.getMember()), re, mailing, true);
-				}
+		}
+		
+		if(changes.getRepoParticipant() != null) {
+			if(changes.getRepoParticipant().booleanValue()) {
+				addParticipants(ureqIdentity, ureqRoles, new IdentitiesAddEvent(changes.getMember()), re, mailing);
+			} else {
+				removeParticipant(ureqIdentity, changes.getMember(), re, mailing, true);
+				deferredEvents.add(RepositoryEntryMembershipModifiedEvent.removed(changes.getMember(), re));
 			}
 		}
 	}
diff --git a/src/main/java/org/olat/repository/manager/RepositoryEntryMembershipProcessor.java b/src/main/java/org/olat/repository/manager/RepositoryEntryMembershipProcessor.java
new file mode 100644
index 0000000000000000000000000000000000000000..03169c542f617752b9af7bd64c62b885189e67fe
--- /dev/null
+++ b/src/main/java/org/olat/repository/manager/RepositoryEntryMembershipProcessor.java
@@ -0,0 +1,89 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); <br>
+ * you may not use this file except in compliance with the License.<br>
+ * You may obtain a copy of the License at the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <p>
+ * Unless required by applicable law or agreed to in writing,<br>
+ * software distributed under the License is distributed on an "AS IS" BASIS, <br>
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
+ * See the License for the specific language governing permissions and <br>
+ * limitations under the License.
+ * <p>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+package org.olat.repository.manager;
+
+import java.util.List;
+
+import org.olat.basesecurity.IdentityRef;
+import org.olat.basesecurity.model.IdentityRefImpl;
+import org.olat.core.commons.services.notifications.NotificationsManager;
+import org.olat.core.gui.control.Event;
+import org.olat.core.util.coordinate.CoordinatorManager;
+import org.olat.core.util.event.GenericEventListener;
+import org.olat.core.util.resource.OresHelper;
+import org.olat.group.manager.BusinessGroupRelationDAO;
+import org.olat.repository.RepositoryEntry;
+import org.olat.repository.RepositoryEntryRef;
+import org.olat.repository.RepositoryManager;
+import org.olat.repository.model.RepositoryEntryMembershipModifiedEvent;
+import org.olat.repository.model.RepositoryEntryRefImpl;
+import org.olat.resource.OLATResource;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+/**
+ * 
+ * Process the removed membership of repository entries.
+ * 
+ * Initial date: 09.02.2015<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+@Service
+public class RepositoryEntryMembershipProcessor implements InitializingBean, GenericEventListener {
+	
+	@Autowired
+	private CoordinatorManager coordinator;
+	@Autowired
+	private NotificationsManager notificationsManager;
+	@Autowired
+	private RepositoryManager repositoryManager;
+	@Autowired
+	private BusinessGroupRelationDAO businessGroupRelationDao;
+	@Autowired
+	private RepositoryEntryRelationDAO repositoryEntryRelationDao;
+	
+	@Override
+	public void afterPropertiesSet() throws Exception {
+		coordinator.getCoordinator().getEventBus().registerFor(this, null, OresHelper.lookupType(RepositoryEntry.class));
+	}
+
+	@Override
+	public void event(Event event) {
+		if(event instanceof RepositoryEntryMembershipModifiedEvent) {
+			RepositoryEntryMembershipModifiedEvent e = (RepositoryEntryMembershipModifiedEvent)event;
+			if(RepositoryEntryMembershipModifiedEvent.IDENTITY_REMOVED.equals(e.getCommand())) {
+				processIdentityRemoved(e.getRepositoryEntryKey(), e.getIdentityKey());
+			}
+		}
+	}
+	
+	private void processIdentityRemoved(Long repoKey, Long identityKey) {
+		IdentityRef identity = new IdentityRefImpl(identityKey);
+		RepositoryEntryRef re = new RepositoryEntryRefImpl(repoKey);
+		
+		List<String> remainingRoles = repositoryEntryRelationDao.getRoles(identity, re);
+		if(remainingRoles.isEmpty()) {
+			OLATResource resource = repositoryManager.lookupRepositoryEntryResource(repoKey);
+			notificationsManager.unsubscribeAllForIdentityAndResId(identity, resource.getResourceableId());
+		}
+	}
+}
\ No newline at end of file
diff --git a/src/main/java/org/olat/repository/model/RepositoryEntryMembershipModifiedEvent.java b/src/main/java/org/olat/repository/model/RepositoryEntryMembershipModifiedEvent.java
new file mode 100644
index 0000000000000000000000000000000000000000..a41033967ec6fd7ca7855e3d8e9c5e4e1803bbbf
--- /dev/null
+++ b/src/main/java/org/olat/repository/model/RepositoryEntryMembershipModifiedEvent.java
@@ -0,0 +1,63 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); <br>
+ * you may not use this file except in compliance with the License.<br>
+ * You may obtain a copy of the License at the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <p>
+ * Unless required by applicable law or agreed to in writing,<br>
+ * software distributed under the License is distributed on an "AS IS" BASIS, <br>
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
+ * See the License for the specific language governing permissions and <br>
+ * limitations under the License.
+ * <p>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+package org.olat.repository.model;
+
+import org.olat.basesecurity.IdentityRef;
+import org.olat.core.util.event.MultiUserEvent;
+import org.olat.repository.RepositoryEntryRef;
+
+/**
+ * 
+ * Initial date: 09.02.2015<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class RepositoryEntryMembershipModifiedEvent extends MultiUserEvent {
+
+	private static final long serialVersionUID = -8624039692057985920L;
+	
+	public static final String IDENTITY_REMOVED = "identity.removed.re";
+	private Long identityKey;
+	private Long repositoryEntryKey;
+
+	
+	/**
+	 * @param command one of the class constants
+	 * @param group
+	 * @param identity
+	 */
+	public RepositoryEntryMembershipModifiedEvent(String command, Long identityKey, Long repositoryEntryKey) {
+		super(command);
+		this.identityKey = identityKey;
+		this.repositoryEntryKey = repositoryEntryKey;
+	}
+
+	public Long getIdentityKey() {
+		return identityKey;
+	}
+
+	public Long getRepositoryEntryKey() {
+		return repositoryEntryKey;
+	}
+	
+	public static RepositoryEntryMembershipModifiedEvent removed(IdentityRef identity, RepositoryEntryRef re) {
+		return new RepositoryEntryMembershipModifiedEvent(IDENTITY_REMOVED, identity.getKey(), re.getKey());
+	}
+}
diff --git a/src/main/java/org/olat/repository/model/RepositoryEntryRefImpl.java b/src/main/java/org/olat/repository/model/RepositoryEntryRefImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..ad010fe21dd6d1dc74e498e420997de769b2a5e6
--- /dev/null
+++ b/src/main/java/org/olat/repository/model/RepositoryEntryRefImpl.java
@@ -0,0 +1,58 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); <br>
+ * you may not use this file except in compliance with the License.<br>
+ * You may obtain a copy of the License at the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <p>
+ * Unless required by applicable law or agreed to in writing,<br>
+ * software distributed under the License is distributed on an "AS IS" BASIS, <br>
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
+ * See the License for the specific language governing permissions and <br>
+ * limitations under the License.
+ * <p>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+package org.olat.repository.model;
+
+import org.olat.repository.RepositoryEntryRef;
+
+/**
+ * 
+ * Initial date: 09.02.2015<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class RepositoryEntryRefImpl implements RepositoryEntryRef {
+	
+	private final Long repoKey;
+	
+	public RepositoryEntryRefImpl(Long repoKey) {
+		this.repoKey = repoKey;
+	}
+
+	@Override
+	public Long getKey() {
+		return repoKey;
+	}
+
+	@Override
+	public int hashCode() {
+		return repoKey == null ? -635465 : repoKey.hashCode();
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if(this == obj) {
+			return true;
+		} else if (obj instanceof RepositoryEntryRefImpl) {
+			RepositoryEntryRefImpl ref = (RepositoryEntryRefImpl)obj;
+			return repoKey != null && repoKey.equals(ref.getKey());
+		}
+		return false;
+	}
+}
\ No newline at end of file
diff --git a/src/main/java/org/olat/repository/ui/author/RepositoryMembersController.java b/src/main/java/org/olat/repository/ui/author/RepositoryMembersController.java
index 5911cfda18d65782ceb1d9a9e22aa9ec4e7854e9..417ccba510d4ed6181e321347c75c90e7c865212 100644
--- a/src/main/java/org/olat/repository/ui/author/RepositoryMembersController.java
+++ b/src/main/java/org/olat/repository/ui/author/RepositoryMembersController.java
@@ -167,7 +167,7 @@ public class RepositoryMembersController extends AbstractMemberListController {
 		MailerResult result = new MailerResult();
 		MailPackage reMailing = new MailPackage(template, result, getWindowControl().getBusinessControl().getAsString(), template != null);
 		List<RepositoryEntryPermissionChangeEvent> repoChanges = changes.generateRepositoryChanges(members);
-		repositoryManager.updateRepositoryEntryMembership(getIdentity(), ureq.getUserSession().getRoles(), repoEntry, repoChanges, reMailing);
+		repositoryManager.updateRepositoryEntryMemberships(getIdentity(), ureq.getUserSession().getRoles(), repoEntry, repoChanges, reMailing);
 
 		//commit all changes to the group memberships
 		List<BusinessGroupMembershipChange> allModifications = changes.generateBusinessGroupMembershipChange(members);
diff --git a/src/test/java/org/olat/group/test/BusinessGroupMembershipProcessorTest.java b/src/test/java/org/olat/group/test/BusinessGroupMembershipProcessorTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..a01f5203d76f038da0d96792e051bd144f1af4d9
--- /dev/null
+++ b/src/test/java/org/olat/group/test/BusinessGroupMembershipProcessorTest.java
@@ -0,0 +1,208 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); <br>
+ * you may not use this file except in compliance with the License.<br>
+ * You may obtain a copy of the License at the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <p>
+ * Unless required by applicable law or agreed to in writing,<br>
+ * software distributed under the License is distributed on an "AS IS" BASIS, <br>
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
+ * See the License for the specific language governing permissions and <br>
+ * limitations under the License.
+ * <p>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+package org.olat.group.test;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.Callable;
+
+import junit.framework.Assert;
+
+import org.junit.Test;
+import org.olat.basesecurity.GroupRoles;
+import org.olat.basesecurity.manager.GroupDAO;
+import org.olat.core.commons.persistence.DB;
+import org.olat.core.commons.services.notifications.NotificationsManager;
+import org.olat.core.commons.services.notifications.Publisher;
+import org.olat.core.commons.services.notifications.PublisherData;
+import org.olat.core.commons.services.notifications.SubscriptionContext;
+import org.olat.core.id.Identity;
+import org.olat.core.util.mail.MailPackage;
+import org.olat.group.BusinessGroup;
+import org.olat.group.BusinessGroupService;
+import org.olat.group.manager.BusinessGroupDAO;
+import org.olat.group.manager.BusinessGroupMembershipProcessor;
+import org.olat.group.manager.BusinessGroupRelationDAO;
+import org.olat.group.ui.edit.BusinessGroupModifiedEvent;
+import org.olat.repository.RepositoryEntry;
+import org.olat.repository.manager.RepositoryEntryRelationDAO;
+import org.olat.test.JunitTestHelper;
+import org.olat.test.OlatTestCase;
+import org.springframework.beans.factory.annotation.Autowired;
+
+/**
+ * 
+ * Initial date: 10.02.2015<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class BusinessGroupMembershipProcessorTest extends OlatTestCase {
+	
+	@Autowired
+	private DB dbInstance;
+	@Autowired
+	private GroupDAO groupDao;
+	@Autowired
+	private BusinessGroupDAO businessGroupDao;
+	@Autowired
+	private NotificationsManager notificationManager;
+	@Autowired
+	private BusinessGroupService businessGroupService;
+	@Autowired
+	private BusinessGroupRelationDAO businessGroupRelationDao;
+	@Autowired
+	private RepositoryEntryRelationDAO repositoryEntryRelationDao;
+	@Autowired
+	private BusinessGroupMembershipProcessor businessGroupMembershipProcessor;
+	
+	@Test
+	public void testUnlinkMemberOfBusinessGroup() {
+		//create a group with members
+		Identity coach = JunitTestHelper.createAndPersistIdentityAsRndUser("mbr-proc-1");
+		Identity id1 = JunitTestHelper.createAndPersistIdentityAsRndUser("mbr-proc-2");
+		Identity id2 = JunitTestHelper.createAndPersistIdentityAsRndUser("mbr-proc-3");
+		BusinessGroup businessGroup = businessGroupDao.createAndPersist(coach, "mbr-proc-1", "mbr-proc-desc", -1, -1, false, false, false, false, false);
+		businessGroupRelationDao.addRole(id1, businessGroup, GroupRoles.participant.name());
+		businessGroupRelationDao.addRole(id2, businessGroup, GroupRoles.participant.name());
+		
+		//create a publisher
+		SubscriptionContext context = new SubscriptionContext(businessGroup, "");
+		PublisherData publisherData = new PublisherData("testGroupPublishers", "e.g. something", null);
+		Publisher publisher = notificationManager.getOrCreatePublisher(context, publisherData);
+		Assert.assertNotNull(publisher);
+		dbInstance.commitAndCloseSession();
+		
+		//subscribe
+		notificationManager.subscribe(coach, context, publisherData);
+		notificationManager.subscribe(id1, context, publisherData);
+		notificationManager.subscribe(id2, context, publisherData);
+		dbInstance.commitAndCloseSession();
+		
+		//remove id1 and check subscription
+		MailPackage mailing = new MailPackage(false);
+		List<Identity> identitiesToRemove = Collections.singletonList(id1);
+		businessGroupService.removeParticipants(coach, identitiesToRemove, businessGroup, mailing);
+		
+		//wait for the remove of subscription
+		waitForCondition(new CheckUnsubscription(id1, context, dbInstance, notificationManager), 5000);
+		
+		//check that subscription of id1 was deleted but not the ones of id2 and coach
+		boolean subscribedId1 = notificationManager.isSubscribed(id1, context);
+		Assert.assertFalse(subscribedId1);
+		boolean subscribedId2 = notificationManager.isSubscribed(id2, context);
+		Assert.assertTrue(subscribedId2);
+		boolean subscribedCoach = notificationManager.isSubscribed(coach, context);
+		Assert.assertTrue(subscribedCoach);
+	}
+	
+	@Test
+	public void testUnlinkMemberOfBusinessGroup_with2Roles() {
+		//create a group with members
+		Identity member = JunitTestHelper.createAndPersistIdentityAsRndUser("mbr-proc-4");
+		BusinessGroup businessGroup = businessGroupDao.createAndPersist(member, "mbr-proc-2", "mbr-proc-desc", -1, -1, false, false, false, false, false);
+		businessGroupRelationDao.addRole(member, businessGroup, GroupRoles.participant.name());
+		
+		//create a publisher
+		SubscriptionContext context = new SubscriptionContext(businessGroup, "");
+		PublisherData publisherData = new PublisherData("testGroupPublishers", "e.g. something", null);
+		notificationManager.getOrCreatePublisher(context, publisherData);
+		notificationManager.subscribe(member, context, publisherData);
+		dbInstance.commitAndCloseSession();
+		
+		//remove id1 as participant and check subscription
+		businessGroupRelationDao.removeRole(member, businessGroup, GroupRoles.participant.name());
+		dbInstance.commitAndCloseSession();
+		
+		//manually trigger the event
+		businessGroupMembershipProcessor.event(new BusinessGroupModifiedEvent(BusinessGroupModifiedEvent.IDENTITY_REMOVED_EVENT, businessGroup, member));
+		dbInstance.commitAndCloseSession();
+	
+		//check that subscription of member was not deleted because it's still coach
+		boolean subscribed = notificationManager.isSubscribed(member, context);
+		Assert.assertTrue(subscribed);
+	}
+	
+	@Test
+	public void testUnlinkRepositoryEntry() {
+		RepositoryEntry re = JunitTestHelper.createAndPersistRepositoryEntry();
+		//create a group with members
+		Identity coach = JunitTestHelper.createAndPersistIdentityAsRndUser("mbr-proc-1");
+		Identity id1 = JunitTestHelper.createAndPersistIdentityAsRndUser("mbr-proc-2");
+		Identity id2 = JunitTestHelper.createAndPersistIdentityAsRndUser("mbr-proc-3");
+		BusinessGroup businessGroup = businessGroupDao.createAndPersist(coach, "mbr-proc-1", "mbr-proc-desc", -1, -1, false, false, false, false, false);
+		businessGroupRelationDao.addRelationToResource(businessGroup, re);
+		businessGroupRelationDao.addRole(id1, businessGroup, GroupRoles.participant.name());
+		businessGroupRelationDao.addRole(id2, businessGroup, GroupRoles.participant.name());
+		repositoryEntryRelationDao.addRole(coach, re, GroupRoles.owner.name());
+		
+		//create a publisher
+		SubscriptionContext context = new SubscriptionContext(re.getOlatResource(), "");
+		PublisherData publisherData = new PublisherData("testGroupPublishers", "e.g. something", null);
+		Publisher publisher = notificationManager.getOrCreatePublisher(context, publisherData);
+		Assert.assertNotNull(publisher);
+		dbInstance.commitAndCloseSession();
+		
+		//subscribe
+		notificationManager.subscribe(coach, context, publisherData);
+		notificationManager.subscribe(id1, context, publisherData);
+		notificationManager.subscribe(id2, context, publisherData);
+		dbInstance.commitAndCloseSession();
+		
+		//remove link between group and repository entry
+		businessGroupService.removeResourceFrom(Collections.singletonList(businessGroup), re);
+		dbInstance.commitAndCloseSession();
+		
+		//wait for the remove of subscription
+		waitForCondition(new CheckUnsubscription(id1, context, dbInstance, notificationManager), 5000);
+		waitForCondition(new CheckUnsubscription(id2, context, dbInstance, notificationManager), 5000);
+		
+		//check that subscription of id1 was deleted but not the ones of id2 and coach
+		boolean subscribedId1 = notificationManager.isSubscribed(id1, context);
+		Assert.assertFalse(subscribedId1);
+		boolean subscribedId2 = notificationManager.isSubscribed(id2, context);
+		Assert.assertFalse(subscribedId2);
+		boolean subscribedCoach = notificationManager.isSubscribed(coach, context);
+		Assert.assertTrue(subscribedCoach);
+	}
+
+	private static class CheckUnsubscription implements Callable<Boolean> {
+		
+		private final DB db;
+		private final NotificationsManager notificationMgr;
+		
+		private final Identity identity;
+		private final SubscriptionContext context;
+		
+		public CheckUnsubscription(Identity identity, SubscriptionContext context, DB db, NotificationsManager notificationMgr) {
+			this.identity = identity;
+			this.context = context;
+			this.db = db;
+			this.notificationMgr = notificationMgr;
+		}
+
+		@Override
+		public Boolean call() throws Exception {
+			boolean subscribed = notificationMgr.isSubscribed(identity, context);
+			db.commitAndCloseSession();
+			return !subscribed;
+		}
+		
+	}
+}
diff --git a/src/test/java/org/olat/repository/manager/RepositoryEntryMembershipProcessorTest.java b/src/test/java/org/olat/repository/manager/RepositoryEntryMembershipProcessorTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..41a66da1d2dd1a341b4f12d0bf1682a56708efed
--- /dev/null
+++ b/src/test/java/org/olat/repository/manager/RepositoryEntryMembershipProcessorTest.java
@@ -0,0 +1,193 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); <br>
+ * you may not use this file except in compliance with the License.<br>
+ * You may obtain a copy of the License at the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <p>
+ * Unless required by applicable law or agreed to in writing,<br>
+ * software distributed under the License is distributed on an "AS IS" BASIS, <br>
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
+ * See the License for the specific language governing permissions and <br>
+ * limitations under the License.
+ * <p>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+package org.olat.repository.manager;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Callable;
+
+import junit.framework.Assert;
+
+import org.junit.Test;
+import org.olat.basesecurity.GroupRoles;
+import org.olat.basesecurity.manager.GroupDAO;
+import org.olat.core.commons.persistence.DB;
+import org.olat.core.commons.services.notifications.NotificationsManager;
+import org.olat.core.commons.services.notifications.Publisher;
+import org.olat.core.commons.services.notifications.PublisherData;
+import org.olat.core.commons.services.notifications.SubscriptionContext;
+import org.olat.core.id.Identity;
+import org.olat.core.util.mail.MailPackage;
+import org.olat.group.BusinessGroup;
+import org.olat.group.BusinessGroupService;
+import org.olat.group.manager.BusinessGroupDAO;
+import org.olat.group.manager.BusinessGroupMembershipProcessor;
+import org.olat.group.manager.BusinessGroupRelationDAO;
+import org.olat.repository.RepositoryEntry;
+import org.olat.repository.RepositoryManager;
+import org.olat.test.JunitTestHelper;
+import org.olat.test.OlatTestCase;
+import org.springframework.beans.factory.annotation.Autowired;
+
+/**
+ * 
+ * Initial date: 10.02.2015<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class RepositoryEntryMembershipProcessorTest extends OlatTestCase {
+
+	@Autowired
+	private DB dbInstance;
+	@Autowired
+	private GroupDAO groupDao;
+	@Autowired
+	private BusinessGroupDAO businessGroupDao;
+	@Autowired
+	private NotificationsManager notificationManager;
+	@Autowired
+	private BusinessGroupService businessGroupService;
+	@Autowired
+	private BusinessGroupRelationDAO businessGroupRelationDao;
+	@Autowired
+	private RepositoryManager repositoryManager;
+	@Autowired
+	private RepositoryEntryRelationDAO repositoryEntryRelationDao;
+	@Autowired
+	private BusinessGroupMembershipProcessor businessGroupMembershipProcessor;
+	
+	@Test
+	public void testRemoveParticipant() {
+		RepositoryEntry re = JunitTestHelper.createAndPersistRepositoryEntry();
+		//create a group with members
+		Identity owner = JunitTestHelper.createAndPersistIdentityAsRndUser("remp-proc-1");
+		Identity member = JunitTestHelper.createAndPersistIdentityAsRndUser("remp-proc-2");
+		Identity participant = JunitTestHelper.createAndPersistIdentityAsRndUser("mbr-proc-3");
+
+		repositoryEntryRelationDao.addRole(owner, re, GroupRoles.owner.name());
+		repositoryEntryRelationDao.addRole(member, re, GroupRoles.coach.name());
+		repositoryEntryRelationDao.addRole(member, re, GroupRoles.participant.name());
+		repositoryEntryRelationDao.addRole(participant, re, GroupRoles.participant.name());
+		
+		//create a publisher
+		SubscriptionContext context = new SubscriptionContext(re.getOlatResource(), "");
+		PublisherData publisherData = new PublisherData("testGroupPublishers", "e.g. something", null);
+		Publisher publisher = notificationManager.getOrCreatePublisher(context, publisherData);
+		Assert.assertNotNull(publisher);
+		dbInstance.commitAndCloseSession();
+		
+		//subscribe
+		notificationManager.subscribe(owner, context, publisherData);
+		notificationManager.subscribe(member, context, publisherData);
+		notificationManager.subscribe(participant, context, publisherData);
+		dbInstance.commitAndCloseSession();
+		
+		//remove member and participant as participant of the repo entry
+		List<Identity> removeIdentities = new ArrayList<>(2);
+		removeIdentities.add(member);
+		removeIdentities.add(participant);
+		MailPackage mailing = new MailPackage(false);
+		repositoryManager.removeParticipants(owner, removeIdentities, re, mailing, false);
+
+		//wait for the remove of subscription
+		waitForCondition(new CheckUnsubscription(participant, context, dbInstance, notificationManager), 5000);
+		sleep(1000);
+		
+		//check that subscription of id1 was deleted but not the ones of id2 and coach
+		boolean subscribedPart = notificationManager.isSubscribed(participant, context);
+		Assert.assertFalse(subscribedPart);
+		boolean subscribedMember = notificationManager.isSubscribed(member, context);
+		Assert.assertTrue(subscribedMember);
+		boolean subscribedOwner = notificationManager.isSubscribed(owner, context);
+		Assert.assertTrue(subscribedOwner);
+	}
+	
+	@Test
+	public void testRemoveCoach_withBusinessGroups() {
+		RepositoryEntry re = JunitTestHelper.createAndPersistRepositoryEntry();
+		//create a group with members
+		Identity owner = JunitTestHelper.createAndPersistIdentityAsRndUser("remp-proc-1");
+		Identity member = JunitTestHelper.createAndPersistIdentityAsRndUser("remp-proc-2");
+		Identity coach = JunitTestHelper.createAndPersistIdentityAsRndUser("mbr-proc-3");
+
+		repositoryEntryRelationDao.addRole(owner, re, GroupRoles.owner.name());
+		repositoryEntryRelationDao.addRole(member, re, GroupRoles.coach.name());
+		repositoryEntryRelationDao.addRole(coach, re, GroupRoles.coach.name());
+		
+		BusinessGroup businessGroup = businessGroupDao.createAndPersist(coach, "mbr-proc-1", "mbr-proc-desc", -1, -1, false, false, false, false, false);
+		businessGroupRelationDao.addRelationToResource(businessGroup, re);
+		
+		//create a publisher
+		SubscriptionContext context = new SubscriptionContext(re.getOlatResource(), "");
+		PublisherData publisherData = new PublisherData("testGroupPublishers", "e.g. something", null);
+		Publisher publisher = notificationManager.getOrCreatePublisher(context, publisherData);
+		Assert.assertNotNull(publisher);
+		dbInstance.commitAndCloseSession();
+		
+		//subscribe
+		notificationManager.subscribe(owner, context, publisherData);
+		notificationManager.subscribe(member, context, publisherData);
+		notificationManager.subscribe(coach, context, publisherData);
+		dbInstance.commitAndCloseSession();
+		
+		//remove member and coach as coach of the repo entry
+		List<Identity> removeIdentities = new ArrayList<>(2);
+		removeIdentities.add(member);
+		removeIdentities.add(coach);
+		repositoryManager.removeTutors(owner, removeIdentities, re);
+
+		//wait for the remove of subscription
+		waitForCondition(new CheckUnsubscription(member, context, dbInstance, notificationManager), 5000);
+		sleep(1000);
+		
+		//check that subscription of id1 was deleted but not the ones of id2 and coach
+		boolean subscribedMember = notificationManager.isSubscribed(member, context);
+		Assert.assertFalse(subscribedMember);
+		boolean subscribedCoach = notificationManager.isSubscribed(coach, context);
+		Assert.assertTrue(subscribedCoach);
+		boolean subscribedOwner = notificationManager.isSubscribed(owner, context);
+		Assert.assertTrue(subscribedOwner);
+	}
+	
+	private static class CheckUnsubscription implements Callable<Boolean> {
+		
+		private final DB db;
+		private final NotificationsManager notificationMgr;
+		
+		private final Identity identity;
+		private final SubscriptionContext context;
+		
+		public CheckUnsubscription(Identity identity, SubscriptionContext context, DB db, NotificationsManager notificationMgr) {
+			this.identity = identity;
+			this.context = context;
+			this.db = db;
+			this.notificationMgr = notificationMgr;
+		}
+
+		@Override
+		public Boolean call() throws Exception {
+			boolean subscribed = notificationMgr.isSubscribed(identity, context);
+			db.commitAndCloseSession();
+			return !subscribed;
+		}
+		
+	}
+
+}
diff --git a/src/test/java/org/olat/test/AllTestsJunit4.java b/src/test/java/org/olat/test/AllTestsJunit4.java
index 151319e3458690840ef94eadeaca78fc462b0ad2..652e4c483faa499a4c815e039c4fb453cbca7521 100644
--- a/src/test/java/org/olat/test/AllTestsJunit4.java
+++ b/src/test/java/org/olat/test/AllTestsJunit4.java
@@ -70,78 +70,80 @@ import org.junit.runners.Suite;
 	org.olat.core.id.IdentityEnvironmentTest.class,
 	org.olat.core.gui.render.VelocityTemplateTest.class,
 	org.olat.core.gui.control.generic.iframe.IFrameDeliveryMapperTest.class,
-	org.olat.note.NoteTest.class,//ok
-	org.olat.user.UserPropertiesPerformanceTest.class,//ok
+	org.olat.note.NoteTest.class,
+	org.olat.user.UserPropertiesPerformanceTest.class,
 	org.olat.user.EmailCheckPerformanceTest.class,//fail
-	org.olat.user.UserTest.class,//ok
-	org.olat.user.UserPropertiesTest.class,//ok
-	org.olat.commons.calendar.ICalFileCalendarManagerTest.class,//ok
-	org.olat.commons.calendar.CalendarImportTest.class,//ok
-	org.olat.commons.calendar.test.CalendarUtilsTest.class,//ok
+	org.olat.user.UserTest.class,
+	org.olat.user.UserPropertiesTest.class,
+	org.olat.commons.calendar.ICalFileCalendarManagerTest.class,
+	org.olat.commons.calendar.CalendarImportTest.class,
+	org.olat.commons.calendar.test.CalendarUtilsTest.class,
 	org.olat.commons.lifecycle.LifeCycleManagerTest.class,//fail christian fragen...
-	org.olat.commons.coordinate.cluster.jms.JMSTest.class,//ok
-	org.olat.commons.coordinate.cluster.lock.LockTest.class,//ok
-	org.olat.commons.coordinate.CoordinatorTest.class,//ok
-	org.olat.core.commons.services.help.spi.ConfluenceLinkSPITest.class,//ok
-	org.olat.core.commons.services.webdav.WebDAVCommandsTest.class,//ok
-	org.olat.core.commons.services.webdav.manager.WebDAVManagerTest.class,//ok
-	org.olat.core.commons.services.webdav.servlets.RequestUtilsTest.class,//ok
-	org.olat.core.commons.services.taskexecutor.PersistentTaskDAOTest.class,//ok
-	org.olat.core.commons.services.taskexecutor.TaskExecutorManagerTest.class,//ok
-	org.olat.admin.user.delete.service.UserDeletionManagerTest.class,//ok
-	org.olat.group.BusinessGroupManagedFlagsTest.class,//ok
-	org.olat.group.test.BGRightManagerTest.class,//ok
-	org.olat.group.test.BGAreaManagerTest.class,//ok
-	org.olat.group.test.BusinessGroupServiceTest.class,//ok
-	org.olat.group.test.BusinessGroupDAOTest.class,//ok
-	org.olat.group.test.BusinessGroupRelationDAOTest.class,//ok
-	org.olat.group.test.BusinessGroupConcurrentTest.class,//ok
-	org.olat.group.test.ContactDAOTest.class,//ok
-	org.olat.fileresource.FileResourceTest.class,//ok
-	org.olat.resource.lock.pessimistic.PLockTest.class,//ok
-	org.olat.resource.references.ReferenceManagerTest.class,//ok
-	org.olat.resource.OLATResourceManagerTest.class,//ok
-	org.olat.basesecurity.manager.GroupDAOTest.class,//ok
-	org.olat.basesecurity.SecurityManagerTest.class,//ok
-	org.olat.basesecurity.GetIdentitiesByPowerSearchTest.class,//ok
-	org.olat.basesecurity.BaseSecurityManagerTest.class,//ok
-	org.olat.user.UserManagerTest.class,//ok
-	org.olat.user.UserNameAndPasswordSyntaxCheckerWithRegexpTest.class,//ok
-	org.olat.repository.manager.RepositoryEntryDAOTest.class,//ok
-	org.olat.repository.manager.RepositoryEntryLifecycleDAOTest.class,//ok
-	org.olat.repository.manager.RepositoryEntryRelationDAOTest.class,//ok
-	org.olat.repository.manager.RepositoryServiceImplTest.class,//ok
-	org.olat.repository.manager.RepositoryEntryStatisticsDAOTest.class,//ok
-	org.olat.repository.manager.RepositoryEntryAuthorQueriesTest.class,//ok
-	org.olat.repository.manager.RepositoryEntryMyCourseQueriesTest.class,//ok
-	org.olat.repository.RepositoryManagerTest.class,//ok
-	org.olat.repository.RepositoryManagerQueryTest.class,//ok
-	org.olat.instantMessaging.InstantMessageDAOTest.class,//ok
-	org.olat.instantMessaging.InstantMessagePreferencesDAOTest.class,//ok
-	org.olat.instantMessaging.RosterDAOTest.class,//ok
-	org.olat.instantMessaging.InstantMessageServiceTest.class,//ok
-	org.olat.course.nodes.en.EnrollmentManagerTest.class,//ok
-	org.olat.course.assessment.AssessmentManagerTest.class,//ok
-	org.olat.course.assessment.manager.UserCourseInformationsManagerTest.class,//ok
-	org.olat.course.assessment.manager.AssessmentModeManagerTest.class,//ok
+	org.olat.commons.coordinate.cluster.jms.JMSTest.class,
+	org.olat.commons.coordinate.cluster.lock.LockTest.class,
+	org.olat.commons.coordinate.CoordinatorTest.class,
+	org.olat.core.commons.services.help.spi.ConfluenceLinkSPITest.class,
+	org.olat.core.commons.services.webdav.WebDAVCommandsTest.class,
+	org.olat.core.commons.services.webdav.manager.WebDAVManagerTest.class,
+	org.olat.core.commons.services.webdav.servlets.RequestUtilsTest.class,
+	org.olat.core.commons.services.taskexecutor.PersistentTaskDAOTest.class,
+	org.olat.core.commons.services.taskexecutor.TaskExecutorManagerTest.class,
+	org.olat.admin.user.delete.service.UserDeletionManagerTest.class,
+	org.olat.group.BusinessGroupManagedFlagsTest.class,
+	org.olat.group.test.BGRightManagerTest.class,
+	org.olat.group.test.BGAreaManagerTest.class,
+	org.olat.group.test.BusinessGroupServiceTest.class,
+	org.olat.group.test.BusinessGroupDAOTest.class,
+	org.olat.group.test.BusinessGroupRelationDAOTest.class,
+	org.olat.group.test.BusinessGroupConcurrentTest.class,
+	org.olat.group.test.ContactDAOTest.class,
+	org.olat.group.test.BusinessGroupMembershipProcessorTest.class,
+	org.olat.fileresource.FileResourceTest.class,
+	org.olat.resource.lock.pessimistic.PLockTest.class,
+	org.olat.resource.references.ReferenceManagerTest.class,
+	org.olat.resource.OLATResourceManagerTest.class,
+	org.olat.basesecurity.manager.GroupDAOTest.class,
+	org.olat.basesecurity.SecurityManagerTest.class,
+	org.olat.basesecurity.GetIdentitiesByPowerSearchTest.class,
+	org.olat.basesecurity.BaseSecurityManagerTest.class,
+	org.olat.user.UserManagerTest.class,
+	org.olat.user.UserNameAndPasswordSyntaxCheckerWithRegexpTest.class,
+	org.olat.repository.manager.RepositoryEntryDAOTest.class,
+	org.olat.repository.manager.RepositoryEntryLifecycleDAOTest.class,
+	org.olat.repository.manager.RepositoryEntryRelationDAOTest.class,
+	org.olat.repository.manager.RepositoryServiceImplTest.class,
+	org.olat.repository.manager.RepositoryEntryStatisticsDAOTest.class,
+	org.olat.repository.manager.RepositoryEntryAuthorQueriesTest.class,
+	org.olat.repository.manager.RepositoryEntryMyCourseQueriesTest.class,
+	org.olat.repository.manager.RepositoryEntryMembershipProcessorTest.class,
+	org.olat.repository.RepositoryManagerTest.class,
+	org.olat.repository.RepositoryManagerQueryTest.class,
+	org.olat.instantMessaging.InstantMessageDAOTest.class,
+	org.olat.instantMessaging.InstantMessagePreferencesDAOTest.class,
+	org.olat.instantMessaging.RosterDAOTest.class,
+	org.olat.instantMessaging.InstantMessageServiceTest.class,
+	org.olat.course.nodes.en.EnrollmentManagerTest.class,
+	org.olat.course.assessment.AssessmentManagerTest.class,
+	org.olat.course.assessment.manager.UserCourseInformationsManagerTest.class,
+	org.olat.course.assessment.manager.AssessmentModeManagerTest.class,
 	org.olat.course.certificate.manager.CertificatesManagerTest.class,
-	org.olat.course.config.CourseConfigManagerImplTest.class,//ok
-	org.olat.course.groupsandrights.CourseGroupManagementTest.class,//ok
-	org.olat.course.editor.PublishProcessTest.class,//ok
-	org.olat.course.CourseXStreamAliasesTest.class,//ok
-	org.olat.modules.iq.IQManagerTest.class,//ok
+	org.olat.course.config.CourseConfigManagerImplTest.class,
+	org.olat.course.groupsandrights.CourseGroupManagementTest.class,
+	org.olat.course.editor.PublishProcessTest.class,
+	org.olat.course.CourseXStreamAliasesTest.class,
+	org.olat.modules.iq.IQManagerTest.class,
 	org.olat.modules.fo.ForumManagerTest.class,//fail
-	org.olat.modules.wiki.WikiUnitTest.class,//ok
-	org.olat.modules.wiki.versioning.diff.CookbookDiffTest.class,//ok
+	org.olat.modules.wiki.WikiUnitTest.class,
+	org.olat.modules.wiki.versioning.diff.CookbookDiffTest.class,
 	org.olat.modules.wiki.gui.components.wikiToHtml.FilterUtilTest.class,
-	org.olat.modules.coach.manager.CoachingDAOTest.class,//ok
-	org.olat.modules.coach.CoachingLargeTest.class,//ok
-	org.olat.properties.PropertyTest.class,//ok
+	org.olat.modules.coach.manager.CoachingDAOTest.class,
+	org.olat.modules.coach.CoachingLargeTest.class,
+	org.olat.properties.PropertyTest.class,
 	org.olat.search.service.document.file.FileDocumentFactoryTest.class,
 	org.olat.search.service.document.file.PDFDocumentTest.class,
 	org.olat.search.service.document.file.OfficeDocumentTest.class,
-	org.olat.core.commons.services.notifications.manager.NotificationsManagerTest.class,//fail
-	org.olat.registration.RegistrationManagerTest.class,//ok
+	org.olat.core.commons.services.notifications.manager.NotificationsManagerTest.class,
+	org.olat.registration.RegistrationManagerTest.class,
 	org.olat.course.nodes.projectbroker.ProjectBrokerManagerTest.class,
 	org.olat.core.commons.persistence.DBTest.class,
 	org.olat.modules.ims.cp.CPManagerTest.class,
diff --git a/src/test/java/org/olat/test/OlatTestCase.java b/src/test/java/org/olat/test/OlatTestCase.java
index c22d8388817aac93a9bffd07331d2588afc4919c..20c597d5da58ac2c3db5e1a489872a1e10a6b718 100644
--- a/src/test/java/org/olat/test/OlatTestCase.java
+++ b/src/test/java/org/olat/test/OlatTestCase.java
@@ -29,6 +29,10 @@ package org.olat.test;
 import java.io.IOException;
 import java.util.Enumeration;
 import java.util.Properties;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 import org.junit.After;
 import org.junit.Before;
@@ -137,6 +141,41 @@ public abstract class OlatTestCase extends AbstractJUnit4SpringContextTests {
 		
 	}
 	
+	protected boolean waitForCondition(final Callable<Boolean> condition, final int timeoutInMilliseconds) {
+		final CountDownLatch countDown = new CountDownLatch(1);
+		final AtomicBoolean result = new AtomicBoolean(false);
+		
+		new Thread(){
+			@Override
+			public void run() {
+				
+				try {
+					int numOfTry = (timeoutInMilliseconds / 100) + 2;
+					for(int i=0; i<numOfTry; i++) {
+						Boolean test = condition.call();
+						if(test != null && test.booleanValue()) {
+							result.set(true);
+							break;
+						} else {
+							result.set(false);
+						}
+					}
+				} catch (Exception e) {
+					log.error("", e);
+					result.set(false);
+				}
+				countDown.countDown();
+			}
+		}.start();
+
+		try {
+			countDown.await(timeoutInMilliseconds, TimeUnit.MILLISECONDS);
+		} catch (InterruptedException e) {
+			log.error("", e);
+		}
+		return result.get();
+	}
+	
 	protected void sleep(int milliSeconds) {
 		try {
 			Thread.sleep(milliSeconds);