From b1085d5315e06875b9b7053c1144eae4af9f82f8 Mon Sep 17 00:00:00 2001
From: srosse <none@none>
Date: Thu, 12 Jan 2012 14:30:21 +0100
Subject: [PATCH] OO-55: fix more issues with concurrent edit/view/delete of
 business group, fix broken equals method of BusinessGroup which generate a
 lot of invisible exceptions (which consume CPU)

---
 .../basesecurity/SecurityGroupImpl.hbm.xml    |   4 +-
 .../olat/basesecurity/SecurityGroupImpl.java  |  19 +++-
 .../org/olat/group/BusinessGroupImpl.java     |  19 ++--
 .../org/olat/group/BusinessGroupManager.java  |   2 +-
 .../olat/group/BusinessGroupManagerImpl.java  |  28 +++--
 .../delete/service/GroupDeletionManager.java  |  26 +++--
 .../group/ui/_i18n/LocalStrings_de.properties |   1 +
 .../ui/edit/BusinessGroupEditController.java  | 104 ++++++++++--------
 .../olat/group/ui/edit/_content/deleted.html  |   4 +
 .../ui/edit/_i18n/LocalStrings_de.properties  |   1 +
 .../ui/edit/_i18n/LocalStrings_en.properties  |   1 +
 .../ui/management/BGManagementController.java |  16 +--
 .../run/BusinessGroupMainRunController.java   |  54 ++++-----
 .../olat/group/ui/run/_content/deleted.html   |   4 +
 .../ui/run/_i18n/LocalStrings_de.properties   |   1 +
 .../ui/run/_i18n/LocalStrings_en.properties   |   1 +
 16 files changed, 169 insertions(+), 116 deletions(-)
 create mode 100644 src/main/java/org/olat/group/ui/edit/_content/deleted.html
 create mode 100644 src/main/java/org/olat/group/ui/run/_content/deleted.html

diff --git a/src/main/java/org/olat/basesecurity/SecurityGroupImpl.hbm.xml b/src/main/java/org/olat/basesecurity/SecurityGroupImpl.hbm.xml
index d40cf9ff86b..e4b4e1c5e44 100644
--- a/src/main/java/org/olat/basesecurity/SecurityGroupImpl.hbm.xml
+++ b/src/main/java/org/olat/basesecurity/SecurityGroupImpl.hbm.xml
@@ -2,14 +2,14 @@
 <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN"	"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
 
 <hibernate-mapping default-lazy="false">
- <class proxy = "org.olat.basesecurity.SecurityGroup" name="org.olat.basesecurity.SecurityGroupImpl" table="o_bs_secgroup">
+ <class proxy="org.olat.basesecurity.SecurityGroup" name="org.olat.basesecurity.SecurityGroupImpl" table="o_bs_secgroup">
      <!-- the default columns -->
     <id name="key" column="id" type="long" unsaved-value="null">
       <generator class="hilo"/>
     </id>
     
     <version name="version" access="field" column="version" type="int"/>
-    <property  name="creationDate" column="creationdate" type="timestamp" />   
+    <property  name="creationDate" column="creationdate" type="timestamp" insert="true" update="false" />   
  
 	</class>
 </hibernate-mapping>
diff --git a/src/main/java/org/olat/basesecurity/SecurityGroupImpl.java b/src/main/java/org/olat/basesecurity/SecurityGroupImpl.java
index b24318a64b2..1df72d61aa5 100644
--- a/src/main/java/org/olat/basesecurity/SecurityGroupImpl.java
+++ b/src/main/java/org/olat/basesecurity/SecurityGroupImpl.java
@@ -29,6 +29,7 @@ import org.olat.core.commons.persistence.PersistentObject;
 import org.olat.core.logging.AssertException;
 
 /**
+ * The object is immutable
  * @author Felix Jost
  */
 public class SecurityGroupImpl extends PersistentObject implements SecurityGroup {
@@ -54,5 +55,21 @@ public class SecurityGroupImpl extends PersistentObject implements SecurityGroup
 		if (key == null) throw new AssertException("not persisted yet");
 		return key;
 	}
-
+	
+	@Override
+	public int hashCode() {
+		return getKey() == null ? 29851 : getKey().hashCode();
+	}
+	
+	@Override
+	public boolean equals(Object obj) {
+		if(this == obj) {
+			return true;
+		}
+		if(obj instanceof SecurityGroup) {
+			SecurityGroup sec = (SecurityGroup)obj;
+			return getKey() != null && getKey().equals(sec.getKey());
+		}
+		return false;
+	}
 }
\ No newline at end of file
diff --git a/src/main/java/org/olat/group/BusinessGroupImpl.java b/src/main/java/org/olat/group/BusinessGroupImpl.java
index 3da70416270..4d16bffb5c0 100644
--- a/src/main/java/org/olat/group/BusinessGroupImpl.java
+++ b/src/main/java/org/olat/group/BusinessGroupImpl.java
@@ -285,23 +285,20 @@ public class BusinessGroupImpl extends PersistentObject implements BusinessGroup
 	 * Compares the keys.
 	 * @see java.lang.Object#equals(java.lang.Object)
 	 */
+	@Override
 	public boolean equals(Object obj) {
-		try {
-			BusinessGroupImpl that = (BusinessGroupImpl)obj;
-			if(this.getKey().equals(that.getKey())) {
-				return true;
-			}
-		} catch (Exception ex) {	
-      //nothing to do
+		if(this == obj) {
+			return true;
+		} else if (obj instanceof BusinessGroup) {
+			BusinessGroup bg = (BusinessGroup)obj;
+			return getKey() != null && getKey().equals(bg.getKey());
 		}
 		return false;
 	}
 	
+	@Override
 	public int hashCode() {
-		if(this.getKey()!=null) {
-			return getKey().intValue();
-		}
-		return 0;
+		return getKey() == null ? 2901 : getKey().hashCode();
 	}
 
 	/**
diff --git a/src/main/java/org/olat/group/BusinessGroupManager.java b/src/main/java/org/olat/group/BusinessGroupManager.java
index f2fe0c04335..78fccfea0bf 100644
--- a/src/main/java/org/olat/group/BusinessGroupManager.java
+++ b/src/main/java/org/olat/group/BusinessGroupManager.java
@@ -488,7 +488,7 @@ public interface BusinessGroupManager {
 	 * Set certain business-group as active (set last-usage and delete time stamp for 'SEND_DELETE_EMAIL_ACTION' in LifeCycleManager):
 	 * @param currBusinessGroup
 	 */
-	public void setLastUsageFor(BusinessGroup currBusinessGroup);
+	public BusinessGroup setLastUsageFor(BusinessGroup currBusinessGroup);
 
 	/**
 	 * Creates business-groups with certain name when no group in a given BGContext with this names already exists.
diff --git a/src/main/java/org/olat/group/BusinessGroupManagerImpl.java b/src/main/java/org/olat/group/BusinessGroupManagerImpl.java
index 4edc4a7f2fe..ed1997563e7 100644
--- a/src/main/java/org/olat/group/BusinessGroupManagerImpl.java
+++ b/src/main/java/org/olat/group/BusinessGroupManagerImpl.java
@@ -40,6 +40,7 @@ import java.util.Locale;
 import java.util.Map;
 import java.util.Set;
 
+import org.hibernate.ObjectNotFoundException;
 import org.hibernate.StaleObjectStateException;
 import org.olat.admin.user.delete.service.UserDeletionManager;
 import org.olat.basesecurity.BaseSecurity;
@@ -558,6 +559,10 @@ public class BusinessGroupManagerImpl extends BasicManager implements BusinessGr
 			Tracing.logAudit("Deleted Business Group", businessGroupTodelete.toString(), this.getClass());
 		} catch(DBRuntimeException dbre) {
 			Throwable th = dbre.getCause();
+			if ((th instanceof ObjectNotFoundException) && th.getMessage().contains("org.olat.group.BusinessGroupImpl")) {
+				//group already deleted
+				return;
+			}
 			if ((th instanceof StaleObjectStateException) &&
 					(th.getMessage().startsWith("Row was updated or deleted by another transaction"))) {
 				// known issue OLAT-3654
@@ -1734,18 +1739,27 @@ public class BusinessGroupManagerImpl extends BasicManager implements BusinessGr
 	/**
 	 * @see org.olat.group.BusinessGroupManager#setLastUsageFor(org.olat.group.BusinessGroup)
 	 */
-	public void setLastUsageFor(final BusinessGroup currBusinessGroup) {
+	public BusinessGroup setLastUsageFor(final BusinessGroup currBusinessGroup) {
 		//o_clusterOK by:cg
-		CoordinatorManager.getInstance().getCoordinator().getSyncer().doInSync(currBusinessGroup, new SyncerExecutor(){
-			public void execute() {
+		return CoordinatorManager.getInstance().getCoordinator().getSyncer().doInSync(currBusinessGroup, new SyncerCallback<BusinessGroup>() {
+			public BusinessGroup execute() {
 				// force a reload from db loadObject(..., true) by evicting it from
 				// hibernates session
 				// cache to catch up on a different thread having commited the update of
 				// the launchcounter
-				BusinessGroup reloadedBusinessGroup = BusinessGroupManagerImpl.getInstance().loadBusinessGroup(currBusinessGroup);
-				reloadedBusinessGroup.setLastUsage(new Date());
-				LifeCycleManager.createInstanceFor(reloadedBusinessGroup).deleteTimestampFor(GroupDeletionManager.SEND_DELETE_EMAIL_ACTION);
-				BusinessGroupManagerImpl.getInstance().updateBusinessGroup(reloadedBusinessGroup);
+				try {
+					BusinessGroup reloadedBusinessGroup = loadBusinessGroup(currBusinessGroup);
+					reloadedBusinessGroup.setLastUsage(new Date());
+					LifeCycleManager.createInstanceFor(reloadedBusinessGroup).deleteTimestampFor(GroupDeletionManager.SEND_DELETE_EMAIL_ACTION);
+					updateBusinessGroup(reloadedBusinessGroup);
+					return reloadedBusinessGroup;
+				} catch(DBRuntimeException e) {
+					if(e.getCause() instanceof ObjectNotFoundException) {
+						//group deleted
+						return null;
+					}
+					throw e;
+				}
 			}
 		});
 	}
diff --git a/src/main/java/org/olat/group/delete/service/GroupDeletionManager.java b/src/main/java/org/olat/group/delete/service/GroupDeletionManager.java
index fcda234a8a8..1e211e94eab 100644
--- a/src/main/java/org/olat/group/delete/service/GroupDeletionManager.java
+++ b/src/main/java/org/olat/group/delete/service/GroupDeletionManager.java
@@ -45,6 +45,8 @@ import org.olat.core.id.Identity;
 import org.olat.core.id.UserConstants;
 import org.olat.core.manager.BasicManager;
 import org.olat.core.util.Util;
+import org.olat.core.util.coordinate.CoordinatorManager;
+import org.olat.core.util.coordinate.SyncerCallback;
 import org.olat.core.util.filter.FilterFactory;
 import org.olat.core.util.i18n.I18nManager;
 import org.olat.core.util.mail.MailTemplate;
@@ -336,15 +338,19 @@ public class GroupDeletionManager extends BasicManager {
 	}
 
 
-	public void setLastUsageNowFor(BusinessGroup group) {
-		group = (BusinessGroup) DBFactory.getInstance().loadObject(group, true);
-		group.setLastUsage(new Date());
-		LifeCycleManager lcManager = LifeCycleManager.createInstanceFor(group);
-		if (lcManager.lookupLifeCycleEntry(SEND_DELETE_EMAIL_ACTION) != null) {
-			logAudit("Group-Deletion: Remove from delete-list group=" + group);
-			LifeCycleManager.createInstanceFor(group).deleteTimestampFor(SEND_DELETE_EMAIL_ACTION);
-		}
-		BusinessGroupManagerImpl.getInstance().updateBusinessGroup(group);		
+	public void setLastUsageNowFor(final BusinessGroup group) {
+		CoordinatorManager.getInstance().getCoordinator().getSyncer().doInSync(group, new SyncerCallback<BusinessGroup>() {
+			public BusinessGroup execute() {
+				BusinessGroup bg =  (BusinessGroup) DBFactory.getInstance().loadObject(group, true);
+				bg.setLastUsage(new Date());
+				LifeCycleManager lcManager = LifeCycleManager.createInstanceFor(bg);
+				if (lcManager.lookupLifeCycleEntry(SEND_DELETE_EMAIL_ACTION) != null) {
+					logAudit("Group-Deletion: Remove from delete-list group=" + bg);
+					LifeCycleManager.createInstanceFor(bg).deleteTimestampFor(SEND_DELETE_EMAIL_ACTION);
+				}
+				BusinessGroupManagerImpl.getInstance().updateBusinessGroup(bg);	
+				return bg;
+			}
+		});
 	}
-
 }
\ No newline at end of file
diff --git a/src/main/java/org/olat/group/ui/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/group/ui/_i18n/LocalStrings_de.properties
index 727bb1dc920..8b79302f343 100644
--- a/src/main/java/org/olat/group/ui/_i18n/LocalStrings_de.properties
+++ b/src/main/java/org/olat/group/ui/_i18n/LocalStrings_de.properties
@@ -24,6 +24,7 @@ create.form.businesspath=Link zu dieser Gruppe
 error.group.name.exists=Dieser Gruppenname wird in diesem Kontext bereits verwendet, w\u00E4hlen Sie einen anderen Namen.
 form.error.disableNonEmptyWaitingList=Die Warteliste ist nicht leer. Zum Ausschalten der Warteliste m\u00FCssen alle Teilnehmer ausgetragen werden.  
 group.type=Arbeitsgruppe
+group.deleted=Diese Gruppe wurde gel\u00F6scht.
 groupsPortlet.description=Schnellzugriff zu den wichtigsten Lern-, Rechte- und Arbeitsgruppen
 groupsPortlet.no_member=Sie wurden aus dieser Gruppe ausgetragen oder die Gruppe ist gel\u00F6scht worden.
 groupsPortlet.nogroups=Sie sind in keiner Gruppe
diff --git a/src/main/java/org/olat/group/ui/edit/BusinessGroupEditController.java b/src/main/java/org/olat/group/ui/edit/BusinessGroupEditController.java
index 16f2ecdd870..59da90bcd07 100644
--- a/src/main/java/org/olat/group/ui/edit/BusinessGroupEditController.java
+++ b/src/main/java/org/olat/group/ui/edit/BusinessGroupEditController.java
@@ -67,6 +67,7 @@ import org.olat.core.logging.activity.ThreadLocalUserActivityLogger;
 import org.olat.core.util.Util;
 import org.olat.core.util.coordinate.CoordinatorManager;
 import org.olat.core.util.coordinate.LockResult;
+import org.olat.core.util.coordinate.SyncerCallback;
 import org.olat.core.util.event.GenericEventListener;
 import org.olat.core.util.mail.MailContext;
 import org.olat.core.util.mail.MailContextImpl;
@@ -84,7 +85,6 @@ import org.olat.group.GroupLoggingAction;
 import org.olat.group.area.BGArea;
 import org.olat.group.area.BGAreaManager;
 import org.olat.group.area.BGAreaManagerImpl;
-import org.olat.group.context.BGContext;
 import org.olat.group.delete.service.GroupDeletionManager;
 import org.olat.group.properties.BusinessGroupPropertyManager;
 import org.olat.group.right.BGRightManager;
@@ -126,10 +126,9 @@ public class BusinessGroupEditController extends BasicController implements Cont
 	private BGRightManager rightManager;
 	private RightsToGroupDataModel rightDataModel;
 	private Choice areasChoice, rightsChoice;
-	private List allAreas, selectedAreas;
-	private List selectedRights;
+	private List<BGArea> selectedAreas;
+	private List<String> selectedRights;
 	private BGRights bgRights;
-	private BGContext bgContext;
 	private TabbedPane tabbedPane;
 	private VelocityContainer vc_edit;
 	private VelocityContainer vc_tab_bgDetails;
@@ -157,14 +156,14 @@ public class BusinessGroupEditController extends BasicController implements Cont
 	 *          controller does no type specific stuff implicit just by looking at
 	 *          the group type. Type specifig features must be flagged.
 	 */
-	public BusinessGroupEditController(UserRequest ureq, WindowControl wControl, BusinessGroup currBusinessGroup,
+	public BusinessGroupEditController(UserRequest ureq, WindowControl wControl, BusinessGroup businessGroup,
 			BGConfigFlags configurationFlags) {
 		super(ureq, wControl);
 		
 		// OLAT-4955: setting the stickyActionType here passes it on to any controller defined in the scope of the editor,
 		//            basically forcing any logging action called within the bg editor to be of type 'admin'
 		getUserActivityLogger().setStickyActionType(ActionType.admin);
-		addLoggingResourceable(LoggingResourceable.wrap(currBusinessGroup));
+		addLoggingResourceable(LoggingResourceable.wrap(businessGroup));
 		
 		// Initialize managers
 		this.areaManager = BGAreaManagerImpl.getInstance();
@@ -172,14 +171,12 @@ public class BusinessGroupEditController extends BasicController implements Cont
 		this.bgm = BusinessGroupManagerImpl.getInstance();
 		// Initialize other members
 
-		this.currBusinessGroup = bgm.loadBusinessGroup(currBusinessGroup); // reload
 		// group
 		this.flags = configurationFlags;
-		this.bgContext = currBusinessGroup.getGroupContext();
 		// Initialize translator:
 		// package translator with default group fallback translators and type
 		// translator
-		setTranslator(BGTranslatorFactory.createBGPackageTranslator(PACKAGE, currBusinessGroup.getType(), ureq.getLocale()));
+		setTranslator(BGTranslatorFactory.createBGPackageTranslator(PACKAGE, businessGroup.getType(), ureq.getLocale()));
 		// Initialize available rights
 		if (flags.isEnabled(BGConfigFlags.RIGHTS)) {
 			// for now only course rights are relevant
@@ -189,15 +186,18 @@ public class BusinessGroupEditController extends BasicController implements Cont
 		}
 		// try to acquire edit lock on business group
 		String locksubkey = "groupEdit";
-		lockEntry = CoordinatorManager.getInstance().getCoordinator().getLocker().acquireLock(currBusinessGroup, ureq.getIdentity(), locksubkey);
+		lockEntry = CoordinatorManager.getInstance().getCoordinator().getLocker().acquireLock(businessGroup, ureq.getIdentity(), locksubkey);
 		if (lockEntry.isSuccess()) {
 			// reload group to minimize stale object exception and update last usage
 			// timestamp
-			currBusinessGroup = BusinessGroupManagerImpl.getInstance().loadBusinessGroup(currBusinessGroup);
-			currBusinessGroup.setLastUsage(new Date(System.currentTimeMillis()));
-			LifeCycleManager.createInstanceFor(currBusinessGroup).deleteTimestampFor(GroupDeletionManager.SEND_DELETE_EMAIL_ACTION);
-			BusinessGroupManagerImpl.getInstance().updateBusinessGroup(currBusinessGroup);
-
+			currBusinessGroup = BusinessGroupManagerImpl.getInstance().setLastUsageFor(businessGroup);
+			if(currBusinessGroup == null) {
+				VelocityContainer vc = createVelocityContainer("deleted");
+				vc.contextPut("name", businessGroup.getName());
+				putInitialPanel(vc);
+				return;
+			}
+			
 			// add as listener to BusinessGroup so we are being notified about
 			// changes.
 			CoordinatorManager.getInstance().getCoordinator().getEventBus().registerFor(this, ureq.getIdentity(), currBusinessGroup);
@@ -419,33 +419,39 @@ public class BusinessGroupEditController extends BasicController implements Cont
 	 * persist the updates
 	 */
 	private void updateBusinessGroup() {
-		// refresh group to prevent stale object exception and context proxy issues
-		this.currBusinessGroup = this.bgm.loadBusinessGroup(this.currBusinessGroup);
-		String bgName = this.modifyBusinessGroupController.getGroupName();
-		String bgDesc = this.modifyBusinessGroupController.getGroupDescription();
-		Integer bgMax = this.modifyBusinessGroupController.getGroupMax();
-		Integer bgMin = this.modifyBusinessGroupController.getGroupMin();
-		Boolean waitingListEnabled = this.modifyBusinessGroupController.isWaitingListEnabled();
-		Boolean autoCloseRanksEnabled = this.modifyBusinessGroupController.isAutoCloseRanksEnabled();
-		
-		this.currBusinessGroup.setName(bgName);
-		this.currBusinessGroup.setDescription(bgDesc);
-		this.currBusinessGroup.setMaxParticipants(bgMax);
-		this.currBusinessGroup.setMinParticipants(bgMin);
-		this.currBusinessGroup.setWaitingListEnabled(waitingListEnabled);
-		if (waitingListEnabled.booleanValue() && (this.currBusinessGroup.getWaitingGroup() == null) ) {
-			// Waitinglist is enabled but not created => Create waitingGroup
-			BaseSecurity securityManager = BaseSecurityManager.getInstance();
-			SecurityGroup waitingGroup = securityManager.createAndPersistSecurityGroup();
-			currBusinessGroup.setWaitingGroup(waitingGroup);
-		}
-		currBusinessGroup.setAutoCloseRanksEnabled(autoCloseRanksEnabled);
-		currBusinessGroup.setLastUsage(new Date(System.currentTimeMillis()));
-		LifeCycleManager.createInstanceFor(currBusinessGroup).deleteTimestampFor(GroupDeletionManager.SEND_DELETE_EMAIL_ACTION);
-		// switch on/off waiting-list in member tab
-		vc_tab_grpmanagement.contextPut("hasWaitingGrp", waitingListEnabled);
+		final BusinessGroup  businessGroup = currBusinessGroup;
+		currBusinessGroup = CoordinatorManager.getInstance().getCoordinator().getSyncer().doInSync(businessGroup, new SyncerCallback<BusinessGroup>() {
+			public BusinessGroup execute() {
+				// refresh group to prevent stale object exception and context proxy issues
+				BusinessGroup bg = bgm.loadBusinessGroup(businessGroup);
+				String bgName = modifyBusinessGroupController.getGroupName();
+				String bgDesc = modifyBusinessGroupController.getGroupDescription();
+				Integer bgMax = modifyBusinessGroupController.getGroupMax();
+				Integer bgMin = modifyBusinessGroupController.getGroupMin();
+				Boolean waitingListEnabled = modifyBusinessGroupController.isWaitingListEnabled();
+				Boolean autoCloseRanksEnabled = modifyBusinessGroupController.isAutoCloseRanksEnabled();
 		
-		bgm.updateBusinessGroup(currBusinessGroup);
+				bg.setName(bgName);
+				bg.setDescription(bgDesc);
+				bg.setMaxParticipants(bgMax);
+				bg.setMinParticipants(bgMin);
+				bg.setWaitingListEnabled(waitingListEnabled);
+				if (waitingListEnabled.booleanValue() && (bg.getWaitingGroup() == null) ) {
+					// Waitinglist is enabled but not created => Create waitingGroup
+					BaseSecurity securityManager = BaseSecurityManager.getInstance();
+					SecurityGroup waitingGroup = securityManager.createAndPersistSecurityGroup();
+					bg.setWaitingGroup(waitingGroup);
+				}
+				bg.setAutoCloseRanksEnabled(autoCloseRanksEnabled);
+				bg.setLastUsage(new Date(System.currentTimeMillis()));
+				LifeCycleManager.createInstanceFor(bg).deleteTimestampFor(GroupDeletionManager.SEND_DELETE_EMAIL_ACTION);
+				// switch on/off waiting-list in member tab
+				vc_tab_grpmanagement.contextPut("hasWaitingGrp", waitingListEnabled);
+				
+				bgm.updateBusinessGroup(bg);
+				return bg;
+			}
+		});
 	}
 
 	/**
@@ -527,14 +533,14 @@ public class BusinessGroupEditController extends BasicController implements Cont
 	 */
 	private VelocityContainer createTabAreas() {
 		VelocityContainer tmp = createVelocityContainer("tab_bgAreas");
-		this.allAreas = areaManager.findBGAreasOfBGContext(this.bgContext);
-		this.selectedAreas = areaManager.findBGAreasOfBusinessGroup(this.currBusinessGroup);
-		this.areaDataModel = new AreasToGroupDataModel(this.allAreas, this.selectedAreas);
+		List allAreas = areaManager.findBGAreasOfBGContext(currBusinessGroup.getGroupContext());
+		selectedAreas = areaManager.findBGAreasOfBusinessGroup(currBusinessGroup);
+		areaDataModel = new AreasToGroupDataModel(allAreas, selectedAreas);
 
 		areasChoice = new Choice("areasChoice", getTranslator());
 		areasChoice.setSubmitKey("submit");
 		areasChoice.setCancelKey("cancel");
-		areasChoice.setTableDataModel(this.areaDataModel);
+		areasChoice.setTableDataModel(areaDataModel);
 		areasChoice.addListener(this);
 		tmp.put("areasChoice", areasChoice);
 		tmp.contextPut("noAreasFound", (allAreas.size() > 0 ? Boolean.FALSE : Boolean.TRUE));
@@ -694,9 +700,11 @@ public class BusinessGroupEditController extends BasicController implements Cont
 	 */
 	@Override
 	protected void doDispose() {
-		CoordinatorManager.getInstance().getCoordinator().getEventBus().deregisterFor(this, this.currBusinessGroup);
-		//release lock on dispose
-		releaseBusinessGroupEditLock();
+		if(currBusinessGroup != null) {
+			CoordinatorManager.getInstance().getCoordinator().getEventBus().deregisterFor(this, currBusinessGroup);
+			//release lock on dispose
+			releaseBusinessGroupEditLock();
+		}
 	}
 
 	private void releaseBusinessGroupEditLock() {
diff --git a/src/main/java/org/olat/group/ui/edit/_content/deleted.html b/src/main/java/org/olat/group/ui/edit/_content/deleted.html
new file mode 100644
index 00000000000..0b3ed42a49e
--- /dev/null
+++ b/src/main/java/org/olat/group/ui/edit/_content/deleted.html
@@ -0,0 +1,4 @@
+<h4 class="b_with_small_icon_left b_group_icon">
+	$name
+</h4>
+$r.translate("group.deleted")
\ No newline at end of file
diff --git a/src/main/java/org/olat/group/ui/edit/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/group/ui/edit/_i18n/LocalStrings_de.properties
index 72ec5b41607..e2c67b1fe78 100644
--- a/src/main/java/org/olat/group/ui/edit/_i18n/LocalStrings_de.properties
+++ b/src/main/java/org/olat/group/ui/edit/_i18n/LocalStrings_de.properties
@@ -129,3 +129,4 @@ group.edit.tab.members=Mitglieder
 group.edit.tab.rights=Berechtigungen
 group.edit.tab.accesscontrol=Veröffentlichung und Buchungskonfiguration
 group.edit.title=Gruppe <i>{0}</i> editieren
+group.deleted=$org.olat.group.ui\:group.deleted
diff --git a/src/main/java/org/olat/group/ui/edit/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/group/ui/edit/_i18n/LocalStrings_en.properties
index c5cb3e907a4..03870ef1a15 100644
--- a/src/main/java/org/olat/group/ui/edit/_i18n/LocalStrings_en.properties
+++ b/src/main/java/org/olat/group/ui/edit/_i18n/LocalStrings_en.properties
@@ -119,3 +119,4 @@ group.edit.tab.details=Description
 group.edit.tab.members=Members
 group.edit.tab.rights=Rights
 group.edit.title=Edit group <i>{0}</i>
+group.deleted=$org.olat.group.ui\:group.deleted
diff --git a/src/main/java/org/olat/group/ui/management/BGManagementController.java b/src/main/java/org/olat/group/ui/management/BGManagementController.java
index 83a354bd1fb..2d2254bb74a 100644
--- a/src/main/java/org/olat/group/ui/management/BGManagementController.java
+++ b/src/main/java/org/olat/group/ui/management/BGManagementController.java
@@ -41,6 +41,7 @@ import org.olat.collaboration.CollaborationTools;
 import org.olat.collaboration.CollaborationToolsFactory;
 import org.olat.core.commons.fullWebApp.LayoutMain3ColsController;
 import org.olat.core.dispatcher.jumpin.JumpInManager;
+import org.olat.core.gui.ShortName;
 import org.olat.core.gui.UserRequest;
 import org.olat.core.gui.components.Component;
 import org.olat.core.gui.components.link.Link;
@@ -96,7 +97,6 @@ import org.olat.group.ui.BusinessGroupTableModel;
 import org.olat.group.ui.NewAreaController;
 import org.olat.group.ui.NewBGController;
 import org.olat.group.ui.area.BGAreaEditController;
-import org.olat.group.ui.area.BGAreaFormController;
 import org.olat.group.ui.area.BGAreaTableModel;
 import org.olat.group.ui.context.BGContextEditController;
 import org.olat.group.ui.context.BGContextEvent;
@@ -108,6 +108,7 @@ import org.olat.group.ui.wizard.BGCopyWizardController;
 import org.olat.group.ui.wizard.BGMultipleCopyWizardController;
 import org.olat.group.ui.wizard.MemberListWizardController;
 import org.olat.modules.co.ContactFormController;
+import org.olat.repository.RepositoryEntry;
 import org.olat.repository.RepositoryTableModel;
 import org.olat.user.HomePageConfig;
 import org.olat.user.HomePageConfigManagerImpl;
@@ -157,7 +158,7 @@ public class BGManagementController extends MainLayoutBasicController implements
 	private static final String CMD_LIST_MEMBERS_WITH_GROUPS = "cmd.list.members.with.groups";
 	private static final String CMD_LIST_MEMBERS_WITH_AREAS = "cmd.list.members.with.areas";
 
-	private Translator areaTrans, userTrans;
+	private Translator userTrans;
 	private BGContext bgContext;
 	private String groupType;
 	private BGConfigFlags flags;
@@ -207,7 +208,7 @@ public class BGManagementController extends MainLayoutBasicController implements
 	private BGAreaManager areaManager;
 
 	// Workflow variables
-	private List areaFilters;
+	private List<ShortName> areaFilters;
 	private BGArea currentAreaFilter;
 	private Component backComponent, currentComponent;
 	private BusinessGroup currentGroup;
@@ -245,11 +246,6 @@ public class BGManagementController extends MainLayoutBasicController implements
 		// 1 - package translator with default group fallback translators and type
 		// translator
 		setTranslator(BGTranslatorFactory.createBGPackageTranslator(PACKAGE, this.groupType, ureq.getLocale()));
-		// 2 - area specific translator
-		if (flags.isEnabled(BGConfigFlags.AREAS)) {
-			//areaTrans = new PackageTranslator(Util.getPackageName(BGAreaForm.class), ureq.getLocale(), trans);
-			areaTrans = Util.createPackageTranslator(BGAreaFormController.class, ureq.getLocale(), getTranslator());
-		}
 		// user translator
 		this.userTrans = Util.createPackageTranslator(UserManager.class, ureq.getLocale());
 
@@ -538,7 +534,6 @@ public class BGManagementController extends MainLayoutBasicController implements
 		} else if (source == confirmDeleteGroup) {
 			if (DialogBoxUIFactory.isYesEvent(event)) { // yes case
 				releaseAdminLockAndGroupMUE();
-				String deletedGroupName = this.currentGroup.getName();
 				LoggingResourceable lri = LoggingResourceable.wrap(currentGroup);
 				doGroupDelete();
 				doGroupList(ureq, false);
@@ -550,7 +545,6 @@ public class BGManagementController extends MainLayoutBasicController implements
 			// TODO event: changed area: update models
 		} else if (source == confirmDeleteArea) {
 			if (DialogBoxUIFactory.isYesEvent(event)) { // yes case
-				String deletedAreaName = this.currentArea.getName();
 				LoggingResourceable lri = LoggingResourceable.wrap(currentArea);
 				doAreaDelete();
 				doAreaList(ureq, false);
@@ -886,7 +880,7 @@ public class BGManagementController extends MainLayoutBasicController implements
 	}
 
 	private void doAddOtherResourcesList(UserRequest ureq) {
-		List repoTableModelEntries = contextManager.findRepositoryEntriesForBGContext(this.bgContext);
+		List<RepositoryEntry> repoTableModelEntries = contextManager.findRepositoryEntriesForBGContext(this.bgContext);
 		if (repoTableModelEntries.size() > 1) {
 			Translator resourceTrans = Util.createPackageTranslator(RepositoryTableModel.class, ureq.getLocale(), getTranslator()); 
 			
diff --git a/src/main/java/org/olat/group/ui/run/BusinessGroupMainRunController.java b/src/main/java/org/olat/group/ui/run/BusinessGroupMainRunController.java
index 7c9fe7f3202..44cd753186b 100644
--- a/src/main/java/org/olat/group/ui/run/BusinessGroupMainRunController.java
+++ b/src/main/java/org/olat/group/ui/run/BusinessGroupMainRunController.java
@@ -173,14 +173,12 @@ public class BusinessGroupMainRunController extends MainLayoutBasicController im
 
 	private Panel mainPanel;
 	private VelocityContainer main, vc_sendToChooserForm, resourcesVC;
-	private Identity identity;
 	private PackageTranslator resourceTrans;
 
 	private BusinessGroup businessGroup;
 
 	private MenuTree bgTree;
 	private LayoutMain3ColsController columnLayoutCtr;
-	private Panel all;
 
 	private Controller collabToolCtr;
 	private Controller chatCtr;
@@ -203,7 +201,6 @@ public class BusinessGroupMainRunController extends MainLayoutBasicController im
 	private BusinessGroupPropertyManager bgpm;
 	private UserSession userSession;
 	private String adminNodeId; // reference to admin menu item
-	private String acNodeId;//fxdiff VCRP-1,2: access control of resources
 
 	// not null indicates tool is enabled
 	private GenericTreeNode nodeFolder;
@@ -231,30 +228,44 @@ public class BusinessGroupMainRunController extends MainLayoutBasicController im
 	 * @param flags
 	 * @param initialViewIdentifier supported are null, "toolforum", "toolfolder"
 	 */
-	public BusinessGroupMainRunController(UserRequest ureq, WindowControl control, BusinessGroup currBusinessGroup, BGConfigFlags flags,
+	public BusinessGroupMainRunController(UserRequest ureq, WindowControl control, BusinessGroup bGroup, BGConfigFlags flags,
 			String initialViewIdentifier) {
 		super(ureq, control);
-		addLoggingResourceable(LoggingResourceable.wrap(currBusinessGroup));
-		ThreadLocalUserActivityLogger.log(GroupLoggingAction.GROUP_OPEN, getClass());
-		this.bgpm = new BusinessGroupPropertyManager(currBusinessGroup);
 		this.flags = flags;
-		this.businessGroup = currBusinessGroup;
-		this.identity = ureq.getIdentity();
+		/*
+		 * lastUsage, update lastUsage if group is run if you can acquire the lock
+		 * on the group for a very short time. If this is not possible, then the
+		 * lastUsage is already up to date within one-day-precision.
+		 */
+		businessGroup = BusinessGroupManagerImpl.getInstance().setLastUsageFor(bGroup);
+		if(businessGroup == null) {
+			VelocityContainer vc = createVelocityContainer("deleted");
+			vc.contextPut("name", bGroup.getName());
+			columnLayoutCtr = new LayoutMain3ColsController(ureq, getWindowControl(), null, null, vc, "grouprun");
+			listenTo(columnLayoutCtr); // cleanup on dispose
+			putInitialPanel(columnLayoutCtr.getInitialComponent());
+			return;
+		}
+
+		addLoggingResourceable(LoggingResourceable.wrap(businessGroup));
+		ThreadLocalUserActivityLogger.log(GroupLoggingAction.GROUP_OPEN, getClass());
+		bgpm = new BusinessGroupPropertyManager(businessGroup);
+	
 		this.userSession = ureq.getUserSession();
 		this.assessmentEventOres = OresHelper.createOLATResourceableType(AssessmentEvent.class);
 
-		boolean isOwner = BaseSecurityManager.getInstance().isIdentityPermittedOnResourceable(identity, Constants.PERMISSION_ACCESS, businessGroup);
+		boolean isOwner = BaseSecurityManager.getInstance().isIdentityPermittedOnResourceable(getIdentity(), Constants.PERMISSION_ACCESS, businessGroup);
 		this.isAdmin = isOwner || flags.isEnabled(BGConfigFlags.IS_GM_ADMIN);
 
 		// Initialize translator:
 		// package translator with default group fallback translators and type
 		// translator
-		setTranslator(BGTranslatorFactory.createBGPackageTranslator(PACKAGE, currBusinessGroup.getType(), ureq.getLocale()));
+		setTranslator(BGTranslatorFactory.createBGPackageTranslator(PACKAGE, businessGroup.getType(), ureq.getLocale()));
 		this.resourceTrans = new PackageTranslator(Util.getPackageName(RepositoryTableModel.class), ureq.getLocale(), getTranslator());
 
 		// main component layed out in panel
 		main = createVelocityContainer("bgrun");
-		exposeGroupDetailsToVC(currBusinessGroup);
+		exposeGroupDetailsToVC(businessGroup);
 
 		mainPanel = new Panel("p_buddygroupRun");
 		mainPanel.setContent(main);
@@ -268,16 +279,9 @@ public class BusinessGroupMainRunController extends MainLayoutBasicController im
 		listenTo(columnLayoutCtr); // cleanup on dispose
 		
 		//
-		all = putInitialPanel(columnLayoutCtr.getInitialComponent());
+		putInitialPanel(columnLayoutCtr.getInitialComponent());
 		// register for AssessmentEvents triggered by this user			
 		userSession.getSingleUserEventCenter().registerFor(this, userSession.getIdentity(), assessmentEventOres);
-		/*
-		 * lastUsage, update lastUsage if group is run if you can acquire the lock
-		 * on the group for a very short time. If this is not possible, then the
-		 * lastUsage is already up to date within one-day-precision.
-		 */
-		
-		BusinessGroupManagerImpl.getInstance().setLastUsageFor(currBusinessGroup);
 		
 		//disposed message controller
 		//must be created beforehand
@@ -288,7 +292,7 @@ public class BusinessGroupMainRunController extends MainLayoutBasicController im
 		setDisposedMsgController(disposedController);
 
 		// add as listener to BusinessGroup so we are being notified about changes.
-		CoordinatorManager.getInstance().getCoordinator().getEventBus().registerFor(this, ureq.getIdentity(), currBusinessGroup);
+		CoordinatorManager.getInstance().getCoordinator().getEventBus().registerFor(this, ureq.getIdentity(), businessGroup);
 
 		// show disabled message when collaboration is disabled (e.g. in a test)		
 		if(AssessmentEvent.isAssessmentStarted(ureq.getUserSession())){
@@ -299,10 +303,10 @@ public class BusinessGroupMainRunController extends MainLayoutBasicController im
 		//check managed
 		//fxdiff VCRP-1,2: access control of resources
 		ACFrontendManager acFrontendManager = (ACFrontendManager)CoreSpringFactory.getBean("acFrontendManager");
-		AccessResult acResult = acFrontendManager.isAccessible(currBusinessGroup, getIdentity(), false);
+		AccessResult acResult = acFrontendManager.isAccessible(businessGroup, getIdentity(), false);
 		if(acResult.isAccessible()) {
 			//ok
-		}  else if (currBusinessGroup != null && acResult.getAvailableMethods().size() > 0) {
+		}  else if (businessGroup != null && acResult.getAvailableMethods().size() > 0) {
 			accessController = ACUIFactory.createAccessController(ureq, getWindowControl(), acResult.getAvailableMethods());
 			listenTo(accessController);
 			mainPanel.setContent(accessController.getInitialComponent());
@@ -987,7 +991,7 @@ public class BusinessGroupMainRunController extends MainLayoutBasicController im
 					// Activate edit menu item
 					bgTree.setSelectedNodeId(ACTIVITY_MENUSELECT_ADMINISTRATION);
 				}
-			} else if (bgmfe.wasMyselfRemoved(identity)) {
+			} else if (bgmfe.wasMyselfRemoved(getIdentity())) {
 				//nothing more here!! The message will be created and displayed upon disposing
 				dispose();//disposed message controller will be set
 			}
@@ -1188,7 +1192,7 @@ public class BusinessGroupMainRunController extends MainLayoutBasicController im
 					gtnChild.setAltText(translate("menutree.ac.alt"));
 					gtnChild.setIconCssClass("b_order_icon");
 					root.addChild(gtnChild);
-					acNodeId = gtnChild.getIdent();
+					//acNodeId = gtnChild.getIdent();
 				}
 			}
 		}
diff --git a/src/main/java/org/olat/group/ui/run/_content/deleted.html b/src/main/java/org/olat/group/ui/run/_content/deleted.html
new file mode 100644
index 00000000000..0b3ed42a49e
--- /dev/null
+++ b/src/main/java/org/olat/group/ui/run/_content/deleted.html
@@ -0,0 +1,4 @@
+<h4 class="b_with_small_icon_left b_group_icon">
+	$name
+</h4>
+$r.translate("group.deleted")
\ No newline at end of file
diff --git a/src/main/java/org/olat/group/ui/run/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/group/ui/run/_i18n/LocalStrings_de.properties
index c294eb3cbee..2f5b19c28f3 100644
--- a/src/main/java/org/olat/group/ui/run/_i18n/LocalStrings_de.properties
+++ b/src/main/java/org/olat/group/ui/run/_i18n/LocalStrings_de.properties
@@ -1,6 +1,7 @@
 #Mon Mar 02 09:54:04 CET 2009
 businessgroup.contact.bodytext=\n\n---\nGehe direkt zur Gruppe {0} \: "{1}"
 businessgroup.contact.subject={0} \:
+group.deleted=$org.olat.group.ui\:group.deleted
 grouprun.configurationchanged=Die Konfiguration dieser Gruppe wurde ver\u00E4ndert. Die Gruppe wurde neu gestartet.
 grouprun.details.description=Beschreibung
 grouprun.details.name=Name der Gruppe
diff --git a/src/main/java/org/olat/group/ui/run/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/group/ui/run/_i18n/LocalStrings_en.properties
index 0b3a0125ad1..c908d6c5d55 100644
--- a/src/main/java/org/olat/group/ui/run/_i18n/LocalStrings_en.properties
+++ b/src/main/java/org/olat/group/ui/run/_i18n/LocalStrings_en.properties
@@ -1,6 +1,7 @@
 #Thu May 26 09:55:51 CEST 2011
 businessgroup.contact.bodytext=\n\n---\nGo immediately to group {0} \: "{1}"
 businessgroup.contact.subject={0} \:
+group.deleted=$org.olat.group.ui\:group.deleted
 grouprun.configurationchanged=This group's configuration has been altered. The group has been restarted.
 grouprun.details.description=Description
 grouprun.details.name=Group name
-- 
GitLab