/** * Verify that the protocols are compatible, switch to a different protocol version, if we need * to. */ private Protocol checkProtocol(Protocol protocol) throws IOException, ProtocolException { ClientVersion clientVersion = protocol.read(namedChannel.getChannel(), Protocol.ClientVersion.class); clientId = clientVersion.getNodeId(); FeederManager.Lease lease = feederManager.leases.get(clientId); if (lease != null) { dbBackup = lease.terminate(); } feederManager.feeders.put(clientId, this); if (clientVersion.getVersion() != protocol.getVersion()) { String message = "Client requested protocol version: " + clientVersion.getVersion() + " but the server version is " + protocol.getVersion(); /* * Simply log the difference on the server, it's up to the client * to reject the protocol version, if it can't accommodate it. */ LoggerUtils.warning(logger, feederManager.getEnvImpl(), message); } protocol.write(protocol.new ServerVersion(), namedChannel); /* In future we may switch protocol versions to accommodate the client. * For now, simply return the one and only version. */ return protocol; }
/** * Processes the request for the list of files that constitute a valid backup. If a leased * DbBackup instance is available, it uses it, otherwise it creates a new instance and uses it * instead. */ private void sendFileList(Protocol protocol) throws IOException, ProtocolException, DatabaseException { /* Wait for the request message. */ protocol.read(namedChannel.getChannel(), Protocol.FileListReq.class); if (dbBackup == null) { dbBackup = new DbBackup(feederManager.getEnvImpl()); dbBackup.startBackup(); } else { feederManager.leaseRenewalCount++; } /* * Remove the subdirectory header of the log files, because the nodes * that need to copy those log files may not configure the spreading * log files into sub directories feature. */ String[] files = dbBackup.getLogFilesInBackupSet(); for (int i = 0; i < files.length; i++) { if (files[i].contains(File.separator)) { files[i] = files[i].substring(files[i].indexOf(File.separator) + 1, files[i].length()); } } protocol.write(protocol.new FileListResp(files), namedChannel); }
/** * Implements the message exchange used to determine whether this feeder is suitable for use the * client's backup needs. The feeder may be unsuitable if it's already busy, or it's not current * enough to service the client's needs. */ private void checkFeeder(Protocol protocol) throws IOException, DatabaseException { protocol.read(namedChannel.getChannel(), FeederInfoReq.class); int feeders = feederManager.getActiveFeederCount() - 1 /* Exclude this one */; VLSN rangeFirst = VLSN.NULL_VLSN; VLSN rangeLast = VLSN.NULL_VLSN; if (feederManager.getEnvImpl() instanceof RepImpl) { /* Include replication stream feeders as a load component. */ RepImpl repImpl = (RepImpl) feederManager.getEnvImpl(); feeders += repImpl.getRepNode().feederManager().activeReplicaCount(); VLSNRange range = repImpl.getVLSNIndex().getRange(); rangeFirst = range.getFirst(); rangeLast = range.getLast(); } protocol.write(protocol.new FeederInfoResp(feeders, rangeFirst, rangeLast), namedChannel); }
/** * 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; } }