mirror of
https://github.com/internetarchive/heritrix3.git
synced 2026-09-26 15:46:31 +00:00
Merge branch 'master' into new-fetchhttp-only
Conflicts: modules/src/main/java/org/archive/modules/CrawlURI.java modules/src/main/java/org/archive/modules/writer/WARCWriterProcessor.java
This commit is contained in:
@@ -41,7 +41,10 @@ import org.archive.io.arc.ARCReader;
|
||||
import org.archive.io.arc.ARCReaderFactory;
|
||||
import org.archive.io.arc.ARCRecord;
|
||||
import org.archive.io.warc.WARCConstants;
|
||||
import org.archive.io.warc.WARCConstants.WARCRecordType;
|
||||
import org.archive.io.warc.WARCRecordInfo;
|
||||
import org.archive.io.warc.WARCWriter;
|
||||
import org.archive.io.warc.WARCWriterPoolSettings;
|
||||
import org.archive.io.warc.WARCWriterPoolSettingsData;
|
||||
import org.archive.uid.RecordIDGenerator;
|
||||
import org.archive.uid.UUIDGenerator;
|
||||
@@ -101,7 +104,7 @@ public class Arc2Warc {
|
||||
getLength());
|
||||
firstRecord.dump(baos);
|
||||
// Add ARC first record content as an ANVLRecord.
|
||||
ANVLRecord ar = new ANVLRecord(1);
|
||||
ANVLRecord ar = new ANVLRecord();
|
||||
ar.addLabelValue("Filedesc", baos.toString());
|
||||
List<String> metadata = new ArrayList<String>(1);
|
||||
metadata.add(ar.toString());
|
||||
@@ -145,6 +148,11 @@ public class Arc2Warc {
|
||||
|
||||
protected void write(final WARCWriter writer, final ARCRecord r)
|
||||
throws IOException {
|
||||
WARCRecordInfo recordInfo = new WARCRecordInfo();
|
||||
recordInfo.setUrl(r.getHeader().getUrl());
|
||||
recordInfo.setContentStream(r);
|
||||
recordInfo.setContentLength(r.getHeader().getLength());
|
||||
recordInfo.setEnforceLength(true);
|
||||
|
||||
// convert ARC date to WARC-Date format
|
||||
String arcDateString = r.getHeader().getDate();
|
||||
@@ -152,6 +160,7 @@ public class Arc2Warc {
|
||||
.withZone(DateTimeZone.UTC)
|
||||
.parseDateTime(arcDateString)
|
||||
.toString(ISODateTimeFormat.dateTimeNoMillis());
|
||||
recordInfo.setCreate14DigitDate(warcDateString);
|
||||
|
||||
ANVLRecord ar = new ANVLRecord();
|
||||
String ip = (String)r.getHeader()
|
||||
@@ -160,6 +169,7 @@ public class Arc2Warc {
|
||||
ar.addLabelValue(WARCConstants.NAMED_FIELD_IP_LABEL, ip);
|
||||
r.getMetaData();
|
||||
}
|
||||
recordInfo.setExtraHeaders(ar);
|
||||
|
||||
// enable reconstruction of ARC from transformed WARC
|
||||
// TODO: deferred for further analysis (see HER-1750)
|
||||
@@ -167,18 +177,17 @@ public class Arc2Warc {
|
||||
|
||||
// If contentBody > 0, assume http headers. Make the mimetype
|
||||
// be application/http. Otherwise, give it ARC mimetype.
|
||||
String warcMimeTypeString;
|
||||
if (r.getHeader().getContentBegin() > 0) {
|
||||
warcMimeTypeString = WARCConstants.HTTP_RESPONSE_MIMETYPE;
|
||||
writer.writeResponseRecord(r.getHeader().getUrl(), warcDateString,
|
||||
warcMimeTypeString, generator.getRecordID(), ar, r,
|
||||
r.getHeader().getLength());
|
||||
recordInfo.setType(WARCRecordType.RESPONSE);
|
||||
recordInfo.setMimetype(WARCConstants.HTTP_RESPONSE_MIMETYPE);
|
||||
recordInfo.setRecordId(generator.getRecordID());
|
||||
} else {
|
||||
warcMimeTypeString = r.getHeader().getMimetype();
|
||||
writer.writeResourceRecord(r.getHeader().getUrl(), warcDateString,
|
||||
warcMimeTypeString, ar, r, r.getHeader().getLength());
|
||||
recordInfo.setType(WARCRecordType.RESOURCE);
|
||||
recordInfo.setMimetype(r.getHeader().getMimetype());
|
||||
recordInfo.setRecordId(((WARCWriterPoolSettings)writer.settings).getRecordIDGenerator().getRecordID());
|
||||
}
|
||||
|
||||
writer.writeRecord(recordInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -19,9 +19,6 @@
|
||||
|
||||
package org.archive.io.warc;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.archive.io.ArchiveFileConstants;
|
||||
|
||||
/**
|
||||
@@ -119,34 +116,29 @@ public interface WARCConstants extends ArchiveFileConstants {
|
||||
/**
|
||||
* WARC Record Types.
|
||||
*/
|
||||
public static final String WARCINFO = "warcinfo";
|
||||
public static final String RESPONSE = "response";
|
||||
public static final String RESOURCE = "resource";
|
||||
public static final String REQUEST = "request";
|
||||
public static final String METADATA = "metadata";
|
||||
public static final String REVISIT = "revisit";
|
||||
public static final String CONVERSION = "conversion";
|
||||
public static final String CONTINUATION = "continuation";
|
||||
enum WARCRecordType {
|
||||
WARCINFO("warcinfo"),
|
||||
RESPONSE("response"),
|
||||
RESOURCE("resource"),
|
||||
REQUEST("request"),
|
||||
METADATA("metadata"),
|
||||
REVISIT("revisit"),
|
||||
CONVERSION("conversion"),
|
||||
CONTINUATION("continuation");
|
||||
|
||||
private String value;
|
||||
private WARCRecordType(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
public static final String TYPE = "type";
|
||||
|
||||
// List of all WARC Record TYPES
|
||||
public static final String [] TYPES = {WARCINFO, RESPONSE, RESOURCE,
|
||||
REQUEST, METADATA, REVISIT, CONVERSION, CONTINUATION};
|
||||
|
||||
// Indices into TYPES array.
|
||||
public static final int WARCINFO_INDEX = 0;
|
||||
public static final int RESPONSE_INDEX = 1;
|
||||
public static final int RESOURCE_INDEX = 2;
|
||||
public static final int REQUEST_INDEX = 3;
|
||||
public static final int METADATA_INDEX = 4;
|
||||
public static final int REVISIT_INDEX = 5;
|
||||
public static final int CONVERSION_INDEX = 6;
|
||||
public static final int CONTINUATION_INDEX = 7;
|
||||
|
||||
// TYPES as List.
|
||||
public static final List<String> TYPES_LIST = Arrays.asList(TYPES);
|
||||
|
||||
/**
|
||||
* WARC-ID
|
||||
*/
|
||||
@@ -201,6 +193,18 @@ public interface WARCConstants extends ArchiveFileConstants {
|
||||
public static final String HEADER_KEY_FILENAME = "WARC-Filename";
|
||||
public static final String HEADER_KEY_ETAG = "WARC-Etag";
|
||||
public static final String HEADER_KEY_LAST_MODIFIED = "WARC-Last-Modified";
|
||||
public static final String HEADER_KEY_REFERS_TO = "WARC-Refers-To";
|
||||
|
||||
/**
|
||||
* These fields help a consumer of the warc to locate the warc record that
|
||||
* {@value #HEADER_KEY_REFERS_TO} refers to.
|
||||
*
|
||||
* @see WARCWriterProcessor
|
||||
*/
|
||||
public static final String HEADER_KEY_REFERS_TO_TARGET_URI = "WARC-Refers-To-Target-URI";
|
||||
public static final String HEADER_KEY_REFERS_TO_DATE = "WARC-Refers-To-Date";
|
||||
public static final String HEADER_KEY_REFERS_TO_FILENAME = "WARC-Refers-To-Filename";
|
||||
public static final String HEADER_KEY_REFERS_TO_FILE_OFFSET = "WARC-Refers-To-File-Offset";
|
||||
|
||||
public static final String PROFILE_REVISIT_IDENTICAL_DIGEST =
|
||||
"http://netpreserve.org/warc/1.0/revisit/identical-payload-digest";
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* This file is part of the Heritrix web crawler (crawler.archive.org).
|
||||
*
|
||||
* Licensed to the Internet Archive (IA) by one or more individual
|
||||
* contributors.
|
||||
*
|
||||
* The IA 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.archive.io.warc;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.net.URI;
|
||||
|
||||
import org.archive.io.warc.WARCConstants.WARCRecordType;
|
||||
import org.archive.util.anvl.ANVLRecord;
|
||||
|
||||
public class WARCRecordInfo {
|
||||
|
||||
protected WARCRecordType type;
|
||||
protected String url;
|
||||
protected String create14DigitDate;
|
||||
protected String mimetype;
|
||||
protected URI recordId;
|
||||
protected ANVLRecord extraHeaders;
|
||||
protected InputStream contentStream;
|
||||
protected long contentLength;
|
||||
protected boolean enforceLength;
|
||||
protected String warcFilename;
|
||||
protected Long warcFileOffset;
|
||||
|
||||
public void setType(WARCRecordType type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public void setUrl(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
public String getCreate14DigitDate() {
|
||||
return create14DigitDate;
|
||||
}
|
||||
|
||||
public void setCreate14DigitDate(String create14DigitDate) {
|
||||
this.create14DigitDate = create14DigitDate;
|
||||
}
|
||||
|
||||
public String getMimetype() {
|
||||
return mimetype;
|
||||
}
|
||||
|
||||
public void setMimetype(String mimetype) {
|
||||
this.mimetype = mimetype;
|
||||
}
|
||||
|
||||
public URI getRecordId() {
|
||||
return recordId;
|
||||
}
|
||||
|
||||
public void setRecordId(URI recordId) {
|
||||
this.recordId = recordId;
|
||||
}
|
||||
|
||||
public ANVLRecord getExtraHeaders() {
|
||||
return extraHeaders;
|
||||
}
|
||||
|
||||
public void setExtraHeaders(ANVLRecord extraHeaders) {
|
||||
this.extraHeaders = extraHeaders;
|
||||
}
|
||||
|
||||
public InputStream getContentStream() {
|
||||
return contentStream;
|
||||
}
|
||||
|
||||
public void setContentStream(InputStream contentStream) {
|
||||
this.contentStream = contentStream;
|
||||
}
|
||||
|
||||
public long getContentLength() {
|
||||
return contentLength;
|
||||
}
|
||||
|
||||
public void setContentLength(long contentLength) {
|
||||
this.contentLength = contentLength;
|
||||
}
|
||||
|
||||
public boolean isEnforceLength() {
|
||||
return enforceLength;
|
||||
}
|
||||
|
||||
public boolean getEnforceLength() {
|
||||
return enforceLength;
|
||||
}
|
||||
|
||||
public void setEnforceLength(boolean enforceLength) {
|
||||
this.enforceLength = enforceLength;
|
||||
}
|
||||
|
||||
public WARCRecordType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public void addExtraHeader(String label, String value) {
|
||||
if (extraHeaders == null) {
|
||||
extraHeaders = new ANVLRecord();
|
||||
}
|
||||
extraHeaders.addLabelValue(label, value);
|
||||
}
|
||||
|
||||
public void setWARCFilename(String warcFilenameWithoutOccupiedSuffix) {
|
||||
this.warcFilename = warcFilenameWithoutOccupiedSuffix;
|
||||
}
|
||||
|
||||
public String getWARCFilename() {
|
||||
return warcFilename;
|
||||
}
|
||||
|
||||
public void setWARCFileOffset(Long startPosition) {
|
||||
this.warcFileOffset = startPosition;
|
||||
}
|
||||
|
||||
public Long getWARCFileOffset() {
|
||||
return warcFileOffset;
|
||||
}
|
||||
}
|
||||
@@ -23,11 +23,11 @@ import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.URI;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
@@ -36,10 +36,11 @@ import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.archive.io.ArchiveFileConstants;
|
||||
import org.archive.io.UTF8Bytes;
|
||||
import org.archive.io.WriterPoolMember;
|
||||
import org.archive.util.ArchiveUtils;
|
||||
import org.archive.util.anvl.ANVLRecord;
|
||||
import org.archive.util.anvl.Element;
|
||||
|
||||
|
||||
@@ -84,7 +85,10 @@ implements WARCConstants {
|
||||
* {@link #resetTmpStats()}, write some records, then add
|
||||
* {@link #getTmpStats()} into its long-term running totals.
|
||||
*/
|
||||
private Map<String,Map<String,Long>> tmpStats;
|
||||
private Map<String, Map<String, Long>> tmpStats;
|
||||
|
||||
/** Temporarily accumulates info on written warc records for use externally. */
|
||||
private LinkedList<WARCRecordInfo> tmpRecordLog = new LinkedList<WARCRecordInfo>();
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
@@ -173,76 +177,58 @@ implements WARCConstants {
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
protected String createRecordHeader(final String type,
|
||||
final String url, final String create14DigitDate,
|
||||
final String mimetype, final URI recordId,
|
||||
final ANVLRecord xtraHeaders, final long contentLength)
|
||||
// protected String createRecordHeader(final String type,
|
||||
// final String url, final String create14DigitDate,
|
||||
// final String mimetype, final URI recordId,
|
||||
// final ANVLRecord xtraHeaders, final long contentLength)
|
||||
protected String createRecordHeader(WARCRecordInfo metaRecord)
|
||||
throws IllegalArgumentException {
|
||||
final StringBuilder sb =
|
||||
new StringBuilder(2048/*A SWAG: TODO: Do analysis.*/);
|
||||
sb.append(WARC_ID).append(CRLF);
|
||||
sb.append(HEADER_KEY_TYPE).append(COLON_SPACE).append(type).
|
||||
sb.append(HEADER_KEY_TYPE).append(COLON_SPACE).append(metaRecord.getType()).
|
||||
append(CRLF);
|
||||
// Do not write a subject-uri if not one present.
|
||||
if (url != null && url.length() > 0) {
|
||||
if (!StringUtils.isEmpty(metaRecord.getUrl())) {
|
||||
sb.append(HEADER_KEY_URI).append(COLON_SPACE).
|
||||
append(checkHeaderValue(url)).append(CRLF);
|
||||
append(checkHeaderValue(metaRecord.getUrl())).append(CRLF);
|
||||
}
|
||||
sb.append(HEADER_KEY_DATE).append(COLON_SPACE).
|
||||
append(create14DigitDate).append(CRLF);
|
||||
if (xtraHeaders != null) {
|
||||
for (final Iterator<Element> i = xtraHeaders.iterator(); i.hasNext();) {
|
||||
append(metaRecord.getCreate14DigitDate()).append(CRLF);
|
||||
if (metaRecord.getExtraHeaders() != null) {
|
||||
for (final Iterator<Element> i = metaRecord.getExtraHeaders().iterator(); i.hasNext();) {
|
||||
sb.append(i.next()).append(CRLF);
|
||||
}
|
||||
}
|
||||
|
||||
sb.append(HEADER_KEY_ID).append(COLON_SPACE).append('<').
|
||||
append(recordId.toString()).append('>').append(CRLF);
|
||||
if (contentLength > 0) {
|
||||
append(metaRecord.getRecordId().toString()).append('>').append(CRLF);
|
||||
if (metaRecord.getContentLength() > 0) {
|
||||
sb.append(CONTENT_TYPE).append(COLON_SPACE).append(
|
||||
checkHeaderLineMimetypeParameter(mimetype)).append(CRLF);
|
||||
checkHeaderLineMimetypeParameter(metaRecord.getMimetype())).append(CRLF);
|
||||
}
|
||||
sb.append(CONTENT_LENGTH).append(COLON_SPACE).
|
||||
append(Long.toString(contentLength)).append(CRLF);
|
||||
append(Long.toString(metaRecord.getContentLength())).append(CRLF);
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #writeRecord(String,String,String,String,URI,ANVLRecord,InputStream,long,boolean)} instead
|
||||
*/
|
||||
protected void writeRecord(final String type, final String url,
|
||||
final String create14DigitDate, final String mimetype,
|
||||
final URI recordId, ANVLRecord xtraHeaders,
|
||||
final InputStream contentStream, final long contentLength)
|
||||
public void writeRecord(WARCRecordInfo recordInfo)
|
||||
throws IOException {
|
||||
writeRecord(type, url, create14DigitDate, mimetype, recordId, xtraHeaders, contentStream, contentLength, true);
|
||||
}
|
||||
|
||||
protected void writeRecord(final String type, final String url,
|
||||
final String create14DigitDate, final String mimetype,
|
||||
final URI recordId, ANVLRecord xtraHeaders,
|
||||
final InputStream contentStream, final long contentLength,
|
||||
boolean enforceLength)
|
||||
throws IOException {
|
||||
if (!TYPES_LIST.contains(type)) {
|
||||
throw new IllegalArgumentException("Unknown record type: " + type);
|
||||
}
|
||||
if (contentLength == 0 &&
|
||||
(xtraHeaders == null || xtraHeaders.size() <= 0)) {
|
||||
if (recordInfo.getContentLength() == 0 &&
|
||||
(recordInfo.getExtraHeaders() == null || recordInfo.getExtraHeaders().size() <= 0)) {
|
||||
throw new IllegalArgumentException("Cannot write record " +
|
||||
"of content-length zero and base headers only.");
|
||||
}
|
||||
|
||||
String header;
|
||||
try {
|
||||
header = createRecordHeader(type, url,
|
||||
create14DigitDate, mimetype, recordId, xtraHeaders,
|
||||
contentLength);
|
||||
header = createRecordHeader(recordInfo);
|
||||
|
||||
} catch (IllegalArgumentException e) {
|
||||
logger.log(Level.SEVERE,"could not write record type: " + type
|
||||
+ "for URL: " + url, e);
|
||||
logger.log(Level.SEVERE,"could not write record type: " + recordInfo.getType()
|
||||
+ "for URL: " + recordInfo.getUrl(), e);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -259,12 +245,13 @@ implements WARCConstants {
|
||||
write(bytes);
|
||||
totalBytes += bytes.length;
|
||||
|
||||
|
||||
if (contentStream != null && contentLength > 0) {
|
||||
if (recordInfo.getContentStream() != null && recordInfo.getContentLength() > 0) {
|
||||
// Write out the header/body separator.
|
||||
write(CRLF_BYTES); // TODO: should this be written even for zero-length?
|
||||
totalBytes += CRLF_BYTES.length;
|
||||
contentBytes += copyFrom(contentStream, contentLength, enforceLength);
|
||||
contentBytes += copyFrom(recordInfo.getContentStream(),
|
||||
recordInfo.getContentLength(),
|
||||
recordInfo.getEnforceLength());
|
||||
totalBytes += contentBytes;
|
||||
}
|
||||
|
||||
@@ -272,26 +259,37 @@ implements WARCConstants {
|
||||
write(CRLF_BYTES);
|
||||
write(CRLF_BYTES);
|
||||
totalBytes += 2 * CRLF_BYTES.length;
|
||||
|
||||
tally(recordInfo.getType(), contentBytes, totalBytes, getPosition() - startPosition);
|
||||
|
||||
recordInfo.setWARCFilename(getFilenameWithoutOccupiedSuffix());
|
||||
recordInfo.setWARCFileOffset(startPosition);
|
||||
tmpRecordLog.add(recordInfo);
|
||||
} finally {
|
||||
postWriteRecordTasks();
|
||||
}
|
||||
|
||||
// TODO: should this be in the finally block?
|
||||
tally(type, contentBytes, totalBytes, getPosition() - startPosition);
|
||||
}
|
||||
|
||||
public String getFilenameWithoutOccupiedSuffix() {
|
||||
String name = getFile().getName();
|
||||
if (name.endsWith(ArchiveFileConstants.OCCUPIED_SUFFIX)) {
|
||||
name = name.substring(0, name.length() - ArchiveFileConstants.OCCUPIED_SUFFIX.length());
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
// if compression is enabled, sizeOnDisk means compressed bytes; if not, it
|
||||
// should be the same as totalBytes (right?)
|
||||
protected void tally(String recordType, long contentBytes, long totalBytes, long sizeOnDisk) {
|
||||
protected void tally(WARCRecordType warcRecordType, long contentBytes, long totalBytes, long sizeOnDisk) {
|
||||
if (tmpStats == null) {
|
||||
tmpStats = new HashMap<String, Map<String,Long>>();
|
||||
}
|
||||
|
||||
// add to stats for this record type
|
||||
Map<String, Long> substats = tmpStats.get(recordType);
|
||||
Map<String, Long> substats = tmpStats.get(warcRecordType.toString());
|
||||
if (substats == null) {
|
||||
substats = new HashMap<String, Long>();
|
||||
tmpStats.put(recordType, substats);
|
||||
tmpStats.put(warcRecordType.toString(), substats);
|
||||
}
|
||||
subtally(substats, contentBytes, totalBytes, sizeOnDisk);
|
||||
|
||||
@@ -349,16 +347,21 @@ implements WARCConstants {
|
||||
|
||||
public URI writeWarcinfoRecord(String filename, final String description)
|
||||
throws IOException {
|
||||
WARCRecordInfo recordInfo = new WARCRecordInfo();
|
||||
recordInfo.setType(WARCRecordType.WARCINFO);
|
||||
recordInfo.setCreate14DigitDate(ArchiveUtils.getLog14Date());
|
||||
recordInfo.setMimetype("application/warc-fields");
|
||||
|
||||
// Strip .open suffix if present.
|
||||
if (filename.endsWith(WriterPoolMember.OCCUPIED_SUFFIX)) {
|
||||
filename = filename.substring(0,
|
||||
filename.length() - WriterPoolMember.OCCUPIED_SUFFIX.length());
|
||||
}
|
||||
ANVLRecord record = new ANVLRecord(2);
|
||||
record.addLabelValue(HEADER_KEY_FILENAME, filename);
|
||||
recordInfo.addExtraHeader(HEADER_KEY_FILENAME, filename);
|
||||
if (description != null && description.length() > 0) {
|
||||
record.addLabelValue(CONTENT_DESCRIPTION, description);
|
||||
recordInfo.addExtraHeader(CONTENT_DESCRIPTION, description);
|
||||
}
|
||||
|
||||
// Add warcinfo body.
|
||||
byte [] warcinfoBody = null;
|
||||
if (settings.getMetadata() == null) {
|
||||
@@ -372,119 +375,19 @@ implements WARCConstants {
|
||||
}
|
||||
warcinfoBody = baos.toByteArray();
|
||||
}
|
||||
URI uri = writeWarcinfoRecord("application/warc-fields", record,
|
||||
new ByteArrayInputStream(warcinfoBody), warcinfoBody.length);
|
||||
recordInfo.setContentStream(new ByteArrayInputStream(warcinfoBody));
|
||||
recordInfo.setContentLength((long) warcinfoBody.length);
|
||||
recordInfo.setEnforceLength(true);
|
||||
|
||||
recordInfo.setRecordId(generateRecordId(TYPE, WARCRecordType.WARCINFO.toString()));
|
||||
|
||||
writeRecord(recordInfo);
|
||||
|
||||
// TODO: If at start of file, and we're writing compressed,
|
||||
// write out our distinctive GZIP extensions.
|
||||
return uri;
|
||||
return recordInfo.getRecordId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a warcinfo to current file.
|
||||
* TODO: Write crawl metadata or pointers to crawl description.
|
||||
* @param mimetype Mimetype of the <code>fileMetadata</code> block.
|
||||
* @param namedFields Named fields. Pass <code>null</code> if none.
|
||||
* @param fileMetadata Metadata about this WARC as RDF, ANVL, etc.
|
||||
* @param fileMetadataLength Length of <code>fileMetadata</code>.
|
||||
* @throws IOException
|
||||
* @return Generated record-id made with
|
||||
* <a href="http://en.wikipedia.org/wiki/Data:_URL">data: scheme</a> and
|
||||
* the current filename.
|
||||
*/
|
||||
public URI writeWarcinfoRecord(final String mimetype,
|
||||
final ANVLRecord namedFields, final InputStream fileMetadata,
|
||||
final long fileMetadataLength)
|
||||
throws IOException {
|
||||
final URI recordid = generateRecordId(TYPE, WARCINFO);
|
||||
writeWarcinfoRecord(ArchiveUtils.getLog14Date(), mimetype, recordid,
|
||||
namedFields, fileMetadata, fileMetadataLength);
|
||||
return recordid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a <code>warcinfo</code> to current file.
|
||||
* The <code>warcinfo</code> type uses its <code>recordId</code> as its URL.
|
||||
* @param recordId URI to use for this warcinfo.
|
||||
* @param create14DigitDate Record creation date as 14 digit date.
|
||||
* @param mimetype Mimetype of the <code>fileMetadata</code>.
|
||||
* @param namedFields Named fields.
|
||||
* @param fileMetadata Metadata about this WARC as RDF, ANVL, etc.
|
||||
* @param fileMetadataLength Length of <code>fileMetadata</code>.
|
||||
* @throws IOException
|
||||
*/
|
||||
public void writeWarcinfoRecord(final String create14DigitDate,
|
||||
final String mimetype, final URI recordId, final ANVLRecord namedFields,
|
||||
final InputStream fileMetadata, final long fileMetadataLength)
|
||||
throws IOException {
|
||||
writeRecord(WARCINFO, null, create14DigitDate, mimetype,
|
||||
recordId, namedFields, fileMetadata, fileMetadataLength, true);
|
||||
}
|
||||
|
||||
public void writeRequestRecord(final String url,
|
||||
final String create14DigitDate, final String mimetype,
|
||||
final URI recordId,
|
||||
final ANVLRecord namedFields, final InputStream request,
|
||||
final long requestLength)
|
||||
throws IOException {
|
||||
writeRecord(REQUEST, url, create14DigitDate,
|
||||
mimetype, recordId, namedFields, request,
|
||||
requestLength, true);
|
||||
}
|
||||
|
||||
public void writeResourceRecord(final String url,
|
||||
final String create14DigitDate, final String mimetype,
|
||||
final ANVLRecord namedFields, final InputStream response,
|
||||
final long responseLength)
|
||||
throws IOException {
|
||||
writeResourceRecord(url, create14DigitDate, mimetype,
|
||||
((WARCWriterPoolSettings)settings).getRecordIDGenerator().getRecordID(),
|
||||
namedFields, response, responseLength);
|
||||
}
|
||||
|
||||
public void writeResourceRecord(final String url,
|
||||
final String create14DigitDate, final String mimetype,
|
||||
final URI recordId,
|
||||
final ANVLRecord namedFields, final InputStream response,
|
||||
final long responseLength)
|
||||
throws IOException {
|
||||
writeRecord(RESOURCE, url, create14DigitDate,
|
||||
mimetype, recordId, namedFields, response,
|
||||
responseLength, true);
|
||||
}
|
||||
|
||||
public void writeResponseRecord(final String url,
|
||||
final String create14DigitDate, final String mimetype,
|
||||
final URI recordId,
|
||||
final ANVLRecord namedFields, final InputStream response,
|
||||
final long responseLength)
|
||||
throws IOException {
|
||||
writeRecord(RESPONSE, url, create14DigitDate,
|
||||
mimetype, recordId, namedFields, response,
|
||||
responseLength, true);
|
||||
}
|
||||
|
||||
public void writeRevisitRecord(final String url,
|
||||
final String create14DigitDate, final String mimetype,
|
||||
final URI recordId,
|
||||
final ANVLRecord namedFields, final InputStream response,
|
||||
final long responseLength)
|
||||
throws IOException {
|
||||
writeRecord(REVISIT, url, create14DigitDate,
|
||||
mimetype, recordId, namedFields, response,
|
||||
responseLength, false);
|
||||
}
|
||||
|
||||
public void writeMetadataRecord(final String url,
|
||||
final String create14DigitDate, final String mimetype,
|
||||
final URI recordId,
|
||||
final ANVLRecord namedFields, final InputStream metadata,
|
||||
final long metadataLength)
|
||||
throws IOException {
|
||||
writeRecord(METADATA, url, create14DigitDate,
|
||||
mimetype, recordId, namedFields, metadata,
|
||||
metadataLength, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see WARCWriter#tmpStats for usage model
|
||||
*/
|
||||
@@ -522,4 +425,12 @@ implements WARCConstants {
|
||||
return 0l;
|
||||
}
|
||||
}
|
||||
|
||||
public void resetTmpRecordLog() {
|
||||
tmpRecordLog.clear();
|
||||
}
|
||||
|
||||
public Iterable<WARCRecordInfo> getTmpRecordLog() {
|
||||
return tmpRecordLog;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,10 +22,10 @@ import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
@@ -42,8 +42,7 @@ import org.archive.io.UTF8Bytes;
|
||||
* Language (ANVL)</a>
|
||||
* @author stack
|
||||
*/
|
||||
public class ANVLRecord extends ArrayList<Element> implements UTF8Bytes {
|
||||
private static final long serialVersionUID = -4610638888453052958L;
|
||||
public class ANVLRecord extends LinkedList<Element> implements UTF8Bytes {
|
||||
private static final Logger logger =
|
||||
Logger.getLogger(ANVLRecord.class.getName());
|
||||
|
||||
@@ -73,8 +72,9 @@ public class ANVLRecord extends ArrayList<Element> implements UTF8Bytes {
|
||||
super(c);
|
||||
}
|
||||
|
||||
/** @deprecated */
|
||||
public ANVLRecord(int initialCapacity) {
|
||||
super(initialCapacity);
|
||||
super();
|
||||
}
|
||||
|
||||
public boolean addLabel(final String l) {
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Heritrix web crawler (crawler.archive.org).
|
||||
*
|
||||
* Licensed to the Internet Archive (IA) by one or more individual
|
||||
* contributors.
|
||||
*
|
||||
* The IA 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.archive.util.anvl;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
|
||||
import org.archive.io.UTF8Bytes;
|
||||
|
||||
/**
|
||||
* List of {@link ANVLRecord}s.
|
||||
* @author stack
|
||||
* @version $Date$ $Version$
|
||||
*/
|
||||
public class ANVLRecords extends ArrayList<ANVLRecord> implements UTF8Bytes {
|
||||
private static final long serialVersionUID = 5361551920550106113L;
|
||||
|
||||
public ANVLRecords() {
|
||||
super();
|
||||
}
|
||||
|
||||
public ANVLRecords(int initialCapacity) {
|
||||
super(initialCapacity);
|
||||
}
|
||||
|
||||
public ANVLRecords(Collection<ANVLRecord> c) {
|
||||
super(c);
|
||||
}
|
||||
|
||||
public byte[] getUTF8Bytes() throws UnsupportedEncodingException {
|
||||
return toString().getBytes(UTF8);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (final Iterator<ANVLRecord> i = iterator(); i.hasNext();) {
|
||||
sb.append(i.next().toString());
|
||||
}
|
||||
return super.toString();
|
||||
}
|
||||
}
|
||||
@@ -130,29 +130,51 @@ extends TmpDirTestCase implements WARCConstants {
|
||||
|
||||
private void writeWarcinfoRecord(WARCWriter writer)
|
||||
throws IOException {
|
||||
WARCRecordInfo recordInfo = new WARCRecordInfo();
|
||||
recordInfo.setType(WARCRecordType.WARCINFO);
|
||||
recordInfo.setUrl(null);
|
||||
recordInfo.setCreate14DigitDate(ArchiveUtils.getLog14Date());
|
||||
recordInfo.setMimetype(ANVLRecord.MIMETYPE);
|
||||
recordInfo.setExtraHeaders(null);
|
||||
recordInfo.setEnforceLength(true);
|
||||
|
||||
ANVLRecord meta = new ANVLRecord();
|
||||
meta.addLabelValue("size", "1G");
|
||||
meta.addLabelValue("operator", "igor");
|
||||
byte [] bytes = meta.getUTF8Bytes();
|
||||
writer.writeWarcinfoRecord(ANVLRecord.MIMETYPE, null,
|
||||
new ByteArrayInputStream(bytes), bytes.length);
|
||||
recordInfo.setContentStream(new ByteArrayInputStream(bytes));
|
||||
recordInfo.setContentLength((long) bytes.length);
|
||||
|
||||
final URI recordid = writer.generateRecordId(WARCWriter.TYPE, WARCRecordType.WARCINFO.toString());
|
||||
recordInfo.setRecordId(recordid);
|
||||
|
||||
writer.writeRecord(recordInfo);
|
||||
}
|
||||
|
||||
protected void writeBasicRecords(final WARCWriter writer)
|
||||
throws IOException {
|
||||
WARCRecordInfo recordInfo = new WARCRecordInfo();
|
||||
recordInfo.setType(WARCRecordType.METADATA);
|
||||
recordInfo.setUrl("http://www.archive.org/");
|
||||
recordInfo.setCreate14DigitDate(ArchiveUtils.get14DigitDate());
|
||||
recordInfo.setMimetype("no/type");
|
||||
recordInfo.setEnforceLength(true);
|
||||
|
||||
ANVLRecord headerFields = new ANVLRecord();
|
||||
headerFields.addLabelValue("x", "y");
|
||||
headerFields.addLabelValue("a", "b");
|
||||
recordInfo.setExtraHeaders(headerFields);
|
||||
|
||||
URI rid = (new UUIDGenerator()).getQualifiedRecordID(TYPE, WARCRecordType.METADATA.toString());
|
||||
recordInfo.setRecordId(rid);
|
||||
|
||||
URI rid = (new UUIDGenerator()).getQualifiedRecordID(TYPE, METADATA);
|
||||
final String content = "Any old content.";
|
||||
for (int i = 0; i < 10; i++) {
|
||||
String body = i + ". " + content;
|
||||
byte [] bodyBytes = body.getBytes(UTF8Bytes.UTF8);
|
||||
writer.writeRecord(METADATA, "http://www.archive.org/",
|
||||
ArchiveUtils.get14DigitDate(), "no/type",
|
||||
rid, headerFields, new ByteArrayInputStream(bodyBytes),
|
||||
(long)bodyBytes.length, true);
|
||||
recordInfo.setContentStream(new ByteArrayInputStream(bodyBytes));
|
||||
recordInfo.setContentLength((long)bodyBytes.length);
|
||||
writer.writeRecord(recordInfo);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -186,23 +208,29 @@ extends TmpDirTestCase implements WARCConstants {
|
||||
*/
|
||||
protected int writeRandomHTTPRecord(WARCWriter w, int index)
|
||||
throws IOException {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
WARCRecordInfo recordInfo = new WARCRecordInfo();
|
||||
recordInfo.setType(WARCRecordType.RESOURCE);
|
||||
recordInfo.setCreate14DigitDate(ArchiveUtils.get14DigitDate());
|
||||
recordInfo.setMimetype("text/html; charset=UTF-8");
|
||||
recordInfo.setRecordId(w.generateRecordId(null));
|
||||
recordInfo.setEnforceLength(true);
|
||||
|
||||
String indexStr = Integer.toString(index);
|
||||
recordInfo.setUrl("http://www.one.net/id=" + indexStr);
|
||||
|
||||
byte[] record = (getContent(indexStr)).getBytes();
|
||||
int recordLength = record.length;
|
||||
recordInfo.setContentLength((long) record.length);
|
||||
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
baos.write(record);
|
||||
recordInfo.setContentStream(new ByteArrayInputStream(baos.toByteArray()));
|
||||
|
||||
// Add named fields for ip, checksum, and relate the metadata
|
||||
// and request to the resource field.
|
||||
ANVLRecord r = new ANVLRecord(1);
|
||||
r.addLabelValue(NAMED_FIELD_IP_LABEL, "127.0.0.1");
|
||||
w.writeResourceRecord(
|
||||
"http://www.one.net/id=" + indexStr,
|
||||
ArchiveUtils.get14DigitDate(),
|
||||
"text/html; charset=UTF-8",
|
||||
r,
|
||||
new ByteArrayInputStream(baos.toByteArray()),
|
||||
recordLength);
|
||||
return recordLength;
|
||||
recordInfo.addExtraHeader(NAMED_FIELD_IP_LABEL, "127.0.0.1");
|
||||
|
||||
w.writeRecord(recordInfo);
|
||||
return record.length;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -352,12 +380,18 @@ extends TmpDirTestCase implements WARCConstants {
|
||||
protected static void writeRecord(WARCWriter w, String url,
|
||||
String mimetype, int len, ByteArrayOutputStream baos)
|
||||
throws IOException {
|
||||
w.writeResourceRecord(url,
|
||||
ArchiveUtils.get14DigitDate(),
|
||||
mimetype,
|
||||
null,
|
||||
new ByteArrayInputStream(baos.toByteArray()),
|
||||
len);
|
||||
WARCRecordInfo recordInfo = new WARCRecordInfo();
|
||||
recordInfo.setType(WARCRecordType.RESOURCE);
|
||||
recordInfo.setUrl(url);
|
||||
recordInfo.setCreate14DigitDate(ArchiveUtils.get14DigitDate());
|
||||
recordInfo.setMimetype(mimetype);
|
||||
recordInfo.setRecordId(w.generateRecordId(null));
|
||||
recordInfo.setExtraHeaders(null);
|
||||
recordInfo.setContentStream(new ByteArrayInputStream(baos.toByteArray()));
|
||||
recordInfo.setContentLength((long) len);
|
||||
recordInfo.setEnforceLength(true);
|
||||
|
||||
w.writeRecord(recordInfo);
|
||||
}
|
||||
|
||||
protected int iterateRecords(WARCReader r)
|
||||
|
||||
@@ -57,6 +57,7 @@ import static org.archive.modules.fetcher.FetchStatusCodes.S_TOO_MANY_RETRIES;
|
||||
import static org.archive.modules.fetcher.FetchStatusCodes.S_UNATTEMPTED;
|
||||
import static org.archive.modules.fetcher.FetchStatusCodes.S_UNFETCHABLE_URI;
|
||||
import static org.archive.modules.recrawl.RecrawlAttributeConstants.A_FETCH_HISTORY;
|
||||
import static org.archive.modules.recrawl.RecrawlAttributeConstants.A_CONTENT_DIGEST_HISTORY;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
@@ -1895,4 +1896,19 @@ implements Reporter, Serializable, OverlayContext {
|
||||
return (HashMap<String,Object>[]) getData().get(A_FETCH_HISTORY);
|
||||
}
|
||||
|
||||
public HashMap<String, Object> getContentDigestHistory() {
|
||||
@SuppressWarnings("unchecked")
|
||||
HashMap<String, Object> contentDigestHistory = (HashMap<String, Object>) getData().get(A_CONTENT_DIGEST_HISTORY);
|
||||
|
||||
if (contentDigestHistory == null) {
|
||||
contentDigestHistory = new HashMap<String, Object>();
|
||||
getData().put(A_CONTENT_DIGEST_HISTORY, contentDigestHistory);
|
||||
}
|
||||
|
||||
return contentDigestHistory;
|
||||
}
|
||||
|
||||
public boolean hasContentDigestHistory() {
|
||||
return getData().get(A_CONTENT_DIGEST_HISTORY) != null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* This file is part of the Heritrix web crawler (crawler.archive.org).
|
||||
*
|
||||
* Licensed to the Internet Archive (IA) by one or more individual
|
||||
* contributors.
|
||||
*
|
||||
* The IA 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.archive.modules.recrawl;
|
||||
|
||||
import org.archive.modules.CrawlURI;
|
||||
|
||||
/**
|
||||
* Represents a store of information, presumably persistent, keyed by content
|
||||
* digest.
|
||||
*
|
||||
* @contributor nlevitt
|
||||
*/
|
||||
public abstract class AbstractContentDigestHistory {
|
||||
/**
|
||||
* Looks up the history by key {@code persistKeyFor(curi)} and loads it into
|
||||
* {@code curi.getContentDigestHistory()}.
|
||||
*
|
||||
* @param curi
|
||||
*/
|
||||
public abstract void load(CrawlURI curi);
|
||||
|
||||
/**
|
||||
* Stores {@code curi.getContentDigestHistory()} for the key
|
||||
* {@code persistKeyFor(curi)}.
|
||||
*
|
||||
* @param curi
|
||||
*/
|
||||
public abstract void store(CrawlURI curi);
|
||||
|
||||
/**
|
||||
*
|
||||
* @param curi
|
||||
* @return {@code curi.getContentDigestSchemeString()}
|
||||
* @throws IllegalStateException if {@code curi.getContentDigestSchemeString()} is null
|
||||
*/
|
||||
protected String persistKeyFor(CrawlURI curi) {
|
||||
String key = curi.getContentDigestSchemeString();
|
||||
if (key == null) {
|
||||
throw new IllegalStateException("cannot load content digest history, CrawlURI does not have content digest value for " + curi);
|
||||
}
|
||||
return key;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
* This file is part of the Heritrix web crawler (crawler.archive.org).
|
||||
*
|
||||
* Licensed to the Internet Archive (IA) by one or more individual
|
||||
* contributors.
|
||||
*
|
||||
* The IA 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.archive.modules.recrawl;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.archive.bdb.BdbModule;
|
||||
import org.archive.modules.CrawlURI;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.Lifecycle;
|
||||
|
||||
import com.sleepycat.bind.serial.SerialBinding;
|
||||
import com.sleepycat.bind.serial.StoredClassCatalog;
|
||||
import com.sleepycat.bind.tuple.StringBinding;
|
||||
import com.sleepycat.collections.StoredSortedMap;
|
||||
import com.sleepycat.je.Database;
|
||||
import com.sleepycat.je.DatabaseException;
|
||||
|
||||
/**
|
||||
* Bdb content digest history store. Must be a toplevel bean in
|
||||
* crawler-beans.cxml in order to receive {@link Lifecycle} events.
|
||||
*
|
||||
* @see AbstractContentDigestHistory
|
||||
* @contributor nlevitt
|
||||
*/
|
||||
public class BdbContentDigestHistory extends AbstractContentDigestHistory implements Lifecycle {
|
||||
|
||||
private static final Logger logger =
|
||||
Logger.getLogger(BdbContentDigestHistory.class.getName());
|
||||
|
||||
protected BdbModule bdb;
|
||||
@Autowired
|
||||
public void setBdbModule(BdbModule bdb) {
|
||||
this.bdb = bdb;
|
||||
}
|
||||
|
||||
protected String historyDbName = "contentDigestHistory";
|
||||
public String getHistoryDbName() {
|
||||
return this.historyDbName;
|
||||
}
|
||||
public void setHistoryDbName(String name) {
|
||||
this.historyDbName = name;
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
protected StoredSortedMap<String, Map> store;
|
||||
protected Database historyDb;
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("rawtypes")
|
||||
public void start() {
|
||||
if (isRunning()) {
|
||||
return;
|
||||
}
|
||||
StoredSortedMap<String, Map> historyMap;
|
||||
try {
|
||||
StoredClassCatalog classCatalog = bdb.getClassCatalog();
|
||||
historyDb = bdb.openDatabase(getHistoryDbName(), historyDbConfig(), true);
|
||||
historyMap = new StoredSortedMap<String, Map>(
|
||||
historyDb,
|
||||
new StringBinding(),
|
||||
new SerialBinding<Map>(classCatalog, Map.class),
|
||||
true);
|
||||
} catch (DatabaseException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
store = historyMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRunning() {
|
||||
return historyDb != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
if (!isRunning()) {
|
||||
return;
|
||||
}
|
||||
// leave other cleanup to BdbModule
|
||||
historyDb = null;
|
||||
}
|
||||
|
||||
protected transient BdbModule.BdbConfig historyDbConfig;
|
||||
protected BdbModule.BdbConfig historyDbConfig() {
|
||||
if (historyDbConfig == null) {
|
||||
historyDbConfig = new BdbModule.BdbConfig();
|
||||
historyDbConfig.setTransactional(false);
|
||||
historyDbConfig.setAllowCreate(true);
|
||||
historyDbConfig.setDeferredWrite(true);
|
||||
}
|
||||
|
||||
return historyDbConfig;
|
||||
}
|
||||
|
||||
public void load(CrawlURI curi) {
|
||||
// make this call in all cases so that the value is initialized and
|
||||
// WARCWriterProcessor knows it should put the info in there
|
||||
HashMap<String, Object> contentDigestHistory = curi.getContentDigestHistory();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> loadedHistory = store.get(persistKeyFor(curi));
|
||||
if (loadedHistory != null) {
|
||||
if (logger.isLoggable(Level.FINER)) {
|
||||
logger.finer("loaded history by digest " + persistKeyFor(curi)
|
||||
+ " for uri " + curi + " - " + loadedHistory);
|
||||
}
|
||||
contentDigestHistory.putAll(loadedHistory);
|
||||
}
|
||||
}
|
||||
|
||||
public void store(CrawlURI curi) {
|
||||
if (!curi.hasContentDigestHistory()
|
||||
|| curi.getContentDigestHistory().isEmpty()) {
|
||||
logger.warning("not saving empty content digest history (do you "
|
||||
+ " have a ContentDigestHistoryLoader in your disposition"
|
||||
+ " chain?) - " + curi);
|
||||
return;
|
||||
}
|
||||
if (logger.isLoggable(Level.FINER)) {
|
||||
logger.finer("storing history by digest " + persistKeyFor(curi)
|
||||
+ " for uri " + curi + " - "
|
||||
+ curi.getContentDigestHistory());
|
||||
}
|
||||
store.put(persistKeyFor(curi), curi.getContentDigestHistory());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* This file is part of the Heritrix web crawler (crawler.archive.org).
|
||||
*
|
||||
* Licensed to the Internet Archive (IA) by one or more individual
|
||||
* contributors.
|
||||
*
|
||||
* The IA 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.archive.modules.recrawl;
|
||||
|
||||
import org.archive.modules.CrawlURI;
|
||||
import org.archive.modules.Processor;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
public class ContentDigestHistoryLoader extends Processor {
|
||||
|
||||
protected AbstractContentDigestHistory contentDigestHistory;
|
||||
@Autowired
|
||||
public void setContentDigestHistory(
|
||||
AbstractContentDigestHistory contentDigestHistory) {
|
||||
this.contentDigestHistory = contentDigestHistory;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean shouldProcess(CrawlURI uri) {
|
||||
return uri.getContentDigest() != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void innerProcess(CrawlURI curi) throws InterruptedException {
|
||||
contentDigestHistory.load(curi);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* This file is part of the Heritrix web crawler (crawler.archive.org).
|
||||
*
|
||||
* Licensed to the Internet Archive (IA) by one or more individual
|
||||
* contributors.
|
||||
*
|
||||
* The IA 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.archive.modules.recrawl;
|
||||
|
||||
import org.archive.modules.CrawlURI;
|
||||
import org.archive.modules.Processor;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
public class ContentDigestHistoryStorer extends Processor {
|
||||
|
||||
protected AbstractContentDigestHistory contentDigestHistory;
|
||||
@Autowired
|
||||
public void setContentDigestHistory(
|
||||
AbstractContentDigestHistory contentDigestHistory) {
|
||||
this.contentDigestHistory = contentDigestHistory;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean shouldProcess(CrawlURI uri) {
|
||||
return uri.getContentDigest() != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void innerProcess(CrawlURI curi) throws InterruptedException {
|
||||
contentDigestHistory.store(curi);
|
||||
}
|
||||
}
|
||||
@@ -32,14 +32,30 @@ public interface RecrawlAttributeConstants {
|
||||
public static final String A_FETCH_HISTORY = "fetch-history";
|
||||
/** content digest */
|
||||
public static final String A_CONTENT_DIGEST = "content-digest";
|
||||
/** header name (and AList key) for last-modified timestamp */
|
||||
/** header name (and AList key) for last-modified timestamp */
|
||||
public static final String A_LAST_MODIFIED_HEADER = "last-modified";
|
||||
/** header name (and AList key) for ETag */
|
||||
/** header name (and AList key) for ETag */
|
||||
public static final String A_ETAG_HEADER = "etag";
|
||||
/** key for status (when in history) */
|
||||
public static final String A_STATUS = "status";
|
||||
/** reference length (content length or virtual length */
|
||||
public static final String A_REFERENCE_LENGTH = "reference-length";
|
||||
|
||||
// constants for uri-agnostic content digest based dedupe
|
||||
/** content digest history map */
|
||||
public static final String A_CONTENT_DIGEST_HISTORY = "content-digest-history";
|
||||
/** url that the content payload was written for */
|
||||
public static final String A_ORIGINAL_URL = "original-url";
|
||||
/** warc record id of warc record with the content payload */
|
||||
public static final String A_WARC_RECORD_ID = "warc-record-id";
|
||||
/** warc filename containing the content payload */
|
||||
public static final String A_WARC_FILENAME = "warc-filename";
|
||||
/** offset into warc file of warc record with content payload */
|
||||
public static final String A_WARC_FILE_OFFSET = "warc-file-offset";
|
||||
/** date content payload was written */
|
||||
public static final String A_ORIGINAL_DATE = "content-written-date";
|
||||
/** number of times we've seen this content digest (1 original + n duplicates) */
|
||||
public static final String A_CONTENT_DIGEST_COUNT = "content-digest-count";
|
||||
|
||||
/**
|
||||
* Writer processors of all types are encouraged to put a 'writeTag'
|
||||
|
||||
@@ -26,16 +26,19 @@ import static org.archive.io.warc.WARCConstants.HEADER_KEY_IP;
|
||||
import static org.archive.io.warc.WARCConstants.HEADER_KEY_LAST_MODIFIED;
|
||||
import static org.archive.io.warc.WARCConstants.HEADER_KEY_PAYLOAD_DIGEST;
|
||||
import static org.archive.io.warc.WARCConstants.HEADER_KEY_PROFILE;
|
||||
import static org.archive.io.warc.WARCConstants.HEADER_KEY_REFERS_TO;
|
||||
import static org.archive.io.warc.WARCConstants.HEADER_KEY_REFERS_TO_DATE;
|
||||
import static org.archive.io.warc.WARCConstants.HEADER_KEY_REFERS_TO_FILENAME;
|
||||
import static org.archive.io.warc.WARCConstants.HEADER_KEY_REFERS_TO_FILE_OFFSET;
|
||||
import static org.archive.io.warc.WARCConstants.HEADER_KEY_REFERS_TO_TARGET_URI;
|
||||
import static org.archive.io.warc.WARCConstants.HEADER_KEY_TRUNCATED;
|
||||
import static org.archive.io.warc.WARCConstants.HTTP_REQUEST_MIMETYPE;
|
||||
import static org.archive.io.warc.WARCConstants.HTTP_RESPONSE_MIMETYPE;
|
||||
import static org.archive.io.warc.WARCConstants.METADATA;
|
||||
import static org.archive.io.warc.WARCConstants.NAMED_FIELD_TRUNCATED_VALUE_HEAD;
|
||||
import static org.archive.io.warc.WARCConstants.NAMED_FIELD_TRUNCATED_VALUE_LENGTH;
|
||||
import static org.archive.io.warc.WARCConstants.NAMED_FIELD_TRUNCATED_VALUE_TIME;
|
||||
import static org.archive.io.warc.WARCConstants.PROFILE_REVISIT_IDENTICAL_DIGEST;
|
||||
import static org.archive.io.warc.WARCConstants.PROFILE_REVISIT_NOT_MODIFIED;
|
||||
import static org.archive.io.warc.WARCConstants.REQUEST;
|
||||
import static org.archive.io.warc.WARCConstants.TYPE;
|
||||
import static org.archive.modules.CoreAttributeConstants.A_DNS_SERVER_IP_LABEL;
|
||||
import static org.archive.modules.CoreAttributeConstants.A_FTP_CONTROL_CONVERSATION;
|
||||
@@ -44,8 +47,14 @@ import static org.archive.modules.CoreAttributeConstants.A_SOURCE_TAG;
|
||||
import static org.archive.modules.CoreAttributeConstants.HEADER_TRUNC;
|
||||
import static org.archive.modules.CoreAttributeConstants.LENGTH_TRUNC;
|
||||
import static org.archive.modules.CoreAttributeConstants.TIMER_TRUNC;
|
||||
import static org.archive.modules.recrawl.RecrawlAttributeConstants.A_CONTENT_DIGEST_COUNT;
|
||||
import static org.archive.modules.recrawl.RecrawlAttributeConstants.A_ETAG_HEADER;
|
||||
import static org.archive.modules.recrawl.RecrawlAttributeConstants.A_LAST_MODIFIED_HEADER;
|
||||
import static org.archive.modules.recrawl.RecrawlAttributeConstants.A_ORIGINAL_DATE;
|
||||
import static org.archive.modules.recrawl.RecrawlAttributeConstants.A_ORIGINAL_URL;
|
||||
import static org.archive.modules.recrawl.RecrawlAttributeConstants.A_WARC_FILENAME;
|
||||
import static org.archive.modules.recrawl.RecrawlAttributeConstants.A_WARC_FILE_OFFSET;
|
||||
import static org.archive.modules.recrawl.RecrawlAttributeConstants.A_WARC_RECORD_ID;
|
||||
import static org.archive.modules.recrawl.RecrawlAttributeConstants.A_WRITE_TAG;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
@@ -69,8 +78,9 @@ import java.util.logging.Logger;
|
||||
import org.apache.commons.httpclient.HttpStatus;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.archive.io.ArchiveFileConstants;
|
||||
import org.archive.io.ReplayInputStream;
|
||||
import org.archive.io.warc.WARCConstants.WARCRecordType;
|
||||
import org.archive.io.warc.WARCRecordInfo;
|
||||
import org.archive.io.warc.WARCWriter;
|
||||
import org.archive.io.warc.WARCWriterPool;
|
||||
import org.archive.io.warc.WARCWriterPoolSettings;
|
||||
@@ -143,6 +153,9 @@ public class WARCWriterProcessor extends WriterPoolProcessor implements WARCWrit
|
||||
/**
|
||||
* Whether to write 'revisit' type records when a URI's history indicates
|
||||
* the previous fetch had an identical content digest. Default is true.
|
||||
*
|
||||
* Decision applies to either URI-based fetch history or URI-agnostic
|
||||
* content digest-based history.
|
||||
*/
|
||||
{
|
||||
setWriteRevisitForIdenticalDigests(true);
|
||||
@@ -240,6 +253,8 @@ public class WARCWriterProcessor extends WriterPoolProcessor implements WARCWrit
|
||||
// They'll be added to totals below, in finally block, after records
|
||||
// have been written.
|
||||
writer.resetTmpStats();
|
||||
writer.resetTmpRecordLog();
|
||||
|
||||
// Write a request, response, and metadata all in the one
|
||||
// 'transaction'.
|
||||
final URI baseid = getRecordID();
|
||||
@@ -266,33 +281,60 @@ public class WARCWriterProcessor extends WriterPoolProcessor implements WARCWrit
|
||||
throw e;
|
||||
} finally {
|
||||
if (writer != null) {
|
||||
if (WARCWriter.getStat(writer.getTmpStats(), WARCWriter.TOTALS, WARCWriter.NUM_RECORDS) > 0l) {
|
||||
addStats(writer.getTmpStats());
|
||||
urlsWritten.incrementAndGet();
|
||||
}
|
||||
if (logger.isLoggable(Level.FINE)) {
|
||||
logger.fine("wrote "
|
||||
+ WARCWriter.getStat(writer.getTmpStats(), WARCWriter.TOTALS, WARCWriter.SIZE_ON_DISK)
|
||||
+ " bytes to " + writer.getFile().getName() + " for " + curi);
|
||||
}
|
||||
setTotalBytesWritten(getTotalBytesWritten() +
|
||||
(writer.getPosition() - position));
|
||||
updateMetadataAfterWrite(curi, writer, position);
|
||||
getPool().returnFile(writer);
|
||||
|
||||
String filename = writer.getFile().getName();
|
||||
if (filename.endsWith(ArchiveFileConstants.OCCUPIED_SUFFIX)) {
|
||||
filename = filename.substring(0, filename.length() - ArchiveFileConstants.OCCUPIED_SUFFIX.length());
|
||||
}
|
||||
curi.addExtraInfo("warcFilename", filename);
|
||||
|
||||
Map<String,Object>[] history = curi.getFetchHistory();
|
||||
if (history != null && history[0] != null) {
|
||||
history[0].put(A_WRITE_TAG, filename);
|
||||
}
|
||||
}
|
||||
}
|
||||
return checkBytesWritten();
|
||||
}
|
||||
|
||||
protected void updateMetadataAfterWrite(final CrawlURI curi,
|
||||
WARCWriter writer, long startPosition) {
|
||||
if (WARCWriter.getStat(writer.getTmpStats(), WARCWriter.TOTALS, WARCWriter.NUM_RECORDS) > 0l) {
|
||||
addStats(writer.getTmpStats());
|
||||
urlsWritten.incrementAndGet();
|
||||
}
|
||||
if (logger.isLoggable(Level.FINE)) {
|
||||
logger.fine("wrote "
|
||||
+ WARCWriter.getStat(writer.getTmpStats(), WARCWriter.TOTALS, WARCWriter.SIZE_ON_DISK)
|
||||
+ " bytes to " + writer.getFile().getName() + " for " + curi);
|
||||
}
|
||||
setTotalBytesWritten(getTotalBytesWritten() + (writer.getPosition() - startPosition));
|
||||
|
||||
curi.addExtraInfo("warcFilename", writer.getFilenameWithoutOccupiedSuffix());
|
||||
// curi.addExtraInfo("warcOffset", startPosition);
|
||||
|
||||
// history for uri-based dedupe
|
||||
Map<String,Object>[] history = curi.getFetchHistory();
|
||||
if (history != null && history[0] != null) {
|
||||
history[0].put(A_WRITE_TAG, writer.getFilenameWithoutOccupiedSuffix());
|
||||
}
|
||||
|
||||
// history for uri-agnostic, content digest based dedupe
|
||||
if (curi.getContentDigest() != null && curi.hasContentDigestHistory()) {
|
||||
for (WARCRecordInfo warcRecord: writer.getTmpRecordLog()) {
|
||||
if ((warcRecord.getType() == WARCRecordType.RESPONSE
|
||||
|| warcRecord.getType() == WARCRecordType.RESOURCE)
|
||||
&& warcRecord.getContentStream() != null
|
||||
&& warcRecord.getContentLength() > 0) {
|
||||
curi.getContentDigestHistory().put(A_ORIGINAL_URL, warcRecord.getUrl());
|
||||
curi.getContentDigestHistory().put(A_WARC_RECORD_ID, warcRecord.getRecordId());
|
||||
curi.getContentDigestHistory().put(A_WARC_FILENAME, warcRecord.getWARCFilename());
|
||||
curi.getContentDigestHistory().put(A_WARC_FILE_OFFSET, warcRecord.getWARCFileOffset());
|
||||
curi.getContentDigestHistory().put(A_ORIGINAL_DATE, warcRecord.getCreate14DigitDate());
|
||||
curi.getContentDigestHistory().put(A_CONTENT_DIGEST_COUNT, 1);
|
||||
} else if (warcRecord.getType() == WARCRecordType.REVISIT
|
||||
&& curi.getAnnotations().contains("warcRevisit:uriAgnosticDigest")) {
|
||||
Integer oldCount = (Integer) curi.getContentDigestHistory().get(A_CONTENT_DIGEST_COUNT);
|
||||
if (oldCount == null) {
|
||||
// shouldn't happen, log a warning?
|
||||
oldCount = 1;
|
||||
}
|
||||
curi.getContentDigestHistory().put(A_CONTENT_DIGEST_COUNT, oldCount + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void addStats(Map<String, Map<String, Long>> substats) {
|
||||
for (String key: substats.keySet()) {
|
||||
@@ -314,35 +356,71 @@ public class WARCWriterProcessor extends WriterPoolProcessor implements WARCWrit
|
||||
}
|
||||
}
|
||||
|
||||
private void writeDnsRecords(final CrawlURI curi, WARCWriter w,
|
||||
protected void writeDnsRecords(final CrawlURI curi, WARCWriter w,
|
||||
final URI baseid, final String timestamp) throws IOException {
|
||||
ANVLRecord headers = null;
|
||||
WARCRecordInfo recordInfo = new WARCRecordInfo();
|
||||
recordInfo.setType(WARCRecordType.RESPONSE);
|
||||
recordInfo.setUrl(curi.toString());
|
||||
recordInfo.setCreate14DigitDate(timestamp);
|
||||
recordInfo.setMimetype(curi.getContentType());
|
||||
recordInfo.setRecordId(baseid);
|
||||
|
||||
recordInfo.setContentLength(curi.getRecorder().getRecordedInput().getSize());
|
||||
recordInfo.setEnforceLength(true);
|
||||
|
||||
String ip = (String)curi.getData().get(A_DNS_SERVER_IP_LABEL);
|
||||
if (ip != null && ip.length() > 0) {
|
||||
headers = new ANVLRecord(1);
|
||||
headers.addLabelValue(HEADER_KEY_IP, ip);
|
||||
recordInfo.addExtraHeader(HEADER_KEY_IP, ip);
|
||||
}
|
||||
writeResponse(w, timestamp, curi.getContentType(), baseid,
|
||||
curi, headers);
|
||||
|
||||
ReplayInputStream ris =
|
||||
curi.getRecorder().getRecordedInput().getReplayInputStream();
|
||||
recordInfo.setContentStream(ris);
|
||||
|
||||
try {
|
||||
w.writeRecord(recordInfo);
|
||||
} finally {
|
||||
IOUtils.closeQuietly(ris);
|
||||
}
|
||||
|
||||
recordInfo.getRecordId();
|
||||
}
|
||||
|
||||
private void writeWhoisRecords(WARCWriter w, CrawlURI curi, URI baseid,
|
||||
protected void writeWhoisRecords(WARCWriter w, CrawlURI curi, URI baseid,
|
||||
String timestamp) throws IOException {
|
||||
ANVLRecord headers = new ANVLRecord(1);
|
||||
WARCRecordInfo recordInfo = new WARCRecordInfo();
|
||||
recordInfo.setType(WARCRecordType.RESPONSE);
|
||||
recordInfo.setUrl(curi.toString());
|
||||
recordInfo.setCreate14DigitDate(timestamp);
|
||||
recordInfo.setMimetype(curi.getContentType());
|
||||
recordInfo.setRecordId(baseid);
|
||||
recordInfo.setContentLength(curi.getRecorder().getRecordedInput().getSize());
|
||||
recordInfo.setEnforceLength(true);
|
||||
|
||||
Object whoisServerIP = curi.getData().get(CoreAttributeConstants.A_WHOIS_SERVER_IP);
|
||||
if (whoisServerIP != null) {
|
||||
headers.addLabelValue(HEADER_KEY_IP, whoisServerIP.toString());
|
||||
recordInfo.addExtraHeader(HEADER_KEY_IP, whoisServerIP.toString());
|
||||
}
|
||||
writeResponse(w, timestamp, curi.getContentType(), baseid, curi, headers);
|
||||
|
||||
ReplayInputStream ris =
|
||||
curi.getRecorder().getRecordedInput().getReplayInputStream();
|
||||
recordInfo.setContentStream(ris);
|
||||
|
||||
try {
|
||||
w.writeRecord(recordInfo);
|
||||
} finally {
|
||||
IOUtils.closeQuietly(ris);
|
||||
}
|
||||
recordInfo.getRecordId();
|
||||
}
|
||||
|
||||
private void writeHttpRecords(final CrawlURI curi, WARCWriter w,
|
||||
protected void writeHttpRecords(final CrawlURI curi, WARCWriter w,
|
||||
final URI baseid, final String timestamp) throws IOException {
|
||||
// Add named fields for ip, checksum, and relate the metadata
|
||||
// and request to the resource field.
|
||||
// TODO: Use other than ANVL (or rename ANVL as NameValue or
|
||||
// use RFC822 (commons-httpclient?).
|
||||
ANVLRecord headers = new ANVLRecord(5);
|
||||
ANVLRecord headers = new ANVLRecord();
|
||||
if (curi.getContentDigest() != null) {
|
||||
headers.addLabelValue(HEADER_KEY_PAYLOAD_DIGEST,
|
||||
curi.getContentDigestSchemeString());
|
||||
@@ -350,7 +428,12 @@ public class WARCWriterProcessor extends WriterPoolProcessor implements WARCWrit
|
||||
headers.addLabelValue(HEADER_KEY_IP, getHostAddress(curi));
|
||||
URI rid;
|
||||
|
||||
if (IdenticalDigestDecideRule.hasIdenticalDigest(curi) &&
|
||||
if (getWriteRevisitForIdenticalDigests()
|
||||
&& curi.hasContentDigestHistory()
|
||||
&& curi.getContentDigestHistory().get(A_ORIGINAL_URL) != null) {
|
||||
rid = writeRevisitUriAgnosticDigest(w, timestamp,
|
||||
HTTP_RESPONSE_MIMETYPE, baseid, curi, headers);
|
||||
} else if (IdenticalDigestDecideRule.hasIdenticalDigest(curi) &&
|
||||
getWriteRevisitForIdenticalDigests()) {
|
||||
rid = writeRevisitDigest(w, timestamp, HTTP_RESPONSE_MIMETYPE,
|
||||
baseid, curi, headers);
|
||||
@@ -377,7 +460,7 @@ public class WARCWriterProcessor extends WriterPoolProcessor implements WARCWrit
|
||||
baseid, curi, headers);
|
||||
}
|
||||
|
||||
headers = new ANVLRecord(1);
|
||||
headers = new ANVLRecord();
|
||||
headers.addLabelValue(HEADER_KEY_CONCURRENT_TO,
|
||||
'<' + rid.toString() + '>');
|
||||
|
||||
@@ -390,9 +473,9 @@ public class WARCWriterProcessor extends WriterPoolProcessor implements WARCWrit
|
||||
}
|
||||
}
|
||||
|
||||
private void writeFtpRecords(WARCWriter w, final CrawlURI curi, final URI baseid,
|
||||
protected void writeFtpRecords(WARCWriter w, final CrawlURI curi, final URI baseid,
|
||||
final String timestamp) throws IOException {
|
||||
ANVLRecord headers = new ANVLRecord(3);
|
||||
ANVLRecord headers = new ANVLRecord();
|
||||
headers.addLabelValue(HEADER_KEY_IP, getHostAddress(curi));
|
||||
String controlConversation = curi.getData().get(A_FTP_CONTROL_CONVERSATION).toString();
|
||||
URI rid = writeFtpControlConversation(w, timestamp, baseid, curi, headers, controlConversation);
|
||||
@@ -408,7 +491,7 @@ public class WARCWriterProcessor extends WriterPoolProcessor implements WARCWrit
|
||||
rid = writeRevisitDigest(w, timestamp, null,
|
||||
baseid, curi, headers, 0);
|
||||
} else {
|
||||
headers = new ANVLRecord(3);
|
||||
headers = new ANVLRecord();
|
||||
// Check for truncated annotation
|
||||
String value = null;
|
||||
Collection<String> anno = curi.getAnnotations();
|
||||
@@ -433,7 +516,7 @@ public class WARCWriterProcessor extends WriterPoolProcessor implements WARCWrit
|
||||
}
|
||||
}
|
||||
if (getWriteMetadata()) {
|
||||
headers = new ANVLRecord(1);
|
||||
headers = new ANVLRecord();
|
||||
headers.addLabelValue(HEADER_KEY_CONCURRENT_TO, '<' + rid.toString() + '>');
|
||||
writeMetadata(w, timestamp, baseid, curi, headers);
|
||||
}
|
||||
@@ -442,12 +525,25 @@ public class WARCWriterProcessor extends WriterPoolProcessor implements WARCWrit
|
||||
protected URI writeFtpControlConversation(WARCWriter w, String timestamp,
|
||||
URI baseid, CrawlURI curi, ANVLRecord headers,
|
||||
String controlConversation) throws IOException {
|
||||
final URI uid = qualifyRecordID(baseid, TYPE, METADATA);
|
||||
|
||||
WARCRecordInfo recordInfo = new WARCRecordInfo();
|
||||
recordInfo.setCreate14DigitDate(timestamp);
|
||||
recordInfo.setUrl(curi.toString());
|
||||
recordInfo.setMimetype(FTP_CONTROL_CONVERSATION_MIMETYPE);
|
||||
recordInfo.setExtraHeaders(headers);
|
||||
recordInfo.setEnforceLength(true);
|
||||
recordInfo.setType(WARCRecordType.METADATA);
|
||||
|
||||
recordInfo.setRecordId(qualifyRecordID(baseid, TYPE, WARCRecordType.METADATA.toString()));
|
||||
|
||||
byte[] b = controlConversation.getBytes("UTF-8");
|
||||
w.writeMetadataRecord(curi.toString(), timestamp,
|
||||
FTP_CONTROL_CONVERSATION_MIMETYPE, uid, headers,
|
||||
new ByteArrayInputStream(b), b.length);
|
||||
return uid;
|
||||
|
||||
recordInfo.setContentStream(new ByteArrayInputStream(b));
|
||||
recordInfo.setContentLength((long) b.length);
|
||||
|
||||
w.writeRecord(recordInfo);
|
||||
|
||||
return recordInfo.getRecordId();
|
||||
}
|
||||
|
||||
protected URI writeRequest(final WARCWriter w,
|
||||
@@ -455,17 +551,29 @@ public class WARCWriterProcessor extends WriterPoolProcessor implements WARCWrit
|
||||
final URI baseid, final CrawlURI curi,
|
||||
final ANVLRecord namedFields)
|
||||
throws IOException {
|
||||
final URI uid = qualifyRecordID(baseid, TYPE, REQUEST);
|
||||
WARCRecordInfo recordInfo = new WARCRecordInfo();
|
||||
recordInfo.setType(WARCRecordType.REQUEST);
|
||||
recordInfo.setUrl(curi.toString());
|
||||
recordInfo.setCreate14DigitDate(timestamp);
|
||||
recordInfo.setMimetype(mimetype);
|
||||
recordInfo.setExtraHeaders(namedFields);
|
||||
recordInfo.setContentLength(curi.getRecorder().getRecordedOutput().getSize());
|
||||
recordInfo.setEnforceLength(true);
|
||||
|
||||
final URI uid = qualifyRecordID(baseid, TYPE, WARCRecordType.REQUEST.toString());
|
||||
recordInfo.setRecordId(uid);
|
||||
|
||||
ReplayInputStream
|
||||
ris = curi.getRecorder().getRecordedOutput().getReplayInputStream();
|
||||
recordInfo.setContentStream(ris);
|
||||
|
||||
try {
|
||||
w.writeRequestRecord(curi.toString(), timestamp, mimetype, uid,
|
||||
namedFields, ris,
|
||||
curi.getRecorder().getRecordedOutput().getSize());
|
||||
w.writeRecord(recordInfo);
|
||||
} finally {
|
||||
IOUtils.closeQuietly(ris);
|
||||
}
|
||||
return uid;
|
||||
|
||||
return recordInfo.getRecordId();
|
||||
}
|
||||
|
||||
protected URI writeResponse(final WARCWriter w,
|
||||
@@ -473,16 +581,27 @@ public class WARCWriterProcessor extends WriterPoolProcessor implements WARCWrit
|
||||
final URI baseid, final CrawlURI curi,
|
||||
final ANVLRecord namedFields)
|
||||
throws IOException {
|
||||
WARCRecordInfo recordInfo = new WARCRecordInfo();
|
||||
recordInfo.setType(WARCRecordType.RESPONSE);
|
||||
recordInfo.setUrl(curi.toString());
|
||||
recordInfo.setCreate14DigitDate(timestamp);
|
||||
recordInfo.setMimetype(mimetype);
|
||||
recordInfo.setRecordId(baseid);
|
||||
recordInfo.setExtraHeaders(namedFields);
|
||||
recordInfo.setContentLength(curi.getRecorder().getRecordedInput().getSize());
|
||||
recordInfo.setEnforceLength(true);
|
||||
|
||||
ReplayInputStream ris =
|
||||
curi.getRecorder().getRecordedInput().getReplayInputStream();
|
||||
recordInfo.setContentStream(ris);
|
||||
|
||||
try {
|
||||
w.writeResponseRecord(curi.toString(), timestamp, mimetype, baseid,
|
||||
namedFields, ris,
|
||||
curi.getRecorder().getRecordedInput().getSize());
|
||||
w.writeRecord(recordInfo);
|
||||
} finally {
|
||||
IOUtils.closeQuietly(ris);
|
||||
}
|
||||
return baseid;
|
||||
|
||||
return recordInfo.getRecordId();
|
||||
}
|
||||
|
||||
protected URI writeResource(final WARCWriter w,
|
||||
@@ -490,15 +609,25 @@ public class WARCWriterProcessor extends WriterPoolProcessor implements WARCWrit
|
||||
final URI baseid, final CrawlURI curi,
|
||||
final ANVLRecord namedFields)
|
||||
throws IOException {
|
||||
WARCRecordInfo recordInfo = new WARCRecordInfo();
|
||||
recordInfo.setType(WARCRecordType.RESOURCE);
|
||||
recordInfo.setUrl(curi.toString());
|
||||
recordInfo.setCreate14DigitDate(timestamp);
|
||||
recordInfo.setMimetype(mimetype);
|
||||
recordInfo.setRecordId(baseid);
|
||||
recordInfo.setExtraHeaders(namedFields);
|
||||
recordInfo.setContentLength(curi.getRecorder().getRecordedInput().getSize());
|
||||
recordInfo.setEnforceLength(true);
|
||||
|
||||
ReplayInputStream ris = curi.getRecorder().getRecordedInput().getReplayInputStream();
|
||||
recordInfo.setContentStream(ris);
|
||||
try {
|
||||
w.writeResourceRecord(curi.toString(), timestamp, mimetype, baseid,
|
||||
namedFields, ris,
|
||||
curi.getRecorder().getRecordedInput().getSize());
|
||||
w.writeRecord(recordInfo);
|
||||
} finally {
|
||||
IOUtils.closeQuietly(ris);
|
||||
}
|
||||
return baseid;
|
||||
|
||||
return recordInfo.getRecordId();
|
||||
}
|
||||
|
||||
protected URI writeRevisitDigest(final WARCWriter w,
|
||||
@@ -518,20 +647,87 @@ public class WARCWriterProcessor extends WriterPoolProcessor implements WARCWrit
|
||||
final String timestamp, final String mimetype, final URI baseid,
|
||||
final CrawlURI curi, final ANVLRecord namedFields,
|
||||
long contentLength) throws IOException {
|
||||
WARCRecordInfo recordInfo = new WARCRecordInfo();
|
||||
recordInfo.setType(WARCRecordType.REVISIT);
|
||||
recordInfo.setUrl(curi.toString());
|
||||
recordInfo.setCreate14DigitDate(timestamp);
|
||||
recordInfo.setMimetype(mimetype);
|
||||
recordInfo.setRecordId(baseid);
|
||||
recordInfo.setContentLength(contentLength);
|
||||
recordInfo.setEnforceLength(false);
|
||||
|
||||
namedFields.addLabelValue(
|
||||
HEADER_KEY_PROFILE, PROFILE_REVISIT_IDENTICAL_DIGEST);
|
||||
namedFields.addLabelValue(
|
||||
HEADER_KEY_TRUNCATED, NAMED_FIELD_TRUNCATED_VALUE_LENGTH);
|
||||
recordInfo.setExtraHeaders(namedFields);
|
||||
|
||||
ReplayInputStream ris =
|
||||
curi.getRecorder().getRecordedInput().getReplayInputStream();
|
||||
recordInfo.setContentStream(ris);
|
||||
|
||||
try {
|
||||
w.writeRevisitRecord(curi.toString(), timestamp, mimetype, baseid,
|
||||
namedFields, ris, contentLength);
|
||||
w.writeRecord(recordInfo);
|
||||
} finally {
|
||||
IOUtils.closeQuietly(ris);
|
||||
}
|
||||
curi.getAnnotations().add("warcRevisit:digest");
|
||||
return baseid;
|
||||
|
||||
return recordInfo.getRecordId();
|
||||
}
|
||||
|
||||
protected URI writeRevisitUriAgnosticDigest(WARCWriter w, String timestamp,
|
||||
String mimetype, URI baseid, CrawlURI curi,
|
||||
ANVLRecord headers) throws IOException {
|
||||
|
||||
WARCRecordInfo recordInfo = new WARCRecordInfo();
|
||||
recordInfo.setType(WARCRecordType.REVISIT);
|
||||
recordInfo.setUrl(curi.toString());
|
||||
recordInfo.setCreate14DigitDate(timestamp);
|
||||
recordInfo.setMimetype(mimetype);
|
||||
recordInfo.setRecordId(baseid);
|
||||
recordInfo.setEnforceLength(false);
|
||||
|
||||
long revisedLength = curi.getRecorder().getRecordedInput().getContentBegin();
|
||||
revisedLength = revisedLength > 0 ? revisedLength : curi.getRecorder().getRecordedInput().getSize();
|
||||
recordInfo.setContentLength(revisedLength);
|
||||
|
||||
headers.addLabelValue(
|
||||
HEADER_KEY_PROFILE, PROFILE_REVISIT_IDENTICAL_DIGEST);
|
||||
headers.addLabelValue(
|
||||
HEADER_KEY_TRUNCATED, NAMED_FIELD_TRUNCATED_VALUE_LENGTH);
|
||||
|
||||
/*
|
||||
* ISO 28500 WARC ISO standard draft says: "The WARC-Refers-To field may
|
||||
* also be used to associate a record of type 'revisit' or 'conversion'
|
||||
* with the preceding record which helped determine the present record
|
||||
* content."
|
||||
*/
|
||||
headers.addLabelValue(HEADER_KEY_REFERS_TO,
|
||||
curi.getContentDigestHistory().get(A_WARC_RECORD_ID).toString());
|
||||
headers.addLabelValue(HEADER_KEY_REFERS_TO_TARGET_URI,
|
||||
curi.getContentDigestHistory().get(A_ORIGINAL_URL).toString());
|
||||
headers.addLabelValue(HEADER_KEY_REFERS_TO_DATE,
|
||||
curi.getContentDigestHistory().get(A_ORIGINAL_DATE).toString());
|
||||
headers.addLabelValue(HEADER_KEY_REFERS_TO_FILENAME,
|
||||
curi.getContentDigestHistory().get(A_WARC_FILENAME).toString());
|
||||
headers.addLabelValue(HEADER_KEY_REFERS_TO_FILE_OFFSET,
|
||||
curi.getContentDigestHistory().get(A_WARC_FILE_OFFSET).toString());
|
||||
|
||||
recordInfo.setExtraHeaders(headers);
|
||||
|
||||
ReplayInputStream ris =
|
||||
curi.getRecorder().getRecordedInput().getReplayInputStream();
|
||||
recordInfo.setContentStream(ris);
|
||||
|
||||
try {
|
||||
w.writeRecord(recordInfo);
|
||||
} finally {
|
||||
IOUtils.closeQuietly(ris);
|
||||
}
|
||||
curi.getAnnotations().add("warcRevisit:uriAgnosticDigest");
|
||||
|
||||
return recordInfo.getRecordId();
|
||||
}
|
||||
|
||||
protected URI writeRevisitNotModified(final WARCWriter w,
|
||||
@@ -539,10 +735,22 @@ public class WARCWriterProcessor extends WriterPoolProcessor implements WARCWrit
|
||||
final URI baseid, final CrawlURI puri,
|
||||
final ANVLRecord namedFields)
|
||||
throws IOException {
|
||||
CrawlURI curi = (CrawlURI) puri;
|
||||
CrawlURI curi = (CrawlURI) puri;
|
||||
|
||||
WARCRecordInfo recordInfo = new WARCRecordInfo();
|
||||
recordInfo.setType(WARCRecordType.REVISIT);
|
||||
recordInfo.setUrl(curi.toString());
|
||||
recordInfo.setCreate14DigitDate(timestamp);
|
||||
recordInfo.setMimetype(null);
|
||||
recordInfo.setRecordId(baseid);
|
||||
recordInfo.setContentLength((long) 0);
|
||||
recordInfo.setEnforceLength(false);
|
||||
|
||||
namedFields.addLabelValue(
|
||||
HEADER_KEY_PROFILE, PROFILE_REVISIT_NOT_MODIFIED);
|
||||
// save just enough context to understand basis of not-modified
|
||||
recordInfo.setExtraHeaders(namedFields);
|
||||
|
||||
if(curi.isHttpTransaction()) {
|
||||
saveHeader(curi, namedFields, A_ETAG_HEADER, HEADER_KEY_ETAG);
|
||||
saveHeader(curi, namedFields, A_LAST_MODIFIED_HEADER, HEADER_KEY_LAST_MODIFIED);
|
||||
@@ -552,14 +760,15 @@ public class WARCWriterProcessor extends WriterPoolProcessor implements WARCWrit
|
||||
NAMED_FIELD_TRUNCATED_VALUE_LENGTH);
|
||||
ReplayInputStream ris =
|
||||
curi.getRecorder().getRecordedInput().getReplayInputStream();
|
||||
recordInfo.setContentStream(ris);
|
||||
|
||||
try {
|
||||
w.writeRevisitRecord(curi.toString(), timestamp, null, baseid,
|
||||
namedFields, ris, 0);
|
||||
w.writeRecord(recordInfo);
|
||||
} finally {
|
||||
IOUtils.closeQuietly(ris);
|
||||
}
|
||||
curi.getAnnotations().add("warcRevisit:notModified");
|
||||
return baseid;
|
||||
return recordInfo.getRecordId();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -579,7 +788,16 @@ public class WARCWriterProcessor extends WriterPoolProcessor implements WARCWrit
|
||||
final URI baseid, final CrawlURI curi,
|
||||
final ANVLRecord namedFields)
|
||||
throws IOException {
|
||||
final URI uid = qualifyRecordID(baseid, TYPE, METADATA);
|
||||
WARCRecordInfo recordInfo = new WARCRecordInfo();
|
||||
recordInfo.setType(WARCRecordType.METADATA);
|
||||
recordInfo.setUrl(curi.toString());
|
||||
recordInfo.setCreate14DigitDate(timestamp);
|
||||
recordInfo.setMimetype(ANVLRecord.MIMETYPE);
|
||||
recordInfo.setExtraHeaders(namedFields);
|
||||
recordInfo.setEnforceLength(true);
|
||||
|
||||
recordInfo.setRecordId(qualifyRecordID(baseid, TYPE, WARCRecordType.METADATA.toString()));
|
||||
|
||||
// Get some metadata from the curi.
|
||||
// TODO: Get all curi metadata.
|
||||
// TODO: Use other than ANVL (or rename ANVL as NameValue or use
|
||||
@@ -640,9 +858,12 @@ public class WARCWriterProcessor extends WriterPoolProcessor implements WARCWrit
|
||||
// Annotations.
|
||||
|
||||
byte [] b = r.getUTF8Bytes();
|
||||
w.writeMetadataRecord(curi.toString(), timestamp, ANVLRecord.MIMETYPE,
|
||||
uid, namedFields, new ByteArrayInputStream(b), b.length);
|
||||
return uid;
|
||||
recordInfo.setContentStream(new ByteArrayInputStream(b));
|
||||
recordInfo.setContentLength((long) b.length);
|
||||
|
||||
w.writeRecord(recordInfo);
|
||||
|
||||
return recordInfo.getRecordId();
|
||||
}
|
||||
|
||||
protected URI getRecordID() throws IOException {
|
||||
@@ -661,7 +882,7 @@ public class WARCWriterProcessor extends WriterPoolProcessor implements WARCWrit
|
||||
if (cachedMetadata != null) {
|
||||
return cachedMetadata;
|
||||
}
|
||||
ANVLRecord record = new ANVLRecord(7);
|
||||
ANVLRecord record = new ANVLRecord();
|
||||
record.addLabelValue("software", "Heritrix/" +
|
||||
ArchiveUtils.VERSION + " http://crawler.archive.org");
|
||||
try {
|
||||
@@ -726,10 +947,10 @@ public class WARCWriterProcessor extends WriterPoolProcessor implements WARCWrit
|
||||
buf.append("Processor: " + getClass().getName() + "\n");
|
||||
buf.append(" Function: Writes WARCs\n");
|
||||
buf.append(" Total CrawlURIs: " + urlsWritten + "\n");
|
||||
buf.append(" Revisit records: " + WARCWriter.getStat(stats, WARCWriter.REVISIT, WARCWriter.NUM_RECORDS) + "\n");
|
||||
buf.append(" Revisit records: " + WARCWriter.getStat(stats, WARCRecordType.REVISIT.toString(), WARCWriter.NUM_RECORDS) + "\n");
|
||||
|
||||
long bytes = WARCWriter.getStat(stats, WARCWriter.RESPONSE, WARCWriter.CONTENT_BYTES)
|
||||
+ WARCWriter.getStat(stats, WARCWriter.RESOURCE, WARCWriter.CONTENT_BYTES);
|
||||
long bytes = WARCWriter.getStat(stats, WARCRecordType.RESPONSE.toString(), WARCWriter.CONTENT_BYTES)
|
||||
+ WARCWriter.getStat(stats, WARCRecordType.RESOURCE.toString(), WARCWriter.CONTENT_BYTES);
|
||||
buf.append(" Crawled content bytes (including http headers): "
|
||||
+ bytes + " (" + ArchiveUtils.formatBytesForDisplay(bytes) + ")\n");
|
||||
|
||||
|
||||
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* This file is part of the Heritrix web crawler (crawler.archive.org).
|
||||
*
|
||||
* Licensed to the Internet Archive (IA) by one or more individual
|
||||
* contributors.
|
||||
*
|
||||
* The IA 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.archive.modules.recrawl;
|
||||
|
||||
import static org.archive.modules.recrawl.RecrawlAttributeConstants.A_ORIGINAL_URL;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.archive.bdb.BdbModule;
|
||||
import org.archive.modules.CrawlURI;
|
||||
import org.archive.net.UURIFactory;
|
||||
import org.archive.spring.ConfigPath;
|
||||
import org.archive.util.Base32;
|
||||
import org.archive.util.TmpDirTestCase;
|
||||
|
||||
public class ContentDigestHistoryTest extends TmpDirTestCase {
|
||||
|
||||
private static Logger logger = Logger.getLogger(ContentDigestHistoryTest.class.getName());
|
||||
|
||||
protected BdbModule bdb;
|
||||
protected BdbContentDigestHistory historyStore;
|
||||
protected ContentDigestHistoryStorer storer;
|
||||
protected ContentDigestHistoryLoader loader;
|
||||
|
||||
protected ContentDigestHistoryLoader loader() throws IOException {
|
||||
if (loader == null) {
|
||||
loader = new ContentDigestHistoryLoader();
|
||||
loader.setContentDigestHistory(historyStore());
|
||||
logger.info("created " + loader);
|
||||
}
|
||||
return loader;
|
||||
}
|
||||
|
||||
protected ContentDigestHistoryStorer storer() throws IOException {
|
||||
if (storer == null) {
|
||||
storer = new ContentDigestHistoryStorer();
|
||||
storer.setContentDigestHistory(historyStore());
|
||||
logger.info("created " + storer);
|
||||
}
|
||||
return storer;
|
||||
}
|
||||
|
||||
protected BdbContentDigestHistory historyStore() throws IOException {
|
||||
if (historyStore == null) {
|
||||
historyStore = new BdbContentDigestHistory();
|
||||
historyStore.setBdbModule(bdb());
|
||||
historyStore.start();
|
||||
logger.info("created " + historyStore);
|
||||
}
|
||||
return historyStore;
|
||||
}
|
||||
|
||||
protected BdbModule bdb() throws IOException {
|
||||
if (bdb == null) {
|
||||
ConfigPath basePath = new ConfigPath("testBase",getTmpDir().getAbsolutePath());
|
||||
ConfigPath bdbDir = new ConfigPath("bdb","bdb");
|
||||
bdbDir.setBase(basePath);
|
||||
FileUtils.deleteDirectory(bdbDir.getFile());
|
||||
|
||||
bdb = new BdbModule();
|
||||
bdb.setDir(bdbDir);
|
||||
bdb.start();
|
||||
logger.info("created " + bdb);
|
||||
}
|
||||
return bdb;
|
||||
}
|
||||
|
||||
public void testBasics() throws InterruptedException, IOException {
|
||||
CrawlURI curi1 = new CrawlURI(UURIFactory.getInstance("http://example.org/1"));
|
||||
|
||||
assertFalse(loader().shouldProcess(curi1));
|
||||
assertFalse(storer().shouldProcess(curi1));
|
||||
|
||||
// sha1 of "monkey\n", point is to have a value there
|
||||
curi1.setContentDigest("sha1", Base32.decode("orfjublpcrnymm4seg5uk6vfoeu7kw6c"));
|
||||
|
||||
assertTrue(loader().shouldProcess(curi1));
|
||||
assertTrue(storer().shouldProcess(curi1));
|
||||
|
||||
assertEquals("sha1:ORFJUBLPCRNYMM4SEG5UK6VFOEU7KW6C", historyStore().persistKeyFor(curi1));
|
||||
|
||||
assertFalse(curi1.hasContentDigestHistory());
|
||||
|
||||
loader().process(curi1);
|
||||
|
||||
assertTrue(curi1.hasContentDigestHistory());
|
||||
assertTrue(curi1.getContentDigestHistory().isEmpty());
|
||||
|
||||
storer().process(curi1);
|
||||
assertTrue(historyStore().store.isEmpty());
|
||||
|
||||
curi1.getContentDigestHistory().put(A_ORIGINAL_URL, "http://example.org/original");
|
||||
// curi1.getContentDigestHistory().put(A_WARC_RECORD_ID, "<urn:uuid:f00dface-d00d-d00d-d00d-0beefface0ff>");
|
||||
// curi1.getContentDigestHistory().put(A_WARC_FILENAME, "test.warc.gz");
|
||||
// curi1.getContentDigestHistory().put(A_WARC_FILE_OFFSET, 98765432l);
|
||||
// curi1.getContentDigestHistory().put(A_ORIGINAL_DATE, "20120101000000");
|
||||
// curi1.getContentDigestHistory().put(A_CONTENT_DIGEST_COUNT, 1);
|
||||
|
||||
loader().process(curi1);
|
||||
assertEquals("http://example.org/original", curi1.getContentDigestHistory().get(A_ORIGINAL_URL));
|
||||
|
||||
storer().process(curi1);
|
||||
|
||||
assertFalse(historyStore().store.isEmpty());
|
||||
assertEquals(1, historyStore().store.size());
|
||||
|
||||
CrawlURI curi2 = new CrawlURI(UURIFactory.getInstance("http://example.org/2"));
|
||||
curi2.setContentDigest("sha1", Base32.decode("orfjublpcrnymm4seg5uk6vfoeu7kw6c"));
|
||||
|
||||
assertFalse(curi2.hasContentDigestHistory());
|
||||
|
||||
loader().process(curi2);
|
||||
|
||||
assertTrue(curi2.hasContentDigestHistory());
|
||||
assertEquals("http://example.org/original", curi2.getContentDigestHistory().get(A_ORIGINAL_URL));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -21,8 +21,6 @@ package org.archive.modules.writer;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URI;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
@@ -32,6 +30,7 @@ import org.apache.commons.httpclient.methods.GetMethod;
|
||||
import org.archive.io.WriterPool;
|
||||
import org.archive.io.WriterPoolMember;
|
||||
import org.archive.io.WriterPoolSettings;
|
||||
import org.archive.io.warc.WARCRecordInfo;
|
||||
import org.archive.io.warc.WARCWriter;
|
||||
import org.archive.io.warc.WARCWriterPoolSettingsData;
|
||||
import org.archive.modules.CrawlMetadata;
|
||||
@@ -45,7 +44,6 @@ import org.archive.uid.RecordIDGenerator;
|
||||
import org.archive.uid.UUIDGenerator;
|
||||
import org.archive.util.FileUtils;
|
||||
import org.archive.util.TmpDirTestCase;
|
||||
import org.archive.util.anvl.ANVLRecord;
|
||||
|
||||
/**
|
||||
* Unit test for {@link WARCWriterProcessor}.
|
||||
@@ -141,11 +139,9 @@ public class WARCWriterProcessorTest extends ProcessorTestBase {
|
||||
public FailWARCWriter(AtomicInteger serial, WARCWriterPoolSettingsData settings) {
|
||||
super(serial, settings);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void writeRecord(String type, String url,
|
||||
String create14DigitDate, String mimetype, URI recordId,
|
||||
ANVLRecord xtraHeaders, InputStream contentStream,
|
||||
long contentLength, boolean enforceLength) throws IOException {
|
||||
public void writeRecord(WARCRecordInfo recordInfo) throws IOException {
|
||||
throw new IOException("pretend no space left on device");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user