package org.dcache.missingfiles; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Required; import java.util.UUID; import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; import dmg.cells.nucleus.Reply; import dmg.cells.nucleus.CellMessageReceiver; import org.dcache.cells.MessageReply; import org.dcache.missingfiles.plugins.Plugin; import org.dcache.missingfiles.plugins.PluginChain; import org.dcache.missingfiles.plugins.PluginVisitor; import org.dcache.missingfiles.plugins.Result; /** * Main entry point for missing file notification */ public class MissingFileHandler implements CellMessageReceiver { private static final Logger _log = LoggerFactory.getLogger(RemoteMissingFileStrategy.class); private ExecutorService _executor; private PluginChain _chain; public Reply messageArrived(MissingFileMessage message) { _log.debug("Received notice {} {}", message.getRequestedPath(), message.getInternalPath()); MessageReply<MissingFileMessage> reply = new MessageReply<>(); Request request = new Request(message, reply); _executor.submit(request); return reply; } @Required public void setPluginChain(PluginChain chain) { _chain = chain; } @Required public void setExecutorService(ExecutorService service) { _executor = service; } private static Action actionFor(Result result) { switch(result) { case FAIL: return Action.FAIL; case RETRY: return Action.RETRY; default: throw new IllegalArgumentException("No Action for Result." + result); } } private static boolean isTerminalResult(Result result) { switch(result) { case FAIL: case RETRY: return true; default: return false; } } /** * Class that handles an individual request. This decouples the action * of processing a request from the message-processing thread. */ public class Request implements PluginVisitor, Runnable { private final MissingFileMessage _msg; private final MessageReply<MissingFileMessage> _reply; private final String _id = UUID.randomUUID().toString(); public Request(MissingFileMessage msg, MessageReply<MissingFileMessage> reply) { _msg = msg; _reply = reply; } public String getId() { return _id; } @Override public void run() { _chain.accept(this); // dropped off the end of the chain, so fail the request replyWith(Action.FAIL); } @Override public boolean visit(Plugin plugin) { Future<Result> future = plugin.accept(_msg.getSubject(), _msg.getRequestedPath(), _msg.getInternalPath()); Result result; try { result = future.get(); } catch (CancellationException e) { _log.debug("Operation was cancelled"); return false; } catch (InterruptedException e) { _log.debug("Interrupted while waiting for plugin result"); return false; } catch (ExecutionException e) { Throwable t = e.getCause(); _log.error("Plugin bug: " + t.getMessage(), t); return true; } if(isTerminalResult(result)) { Action action = actionFor(result); replyWith(action); return false; } return true; } private void replyWith(Action action) { _msg.setAction(action); _reply.reply(_msg); } } }