Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added src/.DS_Store
Binary file not shown.
Binary file added src/main/.DS_Store
Binary file not shown.
Binary file added src/main/java/.DS_Store
Binary file not shown.
Binary file added src/main/java/edu/.DS_Store
Binary file not shown.
Binary file added src/main/java/edu/ucla/.DS_Store
Binary file not shown.
Binary file added src/main/java/edu/ucla/library/.DS_Store
Binary file not shown.
Binary file not shown.
9 changes: 8 additions & 1 deletion src/main/java/edu/ucla/library/avpairtree/CsvItem.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@

package edu.ucla.library.avpairtree;

import java.io.Serializable;

import org.csveed.annotations.CsvCell;
import org.csveed.annotations.CsvFile;
import org.csveed.annotations.CsvIgnore;
Expand All @@ -19,7 +21,7 @@
* The required metadata from the supplied CSV file.
*/
@CsvFile(quote = '"', separator = ',', escape = '"', mappingStrategy = ColumnNameMapper.class)
public class CsvItem {
public class CsvItem implements Serializable{

/**
* The CSV header column for the IIIF access URL. Note that this not used for deserialization; see
Expand Down Expand Up @@ -71,6 +73,11 @@ public class CsvItem {
@CsvIgnore
private static final String PROCESSING_RESULT = "Processed";

/**
* The <code>serialVersionUID</code> for CsvItem.
*/
private static final long serialVersionUID = -2436320678602312189L;

/**
* The property mapped to the item ARK column in the CSV file.
*/
Expand Down
159 changes: 159 additions & 0 deletions src/main/java/edu/ucla/library/avpairtree/JobsQueue.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
package edu.ucla.library.avpairtree;

import java.util.Set;

import info.freelibrary.util.Logger;
import info.freelibrary.util.LoggerFactory;

import io.vertx.core.Future;
import io.vertx.core.Promise;
import io.vertx.core.Vertx;

import io.vertx.core.shareddata.AsyncMap;

/**
* A class that adds and removes jobs from a shared map
*/
public class JobsQueue {
/**
* The watcher verticle's logger.
*/
private static final Logger LOGGER = LoggerFactory.getLogger(JobsQueue.class, MessageCodes.BUNDLE);

/**
* Name of shared map
*/
private static final String MY_JOBS_QUEUE = "jobs-queue";

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's fine if you like, but static field names don't need to start with "my" (only instance fields). Just FYI.

You could also change the visibility to public so that the test could directly reference it rather than redefine it 🤷🏻


/**
* Copy of VertX instance
*/
private final Vertx myVertx;

/**
* Accesses the jobs queue shared data map
*
* @param aVertx A Vert.x instance
*/
public JobsQueue(final Vertx aVertx) {
myVertx = aVertx;
}

/**
* Add job to jobs queue
*
* @param aItem A string of the map name
* @return A future completing task with the map result
*/
public Future<Void> addJobToQueue(final CsvItem aItem) {
return getSharedMap(MY_JOBS_QUEUE).compose(jobsMap -> {
return putJobInMap(jobsMap, aItem);
});
}

/**
* Remove jobs from job queue
*
* @param aArk A string of the map name
* @return A future completing task with the map result
*/
public Future<Void> removeJobInQueue(final String aArk) {
return getSharedMap(MY_JOBS_QUEUE).compose(map -> {
return getKeys(map).compose(jobs -> {
return deleteKeyInMap(map, jobs, aArk);
});
});
}

/**
* Retrieved shared local async data map.
*
* @param aMapName A string of the map name
* @return A future completing task with the map result
*/
private Future<AsyncMap<String, CsvItem>> getSharedMap(final String aMapName) {
final Promise<AsyncMap<String, CsvItem>> promise = Promise.promise();

myVertx.sharedData().<String, CsvItem>getLocalAsyncMap(aMapName, getMap -> {
if (getMap.succeeded()) {
final AsyncMap<String, CsvItem> jobsQueue = getMap.result();
promise.complete(jobsQueue);
} else {
LOGGER.error(MessageCodes.AVPT_027);
promise.fail(getMap.cause());
}
});

return promise.future();
}

/**
*
* @param aJobsQueue A string of the map name
* @param aItem A CsvItem
* @return A future completing task
*/
private Future<Void> putJobInMap(final AsyncMap<String, CsvItem> aJobsQueue, final CsvItem aItem) {
final Promise<Void> promise = Promise.promise();

aJobsQueue.put(aItem.getItemARK(), aItem, put -> {
if (put.succeeded()) {
promise.complete();
} else {
LOGGER.error(MessageCodes.AVPT_028);
promise.fail(put.cause());
}
});

return promise.future();
}

/**
*
* @param aJobsQueue A string of the map name
* @return A future completing task with the Jobs
*/
private Future<Set<String>> getKeys(final AsyncMap<String, CsvItem> aJobsQueue) {
final Promise<Set<String>> promise = Promise.promise();
aJobsQueue.keys(keyCheck -> {
if (keyCheck.succeeded()) {
final Set<String> jobs = keyCheck.result();
promise.complete(jobs);
} else {
LOGGER.error(MessageCodes.AVPT_029);
promise.fail(keyCheck.cause());
}
});

return promise.future();
}

/**
*
* @param aJobsQueue A string of the map name
* @param aJobs A set of the job being removed
* @param aArk A string of the ark being removed
* @return A future completing task
*/
private Future<Void> deleteKeyInMap(final AsyncMap<String, CsvItem> aJobsQueue,
final Set<String> aJobs, final String aArk) {

final Promise<Void> promise = Promise.promise();

if (aJobs.contains(aArk)) {
aJobsQueue.remove(aArk, deleteJob -> {
if (deleteJob.succeeded()) {
promise.complete();
} else {
LOGGER.error(MessageCodes.AVPT_031);
promise.fail(deleteJob.cause());
}
});
} else {
LOGGER.error(MessageCodes.AVPT_030);
promise.fail(MessageCodes.AVPT_030);
}

return promise.future();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ private void configureServer(final JsonObject aConfig, final Promise<Void> aProm

// Associate handlers with operation IDs from the application's OpenAPI specification
routerBuilder.operation(Op.GET_STATUS).handler(new StatusHandler(getVertx()));
//add for JobsStatusHandler

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assuming this is a "to-do", it would be good to explicitly mark it as such by prefixing with TODO (you may have noticed that many IDEs automatically aggregate such comments in a special "problems" area of their UI).


// Create the application server
myServer = vertx.createHttpServer(serverOptions).requestHandler(routerBuilder.createRouter());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import edu.ucla.library.avpairtree.CsvItem;
import edu.ucla.library.avpairtree.MessageCodes;
import edu.ucla.library.avpairtree.Op;
import edu.ucla.library.avpairtree.JobsQueue;

import io.vertx.core.AbstractVerticle;
import io.vertx.core.CompositeFuture;
Expand All @@ -57,10 +58,16 @@ public class WatcherVerticle extends AbstractVerticle {
*/
private static final String SUBSTITUTION_PATTERN = "{}";

/**
* JobsQueue
*/
private JobsQueue myJobsQueue;

@Override
public void start(final Promise<Void> aPromise) {
final DeliveryOptions options = new DeliveryOptions().setSendTimeout(Integer.MAX_VALUE);
final Vertx vertx = getVertx();
myJobsQueue = new JobsQueue(vertx);
final EventBus eventBus = vertx.eventBus();

// Consume messages containing a path location to an uploaded CSV file
Expand Down Expand Up @@ -100,6 +107,8 @@ public void start(final Promise<Void> aPromise) {
.filter(result -> result.body().getClass().equals(CsvItem.class)).map(msg -> {
final CsvItem item = (CsvItem) msg.body();

myJobsQueue.addJobToQueue(item);

Comment on lines +110 to +111

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this statement is misplaced. Here, it will be executed when all of the tasks in the futures List succeed (i.e., after all the jobs have finished). IMO, it should be at the end of the lambda passed to reader.readBeans().forEach(...) above.

LOGGER.info(MessageCodes.AVPT_009, item.getItemARK());
return item;
}).collect(Collectors.toMap(CsvItem::getItemARK, item -> item));
Expand Down Expand Up @@ -204,6 +213,9 @@ private Future<String> updateCSV(final String aCsvFilePath, final Map<String, Cs
if (accessUrlIndex == index) {
if (aCsvItemMap.containsKey(ark)) {
row[index] = constructAccessURL(csvItem);

myJobsQueue.removeJobInQueue(ark);

Comment on lines +216 to +218

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wondering if this one is also misplaced. Is there a reason not to run this for each record (i.e., unconditionally in each loop iteration)?

} else if (originalAccessUrlIndex != -1) {
// Don't overwrite what was already there (e.g. in the case of images)
row[index] = originalRow.get(index + 1);
Expand Down
8 changes: 8 additions & 0 deletions src/main/resources/av-pairtree_messages.xml
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,13 @@
<entry key="AVPT_024">Must provide an object key for the data</entry>
<entry key="AVPT_025">The environment variable AWS_ENDPOINT_URL must be set</entry>
<entry key="AVPT_026">Configuring S3 bucket: {} [region: {}]</entry>
<entry key="AVPT_027">Failure to retrieve shared map</entry>
<entry key="AVPT_028">Put job in Shared Map failed</entry>
<entry key="AVPT_029">Failure to retrieve keys from Sharedmap</entry>
<entry key="AVPT_030">Job was not found in sharedMap</entry>
<entry key="AVPT_031">Failure to remove job from sharedmap</entry>
Comment on lines +35 to +39

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be good to pick one way of writing shared map (space vs. no space, casing) so that e.g. it's predictable for someone searching through the logs for these messages.





</properties>
Binary file added src/main/tools/.DS_Store
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,10 @@ public class CsvItemTest {
*/
@Test
public void testSetPathRoot() {
assertEquals(PATH_ROOT, new CsvItem().setPathRoot(SOUL_WAV).getPathRoot());
final CsvItem csvItem = new CsvItem();

csvItem.setPathRoot(PATH_ROOT);
assertEquals(PATH_ROOT, csvItem.getPathRoot());
Comment on lines -32 to +35

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why the change in argument passed to setPathRoot?

}

/**
Expand Down
Loading