Java tutorial
/* * **************************************************************************** * Copyright 2014-2016 Spectra Logic Corporation. All Rights Reserved. * Licensed under the Apache License, Version 2.0 (the "License"). You may not use * this file except in compliance with the License. A copy of the License is located at * * http://www.apache.org/licenses/LICENSE-2.0 * * or in the "license" file accompanying this file. * This file 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. * **************************************************************************** */ package com.spectralogic.ds3client.helpers; import com.spectralogic.ds3client.Ds3Client; import com.spectralogic.ds3client.Ds3ClientImpl; import com.spectralogic.ds3client.commands.DeleteObjectRequest; import com.spectralogic.ds3client.integration.Util; import com.spectralogic.ds3client.integration.test.helpers.Ds3ClientShim; import com.spectralogic.ds3client.integration.test.helpers.TempStorageIds; import com.spectralogic.ds3client.integration.test.helpers.TempStorageUtil; import com.spectralogic.ds3client.models.ChecksumType; import com.spectralogic.ds3client.models.Contents; import com.spectralogic.ds3client.models.bulk.Ds3Object; import com.spectralogic.ds3client.utils.ResourceUtils; import com.spectralogic.ds3client.IntValue; import org.apache.commons.io.FileUtils; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Before; import org.junit.After; import org.junit.Test; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.net.URISyntaxException; 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.Map; import java.util.Set; import java.util.UUID; import static com.spectralogic.ds3client.integration.Util.deleteAllContents; public class JobImpl_Test { private static final Logger LOG = LoggerFactory.getLogger(JobImpl_Test.class); private static final Ds3Client client = Util.fromEnv(); private static final Ds3ClientHelpers HELPERS = Ds3ClientHelpers.wrap(client); private static final String BUCKET_NAME = "JobImpl_Test"; private static final String TEST_ENV_NAME = "JobImpl_Test"; private static TempStorageIds envStorageIds; private static UUID dataPolicyId; private static final String DIR_NAME = "largeFiles/"; private static final String[] FILE_NAMES = new String[] { "lesmis-copies.txt" }; private long bookSize = -1; private final List<String> bookTitles = new ArrayList<>(); private final List<Ds3Object> objects = new ArrayList<>(); private static Path dirPath; @BeforeClass public static void startup() throws Exception { dataPolicyId = TempStorageUtil.setupDataPolicy(TEST_ENV_NAME, false, ChecksumType.Type.MD5, client); envStorageIds = TempStorageUtil.setup(TEST_ENV_NAME, dataPolicyId, client); setupBucket(dataPolicyId); dirPath = ResourceUtils.loadFileResource(DIR_NAME); } @AfterClass public static void teardown() throws IOException { try { deleteAllContents(client, BUCKET_NAME); } finally { TempStorageUtil.teardown(TEST_ENV_NAME, envStorageIds, client); client.close(); } } @Before public void beforeRunningTestMethod() { try { for (final String book : FILE_NAMES) { final Path objPath = ResourceUtils.loadFileResource(DIR_NAME + book); bookSize = Files.size(objPath); final Ds3Object obj = new Ds3Object(book, bookSize); bookTitles.add(book); objects.add(obj); } } catch (final Throwable t) { fail("Error running beforeRunningTestMethod: " + t.getMessage()); } } @After public void afterRunningTestMethod() { bookTitles.clear(); objects.clear(); try { deleteBigFileFromBlackPearlBucket(); } catch (final Throwable t) { fail("Error deleting file from black pearl: " + t.getMessage()); } } private void deleteBigFileFromBlackPearlBucket() throws IOException { final Ds3ClientHelpers helpers = Ds3ClientHelpers.wrap(client); final Iterable<Contents> objects = helpers.listObjects(BUCKET_NAME); for (final Contents contents : objects) { if (contents.getKey().equals(FILE_NAMES[0])) { client.deleteObject(new DeleteObjectRequest(BUCKET_NAME, contents.getKey())); } } } /** * Creates the test bucket with the specified data policy to prevent cascading test failure * when there are multiple data policies */ private static void setupBucket(final UUID dataPolicy) { try { HELPERS.ensureBucketExists(BUCKET_NAME, dataPolicy); } catch (final Exception e) { LOG.error("Setting up test environment failed: " + e.getMessage()); } } /** * This test verifies that object completed callback handler are added to the correct set. * There are 2 sets: 1 for events we create to let us know when to close channels we have opened; * and 1 for events clients have registered. It's important that we fire the events that close * channels before calling client events, so that clients can rely on the channels having been * closed. * @throws IOException * @throws URISyntaxException * @throws NoSuchMethodException * @throws IllegalAccessException * @throws InvocationTargetException * @throws NoSuchFieldException * @throws ClassNotFoundException */ @Test public void testWriteObjectCompletionEventsPopulateCorrectJobPartTracker() throws IOException, URISyntaxException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, NoSuchFieldException, ClassNotFoundException { final IntValue intValue = new IntValue(); final ObjectCompletedListener objectCompletedListener = new ObjectCompletedListener() { private int numCompletedObjects = 0; @Override public void objectCompleted(final String name) { intValue.increment(); assertTrue(bookTitles.contains(name)); assertEquals(1, ++numCompletedObjects); } }; // This is used just to make sure the callback handler ends up in the right place. final DataTransferredListener dataTransferredListener = new DataTransferredListener() { @Override public void dataTransferred(final long size) { } }; final JobImplJobPartDecoratorPair jobImplJobPartDecoratorPair = createJobPartDecoratorAndEnsureObjectPartTrackersPopulated( objectCompletedListener, dataTransferredListener, AccessMode.ForWriting); // trigger the callback jobImplJobPartDecoratorPair.getJobPartTrackerDecorator().completePart(FILE_NAMES[0], new ObjectPart(0, bookSize)); assertEquals(1, intValue.getValue()); } private enum AccessMode { ForReading, ForWriting } private JobImplJobPartDecoratorPair createJobPartDecoratorAndEnsureObjectPartTrackersPopulated( final ObjectCompletedListener objectCompletedListener, final DataTransferredListener dataTransferredListener, final AccessMode accessMode) throws IOException, URISyntaxException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, NoSuchFieldException, ClassNotFoundException { final Ds3ClientShim ds3ClientShim = new Ds3ClientShim((Ds3ClientImpl) client); final int maxNumBlockAllocationRetries = 1; final int maxNumObjectTransferAttempts = 3; final Ds3ClientHelpers ds3ClientHelpers = Ds3ClientHelpers.wrap(ds3ClientShim, maxNumBlockAllocationRetries, maxNumObjectTransferAttempts); final Ds3ClientHelpers.Job job; if (accessMode == AccessMode.ForWriting) { job = ds3ClientHelpers.startWriteJob(BUCKET_NAME, objects); } else { job = ds3ClientHelpers.startReadJob(BUCKET_NAME, objects); } job.attachObjectCompletedListener(objectCompletedListener); job.attachDataTransferredListener(dataTransferredListener); return getJobImplJobPartDecoratorAndEnsureObjectPartTrackersPopulated(job); } private JobImplJobPartDecoratorPair getJobImplJobPartDecoratorAndEnsureObjectPartTrackersPopulated( final Ds3ClientHelpers.Job job) throws NoSuchFieldException, IllegalAccessException { // Check that the client has one callback registered final Field partTrackerField = job.getClass().getSuperclass().getDeclaredField("jobPartTracker"); partTrackerField.setAccessible(true); final JobImpl.JobPartTrackerDecorator jobPartTrackerDecorator = (JobImpl.JobPartTrackerDecorator) partTrackerField .get(job); final Field clientJobPartTrackerField = jobPartTrackerDecorator.getClass() .getDeclaredField("clientJobPartTracker"); clientJobPartTrackerField.setAccessible(true); final JobPartTrackerImpl clientJobPartTrackerImpl = (JobPartTrackerImpl) clientJobPartTrackerField .get(jobPartTrackerDecorator); final Field clientTackersField = clientJobPartTrackerImpl.getClass().getDeclaredField("trackers"); clientTackersField.setAccessible(true); final Map<String, ObjectPartTracker> clientTrackers = (Map<String, ObjectPartTracker>) clientTackersField .get(clientJobPartTrackerImpl); final ObjectPartTrackerImpl clientObjectPartTrackerImpl = (ObjectPartTrackerImpl) clientTrackers .get(FILE_NAMES[0]); final Field clientObjectCompletedListenersField = clientObjectPartTrackerImpl.getClass() .getDeclaredField("objectCompletedListeners"); clientObjectCompletedListenersField.setAccessible(true); final Set<ObjectCompletedListener> clientObjectCompletedListeners = (Set<ObjectCompletedListener>) clientObjectCompletedListenersField .get(clientObjectPartTrackerImpl); assertEquals(1, clientObjectCompletedListeners.size()); // Data transfer listeners final Field clientDataTransferListenersField = clientObjectPartTrackerImpl.getClass() .getDeclaredField("dataTransferredListeners"); clientDataTransferListenersField.setAccessible(true); final Set<DataTransferredListener> clientDataTransferredListeners = (Set<DataTransferredListener>) clientDataTransferListenersField .get(clientObjectPartTrackerImpl); assertEquals(1, clientDataTransferredListeners.size()); // Check that we have no internal callbacks registered. Registering internal callbacks doesn't happen until you // call transfer final Field internalJobPartTrackerField = jobPartTrackerDecorator.getClass() .getDeclaredField("internalJobPartTracker"); internalJobPartTrackerField.setAccessible(true); final JobPartTrackerImpl internalJobPartTrackerImpl = (JobPartTrackerImpl) internalJobPartTrackerField .get(jobPartTrackerDecorator); final Field internalTackersField = internalJobPartTrackerImpl.getClass().getDeclaredField("trackers"); internalTackersField.setAccessible(true); final Map<String, ObjectPartTracker> internalTrackers = (Map<String, ObjectPartTracker>) internalTackersField .get(internalJobPartTrackerImpl); final ObjectPartTrackerImpl internalObjectPartTrackerImpl = (ObjectPartTrackerImpl) internalTrackers .get(FILE_NAMES[0]); final Field internalObjectCompletedListenersField = internalObjectPartTrackerImpl.getClass() .getDeclaredField("objectCompletedListeners"); internalObjectCompletedListenersField.setAccessible(true); final Set<ObjectCompletedListener> internalObjectCompletedListeners = (Set<ObjectCompletedListener>) internalObjectCompletedListenersField .get(internalObjectPartTrackerImpl); assertEquals(0, internalObjectCompletedListeners.size()); // Data transfer listeners final Field internalDataTransferListenersField = internalObjectPartTrackerImpl.getClass() .getDeclaredField("dataTransferredListeners"); internalDataTransferListenersField.setAccessible(true); final Set<DataTransferredListener> internalDataTransferredListeners = (Set<DataTransferredListener>) internalDataTransferListenersField .get(internalObjectPartTrackerImpl); assertEquals(0, internalDataTransferredListeners.size()); return new JobImplJobPartDecoratorPair(jobPartTrackerDecorator, (JobImpl) job); } private final class JobImplJobPartDecoratorPair { private final JobImpl.JobPartTrackerDecorator jobPartTrackerDecorator; private final JobImpl job; private JobImplJobPartDecoratorPair(final JobImpl.JobPartTrackerDecorator jobPartTrackerDecorator, final JobImpl job) { this.jobPartTrackerDecorator = jobPartTrackerDecorator; this.job = job; } public JobImpl.JobPartTrackerDecorator getJobPartTrackerDecorator() { return jobPartTrackerDecorator; } public JobImpl getJob() { return job; } } /** * This test verifies that there is 1 object completed callback handler registered in the internal job part * tracker. This callback handler does not exist until you call transfer on the write job. * @throws NoSuchMethodException * @throws IOException * @throws ClassNotFoundException * @throws URISyntaxException * @throws IllegalAccessException * @throws InvocationTargetException * @throws NoSuchFieldException */ @Test public void testWriteObjectCompletionPopulatesInternalJobPartTrackers() throws NoSuchMethodException, IOException, ClassNotFoundException, URISyntaxException, IllegalAccessException, InvocationTargetException, NoSuchFieldException { final IntValue intValue = new IntValue(); final ObjectCompletedListener objectCompletedListener = new ObjectCompletedListener() { private int numCompletedObjects = 0; @Override public void objectCompleted(final String name) { intValue.increment(); assertTrue(bookTitles.contains(name)); assertEquals(1, ++numCompletedObjects); } }; final DataTransferredListener dataTransferredListener = new DataTransferredListener() { @Override public void dataTransferred(final long size) { } }; final JobImplJobPartDecoratorPair jobImplJobPartDecoratorPair = createJobPartDecoratorAndEnsureObjectPartTrackersPopulated( objectCompletedListener, dataTransferredListener, AccessMode.ForWriting); jobImplJobPartDecoratorPair.getJob().transfer(new FileObjectPutter(dirPath)); final Field partTrackerField = jobImplJobPartDecoratorPair.getJob().getClass().getSuperclass() .getDeclaredField("jobPartTracker"); partTrackerField.setAccessible(true); final JobImpl.JobPartTrackerDecorator jobPartTrackerDecorator = (JobImpl.JobPartTrackerDecorator) partTrackerField .get(jobImplJobPartDecoratorPair.getJob()); final Field internalJobPartTrackerField = jobPartTrackerDecorator.getClass() .getDeclaredField("internalJobPartTracker"); internalJobPartTrackerField.setAccessible(true); final JobPartTrackerImpl internalJobPartTrackerImpl = (JobPartTrackerImpl) internalJobPartTrackerField .get(jobPartTrackerDecorator); final Field internalTackersField = internalJobPartTrackerImpl.getClass().getDeclaredField("trackers"); internalTackersField.setAccessible(true); final Map<String, ObjectPartTracker> internalTrackers = (Map<String, ObjectPartTracker>) internalTackersField .get(internalJobPartTrackerImpl); final ObjectPartTrackerImpl internalObjectPartTrackerImpl = (ObjectPartTrackerImpl) internalTrackers .get(FILE_NAMES[0]); final Field internalObjectCompletedListenersField = internalObjectPartTrackerImpl.getClass() .getDeclaredField("objectCompletedListeners"); internalObjectCompletedListenersField.setAccessible(true); final Set<ObjectCompletedListener> internalObjectCompletedListeners = (Set<ObjectCompletedListener>) internalObjectCompletedListenersField .get(internalObjectPartTrackerImpl); assertEquals(1, internalObjectCompletedListeners.size()); } @Test public void testWriteObjectCompletionFiresInternalHandlersFirst() throws NoSuchMethodException, IOException, ClassNotFoundException, URISyntaxException, IllegalAccessException, InvocationTargetException, NoSuchFieldException { final IntValue intValue = new IntValue(); final ObjectCompletedListener objectCompletedListener = new ObjectCompletedListener() { private int numCompletedObjects = 0; @Override public void objectCompleted(final String name) { intValue.increment(); assertTrue(bookTitles.contains(name)); assertEquals(1, ++numCompletedObjects); } }; final DataTransferredListener dataTransferredListener = new DataTransferredListener() { @Override public void dataTransferred(final long size) { } }; final JobImplJobPartDecoratorPair jobImplJobPartDecoratorPair = createJobPartDecoratorAndEnsureObjectPartTrackersPopulated( objectCompletedListener, dataTransferredListener, AccessMode.ForWriting); final Path dirPath = ResourceUtils.loadFileResource(DIR_NAME); final JobPartDecoratorInterceptor jobPartDecoratorInterceptor = new JobPartDecoratorInterceptor( jobImplJobPartDecoratorPair); jobPartDecoratorInterceptor.transfer(new FileObjectPutter(dirPath)); assertTrue(jobPartDecoratorInterceptor.bothHandlersFired()); } private static final class JobPartDecoratorInterceptor implements JobPartTracker { private final JobImplJobPartDecoratorPair jobImplJobPartDecoratorPair; private final ObjectCompletedCallbackTracker objectCompletedCallbackTracker; private JobPartDecoratorInterceptor(final JobImplJobPartDecoratorPair jobImplJobPartDecoratorPair) throws NoSuchFieldException, IllegalAccessException { this.jobImplJobPartDecoratorPair = jobImplJobPartDecoratorPair; this.objectCompletedCallbackTracker = new ObjectCompletedCallbackTracker(); // Replace the job part trackers in JobPartTrackerDecorator with JobPartTrackerInterceptor final JobImpl.JobPartTrackerDecorator jobPartTrackerDecorator = jobImplJobPartDecoratorPair .getJobPartTrackerDecorator(); final Field internalJobPartTrackerField = jobPartTrackerDecorator.getClass() .getDeclaredField("internalJobPartTracker"); internalJobPartTrackerField.setAccessible(true); final JobPartTrackerInterceptor internalJobPartTrackerInterceptor = new JobPartTrackerInterceptor( (JobPartTracker) internalJobPartTrackerField.get(jobPartTrackerDecorator), objectCompletedCallbackTracker.getInternalObjectCompletedHandler()); internalJobPartTrackerField.set(jobPartTrackerDecorator, internalJobPartTrackerInterceptor); final Field clientJobPartTrackerField = jobPartTrackerDecorator.getClass() .getDeclaredField("clientJobPartTracker"); clientJobPartTrackerField.setAccessible(true); final JobPartTrackerInterceptor clientJobPartTrackerInterceptor = new JobPartTrackerInterceptor( (JobPartTracker) clientJobPartTrackerField.get(jobPartTrackerDecorator), objectCompletedCallbackTracker.getClientObjectCompletedHandler()); clientJobPartTrackerField.set(jobPartTrackerDecorator, clientJobPartTrackerInterceptor); } @Override public void completePart(final String key, final ObjectPart objectPart) { jobImplJobPartDecoratorPair.getJobPartTrackerDecorator().completePart(key, objectPart); } @Override public boolean containsPart(final String key, final ObjectPart objectPart) { return jobImplJobPartDecoratorPair.getJobPartTrackerDecorator().containsPart(key, objectPart); } @Override public JobPartTracker attachDataTransferredListener(final DataTransferredListener listener) { return jobImplJobPartDecoratorPair.getJobPartTrackerDecorator().attachDataTransferredListener(listener); } @Override public JobPartTracker attachObjectCompletedListener(final ObjectCompletedListener listener) { return jobImplJobPartDecoratorPair.getJobPartTrackerDecorator().attachObjectCompletedListener(listener); } @Override public void removeDataTransferredListener(final DataTransferredListener listener) { jobImplJobPartDecoratorPair.getJobPartTrackerDecorator().removeDataTransferredListener(listener); } @Override public void removeObjectCompletedListener(final ObjectCompletedListener listener) { jobImplJobPartDecoratorPair.getJobPartTrackerDecorator().removeObjectCompletedListener(listener); } private void transfer(final Ds3ClientHelpers.ObjectChannelBuilder channelBuilder) throws IOException { jobImplJobPartDecoratorPair.getJob().transfer(channelBuilder); } private boolean bothHandlersFired() { return objectCompletedCallbackTracker.bothHandlersFired(); } } private static class ObjectCompletedCallbackTracker { private final BooleanValue internalObjectCompletedHandlerCalled = new BooleanValue(); private final BooleanValue clientObjectCompletedHandlerCalled = new BooleanValue(); private final ObjectCompletedCallbackRecorder clientObjectCompletedHandler = new ObjectCompletedCallbackRecorder() { @Override public void onObjectCompletedCalled() { clientObjectCompletedHandlerCalled.setValue(true); if (!internalObjectCompletedHandlerCalled.getValue()) { throw new IllegalStateException( "Client object completed handler called before closing channels."); } } }; private final ObjectCompletedCallbackRecorder internalObjectCompletedHandler = new ObjectCompletedCallbackRecorder() { @Override public void onObjectCompletedCalled() { internalObjectCompletedHandlerCalled.setValue(true); if (clientObjectCompletedHandlerCalled.getValue()) { throw new IllegalStateException( "Client object completed handler called before closing channels."); } } }; public ObjectCompletedCallbackRecorder getClientObjectCompletedHandler() { return clientObjectCompletedHandler; } public ObjectCompletedCallbackRecorder getInternalObjectCompletedHandler() { return internalObjectCompletedHandler; } public boolean bothHandlersFired() { return internalObjectCompletedHandlerCalled.getValue() && clientObjectCompletedHandlerCalled.getValue(); } } private static class BooleanValue { private boolean value = false; private boolean getValue() { return this.value; } private boolean setValue(final boolean value) { this.value = value; return getValue(); } } private interface ObjectCompletedCallbackRecorder { void onObjectCompletedCalled(); } private static final class JobPartTrackerInterceptor implements JobPartTracker { private final JobPartTracker jobPartTracker; private final ObjectCompletedCallbackRecorder objectCompletedCallbackRecorder; private JobPartTrackerInterceptor(final JobPartTracker jobPartTracker, final ObjectCompletedCallbackRecorder objectCompletedCallbackRecorder) { this.jobPartTracker = jobPartTracker; this.objectCompletedCallbackRecorder = objectCompletedCallbackRecorder; } @Override public void completePart(final String key, final ObjectPart objectPart) { objectCompletedCallbackRecorder.onObjectCompletedCalled(); jobPartTracker.completePart(key, objectPart); } @Override public boolean containsPart(final String key, final ObjectPart objectPart) { return jobPartTracker.containsPart(key, objectPart); } @Override public JobPartTracker attachDataTransferredListener(final DataTransferredListener listener) { return jobPartTracker.attachDataTransferredListener(listener); } @Override public JobPartTracker attachObjectCompletedListener(final ObjectCompletedListener listener) { return jobPartTracker.attachObjectCompletedListener(listener); } @Override public void removeDataTransferredListener(final DataTransferredListener listener) { jobPartTracker.removeDataTransferredListener(listener); } @Override public void removeObjectCompletedListener(final ObjectCompletedListener listener) { jobPartTracker.removeObjectCompletedListener(listener); } } @Test public void testWriteObjectRemovesEventsFromCorrectPartTracker() throws IOException, URISyntaxException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, NoSuchFieldException, ClassNotFoundException { final IntValue intValue = new IntValue(); final ObjectCompletedListener objectCompletedListener = new ObjectCompletedListener() { private int numCompletedObjects = 0; @Override public void objectCompleted(final String name) { intValue.increment(); assertTrue(bookTitles.contains(name)); assertEquals(1, ++numCompletedObjects); } }; // This is used just to make sure the callback handler ends up in the right place. final DataTransferredListener dataTransferredListener = new DataTransferredListener() { @Override public void dataTransferred(final long size) { } }; final JobImplJobPartDecoratorPair jobImplJobPartDecoratorPair = createJobPartDecoratorAndEnsureObjectPartTrackersPopulated( objectCompletedListener, dataTransferredListener, AccessMode.ForWriting); jobImplJobPartDecoratorPair.getJob().removeObjectCompletedListener(objectCompletedListener); final JobImpl job = jobImplJobPartDecoratorPair.getJob(); final Field partTrackerField = job.getClass().getSuperclass().getDeclaredField("jobPartTracker"); partTrackerField.setAccessible(true); final JobImpl.JobPartTrackerDecorator jobPartTrackerDecorator = (JobImpl.JobPartTrackerDecorator) partTrackerField .get(job); final Field clientJobPartTrackerField = jobPartTrackerDecorator.getClass() .getDeclaredField("clientJobPartTracker"); clientJobPartTrackerField.setAccessible(true); final JobPartTrackerImpl clientJobPartTrackerImpl = (JobPartTrackerImpl) clientJobPartTrackerField .get(jobPartTrackerDecorator); final Field clientTackersField = clientJobPartTrackerImpl.getClass().getDeclaredField("trackers"); clientTackersField.setAccessible(true); final Map<String, ObjectPartTracker> clientTrackers = (Map<String, ObjectPartTracker>) clientTackersField .get(clientJobPartTrackerImpl); final ObjectPartTrackerImpl clientObjectPartTrackerImpl = (ObjectPartTrackerImpl) clientTrackers .get(FILE_NAMES[0]); final Field clientObjectCompletedListenersField = clientObjectPartTrackerImpl.getClass() .getDeclaredField("objectCompletedListeners"); clientObjectCompletedListenersField.setAccessible(true); final Set<ObjectCompletedListener> clientObjectCompletedListeners = (Set<ObjectCompletedListener>) clientObjectCompletedListenersField .get(clientObjectPartTrackerImpl); assertEquals(0, clientObjectCompletedListeners.size()); // Data transfer listeners job.removeDataTransferredListener(dataTransferredListener); final Field clientDataTransferListenersField = clientObjectPartTrackerImpl.getClass() .getDeclaredField("dataTransferredListeners"); clientDataTransferListenersField.setAccessible(true); final Set<DataTransferredListener> clientDataTransferredListeners = (Set<DataTransferredListener>) clientDataTransferListenersField .get(clientObjectPartTrackerImpl); assertEquals(0, clientDataTransferredListeners.size()); } @Test public void testReadObjectCompletionEventsPopulateCorrectJobPartTracker() throws IOException, URISyntaxException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, NoSuchFieldException, ClassNotFoundException { putBigFileIntoBlackPearl(); final IntValue intValue = new IntValue(); final ObjectCompletedListener objectCompletedListener = new ObjectCompletedListener() { private int numCompletedObjects = 0; @Override public void objectCompleted(final String name) { intValue.increment(); assertTrue(bookTitles.contains(name)); assertEquals(1, ++numCompletedObjects); } }; // This is used just to make sure the callback handler ends up in the right place. final DataTransferredListener dataTransferredListener = new DataTransferredListener() { @Override public void dataTransferred(final long size) { } }; final JobImplJobPartDecoratorPair jobImplJobPartDecoratorPair = createJobPartDecoratorAndEnsureObjectPartTrackersPopulated( objectCompletedListener, dataTransferredListener, AccessMode.ForReading); // trigger the callback jobImplJobPartDecoratorPair.getJobPartTrackerDecorator().completePart(FILE_NAMES[0], new ObjectPart(0, bookSize)); assertEquals(1, intValue.getValue()); } private void putBigFileIntoBlackPearl() throws IOException, URISyntaxException { final int maxNumBlockAllocationRetries = 1; final int maxNumObjectTransferAttempts = 3; final Ds3ClientHelpers ds3ClientHelpers = Ds3ClientHelpers.wrap(client, maxNumBlockAllocationRetries, maxNumObjectTransferAttempts); final Ds3ClientHelpers.Job writeJob = ds3ClientHelpers.startWriteJob(BUCKET_NAME, objects); writeJob.transfer(new FileObjectPutter(dirPath)); } @Test public void testReadObjectCompletionFiresInternalHandlersFirst() throws NoSuchMethodException, IOException, ClassNotFoundException, URISyntaxException, IllegalAccessException, InvocationTargetException, NoSuchFieldException { putBigFileIntoBlackPearl(); final String tempPathPrefix = null; final Path tempDirectory = Files.createTempDirectory(Paths.get("."), tempPathPrefix); final IntValue intValue = new IntValue(); final ObjectCompletedListener objectCompletedListener = new ObjectCompletedListener() { private int numCompletedObjects = 0; @Override public void objectCompleted(final String name) { intValue.increment(); assertTrue(bookTitles.contains(name)); assertEquals(1, ++numCompletedObjects); } }; final DataTransferredListener dataTransferredListener = new DataTransferredListener() { @Override public void dataTransferred(final long size) { } }; try { final JobImplJobPartDecoratorPair jobImplJobPartDecoratorPair = createJobPartDecoratorAndEnsureObjectPartTrackersPopulated( objectCompletedListener, dataTransferredListener, AccessMode.ForReading); final JobPartDecoratorInterceptor jobPartDecoratorInterceptor = new JobPartDecoratorInterceptor( jobImplJobPartDecoratorPair); jobPartDecoratorInterceptor.transfer(new FileObjectGetter(tempDirectory)); assertTrue(jobPartDecoratorInterceptor.bothHandlersFired()); } finally { FileUtils.deleteDirectory(tempDirectory.toFile()); } } }