Added HelloWorldConnector Example
diff --git a/src/com/google/enterprise/adaptor/Application.java b/src/com/google/enterprise/adaptor/Application.java
index 49d96b0..5392432 100644
--- a/src/com/google/enterprise/adaptor/Application.java
+++ b/src/com/google/enterprise/adaptor/Application.java
@@ -89,8 +89,7 @@
* manual shutdown. A shutdown hook is automatically installed that calls
* {@code stop()}.
*/
- public synchronized void start() throws IOException, InterruptedException,
- StartupException {
+ public synchronized void start() throws IOException, InterruptedException {
synchronized (this) {
daemonInit();
@@ -100,7 +99,16 @@
Runtime.getRuntime().addShutdownHook(shutdownHook);
}
- daemonStart();
+ boolean success = false;
+ try {
+ daemonStart();
+ success = true;
+ } finally {
+ if (!success) {
+ // Call daemonDestroy() and remove shutdown hook.
+ stop(0, TimeUnit.SECONDS);
+ }
+ }
}
/**
@@ -112,21 +120,41 @@
if (primaryServer != null) {
throw new IllegalStateException("Already started");
}
- primaryServer = createHttpServer(config);
- dashboardServer = createDashboardHttpServer(config);
- // Because once stopped the server can't be reused, we can't reuse its
- // bind()ed socket if we stop it. So although ideally we would start/stop in
- // daemonStart/daemonStop, we instead must do it in
- // daemonInit/daemonDestroy.
- primaryServer.start();
- dashboardServer.start();
+ boolean success = false;
+ try {
+ primaryServer = createHttpServer(config);
+ dashboardServer = createDashboardHttpServer(config);
+ // Because once stopped the server can't be reused, we can't reuse its
+ // bind()ed socket if we stop it. So although ideally we would start/stop
+ // in daemonStart/daemonStop, we instead must do it in
+ // daemonInit/daemonDestroy.
+ primaryServer.start();
+ dashboardServer.start();
+ success = true;
+ } finally {
+ if (!success) {
+ daemonDestroy(0, TimeUnit.SECONDS);
+ }
+ }
}
/**
* Really start. This must be called after a successful {@link #daemonInit}.
*/
- synchronized void daemonStart() throws IOException, InterruptedException,
- StartupException {
+ synchronized void daemonStart() throws IOException, InterruptedException {
+ boolean success = false;
+ try {
+ realDaemonStart();
+ success = true;
+ } finally {
+ if (!success) {
+ daemonStop(0, TimeUnit.SECONDS);
+ }
+ }
+ }
+
+ private synchronized void realDaemonStart() throws IOException,
+ InterruptedException {
AdaptorContext context = gsa.setup(primaryServer, dashboardServer, null);
long sleepDurationMillis = 8000;
@@ -150,8 +178,7 @@
} catch (InterruptedException ex) {
throw ex;
} catch (StartupException ex) {
- log.log(Level.WARNING, "Failed to initialize adaptor", ex);
- throw ex;
+ throw new RuntimeException("Failed to initialize adaptor", ex);
} catch (Exception ex) {
log.log(Level.WARNING, "Failed to initialize adaptor", ex);
if (shutdownSemaphore.tryAcquire(sleepDurationMillis,
@@ -483,34 +510,13 @@
// Setup providing content.
try {
- try {
- app.start();
- log.info("doc content serving started");
- } catch (StartupException e) {
- throw new RuntimeException("could not start serving", e);
- } catch (IOException e) {
- throw new RuntimeException("could not start serving", e);
- }
+ app.start();
+ log.info("doc content serving started");
} catch (InterruptedException e) {
- // Do not call stop - it has already been called.
+ Thread.currentThread().interrupt();
throw new RuntimeException("could not start serving", e);
- } catch (Throwable t) {
- // Abnormal termination. Make sure any services that may have been
- // started are shut down.
- try {
- app.stop(3, TimeUnit.SECONDS);
- } catch (Throwable ignored) {
- // It was a noble try. Just get out.
- }
- // Now rethrow.
- if (t instanceof RuntimeException) {
- throw (RuntimeException) t;
- } else if (t instanceof Error) {
- throw (Error) t;
- } else {
- // Very unlikely to happen.
- throw new RuntimeException(t);
- }
+ } catch (IOException e) {
+ throw new RuntimeException("could not start serving", e);
}
return app;
}
diff --git a/src/com/google/enterprise/adaptor/Config.java b/src/com/google/enterprise/adaptor/Config.java
index dd187ea..9d1cd53 100644
--- a/src/com/google/enterprise/adaptor/Config.java
+++ b/src/com/google/enterprise/adaptor/Config.java
@@ -75,13 +75,17 @@
* are already URLs and avoid them being inserted into adaptor
generated URLs. Defaults to false
* <tr><td> </td><td>feed.crawlImmediatelyBitEnabled </td><td> send bit telling
- * GSA to crawl immediately. Defaults to false
+ * GSA to crawl immediately.
+ * Defaults to not overriding adaptor's decision which is typically to send
+ * updates as crawl-immediately and let GSA schedule crawl of all other ids
+ * <tr><td> </td><td>feed.noRecrawlBitEnabled </td><td> send bit telling
+ * GSA to crawl your documents only once.
+ * Defaults to not overriding adaptor's decision which is typically to send
+ * all documents as recrawlable (equivalent to value of false)
* <tr><td> </td><td>feed.maxUrls </td><td> set max number of URLs included
* per feed file. Defaults to 5000
* <tr><td> </td><td>feed.name </td><td> source name used in feeds. Generated
* if not provided
- * <tr><td> </td><td>feed.noRecrawlBitEnabled </td><td> send bit telling
- * GSA to crawl your documents only once. Defaults to false
* <tr><td> </td><td>feed.archiveDirectory </td><td> specifies a directory in
* which all feeds sent to the GSA will be archived. Feeds that failed to
* be sent to the GSA will be tagged with "FAILED" in the file name.
@@ -235,8 +239,8 @@
return rawValue;
}
});
- addKey("feed.noRecrawlBitEnabled", "false");
- addKey("feed.crawlImmediatelyBitEnabled", "false");
+ addKey("feed.noRecrawlBitEnabled", "");
+ addKey("feed.crawlImmediatelyBitEnabled", "");
//addKey("feed.noFollowBitEnabled", "false");
addKey("feed.maxUrls", "5000");
addKey("adaptor.pushDocIdsOnStartup", "true");
@@ -442,21 +446,43 @@
return Boolean.parseBoolean(getValue("gsa.acceptsDocControlsHeader"));
}
- /**
- * Optional (default false): Adds no-recrawl bit with sent records in feed
- * file. If connector handles updates and deletes then GSA does not have to
- * recrawl periodically to notice that a document is changed or deleted.
- */
- boolean isFeedNoRecrawlBitEnabled() {
- return Boolean.getBoolean(getValue("feed.noRecrawlBitEnabled"));
+ static class OverridableBoolean {
+ final boolean isOverriden; // whether value is to be overriden
+ final boolean value; // the overriding value
+ private OverridableBoolean(boolean override) {
+ isOverriden = true;
+ value = override;
+ }
+ private OverridableBoolean() {
+ isOverriden = false;
+ value = false; // whatever
+ }
}
/**
- * Optional (default false): Adds crawl-immediately bit with sent records in
+ * Optional: Adds crawl-immediately bit with sent records in
* feed file. This bit makes the sent URL get crawl priority.
*/
- boolean isCrawlImmediatelyBitEnabled() {
- return Boolean.parseBoolean(getValue("feed.crawlImmediatelyBitEnabled"));
+ OverridableBoolean isCrawlImmediatelyBitEnabled() {
+ String provided = getValue("feed.crawlImmediatelyBitEnabled");
+ if ("".equals(provided.trim())) {
+ return new OverridableBoolean();
+ }
+ return new OverridableBoolean(Boolean.parseBoolean(provided));
+ }
+
+ /**
+ * Optional: Adds no-recrawl bit with sent records in feed
+ * file. If connector handles updates and deletes then GSA
+ * does not have to recrawl periodically to notice that a
+ * document is changed or deleted.
+ */
+ OverridableBoolean isFeedNoRecrawlBitEnabled() {
+ String provided = getValue("feed.noRecrawlBitEnabled");
+ if ("".equals(provided.trim())) {
+ return new OverridableBoolean();
+ }
+ return new OverridableBoolean(Boolean.parseBoolean(provided));
}
/**
@@ -720,6 +746,9 @@
public void validate() {
validate(config);
+ if ("".equals(getGsaHostname().trim())) {
+ throw new InvalidConfigurationException("gsa.hostname cannot be empty");
+ }
}
private void validate(Properties config) {
diff --git a/src/com/google/enterprise/adaptor/GsaCommunicationHandler.java b/src/com/google/enterprise/adaptor/GsaCommunicationHandler.java
index f5168a7..db77785 100644
--- a/src/com/google/enterprise/adaptor/GsaCommunicationHandler.java
+++ b/src/com/google/enterprise/adaptor/GsaCommunicationHandler.java
@@ -57,7 +57,6 @@
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.net.ssl.SSLException;
@@ -121,12 +120,6 @@
private KeyPair keyPair;
private AclTransform aclTransform;
- /**
- * Used to stop startup prematurely. When greater than 0, start() should abort
- * immediately because stop() is currently processing. This allows cancelling
- * new start() calls before stop() is done processing.
- */
- private final AtomicInteger shutdownCount = new AtomicInteger();
private ShutdownWaiter waiter;
private final List<Filter> commonFilters = Arrays.asList(new Filter[] {
new AbortImmediatelyFilter(),
@@ -215,7 +208,11 @@
aclTransform = createAclTransform();
GsaFeedFileMaker fileMaker = new GsaFeedFileMaker(docIdCodec, aclTransform,
config.isGsa614FeedWorkaroundEnabled(),
- config.isGsa70AuthMethodWorkaroundEnabled());
+ config.isGsa70AuthMethodWorkaroundEnabled(),
+ config.isCrawlImmediatelyBitEnabled().isOverriden,
+ config.isCrawlImmediatelyBitEnabled().value,
+ config.isFeedNoRecrawlBitEnabled().isOverriden,
+ config.isFeedNoRecrawlBitEnabled().value);
GsaFeedFileArchiver fileArchiver =
new GsaFeedFileArchiver(config.getFeedArchiveDirectory());
docIdSender = new DocIdSender(fileMaker, fileSender, fileArchiver, journal,
diff --git a/src/com/google/enterprise/adaptor/GsaFeedFileMaker.java b/src/com/google/enterprise/adaptor/GsaFeedFileMaker.java
index 37a329d..82056fe 100644
--- a/src/com/google/enterprise/adaptor/GsaFeedFileMaker.java
+++ b/src/com/google/enterprise/adaptor/GsaFeedFileMaker.java
@@ -65,6 +65,10 @@
private final AclTransform aclTransform;
private final boolean separateClosingRecordTagWorkaround;
private final boolean useAuthMethodWorkaround;
+ private final boolean crawlImmediatelyIsOverriden;
+ private final boolean crawlImmediatelyOverrideValue;
+ private final boolean crawlOnceIsOverriden;
+ private final boolean crawlOnceOverrideValue;
public GsaFeedFileMaker(DocIdEncoder encoder, AclTransform aclTransform) {
this(encoder, aclTransform, false, false);
@@ -73,11 +77,26 @@
public GsaFeedFileMaker(DocIdEncoder encoder, AclTransform aclTransform,
boolean separateClosingRecordTagWorkaround,
boolean useAuthMethodWorkaround) {
+ this(encoder, aclTransform, separateClosingRecordTagWorkaround,
+ useAuthMethodWorkaround, false, false, false, false);
+ }
+
+ public GsaFeedFileMaker(DocIdEncoder encoder, AclTransform aclTransform,
+ boolean separateClosingRecordTagWorkaround,
+ boolean useAuthMethodWorkaround,
+ boolean overrideCrawlImmediately,
+ boolean crawlImmediately,
+ boolean overrideCrawlOnce,
+ boolean crawlOnce) {
this.idEncoder = encoder;
this.aclTransform = aclTransform;
this.separateClosingRecordTagWorkaround
= separateClosingRecordTagWorkaround;
this.useAuthMethodWorkaround = useAuthMethodWorkaround;
+ this.crawlImmediatelyIsOverriden = overrideCrawlImmediately;
+ this.crawlImmediatelyOverrideValue = crawlImmediately;
+ this.crawlOnceIsOverriden = overrideCrawlOnce;
+ this.crawlOnceOverrideValue = crawlOnce;
}
/** Adds header to document's root.
@@ -123,10 +142,15 @@
if (docRecord.isToBeLocked()) {
record.setAttribute("lock", "true");
}
- if (docRecord.isToBeCrawledImmediately()) {
+ if (crawlImmediatelyIsOverriden) {
+ record.setAttribute("crawl-immediately",
+ "" + crawlImmediatelyOverrideValue);
+ } else if (docRecord.isToBeCrawledImmediately()) {
record.setAttribute("crawl-immediately", "true");
}
- if (docRecord.isToBeCrawledOnce()) {
+ if (crawlOnceIsOverriden) {
+ record.setAttribute("crawl-once", "" + crawlOnceOverrideValue);
+ } else if (docRecord.isToBeCrawledOnce()) {
record.setAttribute("crawl-once", "true");
}
if (useAuthMethodWorkaround) {
diff --git a/src/com/google/enterprise/adaptor/InvalidConfigurationException.java b/src/com/google/enterprise/adaptor/InvalidConfigurationException.java
index c66798c..bccdf3e 100644
--- a/src/com/google/enterprise/adaptor/InvalidConfigurationException.java
+++ b/src/com/google/enterprise/adaptor/InvalidConfigurationException.java
@@ -21,7 +21,7 @@
*/
public class InvalidConfigurationException extends StartupException {
/**
- * Constructs a new InvalidConfigurationException with no message and no root
+ * Constructs a new InvalidConfigurationException with no message and no
* cause.
*/
public InvalidConfigurationException() {
@@ -29,8 +29,8 @@
}
/**
- * Constructs a InvalidConfigurationException with a supplied message
- * but no root cause.
+ * Constructs a InvalidConfigurationException with a supplied message but no
+ * cause.
*
* @param message the message. Can be retrieved by the {@link #getMessage()}
* method.
@@ -40,13 +40,25 @@
}
/**
- * Constructs a InvalidConfigurationException with message and root cause.
+ * Constructs a InvalidConfigurationException with message and cause.
*
* @param message the message. Can be retrieved by the {@link #getMessage()}
* method.
- * @param rootCause root failure cause
+ * @param cause failure cause
*/
- public InvalidConfigurationException(String message, Throwable rootCause) {
- super(message, rootCause);
+ public InvalidConfigurationException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ /**
+ * Constructs a InvalidConfigurationException with specified cause, copying
+ * its message if cause is non-{@code null}.
+ *
+ * @param message the message. Can be retrieved by the {@link #getMessage()}
+ * method.
+ * @param cause failure cause
+ */
+ public InvalidConfigurationException(Throwable cause) {
+ super(cause);
}
}
diff --git a/src/com/google/enterprise/adaptor/StartupException.java b/src/com/google/enterprise/adaptor/StartupException.java
index c8b5d3a..9464c46 100644
--- a/src/com/google/enterprise/adaptor/StartupException.java
+++ b/src/com/google/enterprise/adaptor/StartupException.java
@@ -17,21 +17,19 @@
/**
* Thrown for unrecoverable startup errors, such as fatal configuration
* errors or running on the wrong platform. StartupExceptions will bypass
- * the retry with back-off recovery logic and immediately terminate the
- * adaptor.
+ * the retry with back-off recovery logic of {@link Application} and immediately
+ * terminate the adaptor.
*/
public class StartupException extends RuntimeException {
/**
- * Constructs a new StartupException with no message and no root
- * cause.
+ * Constructs a new StartupException with no message and no cause.
*/
public StartupException() {
super();
}
/**
- * Constructs a StartupException with a supplied message but no root
- * cause.
+ * Constructs a StartupException with a supplied message but no cause.
*
* @param message the message. Can be retrieved by the {@link #getMessage()}
* method.
@@ -41,13 +39,23 @@
}
/**
- * Constructs a StartupException with message and root cause.
+ * Constructs a StartupException with message and cause.
*
* @param message the message. Can be retrieved by the {@link #getMessage()}
* method.
- * @param rootCause root failure cause
+ * @param cause failure cause
*/
- public StartupException(String message, Throwable rootCause) {
- super(message, rootCause);
+ public StartupException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ /**
+ * Constructs a StartupException with specified cause, copying its message if
+ * cause is non-{@code null}.
+ *
+ * @param cause failure cause
+ */
+ public StartupException(Throwable cause) {
+ super(cause);
}
}
diff --git a/src/com/google/enterprise/adaptor/UnsupportedPlatformException.java b/src/com/google/enterprise/adaptor/UnsupportedPlatformException.java
index c004f36..3cd86d3 100644
--- a/src/com/google/enterprise/adaptor/UnsupportedPlatformException.java
+++ b/src/com/google/enterprise/adaptor/UnsupportedPlatformException.java
@@ -19,11 +19,10 @@
*/
public class UnsupportedPlatformException extends StartupException {
/**
- * Constructs a new UnsupportedPlatformException with a default message.
+ * Constructs a new UnsupportedPlatformException with no message.
*/
public UnsupportedPlatformException() {
- this(System.getProperty("os.name")
- + " is not a supported platform for this adaptor.");
+ super();
}
/**
@@ -35,4 +34,26 @@
public UnsupportedPlatformException(String message) {
super(message);
}
+
+ /**
+ * Constructs a UnsupportedPlatformException with a supplied message and
+ * cause.
+ *
+ * @param message the message. Can be retrieved by the {@link #getMessage()}
+ * method.
+ * @param cause failure cause
+ */
+ public UnsupportedPlatformException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ /**
+ * Constructs a UnsupportedPlatformException with specified cause, copying its
+ * message if cause is non-{@code null}.
+ *
+ * @param cause failure cause
+ */
+ public UnsupportedPlatformException(Throwable cause) {
+ super(cause);
+ }
}
diff --git a/test/com/google/enterprise/adaptor/ApplicationTest.java b/test/com/google/enterprise/adaptor/ApplicationTest.java
index 12a9b8f..bce3236 100644
--- a/test/com/google/enterprise/adaptor/ApplicationTest.java
+++ b/test/com/google/enterprise/adaptor/ApplicationTest.java
@@ -276,10 +276,12 @@
@Test
public void testFailWithStartupException() throws Exception {
+ final StartupException startupException
+ = new StartupException("Unrecoverable error.");
class FailStartupAdaptor extends NullAdaptor {
@Override
public void init(AdaptorContext context) throws Exception {
- throw new StartupException("Unrecoverable error.");
+ throw startupException;
}
}
FailStartupAdaptor adaptor = new FailStartupAdaptor();
@@ -289,12 +291,13 @@
long startTime = System.nanoTime();
try {
app.start();
- fail("Expected a StartupException, but got none.");
- } catch (StartupException expected) {
+ fail("Expected a RuntimeException, but got none.");
+ } catch (RuntimeException expected) {
+ assertEquals(startupException, expected.getCause());
long duration = System.nanoTime() - startTime;
final long nanosInAMilli = 1000 * 1000;
if (duration > 1000 * nanosInAMilli) {
- fail("StartupException took a long time: " + duration);
+ fail("RuntimeException took a long time: " + duration);
}
}
}
diff --git a/test/com/google/enterprise/adaptor/ConfigTest.java b/test/com/google/enterprise/adaptor/ConfigTest.java
index 639d0f4..df4ca4c 100644
--- a/test/com/google/enterprise/adaptor/ConfigTest.java
+++ b/test/com/google/enterprise/adaptor/ConfigTest.java
@@ -49,6 +49,22 @@
}
@Test
+ public void testEmptyGsaHostname() {
+ // Requires gsa.hostname to be non-empty
+ config.setValue("gsa.hostname", "");
+ thrown.expect(InvalidConfigurationException.class);
+ config.validate();
+ }
+
+ @Test
+ public void testWhitespaceOnlyGsaHostname() {
+ // Requires gsa.hostname to be non-empty
+ config.setValue("gsa.hostname", " ");
+ thrown.expect(InvalidConfigurationException.class);
+ config.validate();
+ }
+
+ @Test
public void testAddDuplicateKeyWithValue() {
config.addKey("somekey", "value");
thrown.expect(IllegalStateException.class);
diff --git a/test/com/google/enterprise/adaptor/GsaFeedFileMakerTest.java b/test/com/google/enterprise/adaptor/GsaFeedFileMakerTest.java
index 7eefa33..787448b 100644
--- a/test/com/google/enterprise/adaptor/GsaFeedFileMakerTest.java
+++ b/test/com/google/enterprise/adaptor/GsaFeedFileMakerTest.java
@@ -489,4 +489,174 @@
xml = xml.replaceAll("\r\n", "\n");
assertEquals(golden, xml);
}
+
+ @Test
+ public void testCrawlImmediatelyOverride() throws java.net.URISyntaxException {
+ GsaFeedFileMaker lclMeker = new GsaFeedFileMaker(encoder, aclTransform,
+ false, false,
+ /*override crawl-immediately?*/ true,
+ /*crawl-immediately value*/ false,
+ /*override crawl-once?*/ false,
+ /*crawl-once value*/ false);
+ String golden =
+ "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n"
+ + "<!DOCTYPE gsafeed PUBLIC \"-//Google//DTD GSA Feeds//EN\" \"\">\n"
+ + "<gsafeed>\n"
+ + "<!--GSA EasyConnector-->\n"
+ + "<header>\n"
+ + "<datasource>t3sT</datasource>\n"
+ + "<feedtype>metadata-and-url</feedtype>\n"
+ + "</header>\n"
+ + "<group>\n"
+
+ // (1)
+ + "<record crawl-immediately=\"false\""
+ + " displayurl=\"http://f000nkey.net\" mimetype=\"text/plain\""
+ + " url=\"http://localhost/E11\"/>\n"
+
+ // (2)
+ + "<record crawl-immediately=\"false\""
+ + " displayurl=\"http://yankee.doodle.com\""
+ + " last-modified=\"Thu, 01 Jan 1970 00:00:00 +0000\""
+ + " mimetype=\"text/plain\" url=\"http://localhost/elefenta\"/>\n"
+
+ // (3)
+ + "<record crawl-immediately=\"false\""
+ + " displayurl=\"http://google.com/news\""
+ + " last-modified=\"Fri, 02 Jan 1970 00:00:00 +0000\""
+ + " mimetype=\"text/plain\" url=\"http://localhost/gone\"/>\n"
+
+ // (4)
+ + "<record crawl-immediately=\"false\" crawl-once=\"true\""
+ + " lock=\"true\" mimetype=\"text/plain\""
+ + " url=\"http://localhost/flagson\"/>\n"
+
+ // (5)
+ + "<record action=\"delete\" crawl-immediately=\"false\""
+ + " mimetype=\"text/plain\""
+ + " url=\"http://localhost/deleted\"/>\n"
+
+ + "</group>\n"
+ + "</gsafeed>\n";
+
+ ArrayList<DocIdPusher.Record> ids = new ArrayList<DocIdPusher.Record>();
+ DocIdPusher.Record.Builder attrBuilder
+ = new DocIdPusher.Record.Builder(new DocId("E11"));
+
+ // (1)
+ attrBuilder.setResultLink(new URI("http://f000nkey.net"));
+ ids.add(attrBuilder.build());
+
+ // (2)
+ attrBuilder.setResultLink(new URI("http://yankee.doodle.com"));
+ attrBuilder.setLastModified(new Date(0));
+ attrBuilder.setCrawlImmediately(true);
+ attrBuilder.setDocId(new DocId("elefenta"));
+ ids.add(attrBuilder.build());
+
+ // (3)
+ attrBuilder.setResultLink(new URI("http://google.com/news"));
+ attrBuilder.setLastModified(new Date(1000 * 60 * 60 * 24));
+ attrBuilder.setCrawlImmediately(false);
+ attrBuilder.setCrawlOnce(false);
+ attrBuilder.setDocId(new DocId("gone"));
+ ids.add(attrBuilder.build());
+
+ // (4)
+ ids.add(new DocIdPusher.Record.Builder(new DocId("flagson"))
+ .setLock(true).setCrawlImmediately(true).setCrawlOnce(true).build());
+
+ // (5)
+ ids.add(new DocIdPusher.Record.Builder(new DocId("deleted"))
+ .setDeleteFromIndex(true).build());
+
+ String xml = lclMeker.makeMetadataAndUrlXml("t3sT", ids);
+ xml = xml.replaceAll("\r\n", "\n");
+ assertEquals(golden, xml);
+ }
+
+ @Test
+ public void testCrawlOnceOverride() throws java.net.URISyntaxException {
+ GsaFeedFileMaker lclMeker = new GsaFeedFileMaker(encoder, aclTransform,
+ false, false,
+ /*override crawl-immediately?*/ false,
+ /*crawl-immediately value*/ false,
+ /*override crawl-once?*/ true,
+ /*crawl-once value*/ false);
+ String golden =
+ "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n"
+ + "<!DOCTYPE gsafeed PUBLIC \"-//Google//DTD GSA Feeds//EN\" \"\">\n"
+ + "<gsafeed>\n"
+ + "<!--GSA EasyConnector-->\n"
+ + "<header>\n"
+ + "<datasource>t3sT</datasource>\n"
+ + "<feedtype>metadata-and-url</feedtype>\n"
+ + "</header>\n"
+ + "<group>\n"
+
+ // (1)
+ + "<record crawl-once=\"false\""
+ + " displayurl=\"http://f000nkey.net\" mimetype=\"text/plain\""
+ + " url=\"http://localhost/E11\"/>\n"
+
+ // (2)
+ + "<record crawl-immediately=\"true\" crawl-once=\"false\""
+ + " displayurl=\"http://yankee.doodle.com\""
+ + " last-modified=\"Thu, 01 Jan 1970 00:00:00 +0000\""
+ + " mimetype=\"text/plain\" url=\"http://localhost/elefenta\"/>\n"
+
+ // (3)
+ + "<record crawl-once=\"false\""
+ + " displayurl=\"http://google.com/news\""
+ + " last-modified=\"Fri, 02 Jan 1970 00:00:00 +0000\""
+ + " mimetype=\"text/plain\" url=\"http://localhost/gone\"/>\n"
+
+ // (4)
+ + "<record crawl-immediately=\"true\" crawl-once=\"false\""
+ + " lock=\"true\" mimetype=\"text/plain\""
+ + " url=\"http://localhost/flagson\"/>\n"
+
+ // (5)
+ + "<record action=\"delete\" crawl-once=\"false\""
+ + " mimetype=\"text/plain\""
+ + " url=\"http://localhost/deleted\"/>\n"
+
+ + "</group>\n"
+ + "</gsafeed>\n";
+
+ ArrayList<DocIdPusher.Record> ids = new ArrayList<DocIdPusher.Record>();
+ DocIdPusher.Record.Builder attrBuilder
+ = new DocIdPusher.Record.Builder(new DocId("E11"));
+
+ // (1)
+ attrBuilder.setResultLink(new URI("http://f000nkey.net"));
+ ids.add(attrBuilder.build());
+
+ // (2)
+ attrBuilder.setResultLink(new URI("http://yankee.doodle.com"));
+ attrBuilder.setLastModified(new Date(0));
+ attrBuilder.setCrawlImmediately(true);
+ attrBuilder.setDocId(new DocId("elefenta"));
+ ids.add(attrBuilder.build());
+
+ // (3)
+ attrBuilder.setResultLink(new URI("http://google.com/news"));
+ attrBuilder.setLastModified(new Date(1000 * 60 * 60 * 24));
+ attrBuilder.setCrawlImmediately(false);
+ attrBuilder.setCrawlOnce(false);
+ attrBuilder.setDocId(new DocId("gone"));
+ ids.add(attrBuilder.build());
+
+ // (4)
+ ids.add(new DocIdPusher.Record.Builder(new DocId("flagson"))
+ .setLock(true).setCrawlImmediately(true).setCrawlOnce(true).build());
+
+ // (5)
+ ids.add(new DocIdPusher.Record.Builder(new DocId("deleted"))
+ .setDeleteFromIndex(true).build());
+
+ String xml = lclMeker.makeMetadataAndUrlXml("t3sT", ids);
+ xml = xml.replaceAll("\r\n", "\n");
+ assertEquals(golden, xml);
+ }
}