/** * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.solr.handler; import org.apache.lucene.index.IndexCommit; import org.apache.lucene.index.IndexDeletionPolicy; import org.apache.lucene.index.IndexReader; import org.apache.solr.common.params.CommonParams; import org.apache.solr.common.params.ModifiableSolrParams; import org.apache.solr.common.params.SolrParams; import org.apache.solr.common.util.FastOutputStream; import org.apache.solr.common.util.NamedList; import org.apache.solr.common.util.SimpleOrderedMap; import org.apache.solr.common.util.StrUtils; import org.apache.solr.core.*; import org.apache.solr.request.SolrQueryRequest; import org.apache.solr.response.BinaryQueryResponseWriter; import org.apache.solr.response.SolrQueryResponse; import org.apache.solr.search.SolrIndexSearcher; import org.apache.solr.update.DirectUpdateHandler2; import org.apache.solr.util.RefCounted; import org.apache.solr.util.plugin.SolrCoreAware; import org.apache.commons.io.IOUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.*; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.text.NumberFormat; import java.util.*; import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.atomic.AtomicBoolean; import java.util.zip.Adler32; import java.util.zip.Checksum; import java.util.zip.DeflaterOutputStream; /** * <p> A Handler which provides a REST API for replication and serves replication requests from Slaves. <p/> </p> * <p>When running on the master, it provides the following commands <ol> <li>Get the current replicatable index version * (command=indexversion)</li> <li>Get the list of files for a given index version * (command=filelist&indexversion=<VERSION>)</li> <li>Get full or a part (chunk) of a given index or a config * file (command=filecontent&file=<FILE_NAME>) You can optionally specify an offset and length to get that * chunk of the file. You can request a configuration file by using "cf" parameter instead of the "file" parameter.</li> * <li>Get status/statistics (command=details)</li> </ol> </p> <p>When running on the slave, it provides the following * commands <ol> <li>Perform a snap pull now (command=snappull)</li> <li>Get status/statistics (command=details)</li> * <li>Abort a snap pull (command=abort)</li> <li>Enable/Disable polling the master for new versions (command=enablepoll * or command=disablepoll)</li> </ol> </p> * * @version $Id: ReplicationHandler.java 1203003 2011-11-17 01:50:33Z hossman $ * @since solr 1.4 */ public class ReplicationHandler extends RequestHandlerBase implements SolrCoreAware{ @Override public void inform(SolrCore core) { // TODO Auto-generated method stub } @Override public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception { // TODO Auto-generated method stub } @Override public String getDescription() { // TODO Auto-generated method stub return null; } @Override public String getSourceId() { // TODO Auto-generated method stub return null; } @Override public String getSource() { // TODO Auto-generated method stub return null; } @Override public String getVersion() { // TODO Auto-generated method stub return null; }} // //public class ReplicationHandler extends RequestHandlerBase implements SolrCoreAware { // private static final Logger LOG = LoggerFactory.getLogger(ReplicationHandler.class.getName()); // SolrCore core; // // private SnapPuller snapPuller; // // private ReentrantLock snapPullLock = new ReentrantLock(); // // private String includeConfFiles; // // private NamedList<String> confFileNameAlias = new NamedList<String>(); // // private boolean isMaster = false; // // private boolean isSlave = false; // // private boolean replicateOnOptimize = false; // // private boolean replicateOnCommit = false; // // private boolean replicateOnStart = false; // // private int numTimesReplicated = 0; // // private final Map<String, FileInfo> confFileInfoCache = new HashMap<String, FileInfo>(); // // private Integer reserveCommitDuration = SnapPuller.readInterval("00:00:10"); // // private volatile IndexCommit indexCommitPoint; // // volatile NamedList snapShootDetails; // // private AtomicBoolean replicationEnabled = new AtomicBoolean(true); // // @Override // public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception { // rsp.setHttpCaching(false); // final SolrParams solrParams = req.getParams(); // String command = solrParams.get(COMMAND); // if (command == null) { // rsp.add(STATUS, OK_STATUS); // rsp.add("message", "No command"); // return; // } // // This command does not give the current index version of the master // // It gives the current 'replicateable' index version // if (command.equals(CMD_INDEX_VERSION)) { // IndexCommit commitPoint = indexCommitPoint; // make a copy so it won't change // if (commitPoint != null && replicationEnabled.get()) { // // // // There is a race condition here. The commit point may be changed / deleted by the time // // we get around to reserving it. This is a very small window though, and should not result // // in a catastrophic failure, but will result in the client getting an empty file list for // // the CMD_GET_FILE_LIST command. // // // core.getDeletionPolicy().setReserveDuration(commitPoint.getVersion(), reserveCommitDuration); // rsp.add(CMD_INDEX_VERSION, commitPoint.getVersion()); // rsp.add(GENERATION, commitPoint.getGeneration()); // } else { // // This happens when replication is not configured to happen after startup and no commit/optimize // // has happened yet. // rsp.add(CMD_INDEX_VERSION, 0L); // rsp.add(GENERATION, 0L); // } // } else if (command.equals(CMD_GET_FILE)) { // getFileStream(solrParams, rsp); // } else if (command.equals(CMD_GET_FILE_LIST)) { // getFileList(solrParams, rsp); // } else if (command.equalsIgnoreCase(CMD_BACKUP)) { // doSnapShoot(new ModifiableSolrParams(solrParams), rsp,req); // rsp.add(STATUS, OK_STATUS); // } else if (command.equalsIgnoreCase(CMD_FETCH_INDEX)) { // String masterUrl = solrParams.get(MASTER_URL); // if (!isSlave && masterUrl == null) { // rsp.add(STATUS,ERR_STATUS); // rsp.add("message","No slave configured or no 'masterUrl' Specified"); // return; // } // final SolrParams paramsCopy = new ModifiableSolrParams(solrParams); // new Thread() { // @Override // public void run() { // doFetch(paramsCopy); // } // }.start(); // rsp.add(STATUS, OK_STATUS); // } else if (command.equalsIgnoreCase(CMD_DISABLE_POLL)) { // if (snapPuller != null){ // snapPuller.disablePoll(); // rsp.add(STATUS, OK_STATUS); // } else { // rsp.add(STATUS, ERR_STATUS); // rsp.add("message","No slave configured"); // } // } else if (command.equalsIgnoreCase(CMD_ENABLE_POLL)) { // if (snapPuller != null){ // snapPuller.enablePoll(); // rsp.add(STATUS, OK_STATUS); // }else { // rsp.add(STATUS,ERR_STATUS); // rsp.add("message","No slave configured"); // } // } else if (command.equalsIgnoreCase(CMD_ABORT_FETCH)) { // SnapPuller temp = tempSnapPuller; // if (temp != null){ // temp.abortPull(); // rsp.add(STATUS, OK_STATUS); // } else { // rsp.add(STATUS,ERR_STATUS); // rsp.add("message","No slave configured"); // } // } else if (command.equals(CMD_FILE_CHECKSUM)) { // // this command is not used by anyone // getFileChecksum(solrParams, rsp); // } else if (command.equals(CMD_SHOW_COMMITS)) { // rsp.add(CMD_SHOW_COMMITS, getCommits()); // } else if (command.equals(CMD_DETAILS)) { // rsp.add(CMD_DETAILS, getReplicationDetails(solrParams.getBool("slave",true))); // RequestHandlerUtils.addExperimentalFormatWarning(rsp); // } else if (CMD_ENABLE_REPL.equalsIgnoreCase(command)) { // replicationEnabled.set(true); // rsp.add(STATUS, OK_STATUS); // } else if (CMD_DISABLE_REPL.equalsIgnoreCase(command)) { // replicationEnabled.set(false); // rsp.add(STATUS, OK_STATUS); // } // } // // private List<NamedList> getCommits() { // Map<Long, IndexCommit> commits = core.getDeletionPolicy().getCommits(); // List<NamedList> l = new ArrayList<NamedList>(); // // for (IndexCommit c : commits.values()) { // try { // NamedList nl = new NamedList(); // nl.add("indexVersion", c.getVersion()); // nl.add(GENERATION, c.getGeneration()); // nl.add(CMD_GET_FILE_LIST, c.getFileNames()); // l.add(nl); // } catch (IOException e) { // LOG.warn("Exception while reading files for commit " + c, e); // } // } // return l; // } // // /** // * Gets the checksum of a file // */ // private void getFileChecksum(SolrParams solrParams, SolrQueryResponse rsp) { // Checksum checksum = new Adler32(); // File dir = new File(core.getIndexDir()); // rsp.add(CHECKSUM, getCheckSums(solrParams.getParams(FILE), dir, checksum)); // dir = new File(core.getResourceLoader().getConfigDir()); // rsp.add(CONF_CHECKSUM, getCheckSums(solrParams.getParams(CONF_FILE_SHORT), dir, checksum)); // } // // private Map<String, Long> getCheckSums(String[] files, File dir, Checksum checksum) { // Map<String, Long> checksumMap = new HashMap<String, Long>(); // if (files == null || files.length == 0) // return checksumMap; // for (String file : files) { // File f = new File(dir, file); // Long checkSumVal = getCheckSum(checksum, f); // if (checkSumVal != null) // checksumMap.put(file, checkSumVal); // } // return checksumMap; // } // // static Long getCheckSum(Checksum checksum, File f) { // FileInputStream fis = null; // checksum.reset(); // byte[] buffer = new byte[1024 * 1024]; // int bytesRead; // try { // fis = new FileInputStream(f); // while ((bytesRead = fis.read(buffer)) >= 0) // checksum.update(buffer, 0, bytesRead); // return checksum.getValue(); // } catch (Exception e) { // LOG.warn("Exception in finding checksum of " + f, e); // } finally { // IOUtils.closeQuietly(fis); // } // return null; // } // // private volatile SnapPuller tempSnapPuller; // // void doFetch(SolrParams solrParams) { // String masterUrl = solrParams == null ? null : solrParams.get(MASTER_URL); // if (!snapPullLock.tryLock()) // return; // try { // tempSnapPuller = snapPuller; // if (masterUrl != null) { // NamedList<Object> nl = solrParams.toNamedList(); // nl.remove(SnapPuller.POLL_INTERVAL); // tempSnapPuller = new SnapPuller(nl, this, core); // } // tempSnapPuller.fetchLatestIndex(core); // } catch (Exception e) { // LOG.error("SnapPull failed ", e); // } finally { // tempSnapPuller = snapPuller; // snapPullLock.unlock(); // } // } // // boolean isReplicating() { // return snapPullLock.isLocked(); // } // // private void doSnapShoot(SolrParams params, SolrQueryResponse rsp, SolrQueryRequest req) { // try { // int numberToKeep = params.getInt(NUMBER_BACKUPS_TO_KEEP, Integer.MAX_VALUE); // IndexDeletionPolicyWrapper delPolicy = core.getDeletionPolicy(); // IndexCommit indexCommit = delPolicy.getLatestCommit(); // // if(indexCommit == null) { // indexCommit = req.getSearcher().getReader().getIndexCommit(); // } // // // small race here before the commit point is saved // new SnapShooter(core, params.get("location")).createSnapAsync(indexCommit, numberToKeep, this); // // } catch (Exception e) { // LOG.warn("Exception during creating a snapshot", e); // rsp.add("exception", e); // } // } // // /** // * This method adds an Object of FileStream to the resposnse . The FileStream implements a custom protocol which is // * understood by SnapPuller.FileFetcher // * // * @see org.apache.solr.handler.SnapPuller.FileFetcher // */ // private void getFileStream(SolrParams solrParams, SolrQueryResponse rsp) { // ModifiableSolrParams rawParams = new ModifiableSolrParams(solrParams); // rawParams.set(CommonParams.WT, FILE_STREAM); // rsp.add(FILE_STREAM, new FileStream(solrParams)); // } // // @SuppressWarnings("unchecked") // private void getFileList(SolrParams solrParams, SolrQueryResponse rsp) { // String v = solrParams.get(CMD_INDEX_VERSION); // if (v == null) { // rsp.add("status", "no indexversion specified"); // return; // } // long version = Long.parseLong(v); // IndexCommit commit = core.getDeletionPolicy().getCommitPoint(version); // if (commit == null) { // rsp.add("status", "invalid indexversion"); // return; // } // // reserve the indexcommit for sometime // core.getDeletionPolicy().setReserveDuration(version, reserveCommitDuration); // List<Map<String, Object>> result = new ArrayList<Map<String, Object>>(); // try { // //get all the files in the commit // //use a set to workaround possible Lucene bug which returns same file name multiple times // Collection<String> files = new HashSet<String>(commit.getFileNames()); // for (String fileName : files) { // if(fileName.endsWith(".lock")) continue; // File file = new File(core.getIndexDir(), fileName); // Map<String, Object> fileMeta = getFileInfo(file); // result.add(fileMeta); // } // } catch (IOException e) { // rsp.add("status", "unable to get file names for given indexversion"); // rsp.add("exception", e); // LOG.warn("Unable to get file names for indexCommit version: " // + version, e); // } // rsp.add(CMD_GET_FILE_LIST, result); // if (confFileNameAlias.size() < 1) // return; // LOG.debug("Adding config files to list: " + includeConfFiles); // //if configuration files need to be included get their details // rsp.add(CONF_FILES, getConfFileInfoFromCache(confFileNameAlias, confFileInfoCache)); // } // // /** // * For configuration files, checksum of the file is included because, unlike index files, they may have same content // * but different timestamps. // * <p/> // * The local conf files information is cached so that everytime it does not have to compute the checksum. The cache is // * refreshed only if the lastModified of the file changes // */ // List<Map<String, Object>> getConfFileInfoFromCache(NamedList<String> nameAndAlias, // final Map<String, FileInfo> confFileInfoCache) { // List<Map<String, Object>> confFiles = new ArrayList<Map<String, Object>>(); // synchronized (confFileInfoCache) { // File confDir = new File(core.getResourceLoader().getConfigDir()); // Checksum checksum = null; // for (int i = 0; i < nameAndAlias.size(); i++) { // String cf = nameAndAlias.getName(i); // File f = new File(confDir, cf); // if (!f.exists() || f.isDirectory()) continue; //must not happen // FileInfo info = confFileInfoCache.get(cf); // if (info == null || info.lastmodified != f.lastModified() || info.size != f.length()) { // if (checksum == null) checksum = new Adler32(); // info = new FileInfo(f.lastModified(), cf, f.length(), getCheckSum(checksum, f)); // confFileInfoCache.put(cf, info); // } // Map<String, Object> m = info.getAsMap(); // if (nameAndAlias.getVal(i) != null) m.put(ALIAS, nameAndAlias.getVal(i)); // confFiles.add(m); // } // } // return confFiles; // } // // static class FileInfo { // long lastmodified; // String name; // long size; // long checksum; // // public FileInfo(long lasmodified, String name, long size, long checksum) { // this.lastmodified = lasmodified; // this.name = name; // this.size = size; // this.checksum = checksum; // } // // Map<String, Object> getAsMap() { // Map<String, Object> map = new HashMap<String, Object>(); // map.put(NAME, name); // map.put(SIZE, size); // map.put(LAST_MODIFIED, lastmodified); // map.put(CHECKSUM, checksum); // return map; // } // } // // void disablePoll() { // if (isSlave) // snapPuller.disablePoll(); // } // // void enablePoll() { // if (isSlave) // snapPuller.enablePoll(); // } // // boolean isPollingDisabled() { // return snapPuller.isPollingDisabled(); // } // // int getTimesReplicatedSinceStartup() { // return numTimesReplicated; // } // // void setTimesReplicatedSinceStartup() { // numTimesReplicated++; // } // // long getIndexSize() { // return computeIndexSize(new File(core.getIndexDir())); // } // // private long computeIndexSize(File f) { // if (f.isFile()) // return f.length(); // File[] files = f.listFiles(); // long size = 0; // if (files != null && files.length > 0) { // for (File file : files) size += file.length(); // } // return size; // } // // /** // * Collects the details such as name, size ,lastModified of a file // */ // private Map<String, Object> getFileInfo(File file) { // Map<String, Object> fileMeta = new HashMap<String, Object>(); // fileMeta.put(NAME, file.getName()); // fileMeta.put(SIZE, file.length()); // fileMeta.put(LAST_MODIFIED, file.lastModified()); // return fileMeta; // } // // @Override // public String getDescription() { // return "ReplicationHandler provides replication of index and configuration files from Master to Slaves"; // } // // @Override // public String getSourceId() { // return "$Id: ReplicationHandler.java 1203003 2011-11-17 01:50:33Z hossman $"; // } // // @Override // public String getSource() { // return "$URL: https://svn.apache.org/repos/asf/lucene/dev/branches/lucene_solr_3_5/solr/core/src/java/org/apache/solr/handler/ReplicationHandler.java $"; // } // // @Override // public String getVersion() { // return "$Revision: 1203003 $"; // } // // String readableSize(long size) { // NumberFormat formatter = NumberFormat.getNumberInstance(); // formatter.setMaximumFractionDigits(2); // if (size / (1024 * 1024 * 1024) > 0) { // return formatter.format(size * 1.0d / (1024 * 1024 * 1024)) + " GB"; // } else if (size / (1024 * 1024) > 0) { // return formatter.format(size * 1.0d / (1024 * 1024)) + " MB"; // } else if (size / 1024 > 0) { // return formatter.format(size * 1.0d / 1024) + " KB"; // } else { // return String.valueOf(size) + " bytes"; // } // } // // private long[] getIndexVersion() { // long version[] = new long[2]; // RefCounted<SolrIndexSearcher> searcher = core.getSearcher(); // try { // version[0] = searcher.get().getReader().getIndexCommit().getVersion(); // version[1] = searcher.get().getReader().getIndexCommit().getGeneration(); // } catch (IOException e) { // LOG.warn("Unable to get index version : ", e); // } finally { // searcher.decref(); // } // return version; // } // // @Override // @SuppressWarnings("unchecked") // public NamedList getStatistics() { // NamedList list = super.getStatistics(); // if (core != null) { // list.add("indexSize", readableSize(getIndexSize())); // long[] versionGen = getIndexVersion(); // list.add("indexVersion", versionGen[0]); // list.add(GENERATION, versionGen[1]); // // list.add("indexPath", core.getIndexDir()); // list.add("isMaster", String.valueOf(isMaster)); // list.add("isSlave", String.valueOf(isSlave)); // // SnapPuller snapPuller = tempSnapPuller; // if (snapPuller != null) { // list.add(MASTER_URL, snapPuller.getMasterUrl()); // if (snapPuller.getPollInterval() != null) { // list.add(SnapPuller.POLL_INTERVAL, snapPuller.getPollInterval()); // } // list.add("isPollingDisabled", String.valueOf(isPollingDisabled())); // list.add("isReplicating", String.valueOf(isReplicating())); // long elapsed = getTimeElapsed(snapPuller); // long val = SnapPuller.getTotalBytesDownloaded(snapPuller); // if (elapsed > 0) { // list.add("timeElapsed", elapsed); // list.add("bytesDownloaded", val); // list.add("downloadSpeed", val / elapsed); // } // Properties props = loadReplicationProperties(); // addVal(list, SnapPuller.PREVIOUS_CYCLE_TIME_TAKEN, props, Long.class); // addVal(list, SnapPuller.INDEX_REPLICATED_AT, props, Date.class); // addVal(list, SnapPuller.CONF_FILES_REPLICATED_AT, props, Date.class); // addVal(list, SnapPuller.REPLICATION_FAILED_AT, props, Date.class); // addVal(list, SnapPuller.TIMES_FAILED, props, Integer.class); // addVal(list, SnapPuller.TIMES_INDEX_REPLICATED, props, Integer.class); // addVal(list, SnapPuller.LAST_CYCLE_BYTES_DOWNLOADED, props, Long.class); // addVal(list, SnapPuller.TIMES_CONFIG_REPLICATED, props, Integer.class); // addVal(list, SnapPuller.CONF_FILES_REPLICATED, props, String.class); // } // if (isMaster) { // if (includeConfFiles != null) list.add("confFilesToReplicate", includeConfFiles); // list.add(REPLICATE_AFTER, getReplicateAfterStrings()); // list.add("replicationEnabled", String.valueOf(replicationEnabled.get())); // } // } // return list; // } // // /** // * Used for showing statistics and progress information. // * // * @param showSlaveDetails // */ // private NamedList<Object> getReplicationDetails(boolean showSlaveDetails) { // NamedList<Object> details = new SimpleOrderedMap<Object>(); // NamedList<Object> master = new SimpleOrderedMap<Object>(); // NamedList<Object> slave = new SimpleOrderedMap<Object>(); // // details.add("indexSize", readableSize(getIndexSize())); // details.add("indexPath", core.getIndexDir()); // details.add(CMD_SHOW_COMMITS, getCommits()); // details.add("isMaster", String.valueOf(isMaster)); // details.add("isSlave", String.valueOf(isSlave)); // long[] versionAndGeneration = getIndexVersion(); // details.add("indexVersion", versionAndGeneration[0]); // details.add(GENERATION, versionAndGeneration[1]); // // IndexCommit commit = indexCommitPoint; // make a copy so it won't change // // if (isMaster) { // if (includeConfFiles != null) master.add(CONF_FILES, includeConfFiles); // master.add(REPLICATE_AFTER, getReplicateAfterStrings()); // master.add("replicationEnabled", String.valueOf(replicationEnabled.get())); // } // // if (isMaster && commit != null) { // master.add("replicatableIndexVersion", commit.getVersion()); // master.add("replicatableGeneration", commit.getGeneration()); // } // // SnapPuller snapPuller = tempSnapPuller; // if (showSlaveDetails && snapPuller != null) { // Properties props = loadReplicationProperties(); // try { // NamedList<String> command = new NamedList<String>(); // command.add(COMMAND, CMD_DETAILS); // command.add("slave", "false"); // NamedList nl = snapPuller.getCommandResponse(command); // slave.add("masterDetails", nl.get(CMD_DETAILS)); // } catch (Exception e) { // LOG.warn("Exception while invoking 'details' method for replication on master ", e); // slave.add(ERR_STATUS, "invalid_master"); // } // slave.add(MASTER_URL, snapPuller.getMasterUrl()); // if (snapPuller.getPollInterval() != null) { // slave.add(SnapPuller.POLL_INTERVAL, snapPuller.getPollInterval()); // } // if (snapPuller.getNextScheduledExecTime() != null && !isPollingDisabled()) { // slave.add(NEXT_EXECUTION_AT, new Date(snapPuller.getNextScheduledExecTime()).toString()); // } else if (isPollingDisabled()) { // slave.add(NEXT_EXECUTION_AT, "Polling disabled"); // } // addVal(slave, SnapPuller.INDEX_REPLICATED_AT, props, Date.class); // addVal(slave, SnapPuller.INDEX_REPLICATED_AT_LIST, props, List.class); // addVal(slave, SnapPuller.REPLICATION_FAILED_AT_LIST, props, List.class); // addVal(slave, SnapPuller.TIMES_INDEX_REPLICATED, props, Integer.class); // addVal(slave, SnapPuller.CONF_FILES_REPLICATED, props, Integer.class); // addVal(slave, SnapPuller.TIMES_CONFIG_REPLICATED, props, Integer.class); // addVal(slave, SnapPuller.CONF_FILES_REPLICATED_AT, props, Integer.class); // addVal(slave, SnapPuller.LAST_CYCLE_BYTES_DOWNLOADED, props, Long.class); // addVal(slave, SnapPuller.TIMES_FAILED, props, Integer.class); // addVal(slave, SnapPuller.REPLICATION_FAILED_AT, props, Date.class); // addVal(slave, SnapPuller.PREVIOUS_CYCLE_TIME_TAKEN, props, Long.class); // // slave.add("isPollingDisabled", String.valueOf(isPollingDisabled())); // boolean isReplicating = isReplicating(); // slave.add("isReplicating", String.valueOf(isReplicating)); // if (isReplicating) { // try { // long bytesToDownload = 0; // List<String> filesToDownload = new ArrayList<String>(); // for (Map<String, Object> file : snapPuller.getFilesToDownload()) { // filesToDownload.add((String) file.get(NAME)); // bytesToDownload += (Long) file.get(SIZE); // } // // //get list of conf files to download // for (Map<String, Object> file : snapPuller.getConfFilesToDownload()) { // filesToDownload.add((String) file.get(NAME)); // bytesToDownload += (Long) file.get(SIZE); // } // // slave.add("filesToDownload", filesToDownload); // slave.add("numFilesToDownload", String.valueOf(filesToDownload.size())); // slave.add("bytesToDownload", readableSize(bytesToDownload)); // // long bytesDownloaded = 0; // List<String> filesDownloaded = new ArrayList<String>(); // for (Map<String, Object> file : snapPuller.getFilesDownloaded()) { // filesDownloaded.add((String) file.get(NAME)); // bytesDownloaded += (Long) file.get(SIZE); // } // // //get list of conf files downloaded // for (Map<String, Object> file : snapPuller.getConfFilesDownloaded()) { // filesDownloaded.add((String) file.get(NAME)); // bytesDownloaded += (Long) file.get(SIZE); // } // // Map<String, Object> currentFile = snapPuller.getCurrentFile(); // String currFile = null; // long currFileSize = 0, currFileSizeDownloaded = 0; // float percentDownloaded = 0; // if (currentFile != null) { // currFile = (String) currentFile.get(NAME); // currFileSize = (Long) currentFile.get(SIZE); // if (currentFile.containsKey("bytesDownloaded")) { // currFileSizeDownloaded = (Long) currentFile.get("bytesDownloaded"); // bytesDownloaded += currFileSizeDownloaded; // if (currFileSize > 0) // percentDownloaded = (currFileSizeDownloaded * 100) / currFileSize; // } // } // slave.add("filesDownloaded", filesDownloaded); // slave.add("numFilesDownloaded", String.valueOf(filesDownloaded.size())); // // long estimatedTimeRemaining = 0; // // if (snapPuller.getReplicationStartTime() > 0) { // slave.add("replicationStartTime", new Date(snapPuller.getReplicationStartTime()).toString()); // } // long elapsed = getTimeElapsed(snapPuller); // slave.add("timeElapsed", String.valueOf(elapsed) + "s"); // // if (bytesDownloaded > 0) // estimatedTimeRemaining = ((bytesToDownload - bytesDownloaded) * elapsed) / bytesDownloaded; // float totalPercent = 0; // long downloadSpeed = 0; // if (bytesToDownload > 0) // totalPercent = (bytesDownloaded * 100) / bytesToDownload; // if (elapsed > 0) // downloadSpeed = (bytesDownloaded / elapsed); // if (currFile != null) // slave.add("currentFile", currFile); // slave.add("currentFileSize", readableSize(currFileSize)); // slave.add("currentFileSizeDownloaded", readableSize(currFileSizeDownloaded)); // slave.add("currentFileSizePercent", String.valueOf(percentDownloaded)); // slave.add("bytesDownloaded", readableSize(bytesDownloaded)); // slave.add("totalPercent", String.valueOf(totalPercent)); // slave.add("timeRemaining", String.valueOf(estimatedTimeRemaining) + "s"); // slave.add("downloadSpeed", readableSize(downloadSpeed)); // } catch (Exception e) { // LOG.error("Exception while writing replication details: ", e); // } // } // } // // if (isMaster) // details.add("master", master); // if (isSlave && showSlaveDetails) // details.add("slave", slave); // // NamedList snapshotStats = snapShootDetails; // if (snapshotStats != null) // details.add(CMD_BACKUP, snapshotStats); // // return details; // } // // private void addVal(NamedList nl, String key, Properties props, Class clzz) { // String s = props.getProperty(key); // if (s == null || s.trim().length() == 0) return; // if (clzz == Date.class) { // try { // Long l = Long.parseLong(s); // nl.add(key, new Date(l).toString()); // } catch (NumberFormatException e) {/*no op*/ } // } else if (clzz == List.class) { // String ss[] = s.split(","); // List<String> l = new ArrayList<String>(); // for (int i = 0; i < ss.length; i++) { // l.add(new Date(Long.valueOf(ss[i])).toString()); // } // nl.add(key, l); // } else { // nl.add(key, s); // } // // } // // private List<String> getReplicateAfterStrings() { // List<String> replicateAfter = new ArrayList<String>(); // if (replicateOnCommit) // replicateAfter.add("commit"); // if (replicateOnOptimize) // replicateAfter.add("optimize"); // if (replicateOnStart) // replicateAfter.add("startup"); // return replicateAfter; // } // // private long getTimeElapsed(SnapPuller snapPuller) { // long timeElapsed = 0; // if (snapPuller.getReplicationStartTime() > 0) // timeElapsed = (System.currentTimeMillis() - snapPuller.getReplicationStartTime()) / 1000; // return timeElapsed; // } // // Properties loadReplicationProperties() { // FileInputStream inFile = null; // Properties props = new Properties(); // try { // File f = new File(core.getDataDir(), SnapPuller.REPLICATION_PROPERTIES); // if (f.exists()) { // inFile = new FileInputStream(f); // props.load(inFile); // } // } catch (Exception e) { // LOG.warn("Exception while reading " + SnapPuller.REPLICATION_PROPERTIES); // } finally { // IOUtils.closeQuietly(inFile); // } // return props; // } // // // void refreshCommitpoint() { // IndexCommit commitPoint = core.getDeletionPolicy().getLatestCommit(); // if(replicateOnCommit || (replicateOnOptimize && commitPoint.getSegmentCount() == 1)) { // indexCommitPoint = commitPoint; // } // } // // @SuppressWarnings("unchecked") // public void inform(SolrCore core) { // this.core = core; // registerFileStreamResponseWriter(); // registerCloseHook(); // NamedList slave = (NamedList) initArgs.get("slave"); // boolean enableSlave = isEnabled( slave ); // if (enableSlave) { // tempSnapPuller = snapPuller = new SnapPuller(slave, this, core); // isSlave = true; // } // NamedList master = (NamedList) initArgs.get("master"); // boolean enableMaster = isEnabled( master ); // if (enableMaster) { // includeConfFiles = (String) master.get(CONF_FILES); // if (includeConfFiles != null && includeConfFiles.trim().length() > 0) { // List<String> files = Arrays.asList(includeConfFiles.split(",")); // for (String file : files) { // if (file.trim().length() == 0) continue; // String[] strs = file.split(":"); // // if there is an alias add it or it is null // confFileNameAlias.add(strs[0], strs.length > 1 ? strs[1] : null); // } // LOG.info("Replication enabled for following config files: " + includeConfFiles); // } // List backup = master.getAll("backupAfter"); // boolean backupOnCommit = backup.contains("commit"); // boolean backupOnOptimize = !backupOnCommit && backup.contains("optimize"); // List replicateAfter = master.getAll(REPLICATE_AFTER); // replicateOnCommit = replicateAfter.contains("commit"); // replicateOnOptimize = !replicateOnCommit && replicateAfter.contains("optimize"); // // // if we only want to replicate on optimize, we need the deletion policy to // // save the last optimized commit point. // if (replicateOnOptimize) { // IndexDeletionPolicyWrapper wrapper = core.getDeletionPolicy(); // IndexDeletionPolicy policy = wrapper == null ? null : wrapper.getWrappedDeletionPolicy(); // if (policy instanceof SolrDeletionPolicy) { // SolrDeletionPolicy solrPolicy = (SolrDeletionPolicy)policy; // if (solrPolicy.getMaxOptimizedCommitsToKeep() < 1) { // solrPolicy.setMaxOptimizedCommitsToKeep(1); // } // } else { // LOG.warn("Replication can't call setMaxOptimizedCommitsToKeep on " + policy); // } // } // // if (replicateOnOptimize || backupOnOptimize) { // core.getUpdateHandler().registerOptimizeCallback(getEventListener(backupOnOptimize, replicateOnOptimize)); // } // if (replicateOnCommit || backupOnCommit) { // replicateOnCommit = true; // core.getUpdateHandler().registerCommitCallback(getEventListener(backupOnCommit, replicateOnCommit)); // } // if (replicateAfter.contains("startup")) { // replicateOnStart = true; // RefCounted<SolrIndexSearcher> s = core.getNewestSearcher(false); // try { // IndexReader reader = s==null ? null : s.get().getReader(); // if (reader!=null && reader.getIndexCommit() != null && reader.getIndexCommit().getGeneration() != 1L) { // try { // if(replicateOnOptimize){ // Collection<IndexCommit> commits = IndexReader.listCommits(reader.directory()); // for (IndexCommit ic : commits) { // if(ic.getSegmentCount() == 1){ // if(indexCommitPoint == null || indexCommitPoint.getVersion() < ic.getVersion()) indexCommitPoint = ic; // } // } // } else{ // indexCommitPoint = reader.getIndexCommit(); // } // } finally { // // We don't need to save commit points for replication, the SolrDeletionPolicy // // always saves the last commit point (and the last optimized commit point, if needed) // /*** // if(indexCommitPoint != null){ // core.getDeletionPolicy().saveCommitPoint(indexCommitPoint.getVersion()); // } // ***/ // } // } // if (core.getUpdateHandler() instanceof DirectUpdateHandler2) { // ((DirectUpdateHandler2) core.getUpdateHandler()).forceOpenWriter(); // } else { // LOG.warn("The update handler being used is not an instance or sub-class of DirectUpdateHandler2. " + // "Replicate on Startup cannot work."); // } // // } catch (IOException e) { // LOG.warn("Unable to get IndexCommit on startup", e); // } finally { // if (s!=null) s.decref(); // } // } // String reserve = (String) master.get(RESERVE); // if (reserve != null && !reserve.trim().equals("")) { // reserveCommitDuration = SnapPuller.readInterval(reserve); // } // LOG.info("Commits will be reserved for " + reserveCommitDuration); // isMaster = true; // } // } // // // check master or slave is enabled // private boolean isEnabled( NamedList params ){ // if( params == null ) return false; // Object enable = params.get( "enable" ); // if( enable == null ) return true; // if( enable instanceof String ) // return StrUtils.parseBool( (String)enable ); // return Boolean.TRUE.equals( enable ); // } // // /** // * register a closehook // */ // private void registerCloseHook() { // core.addCloseHook(new CloseHook() { // @Override // public void preClose(SolrCore core) { // if (snapPuller != null) { // snapPuller.destroy(); // } // } // // @Override // public void postClose(SolrCore core) {} // }); // } // // /** // * A ResponseWriter is registered automatically for wt=filestream This response writer is used to transfer index files // * in a block-by-block manner within the same HTTP response. // */ // private void registerFileStreamResponseWriter() { // core.registerResponseWriter(FILE_STREAM, new BinaryQueryResponseWriter() { // public void write(OutputStream out, SolrQueryRequest request, SolrQueryResponse resp) throws IOException { // FileStream stream = (FileStream) resp.getValues().get(FILE_STREAM); // stream.write(out); // } // // public void write(Writer writer, SolrQueryRequest request, SolrQueryResponse response) throws IOException { // throw new RuntimeException("This is a binary writer , Cannot write to a characterstream"); // } // // public String getContentType(SolrQueryRequest request, SolrQueryResponse response) { // return "application/octet-stream"; // } // // public void init(NamedList args) { /*no op*/ } // }); // // } // // /** // * Register a listener for postcommit/optimize // * // * @param snapshoot do a snapshoot // * @param getCommit get a commitpoint also // * // * @return an instance of the eventlistener // */ // private SolrEventListener getEventListener(final boolean snapshoot, final boolean getCommit) { // return new SolrEventListener() { // public void init(NamedList args) {/*no op*/ } // // /** // * This refreshes the latest replicateable index commit and optionally can create Snapshots as well // */ // public void postCommit() { // IndexCommit currentCommitPoint = core.getDeletionPolicy().getLatestCommit(); // // if (getCommit) { // // IndexCommit oldCommitPoint = indexCommitPoint; // indexCommitPoint = currentCommitPoint; // // // We don't need to save commit points for replication, the SolrDeletionPolicy // // always saves the last commit point (and the last optimized commit point, if needed) // /*** // if (indexCommitPoint != null) { // core.getDeletionPolicy().saveCommitPoint(indexCommitPoint.getVersion()); // } // if(oldCommitPoint != null){ // core.getDeletionPolicy().releaseCommitPoint(oldCommitPoint.getVersion()); // } // ***/ // } // if (snapshoot) { // try { // SnapShooter snapShooter = new SnapShooter(core, null); // snapShooter.createSnapAsync(currentCommitPoint, ReplicationHandler.this); // } catch (Exception e) { // LOG.error("Exception while snapshooting", e); // } // } // } // // public void newSearcher(SolrIndexSearcher newSearcher, SolrIndexSearcher currentSearcher) { /*no op*/} // }; // } // // private class FileStream { // private SolrParams params; // // private FastOutputStream fos; // // private Long indexVersion; // private IndexDeletionPolicyWrapper delPolicy; // // public FileStream(SolrParams solrParams) { // params = solrParams; // delPolicy = core.getDeletionPolicy(); // } // // public void write(OutputStream out) throws IOException { // String fileName = params.get(FILE); // String cfileName = params.get(CONF_FILE_SHORT); // String sOffset = params.get(OFFSET); // String sLen = params.get(LEN); // String compress = params.get(COMPRESSION); // String sChecksum = params.get(CHECKSUM); // String sindexVersion = params.get(CMD_INDEX_VERSION); // if (sindexVersion != null) indexVersion = Long.parseLong(sindexVersion); // if (Boolean.parseBoolean(compress)) { // fos = new FastOutputStream(new DeflaterOutputStream(out)); // } else { // fos = new FastOutputStream(out); // } // FileInputStream inputStream = null; // int packetsWritten = 0; // try { // long offset = -1; // int len = -1; // //check if checksum is requested // boolean useChecksum = Boolean.parseBoolean(sChecksum); // if (sOffset != null) // offset = Long.parseLong(sOffset); // if (sLen != null) // len = Integer.parseInt(sLen); // if (fileName == null && cfileName == null) { // //no filename do nothing // writeNothing(); // } // // File file = null; // if (cfileName != null) { // //if if is a conf file read from config diectory // file = new File(core.getResourceLoader().getConfigDir(), cfileName); // } else { // //else read from the indexdirectory // file = new File(core.getIndexDir(), fileName); // } // if (file.exists() && file.canRead()) { // inputStream = new FileInputStream(file); // FileChannel channel = inputStream.getChannel(); // //if offset is mentioned move the pointer to that point // if (offset != -1) // channel.position(offset); // byte[] buf = new byte[(len == -1 || len > PACKET_SZ) ? PACKET_SZ : len]; // Checksum checksum = null; // if (useChecksum) // checksum = new Adler32(); // ByteBuffer bb = ByteBuffer.wrap(buf); // // while (true) { // bb.clear(); // long bytesRead = channel.read(bb); // if (bytesRead <= 0) { // writeNothing(); // fos.close(); // break; // } // fos.writeInt((int) bytesRead); // if (useChecksum) { // checksum.reset(); // checksum.update(buf, 0, (int) bytesRead); // fos.writeLong(checksum.getValue()); // } // fos.write(buf, 0, (int) bytesRead); // fos.flush(); // if (indexVersion != null && (packetsWritten % 5 == 0)) { // //after every 5 packets reserve the commitpoint for some time // delPolicy.setReserveDuration(indexVersion, reserveCommitDuration); // } // packetsWritten++; // } // } else { // writeNothing(); // } // } catch (IOException e) { // LOG.warn("Exception while writing response for params: " + params, e); // } finally { // IOUtils.closeQuietly(inputStream); // } // } // // // /** // * Used to write a marker for EOF // */ // private void writeNothing() throws IOException { // fos.writeInt(0); // fos.flush(); // } // } // // public static final String MASTER_URL = "masterUrl"; // // public static final String STATUS = "status"; // // public static final String COMMAND = "command"; // // public static final String CMD_DETAILS = "details"; // // public static final String CMD_BACKUP = "backup"; // // public static final String CMD_FETCH_INDEX = "fetchindex"; // // public static final String CMD_ABORT_FETCH = "abortfetch"; // // public static final String CMD_GET_FILE_LIST = "filelist"; // // public static final String CMD_GET_FILE = "filecontent"; // // public static final String CMD_FILE_CHECKSUM = "filechecksum"; // // public static final String CMD_DISABLE_POLL = "disablepoll"; // // public static final String CMD_DISABLE_REPL = "disablereplication"; // // public static final String CMD_ENABLE_REPL = "enablereplication"; // // public static final String CMD_ENABLE_POLL = "enablepoll"; // // public static final String CMD_INDEX_VERSION = "indexversion"; // // public static final String CMD_SHOW_COMMITS = "commits"; // // public static final String GENERATION = "generation"; // // public static final String OFFSET = "offset"; // // public static final String LEN = "len"; // // public static final String FILE = "file"; // // public static final String NAME = "name"; // // public static final String SIZE = "size"; // // public static final String LAST_MODIFIED = "lastmodified"; // // public static final String CONF_FILE_SHORT = "cf"; // // public static final String CHECKSUM = "checksum"; // // public static final String ALIAS = "alias"; // // public static final String CONF_CHECKSUM = "confchecksum"; // // public static final String CONF_FILES = "confFiles"; // // public static final String REPLICATE_AFTER = "replicateAfter"; // // public static final String FILE_STREAM = "filestream"; // // public static final int PACKET_SZ = 1024 * 1024; // 1MB // // public static final String RESERVE = "commitReserveDuration"; // // public static final String COMPRESSION = "compression"; // // public static final String EXTERNAL = "external"; // // public static final String INTERNAL = "internal"; // // public static final String ERR_STATUS = "ERROR"; // // public static final String OK_STATUS = "OK"; // // public static final String NEXT_EXECUTION_AT = "nextExecutionAt"; // // public static final String NUMBER_BACKUPS_TO_KEEP = "numberToKeep"; //}