2
* Licensed to the Apache Software Foundation (ASF) under one or more
3
* contributor license agreements. See the NOTICE file distributed with
4
* this work for additional information regarding copyright ownership.
5
* The ASF licenses this file to You under the Apache License, Version 2.0
6
* (the "License"); you may not use this file except in compliance with
7
* the License. You may obtain a copy of the License at
9
* http://www.apache.org/licenses/LICENSE-2.0
11
* Unless required by applicable law or agreed to in writing, software
12
* distributed under the License is distributed on an "AS IS" BASIS,
13
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
* See the License for the specific language governing permissions and
15
* limitations under the License.
17
package org.apache.solr.handler;
20
import java.io.FileInputStream;
21
import java.io.FileNotFoundException;
22
import java.io.FileOutputStream;
23
import java.io.IOException;
24
import java.text.SimpleDateFormat;
25
import java.util.ArrayList;
26
import java.util.Collection;
27
import java.util.Collections;
28
import java.util.Date;
29
import java.util.List;
30
import java.util.Locale;
31
import java.util.regex.Matcher;
32
import java.util.regex.Pattern;
34
import org.apache.commons.io.IOUtils;
35
import org.apache.lucene.index.IndexCommit;
36
import org.apache.lucene.store.Lock;
37
import org.apache.lucene.store.SimpleFSLockFactory;
38
import org.apache.solr.common.util.NamedList;
39
import org.apache.solr.core.IndexDeletionPolicyWrapper;
40
import org.apache.solr.core.SolrCore;
41
import org.slf4j.Logger;
42
import org.slf4j.LoggerFactory;
45
* <p/> Provides functionality equivalent to the snapshooter script </p>
47
* @version $Id: SnapShooter.java 1203003 2011-11-17 01:50:33Z hossman $
50
public class SnapShooter {
51
private static final Logger LOG = LoggerFactory.getLogger(SnapShooter.class.getName());
52
private String snapDir = null;
53
private SolrCore solrCore;
54
private SimpleFSLockFactory lockFactory;
56
public SnapShooter(SolrCore core, String location) throws IOException {
58
if (location == null) snapDir = core.getDataDir();
60
File base = new File(core.getCoreDescriptor().getInstanceDir());
61
snapDir = org.apache.solr.common.util.FileUtils.resolvePath(base, location).getAbsolutePath();
62
File dir = new File(snapDir);
63
if (!dir.exists()) dir.mkdirs();
65
lockFactory = new SimpleFSLockFactory(snapDir);
68
void createSnapAsync(final IndexCommit indexCommit, final ReplicationHandler replicationHandler) {
69
createSnapAsync(indexCommit, Integer.MAX_VALUE, replicationHandler);
72
void createSnapAsync(final IndexCommit indexCommit, final int numberToKeep, final ReplicationHandler replicationHandler) {
73
replicationHandler.core.getDeletionPolicy().saveCommitPoint(indexCommit.getVersion());
78
createSnapshot(indexCommit, numberToKeep, replicationHandler);
83
void createSnapshot(final IndexCommit indexCommit, int numberToKeep, ReplicationHandler replicationHandler) {
84
NamedList details = new NamedList();
85
details.add("startTime", new Date().toString());
86
File snapShotDir = null;
87
String directoryName = null;
90
if(numberToKeep<Integer.MAX_VALUE) {
91
deleteOldBackups(numberToKeep);
93
SimpleDateFormat fmt = new SimpleDateFormat(DATE_FMT, Locale.US);
94
directoryName = "snapshot." + fmt.format(new Date());
95
lock = lockFactory.makeLock(directoryName + ".lock");
96
if (lock.isLocked()) return;
97
snapShotDir = new File(snapDir, directoryName);
98
if (!snapShotDir.mkdir()) {
99
LOG.warn("Unable to create snapshot directory: " + snapShotDir.getAbsolutePath());
102
Collection<String> files = indexCommit.getFileNames();
103
FileCopier fileCopier = new FileCopier(solrCore.getDeletionPolicy(), indexCommit);
104
fileCopier.copyFiles(files, snapShotDir);
106
details.add("fileCount", files.size());
107
details.add("status", "success");
108
details.add("snapshotCompletedAt", new Date().toString());
109
} catch (Exception e) {
110
SnapPuller.delTree(snapShotDir);
111
LOG.error("Exception while creating snapshot", e);
112
details.add("snapShootException", e.getMessage());
114
replicationHandler.core.getDeletionPolicy().releaseCommitPoint(indexCommit.getVersion());
115
replicationHandler.snapShootDetails = details;
119
} catch (IOException e) {
120
LOG.error("Unable to release snapshoot lock: " + directoryName + ".lock");
125
private void deleteOldBackups(int numberToKeep) {
126
File[] files = new File(snapDir).listFiles();
127
List<OldBackupDirectory> dirs = new ArrayList<OldBackupDirectory>();
128
for(File f : files) {
129
OldBackupDirectory obd = new OldBackupDirectory(f);
130
if(obd.dir != null) {
134
Collections.sort(dirs);
136
for(OldBackupDirectory dir : dirs) {
137
if( i > numberToKeep-1 ) {
138
SnapPuller.delTree(dir.dir);
142
private class OldBackupDirectory implements Comparable<OldBackupDirectory>{
145
final Pattern dirNamePattern = Pattern.compile("^snapshot[.](.*)$");
147
OldBackupDirectory(File dir) {
148
if(dir.isDirectory()) {
149
Matcher m = dirNamePattern.matcher(dir.getName());
153
this.timestamp = new SimpleDateFormat(DATE_FMT).parse(m.group(1));
154
} catch(Exception e) {
156
this.timestamp = null;
161
public int compareTo(OldBackupDirectory that) {
162
return that.timestamp.compareTo(this.timestamp);
166
public static final String SNAP_DIR = "snapDir";
167
public static final String DATE_FMT = "yyyyMMddHHmmss";
170
private class FileCopier {
171
private static final int DEFAULT_BUFFER_SIZE = 32768;
172
private byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
173
private IndexCommit indexCommit;
174
private IndexDeletionPolicyWrapper delPolicy;
176
public FileCopier(IndexDeletionPolicyWrapper delPolicy, IndexCommit commit) {
177
this.delPolicy = delPolicy;
178
this.indexCommit = commit;
181
public void copyFiles(Collection<String> files, File destDir) throws IOException {
182
for (String indexFile : files) {
183
File source = new File(solrCore.getIndexDir(), indexFile);
184
copyFile(source, new File(destDir, source.getName()), true);
188
public void copyFile(File source, File destination, boolean preserveFileDate)
190
// check source exists
191
if (!source.exists()) {
192
String message = "File " + source + " does not exist";
193
throw new FileNotFoundException(message);
196
// does destinations directory exist ?
197
if (destination.getParentFile() != null
198
&& !destination.getParentFile().exists()) {
199
destination.getParentFile().mkdirs();
202
// make sure we can write to destination
203
if (destination.exists() && !destination.canWrite()) {
204
String message = "Unable to open file " + destination + " for writing.";
205
throw new IOException(message);
208
FileInputStream input = null;
209
FileOutputStream output = null;
211
input = new FileInputStream(source);
212
output = new FileOutputStream(destination);
217
while (-1 != (n = input.read(buffer))) {
218
output.write(buffer, 0, n);
222
// reserve every 4.6875 MB
225
delPolicy.setReserveDuration(indexCommit.getVersion(), reserveTime);
231
IOUtils.closeQuietly(input);
233
IOUtils.closeQuietly(output);
237
if (source.length() != destination.length()) {
238
String message = "Failed to copy full contents from " + source + " to "
240
throw new IOException(message);
243
if (preserveFileDate) {
244
// file copy should preserve file date
245
destination.setLastModified(source.lastModified());