/*
* Copyright (C) 2006-2016 DLR, Germany
*
* All rights reserved
*
* http://www.rcenvironment.de/
*/
package de.rcenvironment.core.datamanagement.internal;
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 static org.junit.Assert.fail;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.UndeclaredThrowableException;
import java.net.URI;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import org.easymock.EasyMock;
import org.junit.Before;
import org.junit.Test;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceReference;
import de.rcenvironment.core.authorization.AuthorizationException;
import de.rcenvironment.core.communication.api.CommunicationService;
import de.rcenvironment.core.communication.api.PlatformService;
import de.rcenvironment.core.communication.common.CommunicationException;
import de.rcenvironment.core.communication.common.IdentifierException;
import de.rcenvironment.core.communication.common.InstanceNodeId;
import de.rcenvironment.core.communication.common.InstanceNodeSessionId;
import de.rcenvironment.core.communication.common.NodeIdentifierTestUtils;
import de.rcenvironment.core.communication.common.NodeIdentifierUtils;
import de.rcenvironment.core.communication.common.ResolvableNodeId;
import de.rcenvironment.core.communication.testutils.CommunicationServiceDefaultStub;
import de.rcenvironment.core.communication.testutils.PlatformServiceDefaultStub;
import de.rcenvironment.core.configuration.ConfigurationService;
import de.rcenvironment.core.configuration.testutils.MockConfigurationService;
import de.rcenvironment.core.datamanagement.RemotableFileDataService;
import de.rcenvironment.core.datamanagement.backend.DataBackend;
import de.rcenvironment.core.datamanagement.backend.MetaDataBackendService;
import de.rcenvironment.core.datamanagement.commons.BinaryReference;
import de.rcenvironment.core.datamanagement.commons.DataReference;
import de.rcenvironment.core.datamanagement.commons.DistributableInputStream;
import de.rcenvironment.core.datamanagement.commons.MetaDataSet;
import de.rcenvironment.core.datamanagement.testutils.FileDataServiceDefaultStub;
import de.rcenvironment.core.datamodel.api.CompressionFormat;
import de.rcenvironment.core.notification.api.RemotableNotificationService;
import de.rcenvironment.core.utils.common.TempFileServiceAccess;
import de.rcenvironment.core.utils.common.security.AllowRemoteAccess;
/**
* Test cases of {@link FileDataServiceImpl}.
*
* @author Doreen Seider
* @author Robert Mischke (adapted for new upload mechanism; id adaptations)
*/
public class DistributedFileDataServiceImplTest {
private static final String REVISION = "1";
private static final String CLOSE_BRACKET = ")";
private static final String OPEN_BRACKET = "(";
private static final String EQUALS_SIGN = "=";
// should be >UPLOAD_CHUNK_SIZE for proper testing
private static final int UPLOAD_TEST_SIZE = 300000;
// should be <UPLOAD_CHUNK_SIZE for proper testing of single step upload
private static final int SMALL_UPLOAD_TEST_SIZE = 1024;
private static String xmlBackendProvider = "snoopy";
private static String fileBackendProvider = "linus";
private static String dataScheme = "ftp";
private static String catalogBackendProvider = "de.rcenvironment.core.datamanagement.backend.metadata.derby";
private final int read = 7;
private FileDataServiceImpl fileDataService;
private InstanceNodeSessionId localInstanceSessionId;
private InstanceNodeSessionId unreachableInstanceSessionId;
private UUID referenceID;
private DataReference reference;
private DataReference notReachableReference;
// for mock remote testing
private volatile DataReference lastMockRemoteDataReference;
private InputStream is;
private MetaDataSet mds;
private InstanceNodeSessionId mockRemoteInstanceSessionId;
private InstanceNodeId mockRemoteInstanceId;
private MetaDataBackendService catalogBackend = EasyMock.createNiceMock(MetaDataBackendService.class);
private DataBackend dataBackend;
private BackendSupport backendSupport;
private UUID notReachableReferenceID;
private InstanceNodeId localInstanceId;
private InstanceNodeId unreachableInstanceId;
public DistributedFileDataServiceImplTest() {
mockRemoteInstanceSessionId = NodeIdentifierTestUtils.createTestInstanceNodeSessionIdWithDisplayName("mockRemote");
mockRemoteInstanceId = mockRemoteInstanceSessionId.convertToInstanceNodeId();
}
/**
* Set up.
*
* @throws IdentifierException not expected
*/
@Before
public void setUp() throws IdentifierException {
TempFileServiceAccess.setupUnitTestEnvironment();
localInstanceSessionId = NodeIdentifierTestUtils.createTestInstanceNodeSessionIdWithDisplayName("horst");
// TODO use proper conversion method once available
localInstanceId = NodeIdentifierUtils.parseInstanceNodeIdString(localInstanceSessionId.getInstanceNodeIdString());
unreachableInstanceSessionId = NodeIdentifierTestUtils.createTestInstanceNodeSessionIdWithDisplayName("unreachable");
unreachableInstanceId = NodeIdentifierUtils.parseInstanceNodeIdString(unreachableInstanceSessionId.getInstanceNodeIdString());
referenceID = UUID.randomUUID();
notReachableReferenceID = UUID.randomUUID();
Set<BinaryReference> birefs = new HashSet<BinaryReference>();
birefs.add(new BinaryReference(UUID.randomUUID().toString(), CompressionFormat.GZIP, REVISION));
reference = new DataReference(referenceID.toString(), localInstanceId, birefs);
birefs = new HashSet<BinaryReference>();
birefs.add(new BinaryReference(UUID.randomUUID().toString(), CompressionFormat.GZIP, REVISION));
notReachableReference =
new DataReference(notReachableReferenceID.toString(), unreachableInstanceId, birefs);
is = new InputStream() {
@Override
public int read() throws IOException {
return read;
}
};
mds = new MetaDataSet();
fileDataService = new FileDataServiceImpl();
fileDataService.bindCommunicationService(new MockCommunicationService());
fileDataService.bindPlatformService(new MockPlatformService());
fileDataService.activate(EasyMock.createNiceMock(BundleContext.class));
dataBackend = EasyMock.createNiceMock(DataBackend.class);
EasyMock.expect(dataBackend.get(EasyMock.anyObject(URI.class))).andReturn(new InputStream() {
@Override
public int read() throws IOException {
return 0;
}
});
EasyMock.replay(dataBackend);
backendSupport = new BackendSupport();
backendSupport.bindConfigurationService(new DummyConfigurationService());
backendSupport.bindDataBackendService(dataBackend);
backendSupport.activate(createBundleContext(catalogBackend, dataBackend));
}
/**
* Tests successful access to a remote data reference.
*
* @throws IOException if an error occurs.
* @throws CommunicationException on communication error
*/
@Test
public void testGetStreamFromDataReferenceSuccess() throws IOException, CommunicationException {
InputStream stream = fileDataService.getStreamFromDataReference(reference);
assertEquals(read, stream.read());
}
/**
* Test access attempt on an unreachable reference.
*
* @throws IOException if an error occurs.
* @throws CommunicationException on communication error
*/
@Test
public void testGetStreamFromDataReferenceFailure() throws IOException, CommunicationException {
try {
fileDataService.getStreamFromDataReference(notReachableReference);
fail("Exception expected");
} catch (RuntimeException e) {
assertTrue(e.getMessage().contains("DataReference is not equal")); // TODO somewhat brittle
}
}
/**
* Test.
*
* @throws Exception on uncaught errors
*/
@Test
public void testLocalNewReferenceFromStream() throws Exception {
// test local
DataReference dr = fileDataService.newReferenceFromStream(is, mds, localInstanceSessionId);
assertEquals(reference, dr);
// test "null" (should be local)
dr = fileDataService.newReferenceFromStream(is, mds, null);
assertEquals(reference, dr);
}
/**
* Test.
*
* @throws Exception on uncaught errors
*/
@Test
public void testDistributedNewReferenceFromStream() throws Exception {
assertFalse(localInstanceSessionId.equals(mockRemoteInstanceSessionId));
assertFalse(localInstanceId.equals(mockRemoteInstanceId));
InputStream testStream = new ByteArrayInputStream(new byte[UPLOAD_TEST_SIZE]);
DataReference remoteRef = fileDataService.newReferenceFromStream(testStream, mds, mockRemoteInstanceSessionId);
assertNotNull(remoteRef);
assertEquals(lastMockRemoteDataReference, remoteRef);
assertEquals(mockRemoteInstanceId, remoteRef.getInstanceId());
}
/**
* Test for small uploads.
*
* @throws Exception on uncaught errors
*/
@Test
public void testDistributedNewReferenceFromStreamSmallUpload() throws Exception {
assertFalse(localInstanceSessionId.equals(mockRemoteInstanceSessionId));
assertFalse(localInstanceId.equals(mockRemoteInstanceId));
InputStream testStream = new ByteArrayInputStream(new byte[SMALL_UPLOAD_TEST_SIZE]);
DataReference remoteRef = fileDataService.newReferenceFromStream(testStream, mds, mockRemoteInstanceSessionId);
assertNotNull(remoteRef);
assertEquals(lastMockRemoteDataReference, remoteRef);
assertEquals(mockRemoteInstanceId, remoteRef.getInstanceId());
}
/**
* Test for small uploads, stream not returning full buffer.
*
* @throws Exception on uncaught errors
*/
@Test
public void testDistributedNewReferenceFromStreamNoFullBuffer() throws Exception {
assertFalse(localInstanceSessionId.equals(mockRemoteInstanceSessionId));
assertFalse(localInstanceId.equals(mockRemoteInstanceId));
InputStream testStream = new MockInputStream(new byte[SMALL_UPLOAD_TEST_SIZE]);
DataReference remoteRef = fileDataService.newReferenceFromStream(testStream, mds, mockRemoteInstanceSessionId);
assertNotNull(remoteRef);
assertEquals(lastMockRemoteDataReference, remoteRef);
assertEquals(mockRemoteInstanceId, remoteRef.getInstanceId());
}
/**
* Test implementation of the {@link CommunicationService}.
*
* @author Doreen Seider
*/
private class MockCommunicationService extends CommunicationServiceDefaultStub {
@Override
@SuppressWarnings("unchecked")
public <T> T getRemotableService(Class<T> iface, ResolvableNodeId nodeId) {
if (iface == RemotableNotificationService.class
&& nodeId.equals(localInstanceSessionId)) {
return (T) new MockLocalFileDataService();
} else if (nodeId.equals(unreachableInstanceSessionId)) {
return (T) new MockUnreachableFileDataService();
} else {
return (T) new MockRemoteFileDataService();
}
}
}
/**
* Test implementation of the {@link RemotableFileDataService}.
*
* @author Doreen Seider
*/
private class MockLocalFileDataService extends FileDataServiceDefaultStub {
@Override
public InputStream getStreamFromDataReference(DataReference dataReference, Boolean calledFromRemote)
throws AuthorizationException {
if (dataReference.equals(reference)) {
return is;
} else {
throw new RuntimeException();
}
}
@Override
public DataReference newReferenceFromStream(InputStream inputStream,
MetaDataSet metaDataSet) throws AuthorizationException {
if (inputStream.equals(is)
|| inputStream instanceof DistributableInputStream) {
return reference;
} else {
throw new RuntimeException();
}
}
@Override
public void deleteReference(String dataReference) throws AuthorizationException {
if (!dataReference.equals(reference.getBinaryReferences().iterator().next().getBinaryReferenceKey())) {
throw new RuntimeException();
}
}
@Override
public String initializeUpload() throws IOException {
throw new UnsupportedOperationException();
}
@Override
public long appendToUpload(String id, byte[] data) throws IOException {
throw new UnsupportedOperationException();
}
@Override
public void finishUpload(String id, MetaDataSet metaDataSet) throws IOException {
throw new UnsupportedOperationException();
}
@Override
public DataReference pollUploadForDataReference(String id) {
throw new UnsupportedOperationException();
}
}
/**
* Mock of a remote {@link RemotableFileDataService} based on the actual {@link RemotableFileDataServiceImpl} implementation.
*
* @author Robert Mischke (I don't think so; copy/paste artifact? - misc_ro)
*/
private class MockRemoteFileDataService extends RemotableFileDataServiceImpl {
@Override
@AllowRemoteAccess
public DataReference newReferenceFromStream(InputStream inputStream, MetaDataSet metaDataSet)
throws AuthorizationException {
Set<BinaryReference> birefs = new HashSet<BinaryReference>();
birefs.add(new BinaryReference(UUID.randomUUID().toString(), CompressionFormat.GZIP, REVISION));
lastMockRemoteDataReference =
new DataReference(referenceID.toString(), mockRemoteInstanceSessionId.convertToInstanceNodeId(), birefs);
return lastMockRemoteDataReference;
}
@Override
public InputStream getStreamFromDataReference(DataReference dataReference, Boolean calledFromRemote)
throws AuthorizationException {
if (dataReference.equals(reference)) {
return is;
} else {
// TODO review: can this really only happen on programming errors? otherwise, it shouldn't be an RTE - misc_ro
throw new RuntimeException("DataReference is not equal: " + dataReference + " / " + reference);
}
}
}
/**
* Not reachable test implementation of the {@link RemotableFileDataService}. Used for remote upload testing.
*
* @author Doreen Seider
*/
private class MockUnreachableFileDataService implements RemotableFileDataService {
@Override
public InputStream getStreamFromDataReference(DataReference dataReference, Boolean calledFromRemote)
throws AuthorizationException {
throw new UndeclaredThrowableException(null);
}
@Override
public DataReference newReferenceFromStream(InputStream inputStream, MetaDataSet metaDataSet)
throws AuthorizationException {
throw new UndeclaredThrowableException(null);
}
@Override
public String initializeUpload() throws IOException {
throw new UndeclaredThrowableException(null);
}
@Override
public long appendToUpload(String id, byte[] data) throws IOException {
throw new UndeclaredThrowableException(null);
}
@Override
public void finishUpload(String id, MetaDataSet metaDataSet) throws IOException {
throw new UndeclaredThrowableException(null);
}
@Override
public DataReference pollUploadForDataReference(String id) {
throw new UndeclaredThrowableException(null);
}
@Override
public void deleteReference(String dataReference) throws AuthorizationException {
throw new UndeclaredThrowableException(null);
}
@Override
public DataReference uploadInSingleStep(byte[] data, MetaDataSet metaDataSet) throws IOException {
throw new UndeclaredThrowableException(null);
}
}
/**
* Test implementation of the {@link PlatformService}.
*
* @author Doreen Seider
* @author Robert Mischke
*/
private class MockPlatformService extends PlatformServiceDefaultStub {
@Override
public InstanceNodeSessionId getLocalInstanceNodeSessionId() {
return localInstanceSessionId;
}
@Override
public boolean matchesLocalInstance(ResolvableNodeId nodeId) {
return localInstanceSessionId.equals(nodeId);
}
}
/**
* Test implementation for simulating the situation where read() does not return a full buffer.
*
* @author Brigitte Boden
*/
private class MockInputStream extends ByteArrayInputStream {
private static final int CHUNK_SIZE = 256;
MockInputStream(byte[] buf) {
super(buf);
}
@Override
public int read(byte[] b) throws IOException {
if (b.length > CHUNK_SIZE) {
return super.read(b, 0, CHUNK_SIZE);
}
return super.read(b);
}
@Override
public synchronized int read(byte[] b, int off, int len) {
if (len > CHUNK_SIZE) {
return super.read(b, off, CHUNK_SIZE);
}
return super.read(b, off, len);
}
}
/**
* Helper method creating a {@link BundleContext} object which retrieves the given backend services.
*
* @param catalogBackend {@link MetaDataBackendService} to retrieve.
* @param dataBackend {@link DataBackend} to retrieve.
* @return the {@link BundleContext}.
*/
public static BundleContext createBundleContext(MetaDataBackendService catalogBackend, DataBackend dataBackend) {
BundleContext bundleContext = EasyMock.createNiceMock(BundleContext.class);
Bundle bundleMock = EasyMock.createNiceMock(Bundle.class);
EasyMock.expect(bundleMock.getSymbolicName()).andReturn("huebscherName").anyTimes();
EasyMock.replay(bundleMock);
EasyMock.expect(bundleContext.getBundle()).andReturn(bundleMock).anyTimes();
// catalog backend
String catalogFilterString = OPEN_BRACKET + MetaDataBackendService.PROVIDER + EQUALS_SIGN
+ catalogBackendProvider + CLOSE_BRACKET;
ServiceReference catalogServiceRef = EasyMock.createNiceMock(ServiceReference.class);
ServiceReference[] catalogServiceRefs = { catalogServiceRef };
try {
EasyMock.expect(bundleContext.getServiceReferences(MetaDataBackendService.class.getName(), catalogFilterString))
.andReturn(catalogServiceRefs).anyTimes();
} catch (InvalidSyntaxException e) {
throw new RuntimeException(e);
}
EasyMock.expect(bundleContext.getService(catalogServiceRef)).andReturn(catalogBackend).anyTimes();
// data backend
String dataFilterStringForScheme = OPEN_BRACKET + DataBackend.PROVIDER + EQUALS_SIGN + fileBackendProvider + CLOSE_BRACKET;
String dataFilterStringForProvider = OPEN_BRACKET + DataBackend.PROVIDER + EQUALS_SIGN + xmlBackendProvider + CLOSE_BRACKET;
String dataSchemefilterString = OPEN_BRACKET + DataBackend.SCHEME + EQUALS_SIGN + dataScheme + CLOSE_BRACKET;
ServiceReference dataServiceRef = EasyMock.createNiceMock(ServiceReference.class);
ServiceReference[] dataServiceRefs = { dataServiceRef };
try {
EasyMock.expect(bundleContext.getServiceReferences(DataBackend.class.getName(), dataFilterStringForProvider))
.andReturn(dataServiceRefs).anyTimes();
EasyMock.expect(bundleContext.getServiceReferences(DataBackend.class.getName(), dataFilterStringForScheme))
.andReturn(dataServiceRefs).anyTimes();
EasyMock.expect(bundleContext.getServiceReferences(DataBackend.class.getName(), dataSchemefilterString))
.andReturn(dataServiceRefs).anyTimes();
} catch (InvalidSyntaxException e) {
throw new RuntimeException(e);
}
EasyMock.expect(bundleContext.getService((ServiceReference) dataServiceRef)).andReturn(dataBackend).anyTimes();
// for failures
EasyMock.expect(bundleContext.getServiceReference((String) null)).andReturn(null).anyTimes();
EasyMock.replay(bundleContext);
return bundleContext;
}
/**
* Test implementation of {@link ConfigurationService}.
*
* @author Doreen Seider
*/
private class DummyConfigurationService extends MockConfigurationService.ThrowExceptionByDefault {
@SuppressWarnings("unchecked")
@Override
public <T> T getConfiguration(String identifier, Class<T> clazz) {
DataManagementConfiguration config = new DataManagementConfiguration();
config.setMetaDataBackend(catalogBackendProvider);
config.setFileDataBackend(fileBackendProvider);
return (T) config;
}
}
}