mirror of
https://github.com/internetarchive/heritrix3.git
synced 2026-09-13 01:05:47 +00:00
HER-1901 timestamped subdirectory for each launch
* HardLinker.java
renamed FilesystemLinkMaker.java
* FilesystemLinkMaker.java
add support for symbolic links
* CLibrary.java
new method symlink()
* BdbModule.java
use new class name FilesystemLinkMaker
* CrawlJob.java
at crawl launch, create launch directory launch-{timestamp17}, copy cxml there, symlink "current" to launch dir, inform ConfigPaths
* ConfigPath.java
interpolate ${launch-id} in configured paths
* ConfigFile.java
obtainReader() - snapshot config files to launch dir when they are read
* ActionDirectory.java
default doneDir now ${launch-id}/actions-done
actOn() - symlink from old style done dir action/done to done files
* SurtPrefixedDecideRule.java
default surtsDumpFile now ${launch-id}/surts.dump
pathsFixedUp() - this gets called at build time, but we don't want anything written to disk until launch time, so remove call to dumpSurtPrefixSet() here
* CrawlerLoggerModule.java
default logs dir now ${launch-id}/logs
* StatisticsTracker.java
default reports dir now ${launch-id}/reports
* WriterPoolProcessor.java
default writer base path now ${launch-id}
* profile-crawler-beans.cxml
update to reflect new default paths under launch dirs
* PropertyUtils.java
fix javadoc typo
This commit is contained in:
@@ -43,7 +43,7 @@ import org.archive.checkpointing.Checkpoint;
|
||||
import org.archive.checkpointing.Checkpointable;
|
||||
import org.archive.spring.ConfigPath;
|
||||
import org.archive.util.CLibrary;
|
||||
import org.archive.util.HardLinker;
|
||||
import org.archive.util.FilesystemLinkMaker;
|
||||
import org.archive.util.IdentityCacheable;
|
||||
import org.archive.util.ObjectIdentityBdbManualCache;
|
||||
import org.archive.util.ObjectIdentityCache;
|
||||
@@ -487,7 +487,7 @@ public class BdbModule implements Lifecycle, Checkpointable, Closeable {
|
||||
filedata[i] += ","+f.length();
|
||||
if(getUseHardLinkCheckpoints()) {
|
||||
File hardLink = new File(envCpDir,filedata[i]);
|
||||
if (!HardLinker.makeHardLink(f.getAbsolutePath(), hardLink.getAbsolutePath())) {
|
||||
if (!FilesystemLinkMaker.makeHardLink(f.getAbsolutePath(), hardLink.getAbsolutePath())) {
|
||||
LOGGER.log(Level.SEVERE, "unable to create required checkpoint link "+hardLink);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ import java.io.OutputStreamWriter;
|
||||
import java.io.Reader;
|
||||
import java.io.Writer;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.archive.io.ReadSource;
|
||||
|
||||
/**
|
||||
@@ -48,6 +49,10 @@ public class ConfigFile extends ConfigPath implements ReadSource, WriteTarget {
|
||||
if(!getFile().exists()) {
|
||||
getFile().createNewFile();
|
||||
}
|
||||
|
||||
// snapshot file to launch directory
|
||||
FileUtils.copyFileToDirectory(getFile(), currentLaunchDir);
|
||||
|
||||
return new InputStreamReader(
|
||||
new FileInputStream(getFile()),
|
||||
"UTF-8");
|
||||
|
||||
@@ -56,9 +56,10 @@ public class ConfigPath implements Serializable {
|
||||
|
||||
String name;
|
||||
String path;
|
||||
ConfigPath base;
|
||||
transient File resolved;
|
||||
ConfigPath base;
|
||||
|
||||
transient String interpolatedPath;
|
||||
transient File currentLaunchDir;
|
||||
|
||||
public ConfigPath() {
|
||||
super();
|
||||
@@ -68,6 +69,7 @@ public class ConfigPath implements Serializable {
|
||||
super();
|
||||
this.name = name;
|
||||
this.path = path;
|
||||
this.interpolatedPath = path;
|
||||
}
|
||||
|
||||
public ConfigPath getBase() {
|
||||
@@ -93,12 +95,13 @@ public class ConfigPath implements Serializable {
|
||||
@Required
|
||||
public void setPath(String path) {
|
||||
this.path = path;
|
||||
this.interpolatedPath = path;
|
||||
}
|
||||
|
||||
public File getFile() {
|
||||
return (base == null || path.startsWith("/"))
|
||||
? new File(path)
|
||||
: new File(base.getFile(), path);
|
||||
return (base == null || interpolatedPath.startsWith("/"))
|
||||
? new File(interpolatedPath)
|
||||
: new File(base.getFile(), interpolatedPath);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -118,4 +121,10 @@ public class ConfigPath implements Serializable {
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public void informOfLaunch(String currentLaunchId, File currentLaunchDir) {
|
||||
this.currentLaunchDir = currentLaunchDir;
|
||||
// could use PropertyUtils.interpolateWithProperties(String, Properties...), but no real need
|
||||
interpolatedPath = path.replace("${launch-id}", currentLaunchId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,5 +35,6 @@ public interface CLibrary extends Library {
|
||||
Native.loadLibrary((Platform.isWindows() ? "msvcrt" : "c"),
|
||||
CLibrary.class);
|
||||
|
||||
int link(String fromFile, String toFile);
|
||||
int link(String existingPath, String newPath);
|
||||
int symlink(String existingPath, String newPath);
|
||||
}
|
||||
|
||||
+32
-3
@@ -32,7 +32,7 @@ import com.sun.jna.win32.StdCallLibrary;
|
||||
*
|
||||
* @see http://stackoverflow.com/questions/783075/creating-a-hard-link-in-java/3023349#3023349
|
||||
*/
|
||||
public class HardLinker {
|
||||
public class FilesystemLinkMaker {
|
||||
|
||||
// see https://github.com/twall/jna/blob/master/www/GettingStarted.md
|
||||
public interface Kernel32Library extends StdCallLibrary {
|
||||
@@ -63,6 +63,9 @@ public class HardLinker {
|
||||
*/
|
||||
boolean CreateHardLinkA(String newPath, String existingPath, LPSECURITY_ATTRIBUTES lpSecurityAttributes);
|
||||
// boolean CreateHardLinkW(String newPath, String existingPath, LPSECURITY_ATTRIBUTES lpSecurityAttributes);
|
||||
|
||||
// http://msdn.microsoft.com/en-us/library/aa363866%28v=VS.85%29.aspx
|
||||
boolean CreateSymbolicLinkA(String newPath, String existingPath, LPSECURITY_ATTRIBUTES lpSecurityAttributes);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -70,6 +73,7 @@ public class HardLinker {
|
||||
*
|
||||
* @return true on success
|
||||
*/
|
||||
// XXX could handle errors better (examine errno, throw exception...)
|
||||
public static boolean makeHardLink(String existingPath, String newPath) {
|
||||
if (Platform.isWindows()) {
|
||||
return Kernel32Library.INSTANCE.CreateHardLinkA(newPath, existingPath, null);
|
||||
@@ -78,17 +82,42 @@ public class HardLinker {
|
||||
return status == 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Wrapper over platform-dependent system calls to create a symboic link.
|
||||
*
|
||||
* @return true on success
|
||||
*/
|
||||
// XXX could handle errors better (examine errno, throw exception...)
|
||||
public static boolean makeSymbolicLink(String existingPath, String newPath) {
|
||||
if (Platform.isWindows()) {
|
||||
return Kernel32Library.INSTANCE.CreateSymbolicLinkA(newPath, existingPath, null);
|
||||
} else {
|
||||
int status = CLibrary.INSTANCE.symlink(existingPath, newPath);
|
||||
return status == 0;
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
File existingPath = File.createTempFile("heritrixHardLinkTestExistingFile", ".tmp");
|
||||
File newPath = File.createTempFile("heritrixHardLinkTestNewFile", ".tmp");
|
||||
newPath.delete();
|
||||
|
||||
if (HardLinker.makeHardLink(existingPath.getAbsolutePath(), newPath.getAbsolutePath())) {
|
||||
if (FilesystemLinkMaker.makeHardLink(existingPath.getAbsolutePath(), newPath.getAbsolutePath())) {
|
||||
System.out.println("success - made hard link from " + newPath.getAbsolutePath() + " to " + existingPath.getAbsolutePath());
|
||||
} else {
|
||||
System.out.println("failed to make hard link from " + newPath.getAbsolutePath() + " to " + existingPath.getAbsolutePath());
|
||||
}
|
||||
|
||||
existingPath = File.createTempFile("heritrixSymlinkTestExistingFile", ".tmp");
|
||||
newPath = File.createTempFile("heritrixSymlinkTestNewFile", ".tmp");
|
||||
newPath.delete();
|
||||
|
||||
if (FilesystemLinkMaker.makeSymbolicLink(existingPath.getPath(), newPath.getPath())) {
|
||||
System.out.println("success - made symlink from " + newPath.getAbsolutePath() + " to " + existingPath.getAbsolutePath());
|
||||
} else {
|
||||
System.out.println("failed to make symlink from " + newPath.getAbsolutePath() + " to " + existingPath.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ public class PropertyUtils {
|
||||
* the expression is replaced with the empty-string.
|
||||
*
|
||||
* @param original String
|
||||
* @param properties Properties to try in order; first value found (if any) is used
|
||||
* @param props Properties to try in order; first value found (if any) is used
|
||||
* @return modified String
|
||||
*/
|
||||
public static String interpolateWithProperties(String original,
|
||||
|
||||
@@ -41,6 +41,7 @@ import org.apache.commons.lang.StringUtils;
|
||||
import org.archive.modules.seeds.SeedModule;
|
||||
import org.archive.spring.ConfigPath;
|
||||
import org.archive.util.ArchiveUtils;
|
||||
import org.archive.util.FilesystemLinkMaker;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
@@ -100,9 +101,6 @@ public class ActionDirectory implements ApplicationContextAware, Lifecycle, Runn
|
||||
this.delaySeconds = delay;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scratch directory for temporary overflow-to-disk
|
||||
*/
|
||||
protected ConfigPath actionDir =
|
||||
new ConfigPath("ActionDirectory source directory","action");
|
||||
public ConfigPath getActionDir() {
|
||||
@@ -112,16 +110,13 @@ public class ActionDirectory implements ApplicationContextAware, Lifecycle, Runn
|
||||
this.actionDir = actionDir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scratch directory for temporary overflow-to-disk
|
||||
*/
|
||||
protected ConfigPath doneDir =
|
||||
new ConfigPath("ActionDirectory done directory","action/done");
|
||||
new ConfigPath("ActionDirectory done directory","${launch-id}/actions-done");
|
||||
public ConfigPath getDoneDir() {
|
||||
return doneDir;
|
||||
}
|
||||
public void setDoneDir(ConfigPath scratchDir) {
|
||||
this.doneDir = scratchDir;
|
||||
public void setDoneDir(ConfigPath doneDir) {
|
||||
this.doneDir = doneDir;
|
||||
}
|
||||
|
||||
ApplicationContext appCtx;
|
||||
@@ -258,7 +253,19 @@ public class ActionDirectory implements ApplicationContextAware, Lifecycle, Runn
|
||||
// move file to 'done' area with timestamp prefix
|
||||
while(actionFile.exists()) {
|
||||
try {
|
||||
FileUtils.moveFile(actionFile, new File(doneDir.getFile(),timestamp+"."+actionFile.getName()));
|
||||
File doneFile = new File(doneDir.getFile(),timestamp+"."+actionFile.getName());
|
||||
FileUtils.moveFile(actionFile, doneFile);
|
||||
|
||||
// attempt to symlink from action/done/ to done file
|
||||
File actionDoneDirFile = new File(actionDir.getFile(), "done");
|
||||
if (!actionDoneDirFile.equals(doneDir.getFile())) {
|
||||
actionDoneDirFile.mkdirs();
|
||||
File doneSymlinkFile = new File(actionDoneDirFile, doneFile.getName());
|
||||
boolean success = FilesystemLinkMaker.makeSymbolicLink(doneFile.getPath(), doneSymlinkFile.getPath());
|
||||
if (!success) {
|
||||
LOGGER.warning("failed to create symlink from " + doneSymlinkFile + " to " + doneFile);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
LOGGER.log(Level.SEVERE,"unable to move "+actionFile,e);
|
||||
}
|
||||
|
||||
@@ -57,6 +57,7 @@ import org.archive.spring.ConfigPath;
|
||||
import org.archive.spring.ConfigPathConfigurer;
|
||||
import org.archive.spring.PathSharingContext;
|
||||
import org.archive.util.ArchiveUtils;
|
||||
import org.archive.util.FilesystemLinkMaker;
|
||||
import org.archive.util.TextUtils;
|
||||
import org.joda.time.DateTime;
|
||||
import org.springframework.beans.BeanWrapperImpl;
|
||||
@@ -80,7 +81,7 @@ import org.xml.sax.SAXException;
|
||||
*
|
||||
* @contributor gojomo
|
||||
*/
|
||||
public class CrawlJob implements Comparable<CrawlJob>, ApplicationListener {
|
||||
public class CrawlJob implements Comparable<CrawlJob>, ApplicationListener<ApplicationEvent> {
|
||||
private final static Logger LOGGER =
|
||||
Logger.getLogger(CrawlJob.class.getName());
|
||||
|
||||
@@ -97,7 +98,7 @@ public class CrawlJob implements Comparable<CrawlJob>, ApplicationListener {
|
||||
public CrawlJob(File cxml) {
|
||||
primaryConfig = cxml;
|
||||
isLaunchInfoPartial = false;
|
||||
scanJobLog();
|
||||
scanJobLog(); // XXX look at launch directories instead/first?
|
||||
alertThreadGroup = new AlertThreadGroup(getShortName());
|
||||
}
|
||||
|
||||
@@ -428,8 +429,9 @@ public class CrawlJob implements Comparable<CrawlJob>, ApplicationListener {
|
||||
alertThreadGroup.addLogger(getJobLogger());
|
||||
Thread launcher = new Thread(alertThreadGroup, getShortName()+" launchthread") {
|
||||
public void run() {
|
||||
startContext();
|
||||
CrawlController cc = getCrawlController();
|
||||
initLaunchDir();
|
||||
startContext();
|
||||
if(cc!=null) {
|
||||
cc.requestCrawlStart();
|
||||
}
|
||||
@@ -446,6 +448,50 @@ public class CrawlJob implements Comparable<CrawlJob>, ApplicationListener {
|
||||
}
|
||||
}
|
||||
|
||||
protected transient String currentLaunchId;
|
||||
public void initLaunchId() {
|
||||
currentLaunchId = "launch-" + ArchiveUtils.get17DigitDate();
|
||||
LOGGER.info("launch id " + currentLaunchId);
|
||||
}
|
||||
public String getCurrentLaunchId() {
|
||||
return currentLaunchId;
|
||||
}
|
||||
|
||||
protected transient File currentLaunchDir;
|
||||
public File getCurrentLaunchDir() {
|
||||
return currentLaunchDir;
|
||||
}
|
||||
|
||||
protected void initLaunchDir() {
|
||||
initLaunchId();
|
||||
try {
|
||||
currentLaunchDir = new File(getJobDir(), getCurrentLaunchId());
|
||||
if (!currentLaunchDir.mkdir()) {
|
||||
throw new IOException("failed to create directory " + currentLaunchDir);
|
||||
}
|
||||
|
||||
// copy cxml to launch dir
|
||||
FileUtils.copyFileToDirectory(getPrimaryConfig(), currentLaunchDir);
|
||||
|
||||
// attempt to symlink "current" to launch dir
|
||||
File currentSymlink = new File(getJobDir(), "current");
|
||||
currentSymlink.delete();
|
||||
boolean success = FilesystemLinkMaker.makeSymbolicLink(currentLaunchDir.getName(), currentSymlink.getPath());
|
||||
if (!success) {
|
||||
LOGGER.warning("failed to create symlink from " + currentSymlink + " to " + currentLaunchDir);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
LOGGER.log(Level.SEVERE, "failed to initialize launch directory: " + e);
|
||||
currentLaunchDir = null;
|
||||
}
|
||||
|
||||
// fill in ${launch-id} in all config paths, and let config files know
|
||||
// where to snapshot themselves
|
||||
for (ConfigPath configPath: getConfigPaths().values()) {
|
||||
configPath.informOfLaunch(getCurrentLaunchId(), getCurrentLaunchDir());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the context, catching and reporting any BeansExceptions.
|
||||
*/
|
||||
@@ -515,7 +561,6 @@ public class CrawlJob implements Comparable<CrawlJob>, ApplicationListener {
|
||||
*
|
||||
* @return Checkpointer
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public synchronized CheckpointService getCheckpointService() {
|
||||
if(ac==null) {
|
||||
return null;
|
||||
|
||||
@@ -62,7 +62,7 @@ public class CrawlerLoggerModule
|
||||
Checkpointable, SimpleFileLoggerProvider {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
protected ConfigPath path = new ConfigPath(Engine.LOGS_DIR_NAME,"logs");
|
||||
protected ConfigPath path = new ConfigPath(Engine.LOGS_DIR_NAME,"${launch-id}/logs");
|
||||
public ConfigPath getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
@@ -130,9 +130,9 @@ import com.sleepycat.je.DatabaseException;
|
||||
public class StatisticsTracker
|
||||
implements
|
||||
ApplicationContextAware,
|
||||
ApplicationListener,
|
||||
ApplicationListener<ApplicationEvent>,
|
||||
SeedListener,
|
||||
Lifecycle,
|
||||
Lifecycle,
|
||||
Runnable,
|
||||
Checkpointable,
|
||||
BeanNameAware {
|
||||
@@ -153,7 +153,7 @@ public class StatisticsTracker
|
||||
this.bdb = bdb;
|
||||
}
|
||||
|
||||
protected ConfigPath reportsDir = new ConfigPath(Engine.REPORTS_DIR_NAME,"reports");
|
||||
protected ConfigPath reportsDir = new ConfigPath(Engine.REPORTS_DIR_NAME,"${launch-id}/reports");
|
||||
public ConfigPath getReportsDir() {
|
||||
return reportsDir;
|
||||
}
|
||||
|
||||
@@ -123,7 +123,7 @@ http://example.example/example
|
||||
<!-- <property name="seedsAsSurtPrefixes" value="true" /> -->
|
||||
<!-- <property name="alsoCheckVia" value="false" /> -->
|
||||
<!-- <property name="surtsSourceFile" value="" /> -->
|
||||
<!-- <property name="surtsDumpFile" value="surts.dump" /> -->
|
||||
<!-- <property name="surtsDumpFile" value="${launch-id}/surts.dump" /> -->
|
||||
<!-- <property name="surtsSource">
|
||||
<bean class="org.archive.spring.ConfigString">
|
||||
<property name="value">
|
||||
@@ -149,7 +149,7 @@ http://example.example/example
|
||||
<bean class="org.archive.modules.deciderules.surt.SurtPrefixedDecideRule">
|
||||
<property name="decision" value="REJECT"/>
|
||||
<property name="seedsAsSurtPrefixes" value="false"/>
|
||||
<property name="surtsDumpFile" value="negative-surts.dump" />
|
||||
<property name="surtsDumpFile" value="${launch-id}/negative-surts.dump" />
|
||||
<!-- <property name="surtsSource">
|
||||
<bean class="org.archive.spring.ConfigFile">
|
||||
<property name="path" value="negative-surts.txt" />
|
||||
@@ -338,7 +338,7 @@ http://example.example/example
|
||||
<!-- <property name="MaxWaitForIdleMs" value="500" /> -->
|
||||
<!-- <property name="skipIdenticalDigests" value="false" /> -->
|
||||
<!-- <property name="maxTotalBytesToWrite" value="0" /> -->
|
||||
<!-- <property name="directory" value="." /> -->
|
||||
<!-- <property name="directory" value="${launch-id}" /> -->
|
||||
<!-- <property name="storePaths">
|
||||
<list>
|
||||
<value>warcs</value>
|
||||
@@ -517,6 +517,7 @@ http://example.example/example
|
||||
scripts, and other data to be processed during a crawl. -->
|
||||
<bean id="actionDirectory" class="org.archive.crawler.framework.ActionDirectory">
|
||||
<!-- <property name="actionDir" value="action" /> -->
|
||||
<!-- <property name="doneDir" value="${launch-id}/actions-done" /> -->
|
||||
<!-- <property name="initialDelaySeconds" value="10" /> -->
|
||||
<!-- <property name="delaySeconds" value="30" /> -->
|
||||
</bean>
|
||||
@@ -612,7 +613,7 @@ http://example.example/example
|
||||
<!-- STATISTICSTRACKER: standard stats/reporting collector -->
|
||||
<bean id="statisticsTracker"
|
||||
class="org.archive.crawler.reporting.StatisticsTracker" autowire="byName">
|
||||
<!-- <property name="reportsDir" value="reports" /> -->
|
||||
<!-- <property name="reportsDir" value="${launch-id}/reports" /> -->
|
||||
<!-- <property name="liveHostReportSize" value="20" /> -->
|
||||
<!-- <property name="intervalSeconds" value="20" /> -->
|
||||
<!-- <property name="keepSnapshotsCount" value="5" /> -->
|
||||
@@ -622,7 +623,7 @@ http://example.example/example
|
||||
<!-- CRAWLERLOGGERMODULE: shared logging facility -->
|
||||
<bean id="loggerModule"
|
||||
class="org.archive.crawler.reporting.CrawlerLoggerModule">
|
||||
<!-- <property name="path" value="logs" /> -->
|
||||
<!-- <property name="path" value="${launch-id}/logs" /> -->
|
||||
<!-- <property name="crawlLogPath" value="crawl.log" /> -->
|
||||
<!-- <property name="alertsLogPath" value="alerts.log" /> -->
|
||||
<!-- <property name="progressLogPath" value="progress-statistics.log" /> -->
|
||||
|
||||
+1
-2
@@ -111,7 +111,7 @@ implements
|
||||
* Dump file to save SURT prefixes actually used: Useful debugging SURTs.
|
||||
*/
|
||||
protected ConfigFile surtsDumpFile =
|
||||
new ConfigFile("surtsDumpFile","surts.dump");
|
||||
new ConfigFile("surtsDumpFile","${launch-id}/surts.dump");
|
||||
public ConfigFile getSurtsDumpFile() {
|
||||
return surtsDumpFile;
|
||||
}
|
||||
@@ -156,7 +156,6 @@ implements
|
||||
|
||||
public void pathsFixedUp() {
|
||||
readPrefixes();
|
||||
dumpSurtPrefixSet();
|
||||
}
|
||||
|
||||
public void concludedSeedBatch() {
|
||||
|
||||
@@ -225,7 +225,7 @@ implements Lifecycle, Checkpointable, WriterPoolSettings {
|
||||
this.serverCache = serverCache;
|
||||
}
|
||||
|
||||
protected ConfigPath directory = new ConfigPath("writer base path", ".");
|
||||
protected ConfigPath directory = new ConfigPath("writer base path", "${launch-id}");
|
||||
public ConfigPath getDirectory() {
|
||||
return directory;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user