package org.archive.cdxserver.writer; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Locale; import java.util.TimeZone; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.archive.cdxserver.CDXQuery; import org.archive.format.cdx.CDXLine; import org.archive.util.ArchiveUtils; public class MementoLinkWriter extends HttpCDXWriter { protected boolean hasStarted = false; protected String timemapUrl; protected String timegateUrl; protected String format; protected final static String RESUME_KEY = "resumeKey="; public MementoLinkWriter( HttpServletRequest request, HttpServletResponse response, CDXQuery cdxquery, boolean gzip) throws IOException { super(response, gzip); if (cdxquery.getLimit() > 0) { cdxquery.setShowResumeKey(true); } StringBuffer requestUrlBuff = request.getRequestURL(); int index = requestUrlBuff.indexOf(TIMEMAP); if (index > 0) { timegateUrl = requestUrlBuff.substring(0, index); index += TIMEMAP.length() + 1; int endIndex = requestUrlBuff.indexOf("/", index); format = ((endIndex > 0) ? requestUrlBuff.substring(index, endIndex) : requestUrlBuff.substring(index)); } else { timegateUrl = requestUrlBuff.toString(); format = LINK; } String query = request.getQueryString(); if (query != null) { requestUrlBuff.append('?'); requestUrlBuff.append(request.getQueryString()); } this.timemapUrl = requestUrlBuff.toString(); setContentType(APPLICATION_LINK_FORMAT); } @Override public void begin() { // TODO Auto-generated method stub } private static String makeLink(String url, String rel) { return String.format("<%s>; rel=\"%s\"", url, rel); } private static String makeLink(String url, String rel, String type) { return String.format("<%s>; rel=\"%s\"; type=\"%s\"", url, rel, type); } private static String makeLink(String prefix, String url, String rel, String timestamp) { Date date = ArchiveUtils.getDate(timestamp, ERROR_DATE); return String.format("<%s%s/%s>; rel=\"%s\"; datetime=\"%s\"", prefix, timestamp, url, rel, HTTP_LINK_DATE_FORMATTER.format(date)); } public final static String ORIGINAL = "original"; public final static String APPLICATION_LINK_FORMAT = "application/link-format"; public final static String HTTP_LINK_DATE_FORMAT = "E, dd MMM yyyy HH:mm:ss z"; public final static String TIMEGATE = "timegate"; public final static String TIMEMAP = "timemap"; public final static String LINK = "link"; public final static String FIRST = "first memento"; public final static String LAST = "last memento"; public final static String FIRST_LAST = "first last memento"; public final static String MEMENTO = "memento"; public final static Date ERROR_DATE = new Date(0); public final static SimpleDateFormat HTTP_LINK_DATE_FORMATTER; public final static TimeZone GMT_TZ = TimeZone.getTimeZone("GMT"); static { HTTP_LINK_DATE_FORMATTER = new SimpleDateFormat(HTTP_LINK_DATE_FORMAT, Locale.ENGLISH); HTTP_LINK_DATE_FORMATTER.setTimeZone(GMT_TZ); } protected CDXLine prevLine; protected void writeHeader(CDXLine firstLine) { writer.print(makeLink(firstLine.getOriginalUrl(), ORIGINAL)); writer.println(","); Date date = ArchiveUtils.getDate(firstLine.getTimestamp(), ERROR_DATE); writer.print(makeLink(timemapUrl, "self", APPLICATION_LINK_FORMAT) + "; from=\"" + HTTP_LINK_DATE_FORMATTER.format(date) + "\""); // + "; until=\"" // + HTTP_LINK_DATE_FORMATTER.format(last) + "\""); writer.println(","); writer.print(makeLink(timegateUrl + firstLine.getOriginalUrl(), TIMEGATE)); writer.println(","); } protected void writeHeaderAndFirstLine(CDXLine line, String first, String last) { String rel; if (!hasStarted) { writeHeader(prevLine); hasStarted = true; rel = first; } else { if (line != null) { writer.println(","); } rel = last; } if (line != null) { writer.print(makeLink(timegateUrl, line.getOriginalUrl(), rel, line.getTimestamp())); String digest = line.getDigest(); if (!digest.equals(CDXLine.EMPTY_VALUE)) { writer.print("; hash=\"sha1:"); writer.print(digest); writer.print("\""); } } } @Override public int writeLine(CDXLine line) { if (prevLine == null) { prevLine = line; return 1; } writeHeaderAndFirstLine(prevLine, FIRST, MEMENTO); prevLine = line; return 1; } @Override public void writeResumeKey(String resumeKey) { writeHeaderAndFirstLine(prevLine, FIRST_LAST, LAST); CDXLine lastLine = prevLine; prevLine = null; String timestamp = lastLine.getTimestamp(); Date date = ArchiveUtils.getDate(timestamp, null); if (date == null) { return; } StringBuilder sb = new StringBuilder(timemapUrl); int resumeIndex = sb.indexOf(RESUME_KEY); if (resumeIndex > 0) { int end = sb.indexOf("&", resumeIndex); if (end < 0) { end = sb.length(); } sb.replace(resumeIndex + RESUME_KEY.length(), end, resumeKey); } else { sb.append("&"); sb.append(RESUME_KEY); sb.append(resumeKey); } // sb.append(TIMEMAP); // sb.append("/"); // sb.append(format); // sb.append("/"); // sb.append(timestamp); // sb.append("/"); // sb.append(lastLine.getOriginalUrl()); writer.println(","); writer.print(makeLink(sb.toString(), TIMEMAP, APPLICATION_LINK_FORMAT) + "; from=\"" + HTTP_LINK_DATE_FORMATTER.format(date) + "\""); } @Override public void end() { writeHeaderAndFirstLine(prevLine, FIRST_LAST, LAST); } }