/* * (C) Copyright 2015 Nuxeo SA (http://nuxeo.com/) and others. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * Contributors: * Thierry Delprat * Florent Guillaume */ package org.nuxeo.ecm.core.storage.mongodb; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import java.io.IOException; import java.io.InputStream; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.apache.commons.io.IOUtils; import org.junit.AfterClass; import org.junit.Assume; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.nuxeo.ecm.core.api.Blobs; import org.nuxeo.ecm.core.blob.binary.Binary; import org.nuxeo.ecm.core.blob.binary.BinaryGarbageCollector; import org.nuxeo.ecm.core.blob.binary.BinaryManagerStatus; import org.nuxeo.runtime.api.Framework; import org.nuxeo.runtime.test.runner.Features; import org.nuxeo.runtime.test.runner.FeaturesRunner; import org.nuxeo.runtime.test.runner.RuntimeFeature; import com.mongodb.BasicDBObject; import com.mongodb.DBCursor; import com.mongodb.MongoServerException; @RunWith(FeaturesRunner.class) @Features(RuntimeFeature.class) public class TestGridFSBinaryManager { protected static final String CONTENT = "this is a file au caf\u00e9"; protected static final String CONTENT_MD5 = "d25ea4f4642073b7f218024d397dbaef"; protected static final String CONTENT2 = "abc"; protected static final String CONTENT2_MD5 = "900150983cd24fb0d6963f7d28e17f72"; protected static final String CONTENT3 = "defg"; protected static final String CONTENT3_MD5 = "025e4da7edac35ede583f5e8d51aa7ec"; protected static GridFSBinaryManager BINARY_MANAGER; // TODO test should be activated only on explicit system property, not by detecting a default instance @BeforeClass public static void beforeClass() throws Exception { BINARY_MANAGER = new GridFSBinaryManager(); Map<String, String> config = new HashMap<>(); config.put("server", Framework.getProperty("nuxeo.mongodb.server", "localhost")); config.put("dbname", Framework.getProperty("nuxeo.mongodb.dbname", "nuxeo")); config.put("bucket", Framework.getProperty("nuxeo.mongodb.gridfs.bucket", "test.fs")); BINARY_MANAGER.initialize("test", config); } @AfterClass public static void afterClass() { if (BINARY_MANAGER != null) { BINARY_MANAGER.close(); BINARY_MANAGER = null; } } protected GridFSBinaryManager getBinaryManager() { return BINARY_MANAGER; } protected Set<String> listObjects() throws IOException { Set<String> res = new HashSet<>(); try (DBCursor cursor = getBinaryManager().getGridFS().getFileList()) { while (cursor.hasNext()) { String digest = (String) cursor.next().get("filename"); res.add(digest); } } return res; } protected void removeAllObjects() throws IOException { for (String digest : listObjects()) { removeObject(digest); } } protected void removeObject(String digest) throws IOException { getBinaryManager().getGridFS().remove(new BasicDBObject("filename", digest)); } @Before public void setUp() throws Exception { try { removeAllObjects(); } catch (MongoServerException e) { Assume.assumeNoException("MongoDB server is not reachable", e); } } @Test public void testStoreFile() throws Exception { GridFSBinaryManager binaryManager = getBinaryManager(); // store binary byte[] bytes = CONTENT.getBytes("UTF-8"); Binary binary = binaryManager.getBinary(Blobs.createBlob(CONTENT)); assertNotNull(binary); System.out.println(binary.getDigestAlgorithm()); System.out.println(binary.getDigest()); // check binary is here binary = binaryManager.getBinary(CONTENT_MD5); assertNotNull(binary); assertEquals(CONTENT, toString(binary.getStream())); // check that there is only one entry assertEquals(1, listObjects().size()); // store again binaryManager.getBinary(Blobs.createBlob(CONTENT)); // check that there is still only one entry assertEquals(1, listObjects().size()); } /** * NOTE THAT THIS TEST WILL REMOVE ALL FILES IN THE BUCKET!!! */ @Test public void testBinaryManagerGC() throws Exception { GridFSBinaryManager binaryManager = getBinaryManager(); // store binary byte[] bytes = CONTENT.getBytes("UTF-8"); Binary binary = binaryManager.getBinary(Blobs.createBlob(CONTENT)); assertNotNull(binary); assertEquals(Collections.singleton(CONTENT_MD5), listObjects()); // get binary binary = binaryManager.getBinary(CONTENT_MD5); assertNotNull(binary); assertEquals(CONTENT, toString(binary.getStream())); // another binary we'll GC binaryManager.getBinary(Blobs.createBlob(CONTENT2)); // another binary we'll keep binaryManager.getBinary(Blobs.createBlob(CONTENT3)); assertEquals(new HashSet<>(Arrays.asList(CONTENT_MD5, CONTENT2_MD5, CONTENT3_MD5)), listObjects()); // GC in non-delete mode BinaryGarbageCollector gc = binaryManager.getGarbageCollector(); assertFalse(gc.isInProgress()); gc.start(); assertTrue(gc.isInProgress()); gc.mark(CONTENT_MD5); gc.mark(CONTENT3_MD5); assertTrue(gc.isInProgress()); gc.stop(false); assertFalse(gc.isInProgress()); BinaryManagerStatus status = gc.getStatus(); assertEquals(2, status.numBinaries); assertEquals(bytes.length + 4, status.sizeBinaries); assertEquals(1, status.numBinariesGC); assertEquals(3, status.sizeBinariesGC); assertEquals(new HashSet<>(Arrays.asList(CONTENT_MD5, CONTENT2_MD5, CONTENT3_MD5)), listObjects()); // real GC gc = binaryManager.getGarbageCollector(); gc.start(); gc.mark(CONTENT_MD5); gc.mark(CONTENT3_MD5); gc.stop(true); status = gc.getStatus(); assertEquals(2, status.numBinaries); assertEquals(bytes.length + 4, status.sizeBinaries); assertEquals(1, status.numBinariesGC); assertEquals(3, status.sizeBinariesGC); assertEquals(new HashSet<>(Arrays.asList(CONTENT_MD5, CONTENT3_MD5)), listObjects()); // another GC after not marking content3 gc = binaryManager.getGarbageCollector(); gc.start(); gc.mark(CONTENT_MD5); gc.stop(true); status = gc.getStatus(); assertEquals(1, status.numBinaries); assertEquals(bytes.length, status.sizeBinaries); assertEquals(1, status.numBinariesGC); assertEquals(4, status.sizeBinariesGC); assertEquals(Collections.singleton(CONTENT_MD5), listObjects()); } protected static String toString(InputStream stream) throws IOException { return IOUtils.toString(stream, "UTF-8"); } }