/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (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.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is part of dcm4che, an implementation of DICOM(TM) in
* Java(TM), hosted at https://github.com/gunterze/dcm4che.
*
* The Initial Developer of the Original Code is
* Agfa Healthcare.
* Portions created by the Initial Developer are Copyright (C) 2012-2014
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* See @authors listed below
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
package org.dcm4chee.storage.test.unit.service;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.enterprise.inject.Produces;
import javax.inject.Inject;
import org.dcm4che3.conf.api.DicomConfiguration;
import org.dcm4che3.net.Device;
import org.dcm4chee.storage.ContainerEntry;
import org.dcm4chee.storage.StorageContext;
import org.dcm4chee.storage.conf.Container;
import org.dcm4chee.storage.conf.FileCache;
import org.dcm4chee.storage.conf.StorageDevice;
import org.dcm4chee.storage.conf.StorageDeviceExtension;
import org.dcm4chee.storage.conf.StorageSystem;
import org.dcm4chee.storage.conf.StorageSystemGroup;
import org.dcm4chee.storage.conf.StorageSystemStatus;
import org.dcm4chee.storage.filecache.DefaultFileCacheProvider;
import org.dcm4chee.storage.filesystem.FileSystemStorageSystemProvider;
import org.dcm4chee.storage.service.StorageService;
import org.dcm4chee.storage.service.impl.StorageServiceImpl;
import org.dcm4chee.storage.test.unit.util.MockDicomConfiguration;
import org.dcm4chee.storage.test.unit.util.TransientDirectory;
import org.dcm4chee.storage.zip.ZipContainerProvider;
import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.junit.Arquillian;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.asset.EmptyAsset;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
/**
* @author Gunter Zeilinger<gunterze@gmail.com>
*
*/
@RunWith(Arquillian.class)
public class StorageServiceTest {
private static final String NAME = "a/b/c";
private static final Path SRC_PATH = Paths.get("target/test_file");
private static final Path ZIP_PATH = Paths.get("src/test/data/test.zip");
private static final Path CACHE_PATH = Paths.get("target/filecache/fs1/a/b/c");
private static final String[] ENTRY_NAMES = { "entry-1", "entry-2", "entry-3" };
private static final byte[] ENTRY = { 'e', 'n', 't', 'r', 'y' };
private static final String DIGEST = "1043bfc77febe75fafec0c4309faccf1";
@Deployment
public static JavaArchive createDeployment() {
return ShrinkWrap.create(JavaArchive.class)
.addClass(StorageServiceImpl.class)
.addClass(FileSystemStorageSystemProvider.class)
.addClass(DefaultFileCacheProvider.class)
.addClass(ZipContainerProvider.class)
.addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml");
}
@Inject
StorageServiceImpl service;
@Produces @StorageDevice
static Device device = new Device("test");
// @Produces
// private static DicomConfiguration dicomConfiguration = new MockDicomConfiguration();
@Rule
public TransientDirectory storageDir = new TransientDirectory("target/test-storage");
@Rule
public TransientDirectory cacheDir = new TransientDirectory("target/filecache");
@Rule
public TransientDirectory journalDir = new TransientDirectory("target/journaldir");
StorageDeviceExtension ext;
StorageSystemGroup fsGroup;
StorageSystem fs1;
StorageSystem fs2;
StorageSystem fs3;
FileCache fileCache;
Container container;
ExecutorService executor;
@Before
public void setup() throws IOException {
ext = new StorageDeviceExtension();
device.addDeviceExtension(ext);
executor = Executors.newCachedThreadPool();
device.setExecutor(executor);
fsGroup = new StorageSystemGroup();
fsGroup.setGroupID("fs");
ext.addStorageSystemGroup(fsGroup);
fsGroup.addStorageSystem(fs1 = createStorageSystem("fs1", "fs2"));
fsGroup.addStorageSystem(fs2 = createStorageSystem("fs2", "fs3"));
fsGroup.addStorageSystem(fs3 = createStorageSystem("fs3", "fs1"));
fsGroup.setParallelism(2);
container = new Container();
container.setProviderName("org.dcm4chee.storage.zip");
container.setChecksumEntry("MD5SUM");
fsGroup.setContainer(container);
fileCache = new FileCache();
fileCache.setProviderName("org.dcm4chee.storage.filecache");
fileCache.setFileCacheRootDirectory("target/filecache");
fileCache.setJournalRootDirectory("target/journaldir");
fsGroup.setFileCache(fileCache);
}
@After
public void teardown() {
device.removeDeviceExtension(ext);
executor.shutdownNow();
ext = null;
fsGroup = null;
fs1 = null;
fs2 = null;
fs3 = null;
}
@Produces
DicomConfiguration createDicomConfiguration(@StorageDevice Device device) {
return new MockDicomConfiguration(device);
}
private StorageSystem createStorageSystem(String id, String next)
throws IOException {
StorageSystem system = new StorageSystem();
system.setProviderName("org.dcm4chee.storage.filesystem");
system.setStorageSystemID(id);
system.setNextStorageSystemID(next);
system.setStorageSystemPath("target/test-storage/" + id);
system.setStorageSystemStatus(StorageSystemStatus.OK);
system.setMountCheckFile("NO_MOUNT");
Path dir = Files.createDirectories(
Paths.get(system.getStorageSystemPath()));
Files.deleteIfExists(dir.resolve(system.getMountCheckFile()));
return system;
}
@Test
public void testSelectStorageSystem() throws Exception {
Assert.assertArrayEquals(
new String[]{},
fsGroup.getActiveStorageSystemIDs());
Assert.assertSame(fs1, service.selectStorageSystem("fs", 0, false));
Assert.assertArrayEquals(
new String[]{"fs1", "fs2"},
fsGroup.getActiveStorageSystemIDs());
Assert.assertSame(fs2, service.selectStorageSystem("fs", 0, false));
Assert.assertSame(fs1, service.selectStorageSystem("fs", 0, false));
createMountCheckFile(fs2);
Assert.assertSame(fs1, service.selectStorageSystem("fs", 0, false));
Assert.assertArrayEquals(
new String[]{"fs1", "fs3"},
fsGroup.getActiveStorageSystemIDs());
Assert.assertSame(fs3, service.selectStorageSystem("fs", 0, false));
Assert.assertEquals("fs1", fsGroup.getNextStorageSystemID());
deleteMountCheckFile(fs2);
fs2.setStorageSystemStatus(StorageSystemStatus.OK);
fs1.setReadOnly(true);
Assert.assertSame(fs3, service.selectStorageSystem("fs", 0, false));
Assert.assertSame(fs2, service.selectStorageSystem("fs", 0, false));
Assert.assertArrayEquals(
new String[]{"fs3", "fs2"},
fsGroup.getActiveStorageSystemIDs());
Assert.assertEquals("fs3", fsGroup.getNextStorageSystemID());
fs2.setReadOnly(true);
fs3.setReadOnly(true);
Assert.assertNull(service.selectStorageSystem("fs", 0, false));
Assert.assertArrayEquals(
new String[]{},
fsGroup.getActiveStorageSystemIDs());
}
@Test
public void testOpenOutputStream() throws Exception {
StorageContext ctx = service.createStorageContext(fs1);
try ( OutputStream out = service.openOutputStream(ctx, NAME) ) {
out.write(ENTRY);
}
Assert.assertEquals(ENTRY.length,
Files.size(Paths.get(fs1.getStorageSystemPath(), NAME)));
}
@Test
public void testOpenOutputStreamWithFileCache() throws Exception {
fs1.setCacheOnStore(true);
testOpenOutputStream();
Assert.assertEquals(ENTRY.length, Files.size(CACHE_PATH));
}
@Test
public void testCopyInputStream() throws Exception {
StorageContext ctx = service.createStorageContext(fs1);
try (ByteArrayInputStream in = new ByteArrayInputStream(ENTRY)) {
service.copyInputStream(ctx, in, NAME);
}
Assert.assertEquals(ENTRY.length,
Files.size(Paths.get(fs1.getStorageSystemPath(), NAME)));
}
@Test
public void testCopyInputStreamWithFileCache() throws Exception {
fs1.setCacheOnStore(true);
testCopyInputStream();
Assert.assertEquals(ENTRY.length, Files.size(CACHE_PATH));
}
@Test
public void testStoreFile() throws Exception {
StorageContext ctx = service.createStorageContext(fs1);
makeSourceFile();
service.storeFile(ctx, SRC_PATH, NAME);
Assert.assertEquals(ENTRY.length,
Files.size(Paths.get(fs1.getStorageSystemPath(), NAME)));
Assert.assertTrue(Files.exists(SRC_PATH));
}
@Test
public void testStoreFileWithFileCache() throws Exception {
fs1.setCacheOnStore(true);
testStoreFile();
Assert.assertEquals(ENTRY.length, Files.size(CACHE_PATH));
}
@Test
public void testMoveFile() throws Exception {
StorageContext ctx = service.createStorageContext(fs1);
makeSourceFile();
service.moveFile(ctx, SRC_PATH, NAME);
Assert.assertEquals(ENTRY.length,
Files.size(Paths.get(fs1.getStorageSystemPath(), NAME)));
Assert.assertFalse(Files.exists(SRC_PATH));
}
@Test
public void testMoveFileWithFileCache() throws Exception {
fs1.setCacheOnStore(true);
testMoveFile();
Assert.assertEquals(ENTRY.length, Files.size(CACHE_PATH));
}
@Test
public void testStoreContainerEntries() throws Exception {
StorageContext ctx = service.createStorageContext(fs1);
service.storeContainerEntries(ctx, makeEntries(), NAME);
Assert.assertEquals(Files.size(ZIP_PATH),
Files.size(Paths.get(fs1.getStorageSystemPath(), NAME)));
}
@Test
public void testStoreContainerEntriesWithFileCache() throws Exception {
fs1.setCacheOnStore(true);
testStoreContainerEntries();
for (String name : ENTRY_NAMES) {
Assert.assertEquals(ENTRY.length, Files.size(CACHE_PATH.resolve(name)));
}
}
private void createMountCheckFile(StorageSystem system) throws IOException {
Path mountCheckFile = Paths.get(
system.getStorageSystemPath(),
system.getMountCheckFile());
Files.createFile(mountCheckFile);
}
private void deleteMountCheckFile(StorageSystem system) throws IOException {
Path mountCheckFile = Paths.get(
system.getStorageSystemPath(),
system.getMountCheckFile());
Files.delete(mountCheckFile);
}
private static void makeSourceFile() throws IOException {
try (OutputStream out = Files.newOutputStream(SRC_PATH)) {
out.write(ENTRY);
}
}
private static List<ContainerEntry> makeEntries() throws IOException {
makeSourceFile();
ArrayList<ContainerEntry> entries =
new ArrayList<ContainerEntry>(ENTRY_NAMES.length);
for (String name : ENTRY_NAMES) {
entries.add(new ContainerEntry.Builder(name, DIGEST).setSourcePath(SRC_PATH).build());
}
return entries;
}
}