Browse code

FEATBL-800 Fix: dump message history

Thomas Fricker authored on 09/05/2019 13:53:20
Showing 21 changed files
... ...
@@ -43,7 +43,7 @@
43 43
 		<repository>
44 44
 			<id>bluemind-deps</id>
45 45
 			<layout>p2</layout>
46
-			<url>http://forge.blue-mind.net/staging/p2/dependencies/407/</url>
46
+			<url>http://forge.blue-mind.net/staging/p2/dependencies/409/</url>
47 47
 		</repository>
48 48
 		<repository>
49 49
 			<id>tigase</id>
... ...
@@ -31,6 +31,7 @@
31 31
       <plugin id="com.fasterxml.jackson.core.jackson-annotations"/>
32 32
       <plugin id="com.fasterxml.jackson.core.jackson-core"/>
33 33
       <plugin id="com.fasterxml.jackson.core.jackson-databind"/>
34
+      <plugin id="com.github.freva.ascii-table"/>
34 35
       <plugin id="com.google.guava"/>
35 36
       <plugin id="com.hazelcast"/>
36 37
       <plugin id="com.hazelcast.client" fragment="true"/>
... ...
@@ -60,15 +61,16 @@
60 61
       <plugin id="net.bluemind.cli.calendar"/>
61 62
       <plugin id="net.bluemind.cli.cmd.api"/>
62 63
       <plugin id="net.bluemind.cli.contact"/>
63
-      <plugin id="net.bluemind.cli.todolist"/>
64 64
       <plugin id="net.bluemind.cli.directory.common"/>
65 65
       <plugin id="net.bluemind.cli.hollow"/>
66 66
       <plugin id="net.bluemind.cli.hz"/>
67 67
       <plugin id="net.bluemind.cli.index"/>
68 68
       <plugin id="net.bluemind.cli.launcher"/>
69
+      <plugin id="net.bluemind.cli.mail"/>
69 70
       <plugin id="net.bluemind.cli.mapi"/>
70 71
       <plugin id="net.bluemind.cli.metrics"/>
71 72
       <plugin id="net.bluemind.cli.node"/>
73
+      <plugin id="net.bluemind.cli.todolist"/>
72 74
       <plugin id="net.bluemind.cli.user"/>
73 75
       <plugin id="net.bluemind.cli.utils"/>
74 76
       <plugin id="net.bluemind.common.io"/>
75 77
new file mode 100644
... ...
@@ -0,0 +1,7 @@
1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<classpath>
3
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
4
+	<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
5
+	<classpathentry kind="src" path="src"/>
6
+	<classpathentry kind="output" path="bin"/>
7
+</classpath>
0 8
new file mode 100644
... ...
@@ -0,0 +1,28 @@
1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<projectDescription>
3
+	<name>net.bluemind.cli.mail</name>
4
+	<comment></comment>
5
+	<projects>
6
+	</projects>
7
+	<buildSpec>
8
+		<buildCommand>
9
+			<name>org.eclipse.jdt.core.javabuilder</name>
10
+			<arguments>
11
+			</arguments>
12
+		</buildCommand>
13
+		<buildCommand>
14
+			<name>org.eclipse.pde.ManifestBuilder</name>
15
+			<arguments>
16
+			</arguments>
17
+		</buildCommand>
18
+		<buildCommand>
19
+			<name>org.eclipse.pde.SchemaBuilder</name>
20
+			<arguments>
21
+			</arguments>
22
+		</buildCommand>
23
+	</buildSpec>
24
+	<natures>
25
+		<nature>org.eclipse.pde.PluginNature</nature>
26
+		<nature>org.eclipse.jdt.core.javanature</nature>
27
+	</natures>
28
+</projectDescription>
0 29
new file mode 100644
... ...
@@ -0,0 +1,19 @@
1
+Manifest-Version: 1.0
2
+Bundle-ManifestVersion: 2
3
+Bundle-Name: net.bluemind.cli.mail
4
+Bundle-SymbolicName: net.bluemind.cli.mail;singleton:=true
5
+Bundle-Version: 4.1.0.qualifier
6
+Bundle-Activator: net.bluemind.cli.mail.MailCliActivator
7
+Bundle-Vendor: bluemind.net
8
+Require-Bundle: org.eclipse.core.runtime,
9
+ net.bluemind.cli.cmd.api,
10
+ net.bluemind.cli.utils,
11
+ net.bluemind.mailbox.api,
12
+ net.bluemind.authentication.mgmt.api,
13
+ com.google.guava,
14
+ net.bluemind.core.commons,
15
+ net.bluemind.backend.mail.replica.api,
16
+ com.github.freva.ascii-table;bundle-version="1.1.0"
17
+Bundle-RequiredExecutionEnvironment: JavaSE-1.8
18
+Automatic-Module-Name: net.bluemind.cli.mail
19
+Bundle-ActivationPolicy: lazy
0 20
new file mode 100644
... ...
@@ -0,0 +1,5 @@
1
+source.. = src/
2
+output.. = bin/
3
+bin.includes = META-INF/,\
4
+               .,\
5
+               plugin.xml
0 6
new file mode 100644
... ...
@@ -0,0 +1,12 @@
1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<?eclipse version="3.4"?>
3
+<plugin>
4
+   <extension
5
+         point="net.bluemind.cli.cmd.api.cmdlet">
6
+      <registration
7
+            impl="net.bluemind.cli.mail.MailHistoryCommand$Reg"
8
+            priority="60">
9
+      </registration>
10
+   </extension>
11
+
12
+</plugin>
0 13
new file mode 100644
... ...
@@ -0,0 +1,10 @@
1
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
2
+  <modelVersion>4.0.0</modelVersion>
3
+  <parent>
4
+    <groupId>net.bluemind</groupId>
5
+    <version>4.1.0-SNAPSHOT</version>
6
+    <artifactId>net.bluemind.cli.plugins</artifactId>
7
+  </parent>
8
+  <artifactId>net.bluemind.cli.mail</artifactId>
9
+  <packaging>eclipse-plugin</packaging>
10
+</project>
0 11
new file mode 100644
... ...
@@ -0,0 +1,52 @@
1
+/* BEGIN LICENSE
2
+ * Copyright © Blue Mind SAS, 2012-2018
3
+ *
4
+ * This file is part of BlueMind. BlueMind is a messaging and collaborative
5
+ * solution.
6
+ *
7
+ * This program is free software; you can redistribute it and/or modify
8
+ * it under the terms of either the GNU Affero General Public License as
9
+ * published by the Free Software Foundation (version 3 of the License).
10
+ *
11
+ *
12
+ * This program is distributed in the hope that it will be useful,
13
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
15
+ *
16
+ * See LICENSE.txt
17
+ * END LICENSE
18
+ */
19
+package net.bluemind.cli.mail;
20
+
21
+import org.osgi.framework.BundleActivator;
22
+import org.osgi.framework.BundleContext;
23
+
24
+public class MailCliActivator implements BundleActivator {
25
+
26
+	private static BundleContext context;
27
+
28
+	static BundleContext getContext() {
29
+		return context;
30
+	}
31
+
32
+	/*
33
+	 * (non-Javadoc)
34
+	 * 
35
+	 * @see
36
+	 * org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext)
37
+	 */
38
+	public void start(BundleContext bundleContext) throws Exception {
39
+		MailCliActivator.context = bundleContext;
40
+	}
41
+
42
+	/*
43
+	 * (non-Javadoc)
44
+	 * 
45
+	 * @see
46
+	 * org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)
47
+	 */
48
+	public void stop(BundleContext bundleContext) throws Exception {
49
+		MailCliActivator.context = null;
50
+	}
51
+
52
+}
0 53
new file mode 100644
... ...
@@ -0,0 +1,193 @@
1
+/* BEGIN LICENSE
2
+ * Copyright © Blue Mind SAS, 2012-2018
3
+ *
4
+ * This file is part of BlueMind. BlueMind is a messaging and collaborative
5
+ * solution.
6
+ *
7
+ * This program is free software; you can redistribute it and/or modify
8
+ * it under the terms of either the GNU Affero General Public License as
9
+ * published by the Free Software Foundation (version 3 of the License).
10
+ *
11
+ *
12
+ * This program is distributed in the hope that it will be useful,
13
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
15
+ *
16
+ * See LICENSE.txt
17
+ * END LICENSE
18
+ */
19
+package net.bluemind.cli.mail;
20
+
21
+import java.io.File;
22
+import java.io.IOException;
23
+import java.nio.file.Files;
24
+import java.util.Collection;
25
+import java.util.HashSet;
26
+import java.util.List;
27
+import java.util.Map;
28
+import java.util.Map.Entry;
29
+import java.util.Optional;
30
+import java.util.Set;
31
+import java.util.TreeMap;
32
+import java.util.stream.Collectors;
33
+
34
+import com.github.freva.asciitable.AsciiTable;
35
+import com.google.common.base.Strings;
36
+import com.google.common.hash.Hashing;
37
+
38
+import io.airlift.airline.Command;
39
+import io.airlift.airline.Option;
40
+import net.bluemind.backend.mail.replica.api.IDbMailboxRecords;
41
+import net.bluemind.backend.mail.replica.api.IMailReplicaUids;
42
+import net.bluemind.backend.mail.replica.api.IReplicatedMailboxesMgmt;
43
+import net.bluemind.backend.mail.replica.api.MailboxRecordItemUri;
44
+import net.bluemind.cli.cmd.api.CliContext;
45
+import net.bluemind.cli.cmd.api.CliException;
46
+import net.bluemind.cli.cmd.api.ICmdLet;
47
+import net.bluemind.cli.cmd.api.ICmdLetRegistration;
48
+import net.bluemind.core.container.api.IContainers;
49
+import net.bluemind.core.container.model.ContainerDescriptor;
50
+import net.bluemind.core.container.model.ItemChangeLogEntry;
51
+import net.bluemind.core.container.model.ItemChangelog;
52
+import net.bluemind.core.container.model.ItemValue;
53
+import net.bluemind.directory.api.DirEntry;
54
+import net.bluemind.directory.api.DirEntryQuery;
55
+import net.bluemind.directory.api.IDirectory;
56
+
57
+@Command(name = "history", description = "Show mail history")
58
+public class MailHistoryCommand implements ICmdLet, Runnable {
59
+
60
+	private CliContext ctx;
61
+
62
+	@Option(name = "--body-guid", description = "Body-GUID (Email hashed using SHA-1)")
63
+	public String guid;
64
+
65
+	@Option(name = "--eml", description = "Path to an eml file")
66
+	public String eml;
67
+
68
+	private IContainers containerSercvice;
69
+
70
+	@Override
71
+	public void run() {
72
+		this.containerSercvice = ctx.adminApi().instance(IContainers.class);
73
+		if (Strings.isNullOrEmpty(guid)) {
74
+			guid = createGuid();
75
+		}
76
+
77
+		searchMessageHistory();
78
+
79
+	}
80
+
81
+	private void searchMessageHistory() {
82
+
83
+		IReplicatedMailboxesMgmt search = ctx.adminApi().instance(IReplicatedMailboxesMgmt.class);
84
+		List<ItemHistory> items = search.getBodyGuidReferences(guid).stream().map(this::getHistory)
85
+				.collect(Collectors.toList());
86
+		Map<ItemChangeLogEntry, ItemHistory> changelog = new TreeMap<>(this::compareChangeLogEntry);
87
+		items.forEach(item -> item.itemChangelog.entries.forEach(entry -> changelog.put(entry, item)));
88
+
89
+		String[] headers = { "Date", "Type", "Owner", "Folder-UID", "Folder", "Item/IMAP-UID", "Flags" };
90
+		String[][] data = new String[changelog.size()][headers.length];
91
+		int index = 0;
92
+		for (Entry<ItemChangeLogEntry, ItemHistory> entry : changelog.entrySet()) {
93
+			ItemChangeLogEntry clogEntry = entry.getKey();
94
+			ItemHistory item = entry.getValue();
95
+
96
+			data[index][0] = clogEntry.date.toString();
97
+			data[index][1] = clogEntry.type.name();
98
+			data[index][2] = item.user;
99
+			data[index][3] = item.containerDescriptor.uid;
100
+			data[index][4] = item.containerDescriptor.name;
101
+			data[index][5] = item.itemUid;
102
+			data[index][6] = item.flags();
103
+			index++;
104
+		}
105
+
106
+		cleanupFlags(data);
107
+
108
+		ctx.info(AsciiTable.getTable(headers, data));
109
+	}
110
+
111
+	private void cleanupFlags(String[][] data) {
112
+		Set<String> handledItemUids = new HashSet<>();
113
+		for (int i = data.length - 1; i >= 0; i--) {
114
+			String itemUid = data[i][5];
115
+			if (handledItemUids.contains(itemUid)) {
116
+				data[i][6] = "";
117
+			} else {
118
+				handledItemUids.add(itemUid);
119
+			}
120
+		}
121
+	}
122
+
123
+	private ItemHistory getHistory(MailboxRecordItemUri ref) {
124
+		ContainerDescriptor containerDescriptor = containerSercvice.get(ref.containerUid);
125
+		ItemValue<DirEntry> dirEntry = ctx.adminApi().instance(IDirectory.class, containerDescriptor.domainUid)
126
+				.search(DirEntryQuery.filterEntryUid(containerDescriptor.owner)).values.get(0);
127
+		String user = String.format("%s (%s)", dirEntry.displayName, dirEntry.value.kind.name());
128
+		IDbMailboxRecords recordsApi = ctx.adminApi().instance(IDbMailboxRecords.class,
129
+				IMailReplicaUids.uniqueId(ref.containerUid));
130
+		Collection<String> flags = recordsApi.getComplete(ref.itemUid).flags.stream().map(f -> f.name())
131
+				.collect(Collectors.toList());
132
+		ItemChangelog itemChangelog = recordsApi.itemChangelog(ref.itemUid, 0l);
133
+		return new ItemHistory(containerDescriptor, ref.itemUid, ref.bodyGuid, itemChangelog, user, flags);
134
+	}
135
+
136
+	private int compareChangeLogEntry(ItemChangeLogEntry entry1, ItemChangeLogEntry entry2) {
137
+		return Long.compare(entry1.date.getTime(), entry2.date.getTime());
138
+	}
139
+
140
+	@SuppressWarnings("deprecation")
141
+	private String createGuid() {
142
+		try {
143
+			byte[] emlContent = Files.readAllBytes(new File(eml).toPath());
144
+			return Hashing.sha1().hashBytes(emlContent).toString();
145
+		} catch (IOException e) {
146
+			throw new CliException("Cannot hash eml file " + eml, e);
147
+		}
148
+	}
149
+
150
+	public static class ItemHistory {
151
+		public final ContainerDescriptor containerDescriptor;
152
+		public final ItemChangelog itemChangelog;
153
+		public final String user;
154
+		public final String itemUid;
155
+		public final Collection<String> flags;
156
+		public final String bodyGuid;
157
+
158
+		public ItemHistory(ContainerDescriptor containerDescriptor, String itemUid, String bodyGuid,
159
+				ItemChangelog itemChangelog, String user, Collection<String> flags) {
160
+			this.containerDescriptor = containerDescriptor;
161
+			this.itemUid = itemUid;
162
+			this.bodyGuid = bodyGuid;
163
+			this.itemChangelog = itemChangelog;
164
+			this.user = user;
165
+			this.flags = flags;
166
+		}
167
+
168
+		public String flags() {
169
+			return String.join(",", flags);
170
+		}
171
+
172
+	}
173
+
174
+	public static class Reg implements ICmdLetRegistration {
175
+
176
+		@Override
177
+		public Optional<String> group() {
178
+			return Optional.of("mail");
179
+		}
180
+
181
+		@Override
182
+		public Class<? extends ICmdLet> commandClass() {
183
+			return MailHistoryCommand.class;
184
+		}
185
+	}
186
+
187
+	@Override
188
+	public Runnable forContext(CliContext ctx) {
189
+		this.ctx = ctx;
190
+		return this;
191
+	}
192
+
193
+}
... ...
@@ -15,6 +15,7 @@
15 15
 		<module>net.bluemind.cli.launcher</module>
16 16
 		<module>net.bluemind.cli.utils</module>
17 17
 		<module>net.bluemind.cli.metrics</module>
18
+		<module>net.bluemind.cli.mail</module>
18 19
 		<module>net.bluemind.cli.metrics.tests</module>
19 20
 		<module>net.bluemind.cli.directory.common</module>
20 21
                 <module>net.bluemind.cli.adm</module>
... ...
@@ -18,10 +18,13 @@
18 18
  */
19 19
 package net.bluemind.core.container.model;
20 20
 
21
+import net.bluemind.core.api.BMApi;
22
+
21 23
 /**
22 24
  * Util class to build an uri for an item : containerUid/itemUid
23 25
  *
24 26
  */
27
+@BMApi(version = "3")
25 28
 public class ItemUri {
26 29
 
27 30
 	public String containerUid;
... ...
@@ -51,4 +54,36 @@ public class ItemUri {
51 54
 		itemUri.itemUid = itemUid;
52 55
 		return itemUri;
53 56
 	}
57
+
58
+	@Override
59
+	public int hashCode() {
60
+		final int prime = 31;
61
+		int result = 1;
62
+		result = prime * result + ((containerUid == null) ? 0 : containerUid.hashCode());
63
+		result = prime * result + ((itemUid == null) ? 0 : itemUid.hashCode());
64
+		return result;
65
+	}
66
+
67
+	@Override
68
+	public boolean equals(Object obj) {
69
+		if (this == obj)
70
+			return true;
71
+		if (obj == null)
72
+			return false;
73
+		if (getClass() != obj.getClass())
74
+			return false;
75
+		ItemUri other = (ItemUri) obj;
76
+		if (containerUid == null) {
77
+			if (other.containerUid != null)
78
+				return false;
79
+		} else if (!containerUid.equals(other.containerUid))
80
+			return false;
81
+		if (itemUid == null) {
82
+			if (other.itemUid != null)
83
+				return false;
84
+		} else if (!itemUid.equals(other.itemUid))
85
+			return false;
86
+		return true;
87
+	}
88
+
54 89
 }
55 90
new file mode 100644
... ...
@@ -0,0 +1,35 @@
1
+/* BEGIN LICENSE
2
+  * Copyright © Blue Mind SAS, 2012-2017
3
+  *
4
+  * This file is part of BlueMind. BlueMind is a messaging and collaborative
5
+  * solution.
6
+  *
7
+  * This program is free software; you can redistribute it and/or modify
8
+  * it under the terms of either the GNU Affero General Public License as
9
+  * published by the Free Software Foundation (version 3 of the License).
10
+  *
11
+  * This program is distributed in the hope that it will be useful,
12
+  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14
+  *
15
+  * See LICENSE.txt
16
+  * END LICENSE
17
+  */
18
+package net.bluemind.backend.mail.replica.api;
19
+
20
+import java.util.Set;
21
+
22
+import javax.ws.rs.GET;
23
+import javax.ws.rs.Path;
24
+import javax.ws.rs.PathParam;
25
+
26
+import net.bluemind.core.api.BMApi;
27
+
28
+@BMApi(version = "3", internal = true)
29
+@Path("/replicated_mailboxes")
30
+public interface IReplicatedMailboxesMgmt {
31
+
32
+	@GET
33
+	@Path("{guid}/references")
34
+	Set<MailboxRecordItemUri> getBodyGuidReferences(@PathParam("guid") String guid);
35
+}
0 36
new file mode 100644
... ...
@@ -0,0 +1,59 @@
1
+/* BEGIN LICENSE
2
+ * Copyright © Blue Mind SAS, 2012-2018
3
+ *
4
+ * This file is part of BlueMind. BlueMind is a messaging and collaborative
5
+ * solution.
6
+ *
7
+ * This program is free software; you can redistribute it and/or modify
8
+ * it under the terms of either the GNU Affero General Public License as
9
+ * published by the Free Software Foundation (version 3 of the License).
10
+ *
11
+ *
12
+ * This program is distributed in the hope that it will be useful,
13
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
15
+ *
16
+ * See LICENSE.txt
17
+ * END LICENSE
18
+ */
19
+package net.bluemind.backend.mail.replica.api;
20
+
21
+import net.bluemind.core.api.BMApi;
22
+import net.bluemind.core.container.model.ItemUri;
23
+
24
+@BMApi(version = "3")
25
+public class MailboxRecordItemUri extends ItemUri {
26
+
27
+	public String bodyGuid;
28
+	public long imapUid;
29
+	public String owner;
30
+
31
+	@Override
32
+	public int hashCode() {
33
+		final int prime = 31;
34
+		int result = super.hashCode();
35
+		result = prime * result + ((bodyGuid == null) ? 0 : bodyGuid.hashCode());
36
+		result = prime * result + (int) (imapUid ^ (imapUid >>> 32));
37
+		return result;
38
+	}
39
+
40
+	@Override
41
+	public boolean equals(Object obj) {
42
+		if (this == obj)
43
+			return true;
44
+		if (!super.equals(obj))
45
+			return false;
46
+		if (getClass() != obj.getClass())
47
+			return false;
48
+		MailboxRecordItemUri other = (MailboxRecordItemUri) obj;
49
+		if (bodyGuid == null) {
50
+			if (other.bodyGuid != null)
51
+				return false;
52
+		} else if (!bodyGuid.equals(other.bodyGuid))
53
+			return false;
54
+		if (imapUid != other.imapUid)
55
+			return false;
56
+		return true;
57
+	}
58
+
59
+}
... ...
@@ -36,6 +36,7 @@ import net.bluemind.backend.mail.api.MessageBody;
36 36
 import net.bluemind.backend.mail.replica.api.ImapBinding;
37 37
 import net.bluemind.backend.mail.replica.api.MailboxRecord;
38 38
 import net.bluemind.backend.mail.replica.api.MailboxRecord.InternalFlag;
39
+import net.bluemind.backend.mail.replica.api.MailboxRecordItemUri;
39 40
 import net.bluemind.core.container.model.Container;
40 41
 import net.bluemind.core.container.model.Item;
41 42
 import net.bluemind.core.container.model.ItemFlag;
... ...
@@ -242,4 +243,21 @@ public class MailboxRecordStore extends AbstractItemValueStore<MailboxRecord> {
242 243
 		}
243 244
 	}
244 245
 
246
+	public List<MailboxRecordItemUri> getBodyGuidReferences(String guid) throws SQLException {
247
+		String query = "select c.uid, ci.uid, mbr.message_body_guid, mbr.imap_uid, c.owner " //
248
+				+ "FROM t_mailbox_record mbr " //
249
+				+ "JOIN t_container_item ci on ci.id = mbr.item_id " //
250
+				+ "JOIN t_container c on c.id = ci.container_id " //
251
+				+ "WHERE mbr.message_body_guid = ? order by ci.created";
252
+
253
+		return select(query, con -> new MailboxRecordItemUri(), (rs, index, itemUri) -> {
254
+			itemUri.containerUid = rs.getString(index++);
255
+			itemUri.itemUid = rs.getString(index++);
256
+			itemUri.bodyGuid = rs.getString(index++);
257
+			itemUri.imapUid = rs.getLong(index++);
258
+			itemUri.owner = rs.getString(index++);
259
+			return index;
260
+		}, new Object[] { guid });
261
+	}
262
+
245 263
 }
... ...
@@ -40,7 +40,8 @@ Require-Bundle: org.eclipse.core.runtime,
40 40
  net.bluemind.core.task.service,
41 41
  net.bluemind.network.topology,
42 42
  net.bluemind.mailindex,
43
- net.bluemind.system.api
43
+ net.bluemind.system.api,
44
+ net.bluemind.role.api
44 45
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
45 46
 Bundle-ActivationPolicy: lazy
46 47
 Export-Package: net.bluemind.backend.mail.replica.service,
... ...
@@ -7,6 +7,9 @@
7 7
             api="net.bluemind.backend.mail.replica.api.IReplicatedMailboxesRootMgmt">
8 8
       </endpoint>
9 9
       <endpoint
10
+            api="net.bluemind.backend.mail.replica.api.IReplicatedMailboxesMgmt">
11
+      </endpoint>
12
+      <endpoint
10 13
             api="net.bluemind.backend.mail.replica.api.IMailReplicaUids" 
11 14
             inline="true">
12 15
       </endpoint>
... ...
@@ -47,6 +50,9 @@
47 50
    <extension
48 51
          point="net.bluemind.core.rest.serviceFactory">
49 52
       <serviceFactory
53
+            class="net.bluemind.backend.mail.replica.service.ReplicatedMailboxesMgmtServiceFactory">
54
+      </serviceFactory>
55
+      <serviceFactory
50 56
             class="net.bluemind.backend.mail.replica.service.ReplicatedMailboxesRootMgmtServiceFactory">
51 57
       </serviceFactory>
52 58
       <serviceFactory
53 59
new file mode 100644
... ...
@@ -0,0 +1,48 @@
1
+/* BEGIN LICENSE
2
+ * Copyright © Blue Mind SAS, 2012-2017
3
+ *
4
+ * This file is part of Blue Mind. Blue Mind is a messaging and collaborative
5
+ * solution.
6
+ *
7
+ * This program is free software; you can redistribute it and/or modify
8
+ * it under the terms of either the GNU Affero General Public License as
9
+ * published by the Free Software Foundation (version 3 of the License)
10
+ * or the CeCILL as published by CeCILL.info (version 2 of the License).
11
+ *
12
+ * There are special exceptions to the terms and conditions of the
13
+ * licenses as they are applied to this program. See LICENSE.txt in
14
+ * the directory of this program distribution.
15
+ *
16
+ * This program is distributed in the hope that it will be useful,
17
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
18
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
19
+ *
20
+ * See LICENSE.txt
21
+ * END LICENSE
22
+ */
23
+package net.bluemind.backend.mail.replica.service;
24
+
25
+import net.bluemind.backend.mail.replica.api.IReplicatedMailboxesMgmt;
26
+import net.bluemind.backend.mail.replica.service.internal.ReplicatedMailboxesMgmtService;
27
+import net.bluemind.core.api.fault.ServerFault;
28
+import net.bluemind.core.rest.BmContext;
29
+import net.bluemind.core.rest.ServerSideServiceProvider;
30
+
31
+public class ReplicatedMailboxesMgmtServiceFactory
32
+		implements ServerSideServiceProvider.IServerSideServiceFactory<IReplicatedMailboxesMgmt> {
33
+
34
+	@Override
35
+	public Class<IReplicatedMailboxesMgmt> factoryClass() {
36
+		return IReplicatedMailboxesMgmt.class;
37
+	}
38
+
39
+	private IReplicatedMailboxesMgmt getService(BmContext context) {
40
+		return new ReplicatedMailboxesMgmtService(context);
41
+	}
42
+
43
+	@Override
44
+	public IReplicatedMailboxesMgmt instance(BmContext context, String... params) throws ServerFault {
45
+		return getService(context);
46
+	}
47
+
48
+}
... ...
@@ -43,8 +43,8 @@ import net.bluemind.backend.mail.api.MessageBody.RecipientKind;
43 43
 import net.bluemind.backend.mail.replica.api.IDbByContainerReplicatedMailboxes;
44 44
 import net.bluemind.backend.mail.replica.api.IDbMailboxRecords;
45 45
 import net.bluemind.backend.mail.replica.api.IDbMessageBodies;
46
-import net.bluemind.backend.mail.replica.api.ImapBinding;
47 46
 import net.bluemind.backend.mail.replica.api.IMailReplicaUids;
47
+import net.bluemind.backend.mail.replica.api.ImapBinding;
48 48
 import net.bluemind.backend.mail.replica.api.MailboxRecord;
49 49
 import net.bluemind.backend.mail.replica.api.MailboxRecord.InternalFlag;
50 50
 import net.bluemind.backend.mail.replica.api.MailboxReplicaRootDescriptor.Namespace;
... ...
@@ -279,8 +279,7 @@ public class DbMailboxRecordsService extends BaseMailboxRecordsService implement
279 279
 
280 280
 				if (expId != null) {
281 281
 					version = storeService.createWithId(uid, expId, null, uid, mr);
282
-					GuidExpectedIdCache
283
-							.invalidate(IMailReplicaUids.uniqueId(container.uid) + ":" + mr.messageBody);
282
+					GuidExpectedIdCache.invalidate(IMailReplicaUids.uniqueId(container.uid) + ":" + mr.messageBody);
284 283
 				} else {
285 284
 					ExpectedId knownInternalId = BodyInternalIdCache.expectedRecordId(mr.messageBody);
286 285
 					if (knownInternalId == null) {
287 286
new file mode 100644
... ...
@@ -0,0 +1,74 @@
1
+/* BEGIN LICENSE
2
+ * Copyright © Blue Mind SAS, 2012-2017
3
+ *
4
+ * This file is part of Blue Mind. Blue Mind is a messaging and collaborative
5
+ * solution.
6
+ *
7
+ * This program is free software; you can redistribute it and/or modify
8
+ * it under the terms of either the GNU Affero General Public License as
9
+ * published by the Free Software Foundation (version 3 of the License)
10
+ * or the CeCILL as published by CeCILL.info (version 2 of the License).
11
+ *
12
+ * There are special exceptions to the terms and conditions of the
13
+ * licenses as they are applied to this program. See LICENSE.txt in
14
+ * the directory of this program distribution.
15
+ *
16
+ * This program is distributed in the hope that it will be useful,
17
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
18
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
19
+ *
20
+ * See LICENSE.txt
21
+ * END LICENSE
22
+ */
23
+package net.bluemind.backend.mail.replica.service.internal;
24
+
25
+import java.sql.SQLException;
26
+import java.util.Collections;
27
+import java.util.HashSet;
28
+import java.util.List;
29
+import java.util.Set;
30
+import java.util.function.Function;
31
+
32
+import org.slf4j.Logger;
33
+import org.slf4j.LoggerFactory;
34
+
35
+import net.bluemind.backend.mail.replica.api.IReplicatedMailboxesMgmt;
36
+import net.bluemind.backend.mail.replica.api.MailboxRecordItemUri;
37
+import net.bluemind.backend.mail.replica.persistence.MailboxRecordStore;
38
+import net.bluemind.core.rest.BmContext;
39
+
40
+public class ReplicatedMailboxesMgmtService implements IReplicatedMailboxesMgmt {
41
+
42
+	private static final Logger logger = LoggerFactory.getLogger(ReplicatedMailboxesMgmtService.class);
43
+	private final BmContext context;
44
+
45
+	public ReplicatedMailboxesMgmtService(BmContext context) {
46
+		this.context = context;
47
+	}
48
+
49
+	@Override
50
+	public Set<MailboxRecordItemUri> getBodyGuidReferences(String guid) {
51
+		Set<MailboxRecordItemUri> refs = new HashSet<>();
52
+		readRecordsByGuid(guid, refs);
53
+		return refs;
54
+	}
55
+
56
+	private void readRecordsByGuid(String guid, Set<MailboxRecordItemUri> refs) {
57
+		select(refs, store -> {
58
+			try {
59
+				return store.getBodyGuidReferences(guid);
60
+			} catch (SQLException e) {
61
+				logger.warn("Cannot read referenced message bodies by imap-uid", e);
62
+				return Collections.emptyList();
63
+			}
64
+		});
65
+	}
66
+
67
+	private void select(Set<MailboxRecordItemUri> refs, Function<MailboxRecordStore, List<MailboxRecordItemUri>> func) {
68
+		context.getAllMailboxDataSource().forEach(ds -> {
69
+			MailboxRecordStore store = new MailboxRecordStore(ds);
70
+			refs.addAll(func.apply(store));
71
+		});
72
+	}
73
+
74
+}
... ...
@@ -35,8 +35,8 @@ import org.slf4j.Logger;
35 35
 import org.slf4j.LoggerFactory;
36 36
 
37 37
 import net.bluemind.backend.cyrus.partitions.CyrusPartition;
38
-import net.bluemind.backend.mail.replica.api.IReplicatedMailboxesRootMgmt;
39 38
 import net.bluemind.backend.mail.replica.api.IMailReplicaUids;
39
+import net.bluemind.backend.mail.replica.api.IReplicatedMailboxesRootMgmt;
40 40
 import net.bluemind.backend.mail.replica.api.MailboxRecord;
41 41
 import net.bluemind.backend.mail.replica.api.MailboxReplica;
42 42
 import net.bluemind.backend.mail.replica.api.MailboxReplicaRootDescriptor;
... ...
@@ -48,8 +48,8 @@ import net.bluemind.backend.mail.replica.service.internal.hooks.DeletedDataMemen
48 48
 import net.bluemind.backend.mail.replica.utils.SubtreeContainer;
49 49
 import net.bluemind.backend.mail.replica.utils.SubtreeContainer.Subtree;
50 50
 import net.bluemind.core.api.fault.ServerFault;
51
-import net.bluemind.core.container.api.IFlatHierarchyUids;
52 51
 import net.bluemind.core.container.api.IContainers;
52
+import net.bluemind.core.container.api.IFlatHierarchyUids;
53 53
 import net.bluemind.core.container.model.Container;
54 54
 import net.bluemind.core.container.model.ContainerDescriptor;
55 55
 import net.bluemind.core.container.model.ContainerModifiableDescriptor;
... ...
@@ -177,8 +177,7 @@ public class ReplicatedMailboxesRootMgmtService implements IReplicatedMailboxesR
177 177
 	public void delete(MailboxReplicaRootDescriptor root) {
178 178
 		String owner = owner(root, partition.domainUid, null);
179 179
 		if (owner != null) {
180
-			DataSource ds = DataSourceRouter.get(context,
181
-					IFlatHierarchyUids.getIdentifier(owner, partition.domainUid));
180
+			DataSource ds = DataSourceRouter.get(context, IFlatHierarchyUids.getIdentifier(owner, partition.domainUid));
182 181
 			reset((lookup -> {
183 182
 				try {
184 183
 					return lookup.store.findByTypeAndOwner(lookup.containerType, owner);
... ...
@@ -194,8 +193,7 @@ public class ReplicatedMailboxesRootMgmtService implements IReplicatedMailboxesR
194 193
 		IServiceProvider prov = ServerSideServiceProvider.getProvider(context);
195 194
 		IContainers containersApi = prov.instance(IContainers.class);
196 195
 		ContainerStore contStore = new ContainerStore(context, ds, context.getSecurityContext());
197
-		List<Container> recordsContainers = lookup
198
-				.apply(new Lookup(IMailReplicaUids.MAILBOX_RECORDS, contStore));
196
+		List<Container> recordsContainers = lookup.apply(new Lookup(IMailReplicaUids.MAILBOX_RECORDS, contStore));
199 197
 
200 198
 		logger.info("Found {} mailbox_records containers", recordsContainers.size());
201 199
 
... ...
@@ -208,8 +206,7 @@ public class ReplicatedMailboxesRootMgmtService implements IReplicatedMailboxesR
208 206
 			containersApi.delete(cont.uid);
209 207
 		}
210 208
 
211
-		List<Container> mboxReplicaContainers = lookup
212
-				.apply(new Lookup(IMailReplicaUids.REPLICATED_MBOXES, contStore));
209
+		List<Container> mboxReplicaContainers = lookup.apply(new Lookup(IMailReplicaUids.REPLICATED_MBOXES, contStore));
213 210
 		logger.info("Found {} subtrees to clear (type: {})", mboxReplicaContainers.size(),
214 211
 				IMailReplicaUids.REPLICATED_MBOXES);
215 212
 		for (Container cont : mboxReplicaContainers) {