/* * 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; } } }