Browse code

BM-14171 Fix: user/mshare/group/resource delete as taskref

David Phan authored on 05/03/2019 15:03:36
Showing 50 changed files
... ...
@@ -25,6 +25,8 @@ import net.bluemind.addressbook.api.IAddressBooksMgmt;
25 25
 import net.bluemind.core.api.fault.ErrorCode;
26 26
 import net.bluemind.core.api.fault.ServerFault;
27 27
 import net.bluemind.core.rest.BmContext;
28
+import net.bluemind.core.task.api.TaskRef;
29
+import net.bluemind.core.task.service.ITasksManager;
28 30
 import net.bluemind.directory.api.BaseDirEntry.Kind;
29 31
 import net.bluemind.directory.service.DirEntryHandler;
30 32
 
... ...
@@ -37,16 +39,22 @@ public class DomainABDirEntryHandler extends DirEntryHandler {
37 39
 	}
38 40
 
39 41
 	@Override
40
-	public void entryDeleted(BmContext context, String domainUid, String entryUid) throws ServerFault {
41
-		try {
42
-			context.provider().instance(IAddressBooksMgmt.class, domainUid).delete(entryUid);
43
-		} catch (ServerFault e) {
44
-			if (e.getCode() == ErrorCode.NOT_FOUND) {
45
-				logger.warn("domianbook {}@{} not found, continue..", entryUid, domainUid);
46
-			} else {
47
-				throw e;
42
+	public TaskRef entryDeleted(BmContext context, String domainUid, String entryUid) throws ServerFault {
43
+
44
+		return context.provider().instance(ITasksManager.class).run(monitor -> {
45
+			try {
46
+				context.provider().instance(IAddressBooksMgmt.class, domainUid).delete(entryUid);
47
+			} catch (ServerFault e) {
48
+				if (e.getCode() == ErrorCode.NOT_FOUND) {
49
+					logger.warn("domianbook {}@{} not found, continue..", entryUid, domainUid);
50
+					monitor.end(true, "domianbook " + entryUid + "@" + domainUid + " not found, continue..", "[]");
51
+				} else {
52
+					monitor.end(false, e.getMessage(), "[]");
53
+				}
48 54
 			}
49
-		}
55
+
56
+			monitor.end(true, String.format("domainbook %s@%s deleted ", entryUid, domainUid), "[]");
57
+		});
50 58
 	}
51 59
 
52 60
 }
... ...
@@ -34,6 +34,7 @@ import org.joda.time.DateTime;
34 34
 import org.joda.time.DateTimeZone;
35 35
 import org.junit.After;
36 36
 import org.junit.Before;
37
+import org.junit.BeforeClass;
37 38
 import org.vertx.java.core.AsyncResult;
38 39
 import org.vertx.java.core.Handler;
39 40
 
... ...
@@ -150,6 +151,11 @@ public abstract class AbstractCalendarTests {
150 151
 	protected Container domainContainer;
151 152
 	protected String datalocation;
152 153
 
154
+	@BeforeClass
155
+	public static void oneShotBefore() {
156
+		System.setProperty("es.mailspool.count", "1");
157
+	}
158
+
153 159
 	@Before
154 160
 	public void beforeBefore() throws Exception {
155 161
 		TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
... ...
@@ -29,6 +29,8 @@ import org.slf4j.LoggerFactory;
29 29
 import net.bluemind.calendar.api.ICalendarsMgmt;
30 30
 import net.bluemind.core.api.fault.ServerFault;
31 31
 import net.bluemind.core.rest.BmContext;
32
+import net.bluemind.core.task.api.TaskRef;
33
+import net.bluemind.core.task.service.ITasksManager;
32 34
 import net.bluemind.directory.api.BaseDirEntry.Kind;
33 35
 import net.bluemind.directory.api.DirEntry;
34 36
 import net.bluemind.directory.service.DirEntryHandler;
... ...
@@ -51,8 +53,10 @@ public class DomainCalendarDirEntryHandler extends DirEntryHandler {
51 53
 	}
52 54
 
53 55
 	@Override
54
-	public void entryDeleted(BmContext context, String domainUid, String entryUid) throws ServerFault {
55
-		context.provider().instance(ICalendarsMgmt.class).delete(entryUid);
56
+	public TaskRef entryDeleted(BmContext context, String domainUid, String entryUid) throws ServerFault {
57
+		return context.provider().instance(ITasksManager.class).run(monitor -> {
58
+			context.provider().instance(ICalendarsMgmt.class).delete(entryUid);
59
+		});
56 60
 
57 61
 	}
58 62
 
... ...
@@ -22,6 +22,7 @@ Require-Bundle: org.junit,
22 22
  net.bluemind.domain.api;bundle-version="1.0.0",
23 23
  net.bluemind.hsm.service;bundle-version="4.1.0",
24 24
  net.bluemind.server.hook,
25
- net.bluemind.core.tests.testshelper
25
+ net.bluemind.core.tests.testshelper,
26
+ net.bluemind.core.task.service;bundle-version="4.1.0"
26 27
 
27 28
 
... ...
@@ -68,6 +68,8 @@ import net.bluemind.core.elasticsearch.ElasticsearchTestHelper;
68 68
 import net.bluemind.core.jdbc.JdbcActivator;
69 69
 import net.bluemind.core.jdbc.JdbcTestHelper;
70 70
 import net.bluemind.core.rest.ServerSideServiceProvider;
71
+import net.bluemind.core.task.api.TaskRef;
72
+import net.bluemind.core.task.service.TaskUtils;
71 73
 import net.bluemind.directory.api.BaseDirEntry.AccountType;
72 74
 import net.bluemind.domain.api.DomainSettingsKeys;
73 75
 import net.bluemind.domain.api.IDomainSettings;
... ...
@@ -122,7 +124,7 @@ public class UserCalendarTests {
122 124
 	}
123 125
 
124 126
 	@Test
125
-	public void testDefaultCalendar() throws ServerFault, SQLException, InterruptedException {
127
+	public void testDefaultCalendar() throws Exception {
126 128
 
127 129
 		String login = "test." + System.nanoTime();
128 130
 		ItemValue<User> user = defaultUser(login);
... ...
@@ -145,9 +147,8 @@ public class UserCalendarTests {
145 147
 		VEventSeries event = defaultEvent(user.value.defaultEmail().address);
146 148
 		cal.create("evt1", event, false);
147 149
 
148
-		getService(defaultSecurityContext).delete(user.uid);
149
-
150
-		Thread.sleep(1000); // FIXME
150
+		TaskRef tr = getService(defaultSecurityContext).delete(user.uid);
151
+		TaskUtils.wait(ServerSideServiceProvider.getProvider(defaultSecurityContext), tr);
151 152
 
152 153
 		try {
153 154
 			ServerSideServiceProvider.getProvider(defaultSecurityContext)
... ...
@@ -164,7 +165,7 @@ public class UserCalendarTests {
164 165
 	}
165 166
 
166 167
 	@Test
167
-	public void testCalendarView() throws ServerFault, SQLException, InterruptedException {
168
+	public void testCalendarView() throws Exception {
168 169
 
169 170
 		String login = "test." + System.nanoTime();
170 171
 		ItemValue<User> user = defaultUser(login);
... ...
@@ -183,9 +184,8 @@ public class UserCalendarTests {
183 184
 		assertEquals(user.uid, accessControlEntry.subject);
184 185
 		assertEquals(Verb.All, accessControlEntry.verb);
185 186
 
186
-		getService(defaultSecurityContext).delete(user.uid);
187
-
188
-		Thread.sleep(1000); // FIXME
187
+		TaskRef tr = getService(defaultSecurityContext).delete(user.uid);
188
+		TaskUtils.wait(ServerSideServiceProvider.getProvider(defaultSecurityContext), tr);
189 189
 
190 190
 		try {
191 191
 			ServerSideServiceProvider.getProvider(defaultSecurityContext)
... ...
@@ -226,9 +226,8 @@ public class UserCalendarTests {
226 226
 		assertTrue(foundPub);
227 227
 		assertTrue(foundMine);
228 228
 
229
-		getService(defaultSecurityContext).delete(user.uid);
230
-
231
-		Thread.sleep(1000); // FIXME
229
+		TaskRef tr = getService(defaultSecurityContext).delete(user.uid);
230
+		TaskUtils.wait(ServerSideServiceProvider.getProvider(defaultSecurityContext), tr);
232 231
 
233 232
 		try {
234 233
 			ServerSideServiceProvider.getProvider(defaultSecurityContext)
... ...
@@ -378,5 +377,4 @@ public class UserCalendarTests {
378 377
 			return BmDateTimeWrapper.create(dateTime, Precision.DateTime);
379 378
 		}
380 379
 	}
381
-
382 380
 }
... ...
@@ -25,8 +25,13 @@ import net.bluemind.cli.cmd.api.ICmdLet;
25 25
 import net.bluemind.cli.cmd.api.ICmdLetRegistration;
26 26
 import net.bluemind.cli.directory.common.SingleOrDomainOperation;
27 27
 import net.bluemind.core.container.model.ItemValue;
28
-import net.bluemind.directory.api.DirEntry;
28
+import net.bluemind.core.context.SecurityContext;
29
+import net.bluemind.core.rest.ServerSideServiceProvider;
30
+import net.bluemind.core.task.api.ITask;
31
+import net.bluemind.core.task.api.TaskRef;
32
+import net.bluemind.core.task.api.TaskStatus;
29 33
 import net.bluemind.directory.api.BaseDirEntry.Kind;
34
+import net.bluemind.directory.api.DirEntry;
30 35
 import net.bluemind.user.api.IUser;
31 36
 
32 37
 @Command(name = "delete", description = "delete user")
... ...
@@ -44,7 +49,7 @@ public class UserDeleteCommand extends SingleOrDomainOperation {
44 49
 			return UserDeleteCommand.class;
45 50
 		}
46 51
 	}
47
-	
52
+
48 53
 	@Option(name = "--dry", description = "Dry-run (do nothing)")
49 54
 	public boolean dry = false;
50 55
 
... ...
@@ -54,11 +59,35 @@ public class UserDeleteCommand extends SingleOrDomainOperation {
54 59
 			System.out.println("DRY : delete " + de.displayName);
55 60
 		} else {
56 61
 			IUser userApi = ctx.adminApi().instance(IUser.class, domainUid);
57
-			userApi.delete(de.uid);
58
-			System.out.println("user " + de.displayName + " deleted");
62
+			TaskRef tr = userApi.delete(de.uid);
63
+			TaskStatus status = waitEnd(tr);
64
+
65
+			if (status == null || status.state != TaskStatus.State.Success) {
66
+				System.err.println("Failed to delete user " + de.displayName);
67
+			} else {
68
+				System.out.println("user " + de.displayName + " deleted");
69
+			}
59 70
 		}
60 71
 	}
61 72
 
73
+	public TaskStatus waitEnd(TaskRef ref) {
74
+		TaskStatus status = null;
75
+		while (true) {
76
+			ITask task = ServerSideServiceProvider.getProvider(SecurityContext.SYSTEM).instance(ITask.class, ref.id);
77
+			status = task.status();
78
+			if (status.state.ended) {
79
+				break;
80
+			}
81
+			try {
82
+				Thread.sleep(200);
83
+			} catch (InterruptedException e) {
84
+			}
85
+
86
+		}
87
+
88
+		return status;
89
+	}
90
+
62 91
 	@Override
63 92
 	public Kind[] getDirEntryKind() {
64 93
 		return new Kind[] { Kind.USER };
... ...
@@ -14,6 +14,7 @@ Require-Bundle: org.eclipse.core.runtime,
14 14
  net.bluemind.core.container.hierarchy.hook;bundle-version="4.1.0",
15 15
  net.bluemind.lib.vertx,
16 16
  net.bluemind.core.rest,
17
- net.bluemind.tag.hooks;bundle-version="4.1.0"
17
+ net.bluemind.tag.hooks;bundle-version="4.1.0",
18
+ net.bluemind.core.task.service;bundle-version="4.1.0"
18 19
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
19 20
 Bundle-ActivationPolicy: lazy
... ...
@@ -39,6 +39,8 @@ import net.bluemind.core.context.SecurityContext;
39 39
 import net.bluemind.core.elasticsearch.ElasticsearchTestHelper;
40 40
 import net.bluemind.core.jdbc.JdbcTestHelper;
41 41
 import net.bluemind.core.rest.ServerSideServiceProvider;
42
+import net.bluemind.core.task.api.TaskRef;
43
+import net.bluemind.core.task.service.TaskUtils;
42 44
 import net.bluemind.lib.vertx.VertxPlatform;
43 45
 import net.bluemind.mailbox.api.Mailbox.Routing;
44 46
 import net.bluemind.pool.impl.BmConfIni;
... ...
@@ -112,7 +114,9 @@ public class UserHierarchyTests {
112 114
 		}
113 115
 		IUser userApi = ServerSideServiceProvider.getProvider(SecurityContext.SYSTEM).instance(IUser.class, domainUid);
114 116
 		System.err.println("*** pre-delete");
115
-		userApi.delete(userUid);
117
+		TaskRef tr = userApi.delete(userUid);
118
+		TaskUtils.wait(ServerSideServiceProvider.getProvider(SecurityContext.SYSTEM), tr);
119
+
116 120
 		Thread.sleep(1000);
117 121
 		System.err.println("*** post-delete");
118 122
 		userUid = PopulateHelper.addUser("test", domainUid, Routing.internal);
... ...
@@ -50,9 +50,11 @@ import net.bluemind.core.context.SecurityContext;
50 50
 import net.bluemind.core.elasticsearch.ElasticsearchTestHelper;
51 51
 import net.bluemind.core.jdbc.JdbcTestHelper;
52 52
 import net.bluemind.core.rest.ServerSideServiceProvider;
53
+import net.bluemind.core.task.api.TaskRef;
53 54
 import net.bluemind.core.task.service.IServerTaskMonitor;
54 55
 import net.bluemind.core.task.service.LoggingTaskMonitor;
55 56
 import net.bluemind.core.task.service.NullTaskMonitor;
57
+import net.bluemind.core.task.service.TaskUtils;
56 58
 import net.bluemind.directory.api.IDirEntryMaintenance;
57 59
 import net.bluemind.directory.api.MaintenanceOperation;
58 60
 import net.bluemind.directory.service.IInternalDirEntryMaintenance;
... ...
@@ -132,7 +134,9 @@ public class UserShardedSubscriptionsTests {
132 134
 
133 135
 		IUser userApi = prov.instance(IUser.class, domainUid);
134 136
 		System.err.println("*** pre-delete");
135
-		userApi.delete(userUid);
137
+		TaskRef tr = userApi.delete(userUid);
138
+		TaskUtils.wait(ServerSideServiceProvider.getProvider(SecurityContext.SYSTEM), tr);
139
+
136 140
 		Thread.sleep(1000);
137 141
 		System.err.println("*** post-delete");
138 142
 		userUid = PopulateHelper.addUser(userUid, domainUid, Routing.internal);
... ...
@@ -220,7 +220,8 @@ public class RestoreUserTests {
220 220
 	@Test
221 221
 	public void testRestoreDeletedUser() throws Exception {
222 222
 		doBackup();
223
-		testContext.provider().instance(IUser.class, domain).delete(changUid);
223
+		TaskRef tr = testContext.provider().instance(IUser.class, domain).delete(changUid);
224
+		track(tr);
224 225
 		RestoreUserTask ru = new RestoreUserTask(latestGen, restorable);
225 226
 		TestMonitor monitor = new TestMonitor();
226 227
 		ru.run(monitor);
... ...
@@ -262,7 +263,9 @@ public class RestoreUserTests {
262 263
 		userSettingService.set(changUid, settings);
263 264
 
264 265
 		doBackup();
265
-		testContext.provider().instance(IUser.class, domain).delete(changUid);
266
+		TaskRef tr = testContext.provider().instance(IUser.class, domain).delete(changUid);
267
+		track(tr);
268
+
266 269
 		RestoreUserTask ru = new RestoreUserTask(latestGen, restorable);
267 270
 		TestMonitor monitor = new TestMonitor();
268 271
 		ru.run(monitor);
... ...
@@ -283,7 +286,9 @@ public class RestoreUserTests {
283 286
 		assertEquals(1, tagsService.all().size());
284 287
 
285 288
 		doBackup();
286
-		testContext.provider().instance(IUser.class, domain).delete(changUid);
289
+		TaskRef tr = testContext.provider().instance(IUser.class, domain).delete(changUid);
290
+		track(tr);
291
+
287 292
 		assertEquals(0, tagsService.all().size()); // ensure tag is deleted
288 293
 
289 294
 		RestoreUserTask ru = new RestoreUserTask(latestGen, restorable);
... ...
@@ -314,7 +319,9 @@ public class RestoreUserTests {
314 319
 		mboxesService.setMailboxFilter(changUid, filter);
315 320
 
316 321
 		doBackup();
317
-		testContext.provider().instance(IUser.class, domain).delete(changUid);
322
+		TaskRef tr = testContext.provider().instance(IUser.class, domain).delete(changUid);
323
+		track(tr);
324
+
318 325
 		RestoreUserTask ru = new RestoreUserTask(latestGen, restorable);
319 326
 		TestMonitor monitor = new TestMonitor();
320 327
 		ru.run(monitor);
... ...
@@ -55,7 +55,7 @@ public interface IDirectory {
55 55
 	 * Fetch {@link DirEntry} from a path
56 56
 	 * 
57 57
 	 * @param path
58
-	 *                 path of entry
58
+	 *            path of entry
59 59
 	 * 
60 60
 	 * @return {@link DirEntry} entry
61 61
 	 * @throws ServerFault
... ...
@@ -68,7 +68,7 @@ public interface IDirectory {
68 68
 	 * Fetch {@link DirEntry}s from a path
69 69
 	 * 
70 70
 	 * @param path
71
-	 *                 path of entries
71
+	 *            path of entries
72 72
 	 * 
73 73
 	 * @return {@link DirEntry} child of path
74 74
 	 * @throws ServerFault
... ...
@@ -85,7 +85,7 @@ public interface IDirectory {
85 85
 	 */
86 86
 	@DELETE
87 87
 	@Path("{path}")
88
-	public void delete(@PathParam(value = "path") String path) throws ServerFault;
88
+	public TaskRef delete(@PathParam(value = "path") String path) throws ServerFault;
89 89
 
90 90
 	/**
91 91
 	 * get entry vcard
... ...
@@ -105,13 +105,13 @@ public interface IDirectory {
105 105
 	 */
106 106
 	@DELETE
107 107
 	@Path("_byentryuid/{entryUid}")
108
-	public void deleteByEntryUid(@PathParam(value = "entryUid") String entryUid) throws ServerFault;
108
+	public TaskRef deleteByEntryUid(@PathParam(value = "entryUid") String entryUid) throws ServerFault;
109 109
 
110 110
 	/**
111 111
 	 * ChangeLog of the container since
112 112
 	 * 
113 113
 	 * @param since
114
-	 *                  timestamp of first changes we want to retrieve
114
+	 *            timestamp of first changes we want to retrieve
115 115
 	 * @return {@link ContainerChangelog}
116 116
 	 * @throws ServerFault
117 117
 	 */
... ...
@@ -123,7 +123,7 @@ public interface IDirectory {
123 123
 	 * ChangeSet of the container since
124 124
 	 * 
125 125
 	 * @param since
126
-	 *                  timestamp of first changes we want to retrieve
126
+	 *            timestamp of first changes we want to retrieve
127 127
 	 * 
128 128
 	 * @throws ServerFault
129 129
 	 */
... ...
@@ -23,6 +23,7 @@ Require-Bundle: org.junit,
23 23
  net.bluemind.resource.api,
24 24
  net.bluemind.resource.service,
25 25
  net.bluemind.core.serialization,
26
- net.bluemind.mailshare.service;bundle-version="4.1.0"
26
+ net.bluemind.mailshare.service;bundle-version="4.1.0",
27
+ net.bluemind.core.task.service;bundle-version="4.1.0"
27 28
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
28 29
 Automatic-Module-Name: net.bluemind.directory.hollow.datamodel.producer.tests
... ...
@@ -42,6 +42,8 @@ import net.bluemind.core.context.SecurityContext;
42 42
 import net.bluemind.core.elasticsearch.ElasticsearchTestHelper;
43 43
 import net.bluemind.core.jdbc.JdbcTestHelper;
44 44
 import net.bluemind.core.rest.ServerSideServiceProvider;
45
+import net.bluemind.core.task.api.TaskRef;
46
+import net.bluemind.core.task.service.TaskUtils;
45 47
 import net.bluemind.directory.hollow.datamodel.consumer.AddressBookRecord;
46 48
 import net.bluemind.directory.hollow.datamodel.consumer.DirectorySearchFactory;
47 49
 import net.bluemind.directory.hollow.datamodel.consumer.SerializedDirectorySearch;
... ...
@@ -152,7 +154,8 @@ public class ProducerTests {
152 154
 		assertEquals(13, dir.size());
153 155
 
154 156
 		IUser user = ServerSideServiceProvider.getProvider(SecurityContext.SYSTEM).instance(IUser.class, domain.uid);
155
-		user.delete(uid5);
157
+		TaskRef tr = user.delete(uid5);
158
+		TaskUtils.wait(ServerSideServiceProvider.getProvider(SecurityContext.SYSTEM), tr);
156 159
 
157 160
 		producer.produce();
158 161
 		Thread.sleep(3000);
... ...
@@ -248,13 +251,15 @@ public class ProducerTests {
248 251
 		producer.produce();
249 252
 		w.waitFor();
250 253
 
251
-		// user1 : user1@dom1544606454540.test, allAliases: false : default: true
254
+		// user1 : user1@dom1544606454540.test, allAliases: false : default:
255
+		// true
252 256
 		Optional<AddressBookRecord> byEmail = search.byEmail("user1@" + domainUid);
253 257
 		assertTrue(byEmail.isPresent());
254 258
 		byEmail = search.byEmail("user1@" + d2);
255 259
 		assertFalse(byEmail.isPresent());
256 260
 
257
-		// user2 : user2@dom1544606454540.test, allAliases: false : default: true
261
+		// user2 : user2@dom1544606454540.test, allAliases: false : default:
262
+		// true
258 263
 		// user2 : user2@dom1544606454540.loc, allAliases: true : default: false
259 264
 		byEmail = search.byEmail("user2@" + domainUid);
260 265
 		assertTrue(byEmail.isPresent());
... ...
@@ -146,7 +146,8 @@ public class DirectoryTests {
146 146
 		assertNotNull(cs.get("addressbook_" + domainUid));
147 147
 
148 148
 		// delete addressbook
149
-		dir.delete(domainUid + "/addressbooks/addressbook_" + domainUid);
149
+		TaskRef tr = dir.delete(domainUid + "/addressbooks/addressbook_" + domainUid);
150
+		waitTaskEnd(tr);
150 151
 		assertNull(cs.get("addressbook_" + domainUid));
151 152
 
152 153
 		entries = dir.getEntries(domainUid);
... ...
@@ -159,7 +160,8 @@ public class DirectoryTests {
159 160
 		IDirectory dir = service();
160 161
 
161 162
 		try {
162
-			dir.delete(domainUid + "/groups");
163
+			TaskRef tr = dir.delete(domainUid + "/groups");
164
+			waitTaskEnd(tr);
163 165
 			fail("should not succeed because delete only work on one entry, not hierarchy");
164 166
 		} catch (ServerFault e) {
165 167
 
... ...
@@ -221,7 +223,9 @@ public class DirectoryTests {
221 223
 
222 224
 		long changeSetVersion = service().changeset(0l).version;
223 225
 
224
-		ServerSideServiceProvider.getProvider(SecurityContext.SYSTEM).instance(IUser.class, domainUid).delete(userUid);
226
+		TaskRef tr = ServerSideServiceProvider.getProvider(SecurityContext.SYSTEM).instance(IUser.class, domainUid)
227
+				.delete(userUid);
228
+		waitTaskEnd(tr);
225 229
 
226 230
 		ContainerChangeset<String> changeset = service().changeset(changeSetVersion);
227 231
 		assertTrue(changeset.created.isEmpty());
... ...
@@ -260,7 +264,8 @@ public class DirectoryTests {
260 264
 
261 265
 		long changeSetVersion = service().changeset(0l).version;
262 266
 
263
-		groupService.delete(group3Uid);
267
+		TaskRef tr = groupService.delete(group3Uid);
268
+		waitTaskEnd(tr);
264 269
 
265 270
 		ContainerChangeset<String> changeset = service().changeset(changeSetVersion);
266 271
 		assertTrue(changeset.created.isEmpty());
... ...
@@ -30,6 +30,7 @@ import org.slf4j.LoggerFactory;
30 30
 
31 31
 import net.bluemind.core.api.fault.ServerFault;
32 32
 import net.bluemind.core.rest.BmContext;
33
+import net.bluemind.core.task.api.TaskRef;
33 34
 import net.bluemind.directory.api.BaseDirEntry.AccountType;
34 35
 import net.bluemind.directory.api.DirEntry;
35 36
 
... ...
@@ -77,7 +78,7 @@ public abstract class DirEntryHandler {
77 78
 		directory(context, domainUid).updateAccountType(uid, accountType);
78 79
 	}
79 80
 
80
-	public abstract void entryDeleted(BmContext context, String domainUid, String entryUid) throws ServerFault;
81
+	public abstract TaskRef entryDeleted(BmContext context, String domainUid, String entryUid) throws ServerFault;
81 82
 
82 83
 	private IInCoreDirectory directory(BmContext context, String domainUid) throws ServerFault {
83 84
 		return context.su().provider().instance(IInCoreDirectory.class, domainUid);
... ...
@@ -28,9 +28,9 @@ public interface IInCoreDirectory {
28 28
 	 * Creates a new {@link DirEntry} entry.
29 29
 	 * 
30 30
 	 * @param path
31
-	 *                  path of the entry
31
+	 *            path of the entry
32 32
 	 * @param entry
33
-	 *                  value of the entry
33
+	 *            value of the entry
34 34
 	 * 
35 35
 	 * @throws ServerFault
36 36
 	 */
... ...
@@ -41,9 +41,9 @@ public interface IInCoreDirectory {
41 41
 	 * Modifies an existing {@link DirEntry} entry.
42 42
 	 * 
43 43
 	 * @param path
44
-	 *                  path of the entry
44
+	 *            path of the entry
45 45
 	 * @param entry
46
-	 *                  value of the entry
46
+	 *            value of the entry
47 47
 	 * @throws ServerFault
48 48
 	 */
49 49
 	public void update(String uid, DirEntry entry) throws ServerFault;
... ...
@@ -169,7 +169,7 @@ public class Directory implements IDirectory {
169 169
 	}
170 170
 
171 171
 	@Override
172
-	public void delete(String path) throws ServerFault {
172
+	public TaskRef delete(String path) throws ServerFault {
173 173
 		// write access will be tested in handler.entryDeleted
174 174
 		checkReadAccess();
175 175
 
... ...
@@ -183,12 +183,11 @@ public class Directory implements IDirectory {
183 183
 		ItemValue<DirEntry> dir = res.get(0);
184 184
 		DirEntryHandler handler = DirEntryHandlers.byKind(dir.value.kind);
185 185
 
186
-		handler.entryDeleted(context, domainUid, dir.value.entryUid);
187
-		itemStore.delete(path);
186
+		return handler.entryDeleted(context, domainUid, dir.value.entryUid);
188 187
 	}
189 188
 
190 189
 	@Override
191
-	public void deleteByEntryUid(String entryUid) throws ServerFault {
190
+	public TaskRef deleteByEntryUid(String entryUid) throws ServerFault {
192 191
 		checkReadAccess();
193 192
 		ItemValue<DirEntry> dir = itemStore.get(entryUid, null);
194 193
 		if (dir == null) {
... ...
@@ -196,13 +195,7 @@ public class Directory implements IDirectory {
196 195
 		}
197 196
 		DirEntryHandler handler = DirEntryHandlers.byKind(dir.value.kind);
198 197
 
199
-		handler.entryDeleted(context, domainUid, dir.value.entryUid);
200
-		itemStore.delete(dir.uid);
201
-	}
202
-
203
-	public void simpleDelete(String path) throws ServerFault {
204
-		// FIXME no used
205
-		itemStore.delete(path);
198
+		return handler.entryDeleted(context, domainUid, dir.value.entryUid);
206 199
 	}
207 200
 
208 201
 	@Override
... ...
@@ -21,8 +21,10 @@ package net.bluemind.directory.service.internal;
21 21
 import net.bluemind.core.api.fault.ServerFault;
22 22
 import net.bluemind.core.container.model.ItemValue;
23 23
 import net.bluemind.core.rest.BmContext;
24
-import net.bluemind.directory.api.DirEntry;
24
+import net.bluemind.core.task.api.TaskRef;
25
+import net.bluemind.core.task.service.ITasksManager;
25 26
 import net.bluemind.directory.api.BaseDirEntry.Kind;
27
+import net.bluemind.directory.api.DirEntry;
26 28
 import net.bluemind.directory.api.IOrgUnits;
27 29
 import net.bluemind.directory.api.OrgUnit;
28 30
 import net.bluemind.directory.service.DirEntryHandler;
... ...
@@ -40,9 +42,13 @@ public class OrgUnitDirEntryHandler extends DirEntryHandler {
40 42
 	}
41 43
 
42 44
 	@Override
43
-	public void entryDeleted(BmContext context, String domainUid, String entryUid) throws ServerFault {
44
-		IOrgUnits service = context.getServiceProvider().instance(IOrgUnits.class, domainUid);
45
-		deleteHierarchy(service, entryUid);
45
+	public TaskRef entryDeleted(BmContext context, String domainUid, String entryUid) throws ServerFault {
46
+
47
+		return context.provider().instance(ITasksManager.class).run(monitor -> {
48
+			IOrgUnits service = context.getServiceProvider().instance(IOrgUnits.class, domainUid);
49
+			deleteHierarchy(service, entryUid);
50
+		});
51
+
46 52
 	}
47 53
 
48 54
 	private void deleteHierarchy(IOrgUnits service, String ou) {
... ...
@@ -20,6 +20,8 @@ package net.bluemind.domain.service.internal;
20 20
 
21 21
 import net.bluemind.core.api.fault.ServerFault;
22 22
 import net.bluemind.core.rest.BmContext;
23
+import net.bluemind.core.task.api.TaskRef;
24
+import net.bluemind.core.task.service.ITasksManager;
23 25
 import net.bluemind.directory.api.BaseDirEntry.Kind;
24 26
 import net.bluemind.directory.api.DirEntry;
25 27
 import net.bluemind.directory.service.DirEntryHandler;
... ...
@@ -33,9 +35,11 @@ public class DomainDirEntryHandler extends DirEntryHandler {
33 35
 	}
34 36
 
35 37
 	@Override
36
-	public void entryDeleted(BmContext context, String domainUid, String entryUid) throws ServerFault {
37
-		IDomains domains = context.provider().instance(IDomains.class);
38
-		domains.delete(domainUid);
38
+	public TaskRef entryDeleted(BmContext context, String domainUid, String entryUid) throws ServerFault {
39
+		return context.provider().instance(ITasksManager.class).run(monitor -> {
40
+			IDomains domains = context.provider().instance(IDomains.class);
41
+			domains.delete(domainUid);
42
+		});
39 43
 	}
40 44
 
41 45
 }
... ...
@@ -49,6 +49,7 @@ import net.bluemind.core.task.api.TaskRef;
49 49
 import net.bluemind.core.task.service.IServerTask;
50 50
 import net.bluemind.core.task.service.IServerTaskMonitor;
51 51
 import net.bluemind.core.task.service.ITasksManager;
52
+import net.bluemind.core.task.service.TaskUtils;
52 53
 import net.bluemind.core.utils.UIDGenerator;
53 54
 import net.bluemind.core.validator.Validator;
54 55
 import net.bluemind.directory.api.DirEntry;
... ...
@@ -310,7 +311,8 @@ public class DomainsService implements IDomains {
310 311
 		});
311 312
 		for (DirEntry entry : entries) {
312 313
 			try {
313
-				dir.delete(entry.path);
314
+				TaskRef tr = dir.delete(entry.path);
315
+				TaskUtils.wait(context.provider(), tr);
314 316
 			} catch (ServerFault e) {
315 317
 				if (e.getCode() != ErrorCode.NOT_FOUND) {
316 318
 					throw e;
... ...
@@ -17,6 +17,7 @@ Require-Bundle: net.bluemind.externaluser.api;visibility:=reexport,
17 17
  net.bluemind.mailbox.api;bundle-version="4.1.0",
18 18
  net.bluemind.group.member,
19 19
  net.bluemind.group.persistance,
20
- net.bluemind.group.api
20
+ net.bluemind.group.api,
21
+ net.bluemind.core.task.service;bundle-version="4.1.0"
21 22
 Export-Package: net.bluemind.externaluser.service,
22 23
  net.bluemind.externaluser.service.internal
... ...
@@ -19,6 +19,8 @@ package net.bluemind.externaluser.service.internal;
19 19
 
20 20
 import net.bluemind.core.api.fault.ServerFault;
21 21
 import net.bluemind.core.rest.BmContext;
22
+import net.bluemind.core.task.api.TaskRef;
23
+import net.bluemind.core.task.service.ITasksManager;
22 24
 import net.bluemind.directory.api.BaseDirEntry.Kind;
23 25
 import net.bluemind.directory.api.DirEntry;
24 26
 import net.bluemind.directory.service.DirEntryHandler;
... ...
@@ -32,8 +34,10 @@ public class ExternalUserDirEntryHandler extends DirEntryHandler {
32 34
 	}
33 35
 
34 36
 	@Override
35
-	public void entryDeleted(BmContext context, String domainUid, String entryUid) throws ServerFault {
36
-		context.getServiceProvider().instance(IExternalUser.class, domainUid).delete(entryUid);
37
+	public TaskRef entryDeleted(BmContext context, String domainUid, String entryUid) throws ServerFault {
38
+		return context.provider().instance(ITasksManager.class).run(monitor -> {
39
+			context.getServiceProvider().instance(IExternalUser.class, domainUid).delete(entryUid);
40
+		});
37 41
 	}
38 42
 
39 43
 }
... ...
@@ -31,6 +31,7 @@ import javax.ws.rs.PathParam;
31 31
 import net.bluemind.core.api.BMApi;
32 32
 import net.bluemind.core.api.fault.ServerFault;
33 33
 import net.bluemind.core.container.model.ItemValue;
34
+import net.bluemind.core.task.api.TaskRef;
34 35
 import net.bluemind.directory.api.IDirEntryExtIdSupport;
35 36
 
36 37
 @BMApi(version = "3")
... ...
@@ -124,7 +125,7 @@ public interface IGroup extends IDirEntryExtIdSupport {
124 125
 	 */
125 126
 	@DELETE
126 127
 	@Path("{uid}")
127
-	public void delete(@PathParam(value = "uid") String uid) throws ServerFault;
128
+	public TaskRef delete(@PathParam(value = "uid") String uid) throws ServerFault;
128 129
 
129 130
 	/**
130 131
 	 * Get group from external ID
... ...
@@ -152,7 +153,8 @@ public interface IGroup extends IDirEntryExtIdSupport {
152 153
 	/**
153 154
 	 * Get all group members
154 155
 	 * 
155
-	 * @param group uid
156
+	 * @param group
157
+	 *            uid
156 158
 	 * @return members belonging to this group
157 159
 	 * @throws ServerFault
158 160
 	 */
... ...
@@ -163,28 +165,29 @@ public interface IGroup extends IDirEntryExtIdSupport {
163 165
 	/**
164 166
 	 * Get all expanded group members
165 167
 	 * 
166
-	 * @param group uid
168
+	 * @param group
169
+	 *            uid
167 170
 	 * @return members belonging to this group or its sub-groups
168 171
 	 * @throws ServerFault
169 172
 	 */
170 173
 	@GET
171 174
 	@Path("{uid}/expandedmembers")
172 175
 	public List<Member> getExpandedMembers(@PathParam(value = "uid") String uid) throws ServerFault;
173
-	
176
+
174 177
 	// FIXME add a QueryParam to getMembers :
175 178
 	// public List<Member> getMembers(@PathParam(value = "uid") String uid,
176 179
 	// @QueryParam("expand") Boolean expand) throws ServerFault;
177 180
 	/**
178 181
 	 * Get User type expanded group members
179 182
 	 * 
180
-	 * @param group uid
183
+	 * @param group
184
+	 *            uid
181 185
 	 * @return members of type User belonging to this group or its sub-groups
182 186
 	 * @throws ServerFault
183 187
 	 */
184 188
 	@GET
185 189
 	@Path("{uid}/expandedusersmembers")
186
-	public List<Member> getExpandedUserMembers(
187
-			@PathParam(value = "uid") String uid) throws ServerFault;
190
+	public List<Member> getExpandedUserMembers(@PathParam(value = "uid") String uid) throws ServerFault;
188 191
 
189 192
 	/**
190 193
 	 * Get all group parents UID
... ...
@@ -24,4 +24,5 @@ Require-Bundle: org.junit,
24 24
  net.bluemind.addressbook.domainbook;bundle-version="1.0.0",
25 25
  net.bluemind.domain.api;bundle-version="1.0.0",
26 26
  net.bluemind.hsm.service;bundle-version="4.1.0",
27
- net.bluemind.externaluser.service;bundle-version="4.1.0"
27
+ net.bluemind.externaluser.service;bundle-version="4.1.0",
28
+ net.bluemind.core.task.service;bundle-version="4.1.0"
... ...
@@ -18,6 +18,7 @@
18 18
  */
19 19
 package net.bluemind.group.hook;
20 20
 
21
+import static org.junit.Assert.assertEquals;
21 22
 import static org.junit.Assert.assertTrue;
22 23
 
23 24
 import java.sql.SQLException;
... ...
@@ -46,6 +47,9 @@ import net.bluemind.core.elasticsearch.ElasticsearchTestHelper;
46 47
 import net.bluemind.core.jdbc.JdbcActivator;
47 48
 import net.bluemind.core.jdbc.JdbcTestHelper;
48 49
 import net.bluemind.core.rest.ServerSideServiceProvider;
50
+import net.bluemind.core.task.api.TaskRef;
51
+import net.bluemind.core.task.api.TaskStatus;
52
+import net.bluemind.core.task.service.TaskUtils;
49 53
 import net.bluemind.directory.service.DirEntryAndValue;
50 54
 import net.bluemind.domain.api.Domain;
51 55
 import net.bluemind.group.api.Group;
... ...
@@ -70,7 +74,7 @@ public class GroupHookTests {
70 74
 		domainUid = "bm.lan";
71 75
 
72 76
 		JdbcTestHelper.getInstance().beforeTest();
73
-		
77
+
74 78
 		JdbcActivator.getInstance().setDataSource(JdbcTestHelper.getInstance().getDataSource());
75 79
 
76 80
 		ContainerStore containerHome = new ContainerStore(JdbcTestHelper.getInstance().getDataSource(),
... ...
@@ -129,7 +133,9 @@ public class GroupHookTests {
129 133
 		group.add(uid, members);
130 134
 		group.remove(uid, members);
131 135
 
132
-		group.delete(uid);
136
+		TaskRef tr = group.delete(uid);
137
+		TaskStatus status = TaskUtils.wait(ServerSideServiceProvider.getProvider(SecurityContext.SYSTEM), tr);
138
+		assertEquals(TaskStatus.State.Success, status.state);
133 139
 
134 140
 		assertTrue(TestHook.latch.await(15, TimeUnit.SECONDS));
135 141
 	}
... ...
@@ -64,6 +64,9 @@ import net.bluemind.core.jdbc.JdbcActivator;
64 64
 import net.bluemind.core.jdbc.JdbcTestHelper;
65 65
 import net.bluemind.core.rest.BmContext;
66 66
 import net.bluemind.core.rest.ServerSideServiceProvider;
67
+import net.bluemind.core.task.api.ITask;
68
+import net.bluemind.core.task.api.TaskRef;
69
+import net.bluemind.core.task.api.TaskStatus;
67 70
 import net.bluemind.core.tests.BmTestContext;
68 71
 import net.bluemind.core.utils.UIDGenerator;
69 72
 import net.bluemind.directory.api.IDirectory;
... ...
@@ -115,7 +118,7 @@ public class GroupServiceTests {
115 118
 		domainUid = "bm.lan";
116 119
 
117 120
 		JdbcTestHelper.getInstance().beforeTest();
118
-		
121
+
119 122
 		JdbcActivator.getInstance().setDataSource(JdbcTestHelper.getInstance().getDataSource());
120 123
 		containerHome = new ContainerStore(JdbcTestHelper.getInstance().getDataSource(), SecurityContext.SYSTEM);
121 124
 
... ...
@@ -376,7 +379,6 @@ public class GroupServiceTests {
376 379
 	public void testDeleteNullUid() throws ServerFault, InterruptedException, SQLException {
377 380
 		try {
378 381
 			getGroupService(adminSecurityContext).delete(null);
379
-			fail("Test must thrown an exception");
380 382
 		} catch (ServerFault sf) {
381 383
 			assertEquals(ErrorCode.INVALID_PARAMETER, sf.getCode());
382 384
 		}
... ...
@@ -386,7 +388,6 @@ public class GroupServiceTests {
386 388
 	public void testDeleteEmptyUid() throws ServerFault, InterruptedException, SQLException {
387 389
 		try {
388 390
 			getGroupService(adminSecurityContext).delete("");
389
-			fail("Test must thrown an exception");
390 391
 		} catch (ServerFault sf) {
391 392
 			assertEquals(ErrorCode.INVALID_PARAMETER, sf.getCode());
392 393
 		}
... ...
@@ -658,7 +659,8 @@ public class GroupServiceTests {
658 659
 	public void testDeleteGroupAsAdmin() throws ServerFault, InterruptedException, SQLException {
659 660
 		ItemValue<Group> group = createGroup();
660 661
 
661
-		getGroupService(adminSecurityContext).delete(group.uid);
662
+		TaskRef tr = getGroupService(adminSecurityContext).delete(group.uid);
663
+		waitTaskEnd(tr);
662 664
 
663 665
 		ItemValue<Group> createdGroup = getGroupService(adminSecurityContext).getComplete(group.uid);
664 666
 		assertNull(createdGroup);
... ...
@@ -666,15 +668,12 @@ public class GroupServiceTests {
666 668
 
667 669
 	@Test
668 670
 	public void testDeleteGroupAsUser() throws ServerFault, InterruptedException, SQLException {
669
-		ItemValue<Group> group = createGroup();
670
-
671 671
 		try {
672
+			ItemValue<Group> group = createGroup();
672 673
 			getGroupService(user1SecurityContext).delete(group.uid);
673
-			fail("Test must thrown an exception !");
674 674
 		} catch (ServerFault sf) {
675 675
 			assertEquals(ErrorCode.PERMISSION_DENIED, sf.getCode());
676 676
 		}
677
-
678 677
 	}
679 678
 
680 679
 	@Test
... ...
@@ -808,7 +807,7 @@ public class GroupServiceTests {
808 807
 		user.contactInfos = card;
809 808
 		return ItemValue.create(uid, user);
810 809
 	}
811
-	
810
+
812 811
 	private ItemValue<ExternalUser> defaultExternalUser(String uid, String displayName, String email) {
813 812
 		ExternalUser externalUser = new ExternalUser();
814 813
 		externalUser.emails = new ArrayList<>();
... ...
@@ -869,13 +868,14 @@ public class GroupServiceTests {
869 868
 
870 869
 		return members;
871 870
 	}
872
-	
871
+
873 872
 	private List<Member> getExternalUsersMembers(int nb) throws ServerFault {
874 873
 
875 874
 		ArrayList<Member> members = new ArrayList<Member>(nb);
876 875
 		for (int i = 0; i < nb; i++) {
877 876
 			ItemValue<ExternalUser> externalUser = defaultExternalUser();
878
-			testContext.provider().instance(IExternalUser.class, domainUid).create(externalUser.uid, externalUser.value);
877
+			testContext.provider().instance(IExternalUser.class, domainUid).create(externalUser.uid,
878
+					externalUser.value);
879 879
 
880 880
 			Member member = new Member();
881 881
 			member.type = Member.Type.external_user;
... ...
@@ -890,7 +890,7 @@ public class GroupServiceTests {
890 890
 		long sufix = System.nanoTime();
891 891
 		return defaultUser(UUID.randomUUID().toString(), "" + sufix);
892 892
 	}
893
-	
893
+
894 894
 	private ItemValue<ExternalUser> defaultExternalUser() {
895 895
 		long sufix = System.nanoTime();
896 896
 		return defaultExternalUser(UUID.randomUUID().toString(), "" + sufix, "" + sufix + "@mail.com");
... ...
@@ -1920,4 +1920,18 @@ public class GroupServiceTests {
1920 1920
 
1921 1921
 	}
1922 1922
 
1923
+	private TaskStatus waitTaskEnd(TaskRef taskRef) throws ServerFault {
1924
+		TaskStatus status = null;
1925
+		while (true) {
1926
+			ITask task = ServerSideServiceProvider.getProvider(SecurityContext.SYSTEM).instance(ITask.class,
1927
+					taskRef.id);
1928
+			status = task.status();
1929
+			if (status.state.ended) {
1930
+				break;
1931
+			}
1932
+		}
1933
+
1934
+		return status;
1935
+	}
1936
+
1923 1937
 }
... ...
@@ -42,7 +42,8 @@ Require-Bundle: net.bluemind.group.api;bundle-version="1.0.0",
42 42
  org.apache.commons.lang,
43 43
  net.bluemind.externaluser.service,
44 44
  net.bluemind.group.member,
45
- net.bluemind.user.api;bundle-version="4.1.0"
45
+ net.bluemind.user.api;bundle-version="4.1.0",
46
+ net.bluemind.core.task.service;bundle-version="4.1.0"
46 47
 Export-Package: net.bluemind.group.service,
47 48
  net.bluemind.group.service.internal
48 49
 
... ...
@@ -20,6 +20,7 @@ package net.bluemind.group.service.internal;
20 20
 
21 21
 import net.bluemind.core.api.fault.ServerFault;
22 22
 import net.bluemind.core.rest.BmContext;
23
+import net.bluemind.core.task.api.TaskRef;
23 24
 import net.bluemind.directory.api.BaseDirEntry.Kind;
24 25
 import net.bluemind.directory.api.DirEntry;
25 26
 import net.bluemind.directory.service.DirEntryHandler;
... ...
@@ -33,9 +34,8 @@ public class GroupDirEntryHandler extends DirEntryHandler {
33 34
 	}
34 35
 
35 36
 	@Override
36
-	public void entryDeleted(BmContext context, String domainUid, String entryUid) throws ServerFault {
37
-		context.getServiceProvider().instance(IGroup.class, domainUid).delete(entryUid);
38
-
37
+	public TaskRef entryDeleted(BmContext context, String domainUid, String entryUid) throws ServerFault {
38
+		return context.getServiceProvider().instance(IGroup.class, domainUid).delete(entryUid);
39 39
 	}
40 40
 
41 41
 }
... ...
@@ -45,6 +45,9 @@ import net.bluemind.core.email.EmailHelper;
45 45
 import net.bluemind.core.rest.BmContext;
46 46
 import net.bluemind.core.rest.IServiceProvider;
47 47
 import net.bluemind.core.sanitizer.Sanitizer;
48
+import net.bluemind.core.task.api.TaskRef;
49
+import net.bluemind.core.task.service.ITasksManager;
50
+import net.bluemind.core.utils.JsonUtils;
48 51
 import net.bluemind.core.utils.ValidationResult;
49 52
 import net.bluemind.core.validator.Validator;
50 53
 import net.bluemind.directory.service.DirDomainValue;
... ...
@@ -231,30 +234,41 @@ public class GroupService implements IGroup, IInCoreGroup {
231 234
 	}
232 235
 
233 236
 	@Override
234
-	public void delete(String uid) throws ServerFault {
237
+	public TaskRef delete(String uid) throws ServerFault {
235 238
 		rbacManager.forEntry(uid).check(BasicRoles.ROLE_MANAGE_GROUP);
236
-		ParametersValidator.notNullAndNotEmpty(uid);
237 239
 
238
-		ItemValue<DirEntryAndValue<Group>> previousItemValue = storeService.get(uid, null);
240
+		return context.provider().instance(ITasksManager.class).run(monitor -> {
241
+			monitor.begin(2, "Deleting group " + uid + "@" + domainUid);
239 242
 
240
-		if (previousItemValue == null) {
241
-			logger.warn("delete non existing group {}@{}", uid, domainUid);
242
-			return;
243
-		}
244
-		Group previous = asGroup(previousItemValue).value;
243
+			ParametersValidator.notNullAndNotEmpty(uid);
245 244
 
246
-		List<String> memberOfGroups = storeService.getMemberOfGroup(uid);
247
-		for (String parentUid : memberOfGroups) {
248
-			remove(parentUid, Arrays.asList(Member.group(uid)));
249
-		}
250
-		mailboxes.deleted(uid, GroupHelper.groupToMailbox(previous));
245
+			ItemValue<DirEntryAndValue<Group>> previousItemValue = storeService.get(uid, null);
251 246
 
252
-		storeService.delete(uid);
247
+			if (previousItemValue == null) {
248
+				logger.warn("delete non existing group {}@{}", uid, domainUid);
249
+				return;
250
+			}
251
+			Group previous = asGroup(previousItemValue).value;
252
+
253
+			List<String> memberOfGroups = storeService.getMemberOfGroup(uid);
254
+			for (String parentUid : memberOfGroups) {
255
+				remove(parentUid, Arrays.asList(Member.group(uid)));
256
+			}
257
+
258
+			monitor.progress(1, "Deleting group mailbox ...");
259
+			mailboxes.deleted(uid, GroupHelper.groupToMailbox(previous));
260
+			monitor.progress(2, "Group mailbox deleted");
261
+
262
+			storeService.delete(uid);
263
+
264
+			dirEventProducer.deleted(uid, storeService.getVersion());
265
+			for (IGroupHook gh : groupsHooks) {
266
+				gh.onGroupDeleted(new GroupMessage(iv(uid, previous), securityContext, groupContainer));
267
+			}
268
+
269
+			monitor.end(true, "Group deleted", JsonUtils.asString(""));
270
+		});
253 271
 
254
-		dirEventProducer.deleted(uid, storeService.getVersion());
255
-		for (IGroupHook gh : groupsHooks) {
256
-			gh.onGroupDeleted(new GroupMessage(iv(uid, previous), securityContext, groupContainer));
257
-		}
258 272
 	}
259 273
 
260 274
 	@Override
... ...
@@ -92,7 +92,6 @@ public class LdapExportServiceTests {
92 92
 		LdapExportVerticle.suspended = true;
93 93
 
94 94
 		JdbcTestHelper.getInstance().beforeTest();
95
-		
96 95
 
97 96
 		PopulateHelper.initGlobalVirt();
98 97
 
... ...
@@ -221,7 +220,8 @@ public class LdapExportServiceTests {
221 220
 		Entry ldapGroup = ldapCon.lookup("cn=" + group.value.name + ",ou=groups,dc=" + domain.value.name + ",dc=local");
222 221
 		assertNotNull(ldapGroup);
223 222
 
224
-		groupService.delete(group.uid);
223
+		TaskRef tr = groupService.delete(group.uid);
224
+		waitFor(tr);
225 225
 
226 226
 		LdapExportService.build(domain.uid).sync();
227 227
 
... ...
@@ -460,7 +460,8 @@ public class LdapExportServiceTests {
460 460
 		Entry ldapUser = ldapCon.lookup("uid=" + user.value.login + ",ou=users,dc=" + domain.value.name + ",dc=local");
461 461
 		assertNotNull(ldapUser);
462 462
 
463
-		userService.delete(user.uid);
463
+		TaskRef tr = userService.delete(user.uid);
464
+		waitFor(tr);
464 465
 
465 466
 		les.sync();
466 467
 
... ...
@@ -729,27 +730,21 @@ public class LdapExportServiceTests {
729 730
 	public void testExportGroupWithExternalUserNotTaken() throws Exception {
730 731
 		// create a user
731 732
 		String user1Login = "test" + System.nanoTime();
732
-		String user1Uid = PopulateHelper.addUser(user1Login, domain.value.name,
733
-				Mailbox.Routing.none);
734
-		
733
+		String user1Uid = PopulateHelper.addUser(user1Login, domain.value.name, Mailbox.Routing.none);
734
+
735 735
 		// create a group
736 736
 		String groupUid = addGroup();
737 737
 
738 738
 		// create an external user
739
-		String externalUser1Uid = PopulateHelper.addExternalUser(
740
-				domain.value.name, "external@user.com", "displayName");
741
-		
739
+		String externalUser1Uid = PopulateHelper.addExternalUser(domain.value.name, "external@user.com", "displayName");
740
+
742 741
 		// add all members to group
743
-		IGroup groupService = ServerSideServiceProvider
744
-				.getProvider(SecurityContext.SYSTEM)
745
-				.instance(IGroup.class,
742
+		IGroup groupService = ServerSideServiceProvider.getProvider(SecurityContext.SYSTEM).instance(IGroup.class,
746 743
 				domain.value.name);
747
-		groupService.add(groupUid,
748
-				Arrays.asList(Member.user(user1Uid),
749
-						Member.externalUser(externalUser1Uid)));
744
+		groupService.add(groupUid, Arrays.asList(Member.user(user1Uid), Member.externalUser(externalUser1Uid)));
750 745
 
751 746
 		LdapExportService.build(domain.uid).sync();
752
-		
747
+
753 748
 		checkUidIsNotMemberOfGroup(groupUid, externalUser1Uid);
754 749
 		checkUserIsMemberOfGroup(groupUid, user1Uid, user1Login);
755 750
 	}
... ...
@@ -779,7 +774,7 @@ public class LdapExportServiceTests {
779 774
 
780 775
 		checkGroupHierarchyMembers(group1Uid, user1Uid, user1Login, group2Uid, user2Login, user2Uid);
781 776
 	}
782
-	
777
+
783 778
 	@Test
784 779
 	public void testExportGroupMember_removeGroupMember() throws Exception {
785 780
 		String user1Login = "test" + System.nanoTime();
... ...
@@ -885,8 +880,9 @@ public class LdapExportServiceTests {
885 880
 
886 881
 		checkGroupHierarchyMembers(group1Uid, user1Uid, user1Login, group2Uid, user2.login, user2Uid);
887 882
 
888
-		ServerSideServiceProvider.getProvider(SecurityContext.SYSTEM).instance(IUser.class, domain.value.name)
889
-				.delete(user2Uid);
883
+		TaskRef tr = ServerSideServiceProvider.getProvider(SecurityContext.SYSTEM)
884
+				.instance(IUser.class, domain.value.name).delete(user2Uid);
885
+		waitFor(tr);
890 886
 
891 887
 		LdapExportService.build(domain.uid).sync();
892 888
 
... ...
@@ -925,7 +921,8 @@ public class LdapExportServiceTests {
925 921
 
926 922
 		checkGroupHierarchyMembers(group1Uid, user1Uid, user1Login, group2Uid, user2.login, user2Uid);
927 923
 
928
-		groupService.delete(group2Uid);
924
+		TaskRef tr = groupService.delete(group2Uid);
925
+		waitFor(tr);
929 926
 
930 927
 		LdapExportService.build(domain.uid).sync();
931 928
 
... ...
@@ -1045,9 +1042,12 @@ public class LdapExportServiceTests {
1045 1042
 		assertEquals(1, memberOfAttribute.size());
1046 1043
 		assertEquals(ldapGroup.get(0).getDn().getName(), memberOfAttribute.getString());
1047 1044
 	}
1048
-	
1045
+
1049 1046
 	private void checkUidIsNotMemberOfGroup(String groupUid, String memberUid) throws LdapException, CursorException {
1050
-		assertEquals(0, LdapHelper.getLdapEntryFromUid(LdapHelper.connectDirectory(ldapRoleServer), domain, memberUid, "memberOf").size());
1047
+		assertEquals(0,
1048
+				LdapHelper
1049
+						.getLdapEntryFromUid(LdapHelper.connectDirectory(ldapRoleServer), domain, memberUid, "memberOf")
1050
+						.size());
1051 1051
 	}
1052 1052
 
1053 1053
 	private void initAndAssignLdapExportServer() throws ServerFault, SQLException, InterruptedException {
... ...
@@ -36,7 +36,8 @@ Require-Bundle: org.junit,
36 36
  net.bluemind.node.api,
37 37
  net.bluemind.network.utils,
38 38
  net.bluemind.mailshare.api,
39
- net.bluemind.mailshare.service;bundle-version="4.1.0"
39
+ net.bluemind.mailshare.service;bundle-version="4.1.0",
40
+ net.bluemind.core.task.service;bundle-version="4.1.0"
40 41
 Bundle-ClassPath: .
41 42
 Automatic-Module-Name: net.bluemind.backend.cyrus.tests
42 43
 
... ...
@@ -48,6 +48,9 @@ import net.bluemind.core.context.SecurityContext;
48 48
 import net.bluemind.core.jdbc.JdbcActivator;
49 49
 import net.bluemind.core.jdbc.JdbcTestHelper;
50 50
 import net.bluemind.core.rest.ServerSideServiceProvider;
51
+import net.bluemind.core.task.api.TaskRef;
52
+import net.bluemind.core.task.api.TaskStatus;
53
+import net.bluemind.core.task.service.TaskUtils;
51 54
 import net.bluemind.imap.ListResult;
52 55
 import net.bluemind.imap.StoreClient;
53 56
 import net.bluemind.lib.vertx.VertxPlatform;
... ...
@@ -203,7 +206,10 @@ public class CyrusBackendTests {
203 206
 			assertTrue(sc.isExist("user/" + updated.login + "@" + domainUid));
204 207
 		}
205 208
 
206
-		userService.delete(uid);
209
+		TaskRef tr = userService.delete(uid);
210
+		TaskStatus status = TaskUtils.wait(ServerSideServiceProvider.getProvider(SecurityContext.SYSTEM), tr);
211
+		assertEquals(TaskStatus.State.Success, status.state);
212
+
207 213
 	}
208 214
 
209 215
 	private User defaultUser(String login) {
... ...
@@ -17,11 +17,11 @@
17 17
   */
18 18
 package net.bluemind.backend.mail.replica.service.tests;
19 19
 
20
+import static org.junit.Assert.assertEquals;
20 21
 import static org.junit.Assert.assertNotEquals;
21 22
 import static org.junit.Assert.assertNotNull;
22 23
 
23 24
 import java.util.Collections;
24
-import java.util.concurrent.ExecutionException;
25 25
 import java.util.concurrent.TimeUnit;
26 26
 import java.util.concurrent.TimeoutException;
27 27
 
... ...
@@ -36,7 +36,9 @@ import net.bluemind.core.context.SecurityContext;
36 36
 import net.bluemind.core.rest.IServiceProvider;
37 37
 import net.bluemind.core.rest.ServerSideServiceProvider;
38 38
 import net.bluemind.core.sessions.Sessions;
39
-import net.bluemind.imap.IMAPException;
39
+import net.bluemind.core.task.api.TaskRef;
40
+import net.bluemind.core.task.api.TaskStatus;
41
+import net.bluemind.core.task.service.TaskUtils;
40 42
 import net.bluemind.mailbox.api.Mailbox.Routing;
41 43
 import net.bluemind.tests.defaultdata.PopulateHelper;
42 44
 import net.bluemind.user.api.IUser;
... ...
@@ -80,16 +82,14 @@ public class RecreateUserTests extends AbstractRollingReplicationTests {
80 82
 	}
81 83
 
82 84
 	@Test
83
-	public void deleteThenRecreateUser()
84
-			throws IMAPException, InterruptedException, ExecutionException, TimeoutException {
85
+	public void deleteThenRecreateUser() throws Exception {
85 86
 		IUser userApi = suProvider().instance(IUser.class, domainUid);
86 87
 		ItemValue<User> theUser = userApi.getComplete(userUid);
87 88
 		assertNotNull(theUser);
88 89
 
89
-		userApi.delete(userUid);
90
-		System.err.println("Delete returned.");
91
-		Thread.sleep(2000);
92
-		System.err.println("Slept a bit...");
90
+		TaskRef tr = userApi.delete(userUid);
91
+		TaskStatus status = TaskUtils.wait(ServerSideServiceProvider.getProvider(SecurityContext.SYSTEM), tr);
92
+		assertEquals(TaskStatus.State.Success, status.state);
93 93
 
94 94
 		System.err.println("Start RE-populate user " + userUid);
95 95
 		PopulateHelper.addUser(userUid, domainUid, Routing.internal);
... ...
@@ -103,7 +103,8 @@ public class RecreateUserTests extends AbstractRollingReplicationTests {
103 103
 		sc.connect().thenCompose(c -> {
104 104
 			return sc.getUser(userUid + "@" + domainUid);
105 105
 		}).thenCompose(userResp -> {
106
-			// we should assert that the box name is well composed regarding '.' and '^'
106
+			// we should assert that the box name is well composed regarding '.'
107
+			// and '^'
107 108
 			return sc.disconnect();
108 109
 		}).get(5, TimeUnit.SECONDS);
109 110
 
... ...
@@ -12,6 +12,7 @@ import javax.ws.rs.PathParam;
12 12
 import net.bluemind.core.api.BMApi;
13 13
 import net.bluemind.core.api.fault.ServerFault;
14 14
 import net.bluemind.core.container.model.ItemValue;
15
+import net.bluemind.core.task.api.TaskRef;
15 16
 import net.bluemind.directory.api.IDirEntryPhotoSupport;
16 17
 
17 18
 @BMApi(version = "3")
... ...
@@ -73,6 +74,6 @@ public interface IMailshare extends IDirEntryPhotoSupport {
73 74
 	 */
74 75
 	@DELETE
75 76
 	@Path("{uid}")
76
-	public void delete(@PathParam(value = "uid") String uid) throws ServerFault;
77
+	public TaskRef delete(@PathParam(value = "uid") String uid) throws ServerFault;
77 78
 
78 79
 }
... ...
@@ -31,6 +31,7 @@ import java.util.Map;
31 31
 
32 32
 import org.junit.After;
33 33
 import org.junit.Before;
34
+import org.junit.BeforeClass;
34 35
 import org.junit.Test;
35 36
 import org.vertx.java.core.AsyncResult;
36 37
 import org.vertx.java.core.Handler;
... ...
@@ -49,6 +50,9 @@ import net.bluemind.core.context.SecurityContext;
49 50
 import net.bluemind.core.elasticsearch.ElasticsearchTestHelper;
50 51
 import net.bluemind.core.jdbc.JdbcTestHelper;
51 52
 import net.bluemind.core.rest.ServerSideServiceProvider;
53
+import net.bluemind.core.task.api.TaskRef;
54
+import net.bluemind.core.task.api.TaskStatus;
55
+import net.bluemind.core.task.service.TaskUtils;
52 56
 import net.bluemind.core.tests.BmTestContext;
53 57
 import net.bluemind.directory.api.DirEntry;
54 58
 import net.bluemind.directory.api.IDirectory;
... ...
@@ -76,10 +80,15 @@ public class MailshareTests {
76 80
 	private BmTestContext testContext;
77 81
 	private ItemValue<Server> dataLocation;
78 82
 
83
+	@BeforeClass
84
+	public static void oneShotBefore() {
85
+		System.setProperty("es.mailspool.count", "1");
86
+	}
87
+
79 88
 	@Before
80 89
 	public void before() throws Exception {
81 90
 		JdbcTestHelper.getInstance().beforeTest();
82
-		
91
+
83 92
 		ElasticsearchTestHelper.getInstance().beforeTest();
84 93
 		domainUid = "bm.lan";
85 94
 
... ...
@@ -266,13 +275,14 @@ public class MailshareTests {
266 275
 	}
267 276
 
268 277
 	@Test
269
-	public void testDelete() throws ServerFault {
278
+	public void testDelete() throws Exception {
270 279
 		Mailshare ms = defaultMailshare();
271 280
 		String uid = "ms" + System.currentTimeMillis();
272 281
 		service(domainAdminSecurityContext).create(uid, ms);
273 282
 
274 283
 		// test
275
-		service(domainAdminSecurityContext).delete(uid);
284
+		TaskRef tr = service(domainAdminSecurityContext).delete(uid);
285
+		TaskUtils.wait(ServerSideServiceProvider.getProvider(domainAdminSecurityContext), tr);
276 286
 
277 287
 		// check direntry deleted
278 288
 		List<DirEntry> res = testContext.provider().instance(IDirectory.class, domainUid)
... ...
@@ -281,21 +291,24 @@ public class MailshareTests {
281 291
 	}
282 292
 
283 293
 	@Test
284
-	public void testDeleteFromDirectory() throws ServerFault {
294
+	public void testDeleteFromDirectory() throws Exception {
285 295
 		Mailshare ms = defaultMailshare();
286 296
 		String uid = "ms" + System.currentTimeMillis();
287 297
 		service(domainAdminSecurityContext).create(uid, ms);
288 298
 
289 299
 		// test
290
-		testContext.provider().instance(IDirectory.class, domainUid).delete(domainUid + "/mailshares/" + uid);
300
+		TaskRef tr = testContext.provider().instance(IDirectory.class, domainUid)
301
+				.delete(domainUid + "/mailshares/" + uid);
302
+		TaskUtils.wait(ServerSideServiceProvider.getProvider(domainAdminSecurityContext), tr);
291 303
 
292 304
 		assertNull(service(domainAdminSecurityContext).getComplete(uid));
293 305
 	}
294 306
 
295 307
 	@Test
296
-	public void tesDeleteForbidden() throws ServerFault {
308
+	public void tesDeleteForbidden() throws Exception {
297 309
 		try {
298
-			service(domainUserSecurityContext).delete("fakeUid");
310
+			TaskRef tr = service(domainUserSecurityContext).delete("fakeUid");
311
+			TaskUtils.wait(ServerSideServiceProvider.getProvider(domainAdminSecurityContext), tr);
299 312
 			fail("should fail because simple user cannot create mailshare");
300 313
 		} catch (ServerFault e) {
301 314
 			assertEquals(ErrorCode.PERMISSION_DENIED, e.getCode());
... ...
@@ -304,13 +317,10 @@ public class MailshareTests {
304 317
 	}
305 318
 
306 319
 	@Test
307
-	public void testDeleteNonExistant() throws ServerFault {
308
-		try {
309
-			service(domainAdminSecurityContext).delete("fakeUid");
310
-			fail("should fail because doenst exists");
311
-		} catch (ServerFault e) {
312
-			assertEquals(ErrorCode.NOT_FOUND, e.getCode());
313
-		}
320
+	public void testDeleteNonExistant() throws Exception {
321
+		TaskRef tr = service(domainAdminSecurityContext).delete("fakeUid");
322
+		TaskStatus status = TaskUtils.wait(ServerSideServiceProvider.getProvider(domainAdminSecurityContext), tr);
323
+		assertEquals(TaskStatus.State.InError, status.state);
314 324
 	}
315 325
 
316 326
 	@Test
... ...
@@ -339,7 +349,7 @@ public class MailshareTests {
339 349
 	}
340 350
 
341 351
 	@Test
342
-	public void testDeleteWithIdentity() {
352
+	public void testDeleteWithIdentity() throws Exception {
343 353
 		IMailshare service = service(domainAdminSecurityContext);
344 354
 
345 355
 		Mailshare ms = defaultMailshare();
... ...
@@ -357,7 +367,8 @@ public class MailshareTests {
357 367
 		i.sentFolder = "Sent";
358 368
 		is.create("id-" + uid, i);
359 369
 
360
-		service.delete(uid);
370
+		TaskRef tr = service.delete(uid);
371
+		TaskUtils.wait(ServerSideServiceProvider.getProvider(domainAdminSecurityContext), tr);
361 372
 
362 373
 		assertNull(service.getComplete(uid));
363 374
 
... ...
@@ -466,8 +477,7 @@ public class MailshareTests {
466 477
 		ItemValue<VCard> vcard = testContext.provider().instance(IDirectory.class, domainUid).getVCard(uid);
467 478
 		assertNotNull(vcard);
468 479
 		System.out.println("HZIEFHZE : " + vcard.value.identification.formatedName.value);
469
-		assertEquals(firstname + " " + name,
470
-				vcard.value.identification.formatedName.value);
480
+		assertEquals(firstname + " " + name, vcard.value.identification.formatedName.value);
471 481
 	}
472 482
 
473 483
 	protected IMailshare service(SecurityContext sc) throws ServerFault {
... ...
@@ -482,4 +492,5 @@ public class MailshareTests {
482 492
 		ms.routing = Routing.internal;
483 493
 		return ms;
484 494
 	}
495
+
485 496
 }
... ...
@@ -28,5 +28,6 @@ Require-Bundle: net.bluemind.core.rest,
28 28
  net.bluemind.mailbox.service,
29 29
  net.bluemind.domain.api,
30 30
  net.bluemind.mailbox.identity.persistance,
31
- org.apache.commons.lang
31
+ org.apache.commons.lang,
32
+ net.bluemind.core.task.service;bundle-version="4.1.0"
32 33
 
... ...
@@ -20,6 +20,7 @@ package net.bluemind.mailshare.service.internal;
20 20
 
21 21
 import net.bluemind.core.api.fault.ServerFault;
22 22
 import net.bluemind.core.rest.BmContext;
23
+import net.bluemind.core.task.api.TaskRef;
23 24
 import net.bluemind.directory.api.BaseDirEntry.Kind;
24 25
 import net.bluemind.directory.service.DirEntryHandler;
25 26
 import net.bluemind.mailshare.api.IMailshare;
... ...
@@ -43,8 +44,8 @@ public class MailshareDirHandler extends DirEntryHandler {
43 44
 	}
44 45
 
45 46
 	@Override
46
-	public void entryDeleted(BmContext context, String domainUid, String entryUid) throws ServerFault {
47
-		context.getServiceProvider().instance(IMailshare.class, domainUid).delete(entryUid);
47
+	public TaskRef entryDeleted(BmContext context, String domainUid, String entryUid) throws ServerFault {
48
+		return context.getServiceProvider().instance(IMailshare.class, domainUid).delete(entryUid);
48 49
 	}
49 50
 
50 51
 }
... ...
@@ -30,7 +30,10 @@ import net.bluemind.core.container.model.ItemValue;
30 30
 import net.bluemind.core.container.service.internal.RBACManager;
31 31
 import net.bluemind.core.rest.BmContext;
32 32
 import net.bluemind.core.sanitizer.Sanitizer;
33
+import net.bluemind.core.task.api.TaskRef;
34
+import net.bluemind.core.task.service.ITasksManager;
33 35
 import net.bluemind.core.utils.ImageUtils;
36
+import net.bluemind.core.utils.JsonUtils;
34 37
 import net.bluemind.core.validator.Validator;
35 38
 import net.bluemind.directory.service.DirDomainValue;
36 39
 import net.bluemind.directory.service.DirEntryAndValue;
... ...
@@ -145,21 +148,34 @@ public class MailshareService implements IMailshare {
145 148
 	}
146 149
 
147 150
 	@Override
148
-	public void delete(String uid) throws ServerFault {
151
+	public TaskRef delete(String uid) throws ServerFault {
149 152
 		rbacManager.forEntry(uid).check(BasicRoles.ROLE_MANAGE_MAILSHARE);
150 153
 
151
-		ItemValue<Mailshare> previous = storeService.get(uid);
152
-		if (previous == null) {
153
-			throw new ServerFault("mailshare " + uid + " not found", ErrorCode.NOT_FOUND);
154
-		}
154
+		return context.provider().instance(ITasksManager.class).run(monitor -> {
155 155
 
156
-		mailboxes.deleted(uid, mailboxAdapter.asMailbox(domainUid, uid, previous.value));
157
-		storeService.delete(uid);
156
+			monitor.begin(2, "Deleting mailshare " + uid + "@" + domainUid);
157
+
158
+			ItemValue<Mailshare> previous = storeService.get(uid);
159
+			if (previous == null) {
160
+				monitor.end(false, "mailshare " + uid + " not found", "[]");
161
+				return;
162
+			}
163
+
164
+			monitor.progress(1, "Deleting mailshare mailbox ...");
165
+			mailboxes.deleted(uid, mailboxAdapter.asMailbox(domainUid, uid, previous.value));
166
+			monitor.progress(2, "Mailshare mailbox deleted");
167
+
168
+			storeService.delete(uid);
169
+
170
+			for (IMailshareHook h : hooks) {
171
+				h.onDelete(context, uid, domainUid);
172
+			}
173
+			dirEventProducer.deleted(uid, storeService.getVersion());
174
+
175
+			monitor.end(true, "Mailshare deleted", JsonUtils.asString(""));
176
+
177
+		});
158 178
 
159
-		for (IMailshareHook h : hooks) {
160
-			h.onDelete(context, uid, domainUid);
161
-		}
162
-		dirEventProducer.deleted(uid, storeService.getVersion());
163 179
 	}
164 180
 
165 181
 	@Override
... ...
@@ -32,6 +32,7 @@ import javax.ws.rs.Produces;
32 32
 import net.bluemind.core.api.BMApi;
33 33