NCEAS / ezid

Implementation of the EZID identifier services in a Java library
Other
4 stars 4 forks source link

Clients can't catch exceptions when they call some methods in the EZIDClient class which run in different threads #17

Open taojing2002 opened 10 months ago

taojing2002 commented 10 months ago

In the EZIDClient class, methods such as, create, createOrUpdate, setMetadata, and delete run on a different thread. When errors happen in another thread, it only logs in a file. So this cause clients can't capture the exception. Here is a ticket to describe the issue in Metacat: https://github.com/NCEAS/metacat/issues/1545

We need provide a mechanism for client to catch the exceptions even though we still keep the multi-thread feature.

taojing2002 commented 10 months ago

Here is my proposal. First, change the class of EZIDServiceRequest to implement the Callable interface rather than Runnable interface. The call method in the Callable interface can throw an exception but the run method in the Runnable interface can't. So the method can be:

public String call() throws EZIDException {
    String result = "success" ;
    .......
    return result;
}

The EZIDException will not be swallowed by the call method. Currently, we just log the error message in the run method if the exception raises.

In order for the client to get the thrown exception, we need give the client a Future object. Here is our current implementation of the setMetadata method in the EZIDClient class:

public void setMetadata(String identifier, HashMap<String, String> metadata) throws InterruptedException {
        EZIDServiceRequest request = new EZIDServiceRequest(ezid, EZIDServiceRequest.SETMETADATA, identifier, metadata);
        executor.execute(request);
}

Here is the new implementation and it returns a Future object:

public Future<String> setMetadata(String identifier, HashMap<String, String> metadata) throws InterruptedException {
        EZIDServiceRequest request = new EZIDServiceRequest(ezid, EZIDServiceRequest.SETMETADATA, identifier, metadata);
        return executor.submit(request);
}

When the client calls the setMetadata method, it has a Future object and it can check if an exception happened in the other thread like this:

Future future = ezid.setMetadata(id, metadata);
try {
       future.get()); 
} catch (ExecutionException ex) {
       relogin();
}

The EzidException thrown by the call method will be wrapped by the ExecutionException when we call future.get(). So the client can catch the exception and re-login.