/** The main driver loop that enforces the protocol message sequence and implements it. */ @Override public void run() { /* The initial protocol */ Protocol protocol = new Protocol(feederManager.nameIdPair, Protocol.VERSION, feederManager.getEnvImpl()); try { configureChannel(); protocol = checkProtocol(protocol); checkFeeder(protocol); sendFileList(protocol); sendRequestedFiles(protocol); /* Done, cleanup */ dbBackup.endBackup(); dbBackup = null; } catch (ClosedByInterruptException e) { LoggerUtils.fine( logger, feederManager.getEnvImpl(), "Ignoring ClosedByInterruptException normal shutdown"); } catch (IOException e) { LoggerUtils.warning(logger, feederManager.getEnvImpl(), " IO Exception: " + e.getMessage()); } catch (ProtocolException e) { LoggerUtils.severe( logger, feederManager.getEnvImpl(), " Protocol Exception: " + e.getMessage()); } catch (Exception e) { throw new EnvironmentFailureException( feederManager.getEnvImpl(), EnvironmentFailureReason.UNCAUGHT_EXCEPTION, e); } finally { try { namedChannel.getChannel().close(); } catch (IOException e) { LoggerUtils.warning( logger, feederManager.getEnvImpl(), "Log File feeder io exception on " + "channel close: " + e.getMessage()); } shutdown(); if (dbBackup != null) { if (feederManager.shutdown.get()) { dbBackup.endBackup(); } else { /* * Establish lease so client can resume within the lease * period. */ feederManager.new Lease(clientId, feederManager.leaseDuration, dbBackup); LoggerUtils.info( logger, feederManager.getEnvImpl(), "Lease created for node: " + clientId); } } LoggerUtils.info( logger, feederManager.getEnvImpl(), "Log file feeder for client: " + clientId + " exited"); } }
/** * Send files in response to request messages. The request sequence looks like the following: * * <p>[FileReq | StatReq]+ Done * * <p>The response sequence to a FileReq looks like: * * <p>FileStart <file byte stream> FileEnd * * <p>and that for a StatReq, is simply a StatResp */ private void sendRequestedFiles(Protocol protocol) throws IOException, ProtocolException, DatabaseException { try { while (true) { FileReq fileReq = protocol.read(namedChannel.getChannel(), FileReq.class); final String fileName = fileReq.getFileName(); /* * Calculate the full path for a specified log file name, * especially when this Feeder is configured to run with sub * directories. */ FileManager fMgr = feederManager.getEnvImpl().getFileManager(); File file = new File(fMgr.getFullFileName(fileName)); if (!file.exists()) { throw EnvironmentFailureException.unexpectedState("Log file not found: " + fileName); } /* Freeze the length and last modified date. */ final long length = file.length(); final long lastModified = file.lastModified(); byte digest[] = null; FileInfoResp resp = null; Protocol.FileInfoResp cachedResp = feederManager.statResponses.get(fileName); byte cachedDigest[] = ((cachedResp != null) && (cachedResp.getFileLength() == length) && (cachedResp.getLastModifiedTime() == lastModified)) ? cachedResp.getDigestSHA1() : null; if (fileReq instanceof FileInfoReq) { if (cachedDigest != null) { digest = cachedDigest; } else if (((FileInfoReq) fileReq).getNeedSHA1()) { digest = getSHA1Digest(file, length).digest(); } else { // Digest not requested digest = new byte[0]; } resp = protocol.new FileInfoResp(fileName, length, lastModified, digest); } else { protocol.write(protocol.new FileStart(fileName, length, lastModified), namedChannel); digest = sendFileContents(file, length); if ((cachedDigest != null) && !Arrays.equals(cachedDigest, digest)) { throw EnvironmentFailureException.unexpectedState( "Inconsistent cached and computed digests"); } resp = protocol.new FileEnd(fileName, length, lastModified, digest); } /* Cache for subsequent requests, if it was computed. */ if (digest.length > 0) { feederManager.statResponses.put(fileName, resp); } protocol.write(resp, namedChannel); } } catch (ProtocolException pe) { if (pe.getUnexpectedMessage() instanceof Protocol.Done) { return; } throw pe; } }