Skip to content

Commit d6defbc

Browse files
christophstroblodrotbohm
authored andcommitted
DATAMONGO-1039 - Polish db clean hook implementation.
- Refactored internal structure. - Updated documentation. - Added some tests Original pull request: #222.
1 parent ed47850 commit d6defbc

File tree

3 files changed

+354
-79
lines changed

3 files changed

+354
-79
lines changed

spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/CleanMongoDB.java

Lines changed: 244 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import java.net.UnknownHostException;
1919
import java.util.Arrays;
2020
import java.util.Collection;
21+
import java.util.Collections;
2122
import java.util.HashSet;
2223
import java.util.Set;
2324

@@ -27,25 +28,35 @@
2728
import org.slf4j.Logger;
2829
import org.slf4j.LoggerFactory;
2930
import org.springframework.util.CollectionUtils;
31+
import org.springframework.util.StringUtils;
3032

3133
import com.mongodb.DB;
34+
import com.mongodb.DBCollection;
3235
import com.mongodb.MongoClient;
3336

3437
/**
38+
* {@link CleanMongoDB} is a junit {@link TestRule} implementation to be used as for wiping data from MongoDB instance.
39+
* MongoDB specific system databases like {@literal admin} and {@literal local} remain untouched. The rule will apply
40+
* <strong>after</strong> the base {@link Statement}. <br />
41+
* Use as {@link org.junit.ClassRule} to wipe data after finishing all tests within a class or as {@link org.junit.Rule}
42+
* to do so after each {@link org.junit.Test}.
43+
*
3544
* @author Christoph Strobl
45+
* @since 1.6
3646
*/
3747
public class CleanMongoDB implements TestRule {
3848

3949
private static final Logger LOGGER = LoggerFactory.getLogger(CleanMongoDB.class);
4050

41-
public enum Types {
51+
/**
52+
* Defines contents of MongoDB.
53+
*/
54+
public enum Struct {
4255
DATABASE, COLLECTION, INDEX;
4356
}
4457

58+
@SuppressWarnings("serial")//
4559
private Set<String> preserveDatabases = new HashSet<String>() {
46-
47-
private static final long serialVersionUID = -8698807376808700046L;
48-
4960
{
5061
add("admin");
5162
add("local");
@@ -54,57 +65,278 @@ public enum Types {
5465

5566
private Set<String> dbNames = new HashSet<String>();
5667
private Set<String> collectionNames = new HashSet<String>();
57-
private Set<Types> types = new HashSet<CleanMongoDB.Types>();
68+
private Set<Struct> types = new HashSet<CleanMongoDB.Struct>();
5869
private MongoClient client;
5970

71+
/**
72+
* Create new instance using an internal {@link MongoClient}.
73+
*/
6074
public CleanMongoDB() {
6175
this(null);
6276
}
6377

78+
/**
79+
* Create new instance using an internal {@link MongoClient} connecting to specified instance running at host:port.
80+
*
81+
* @param host
82+
* @param port
83+
* @throws UnknownHostException
84+
*/
6485
public CleanMongoDB(String host, int port) throws UnknownHostException {
6586
this(new MongoClient(host, port));
6687
}
6788

89+
/**
90+
* Create new instance using the given client.
91+
*
92+
* @param client
93+
*/
6894
public CleanMongoDB(MongoClient client) {
6995
this.client = client;
7096
}
7197

98+
/**
99+
* Removes everything by dropping every single {@link DB}.
100+
*
101+
* @return
102+
*/
72103
public static CleanMongoDB everything() {
73104

74105
CleanMongoDB cleanMongoDB = new CleanMongoDB();
75-
cleanMongoDB.clean(Types.DATABASE);
106+
cleanMongoDB.clean(Struct.DATABASE);
76107
return cleanMongoDB;
77108
}
78109

110+
/**
111+
* Removes everything from the databases with given name by dropping the according {@link DB}.
112+
*
113+
* @param dbNames
114+
* @return
115+
*/
79116
public static CleanMongoDB databases(String... dbNames) {
80117

81118
CleanMongoDB cleanMongoDB = new CleanMongoDB();
82-
cleanMongoDB.clean(Types.DATABASE);
83-
cleanMongoDB.collectionNames.addAll(Arrays.asList(dbNames));
119+
cleanMongoDB.clean(Struct.DATABASE);
120+
cleanMongoDB.useDatabases(dbNames);
121+
return cleanMongoDB;
122+
}
123+
124+
/**
125+
* Drops the {@link DBCollection} with given names from every single {@link DB} containing them.
126+
*
127+
* @param collectionNames
128+
* @return
129+
*/
130+
public static CleanMongoDB collections(String... collectionNames) {
131+
return collections("", Arrays.asList(collectionNames));
132+
}
133+
134+
/**
135+
* Drops the {@link DBCollection} with given names from the named {@link DB}.
136+
*
137+
* @param dbName
138+
* @param collectionNames
139+
* @return
140+
*/
141+
public static CleanMongoDB collections(String dbName, Collection<String> collectionNames) {
142+
143+
CleanMongoDB cleanMongoDB = new CleanMongoDB();
144+
cleanMongoDB.clean(Struct.COLLECTION);
145+
cleanMongoDB.useCollections(dbName, collectionNames);
84146
return cleanMongoDB;
85147
}
86148

149+
/**
150+
* Drops all index structures from every single {@link DBCollection}.
151+
*
152+
* @return
153+
*/
87154
public static CleanMongoDB indexes() {
155+
return indexes(Collections.<String> emptySet());
156+
}
157+
158+
/**
159+
* Drops all index structures from every single {@link DBCollection}.
160+
*
161+
* @param collectionNames
162+
* @return
163+
*/
164+
public static CleanMongoDB indexes(Collection<String> collectionNames) {
88165

89166
CleanMongoDB cleanMongoDB = new CleanMongoDB();
90-
cleanMongoDB.clean(Types.INDEX);
167+
cleanMongoDB.clean(Struct.INDEX);
168+
cleanMongoDB.useCollections(collectionNames);
91169
return cleanMongoDB;
92170
}
93171

94-
public CleanMongoDB clean(Types... types) {
172+
/**
173+
* Define {@link Struct} to be cleaned.
174+
*
175+
* @param types
176+
* @return
177+
*/
178+
public CleanMongoDB clean(Struct... types) {
95179

96180
this.types.addAll(Arrays.asList(types));
97181
return this;
98182
}
99183

100-
public Statement apply() {
184+
/**
185+
* Defines the {@link DB}s to be used. <br />
186+
* Impact along with {@link CleanMongoDB#clean(Struct...)}:
187+
* <ul>
188+
* <li>{@link Struct#DATABASE}: Forces drop of named databases.</li>
189+
* <li>{@link Struct#COLLECTION}: Forces drop of collections within named databases.</li>
190+
* <li>{@link Struct#INDEX}: Removes index within collections of named databases.</li>
191+
* </ul>
192+
*
193+
* @param dbNames
194+
* @return
195+
*/
196+
public CleanMongoDB useDatabases(String... dbNames) {
197+
198+
this.dbNames.addAll(Arrays.asList(dbNames));
199+
return this;
200+
}
201+
202+
/**
203+
* Excludes the given {@link DB}s from being processed.
204+
*
205+
* @param dbNames
206+
* @return
207+
*/
208+
public CleanMongoDB preserveDatabases(String... dbNames) {
209+
this.preserveDatabases.addAll(Arrays.asList(dbNames));
210+
return this;
211+
}
212+
213+
/**
214+
* Defines the {@link DBCollection}s to be used. <br />
215+
* Impact along with {@link CleanMongoDB#clean(Struct...)}:
216+
* <ul>
217+
* <li>{@link Struct#COLLECTION}: Forces drop of named collections.</li>
218+
* <li>{@link Struct#INDEX}: Removes index within named collections.</li>
219+
* </ul>
220+
*
221+
* @param collectionNames
222+
* @return
223+
*/
224+
public CleanMongoDB useCollections(String... collectionNames) {
225+
return useCollections(Arrays.asList(collectionNames));
226+
}
227+
228+
private CleanMongoDB useCollections(Collection<String> collectionNames) {
229+
return useCollections("", collectionNames);
230+
}
231+
232+
/**
233+
* Defines the {@link DBCollection}s and {@link DB} to be used. <br />
234+
* Impact along with {@link CleanMongoDB#clean(Struct...)}:
235+
* <ul>
236+
* <li>{@link Struct#COLLECTION}: Forces drop of named collections in given db.</li>
237+
* <li>{@link Struct#INDEX}: Removes index within named collections in given db.</li>
238+
* </ul>
239+
*
240+
* @param collectionNames
241+
* @return
242+
*/
243+
public CleanMongoDB useCollections(String db, Collection<String> collectionNames) {
244+
245+
if (StringUtils.hasText(db)) {
246+
this.dbNames.add(db);
247+
}
248+
249+
if (!CollectionUtils.isEmpty(collectionNames)) {
250+
this.collectionNames.addAll(collectionNames);
251+
}
252+
return this;
253+
}
254+
255+
Statement apply() {
101256
return apply(null, null);
102257
}
103258

259+
/*
260+
* (non-Javadoc)
261+
* @see org.junit.rules.TestRule#apply(org.junit.runners.model.Statement, org.junit.runner.Description)
262+
*/
104263
public Statement apply(Statement base, Description description) {
105264
return new MongoCleanStatement(base);
106265
}
107266

267+
private void doClean() {
268+
269+
Collection<String> dbNamesToUse = initDbNames();
270+
271+
for (String dbName : dbNamesToUse) {
272+
273+
if (isPreserved(dbName) || dropDbIfRequired(dbName)) {
274+
continue;
275+
}
276+
277+
DB db = client.getDB(dbName);
278+
dropCollectionsOrIndexIfRequried(db, initCollectionNames(db));
279+
}
280+
}
281+
282+
private boolean dropDbIfRequired(String dbName) {
283+
284+
if (!types.contains(Struct.DATABASE)) {
285+
return false;
286+
}
287+
288+
client.dropDatabase(dbName);
289+
LOGGER.debug("Dropping DB '{}'. ", dbName);
290+
return true;
291+
}
292+
293+
private void dropCollectionsOrIndexIfRequried(DB db, Collection<String> collectionsToUse) {
294+
295+
for (String collectionName : collectionsToUse) {
296+
297+
if (db.collectionExists(collectionName)) {
298+
299+
DBCollection collection = db.getCollectionFromString(collectionName);
300+
if (collection != null) {
301+
302+
if (types.contains(Struct.COLLECTION)) {
303+
collection.drop();
304+
LOGGER.debug("Dropping collection '{}' for DB '{}'. ", collectionName, db.getName());
305+
} else if (types.contains(Struct.INDEX)) {
306+
collection.dropIndexes();
307+
LOGGER.debug("Dropping indexes in collection '{}' for DB '{}'. ", collectionName, db.getName());
308+
}
309+
}
310+
}
311+
}
312+
}
313+
314+
private boolean isPreserved(String dbName) {
315+
return preserveDatabases.contains(dbName.toLowerCase());
316+
}
317+
318+
private Collection<String> initDbNames() {
319+
320+
Collection<String> dbNamesToUse = dbNames;
321+
if (dbNamesToUse.isEmpty()) {
322+
dbNamesToUse = client.getDatabaseNames();
323+
}
324+
return dbNamesToUse;
325+
}
326+
327+
private Collection<String> initCollectionNames(DB db) {
328+
329+
Collection<String> collectionsToUse = collectionNames;
330+
if (CollectionUtils.isEmpty(collectionsToUse)) {
331+
collectionsToUse = db.getCollectionNames();
332+
}
333+
return collectionsToUse;
334+
}
335+
336+
/**
337+
* @author Christoph Strobl
338+
* @since 1.6
339+
*/
108340
private class MongoCleanStatement extends Statement {
109341

110342
private final Statement base;
@@ -126,61 +358,12 @@ public void evaluate() throws Throwable {
126358
isInternal = true;
127359
}
128360

129-
Collection<String> dbNamesToUse = dbNames;
130-
if (dbNamesToUse.isEmpty()) {
131-
dbNamesToUse = client.getDatabaseNames();
132-
}
133-
134-
for (String dbName : dbNamesToUse) {
135-
136-
if (preserveDatabases.contains(dbName.toLowerCase())) {
137-
continue;
138-
}
139-
140-
if (types.contains(Types.DATABASE)) {
141-
client.dropDatabase(dbName);
142-
LOGGER.debug("Dropping DB '{}'. ", dbName);
143-
}
144-
145-
if (types.contains(Types.COLLECTION)) {
146-
147-
DB db = client.getDB(dbName);
148-
Collection<String> collectionsToUse = initCollectionNames(db);
149-
for (String collectionName : collectionsToUse) {
150-
if (db.collectionExists(collectionName)) {
151-
db.getCollectionFromString(collectionName).drop();
152-
LOGGER.debug("Dropping collection '{}' for DB '{}'. ", collectionName, dbName);
153-
}
154-
}
155-
}
156-
157-
if (types.contains(Types.INDEX)) {
158-
159-
DB db = client.getDB(dbName);
160-
Collection<String> collectionsToUse = initCollectionNames(db);
161-
for (String collectionName : collectionsToUse) {
162-
if (db.collectionExists(collectionName)) {
163-
db.getCollectionFromString(collectionName).dropIndexes();
164-
LOGGER.debug("Dropping indexes in collection '{}' for DB '{}'. ", collectionName, dbName);
165-
}
166-
}
167-
}
168-
}
361+
doClean();
169362

170363
if (isInternal) {
171364
client.close();
172365
client = null;
173366
}
174367
}
175-
176-
private Collection<String> initCollectionNames(DB db) {
177-
178-
Collection<String> collectionsToUse = collectionNames;
179-
if (CollectionUtils.isEmpty(collectionsToUse)) {
180-
collectionsToUse = db.getCollectionNames();
181-
}
182-
return collectionsToUse;
183-
}
184368
}
185-
186369
}

0 commit comments

Comments
 (0)