Move Adaptor.init() calling to Application This allows using GsaCommunicationHandler as a library.
diff --git a/src/com/google/enterprise/adaptor/Application.java b/src/com/google/enterprise/adaptor/Application.java index e390776..88665ea 100644 --- a/src/com/google/enterprise/adaptor/Application.java +++ b/src/com/google/enterprise/adaptor/Application.java
@@ -25,7 +25,7 @@ import java.io.IOException; import java.net.*; import java.security.NoSuchAlgorithmException; -import java.util.Arrays; +import java.util.*; import java.util.concurrent.*; import java.util.logging.Level; import java.util.logging.Logger; @@ -48,6 +48,14 @@ private final Config config; private final GsaCommunicationHandler gsa; + private final ConfigModificationListener configModListener + = new ConfigModListener(); + /** + * An "inverted" semaphore that has permits available when stop() is running; + * at all other times it has no permits. This allows start() to sleep on the + * semaphore and be woken when stop() is called. + */ + private final Semaphore shutdownSemaphore = new Semaphore(0); private Thread shutdownHook; private HttpServer primaryServer; private HttpServer dashboardServer; @@ -64,7 +72,7 @@ * manual shutdown. A shutdown hook is automatically installed that calls * {@code stop()}. */ - public void start() throws IOException, InterruptedException { + public synchronized void start() throws IOException, InterruptedException { synchronized (this) { daemonInit(); @@ -99,24 +107,62 @@ /** * Really start. This must be called after a successful {@link #daemonInit}. */ - void daemonStart() throws IOException, InterruptedException { - gsa.start(primaryServer, dashboardServer); + synchronized void daemonStart() throws IOException, InterruptedException { + AdaptorContext context = gsa.setup(primaryServer, dashboardServer, null); + + long sleepDurationMillis = 8000; + // An hour. + long maxSleepDurationMillis = 60 * 60 * 1000; + // Loop until 1) the adaptor starts successfully, 2) stop() is called, or + // 3) Thread.interrupt() is called on this thread (which we don't do). + // Retrying to start the adaptor is helpful in cases where it needs + // initialization data from a repository that is temporarily down; if the + // adaptor is running as a service, we don't want to stop starting simply + // because another computer is down while we start (which would easily be + // the case after a power failure). + while (true) { + try { + gsa.tryToPutVersionIntoConfig(); + String adaptorType = gsa.getAdaptor().getClass().getName(); + log.log(Level.INFO, "about to init {0}", adaptorType); + gsa.getAdaptor().init(context); + break; + } catch (InterruptedException ex) { + throw ex; + } catch (Exception ex) { + log.log(Level.WARNING, "Failed to initialize adaptor", ex); + if (shutdownSemaphore.tryAcquire(sleepDurationMillis, + TimeUnit.MILLISECONDS)) { + shutdownSemaphore.release(); + // Shutdown initiated. + return; + } + sleepDurationMillis + = Math.min(sleepDurationMillis * 2, maxSleepDurationMillis); + gsa.ensureLatestConfigLoaded(); + } + } + + config.addConfigModificationListener(configModListener); + gsa.start(); } /** * Stop processing incoming requests and background tasks, allowing graceful * shutdown. */ - public synchronized void stop(long time, TimeUnit unit) { + public void stop(long time, TimeUnit unit) { daemonStop(time, unit); daemonDestroy(time, unit); - if (shutdownHook != null) { - try { - Runtime.getRuntime().removeShutdownHook(shutdownHook); - } catch (IllegalStateException ex) { - // Already executing hook. + synchronized (this) { + if (shutdownHook != null) { + try { + Runtime.getRuntime().removeShutdownHook(shutdownHook); + } catch (IllegalStateException ex) { + // Already executing hook. + } + shutdownHook = null; } - shutdownHook = null; } } @@ -124,11 +170,35 @@ * Stop all the services we provide. This is the opposite of {@link * #daemonStart}. */ - synchronized void daemonStop(long time, TimeUnit unit) { - if (primaryServer == null) { - throw new IllegalStateException("Already stopped"); + void daemonStop(long time, TimeUnit unit) { + shutdownSemaphore.release(); + try { + synchronized (this) { + if (primaryServer == null) { + throw new IllegalStateException("Already stopped"); + } + config.removeConfigModificationListener(configModListener); + gsa.stop(time, unit); + try { + gsa.getAdaptor().destroy(); + } finally { + gsa.teardown(); + } + } + } finally { + boolean interrupted = false; + while (true) { + try { + shutdownSemaphore.acquire(); + break; + } catch (InterruptedException ex) { + interrupted = true; + } + } + if (interrupted) { + Thread.currentThread().interrupt(); + } } - gsa.stop(time, unit); } /** @@ -408,4 +478,35 @@ } } } + + private class ConfigModListener implements ConfigModificationListener { + @Override + public void configModified(ConfigModificationEvent ev) { + Set<String> modifiedKeys = ev.getModifiedKeys(); + if (modifiedKeys.contains("adaptor.fullListingSchedule")) { + gsa.rescheduleFullListing( + ev.getNewConfig().getAdaptorFullListingSchedule()); + } + + // List of "safe" keys that can be updated without a restart. + List<String> safeKeys = Arrays.asList("adaptor.fullListingSchedule"); + // Set of "unsafe" keys that have been modified. + Set<String> modifiedKeysRequiringRestart + = new HashSet<String>(modifiedKeys); + modifiedKeysRequiringRestart.removeAll(safeKeys); + // If there are modified "unsafe" keys, then we restart things to make + // sure all the code is up-to-date with the new values. + if (!modifiedKeysRequiringRestart.isEmpty()) { + log.warning("Unsafe configuration keys modified. To ensure a sane " + + "state, the adaptor is restarting."); + daemonStop(3, TimeUnit.SECONDS); + try { + daemonStart(); + } catch (Exception ex) { + log.log(Level.SEVERE, "Automatic restart failed", ex); + throw new RuntimeException(ex); + } + } + } + } }
diff --git a/src/com/google/enterprise/adaptor/GsaCommunicationHandler.java b/src/com/google/enterprise/adaptor/GsaCommunicationHandler.java index 2bc3425..fabb141 100644 --- a/src/com/google/enterprise/adaptor/GsaCommunicationHandler.java +++ b/src/com/google/enterprise/adaptor/GsaCommunicationHandler.java
@@ -46,8 +46,6 @@ private final Adaptor adaptor; private final Config config; private final Journal journal; - private final GsaConfigModListener gsaConfigModListener - = new GsaConfigModListener(); private AdaptorContextImpl adaptorContext; /** * Cron-style scheduler. Available for other uses, but necessary for @@ -95,12 +93,8 @@ private HttpServerScope dashboardScope; private Dashboard dashboard; private SensitiveValueCodec secureValueCodec; - /** - * Used to stop startup prematurely. This allows cancelling an already-running - * start(). If start fails, a stale shuttingDownLatch can remain, thus it does - * not provide any information as to whether a start() call is running. - */ - private volatile CountDownLatch shuttingDownLatch; + 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 @@ -122,19 +116,12 @@ } /** - * Starts listening for communications from GSA. {@code contextPrefix} + * Start services necessary for handling outgoing requests. {@code ""} is used + * for {@code contextPrefix} if the passed value is {@code null}. */ - public synchronized void start(HttpServer server, HttpServer dashboardServer) - throws IOException, InterruptedException { - start(server, dashboardServer, null); - } - - /** - * Starts listening for communications from GSA. {@code ""} is used for - * {@code contextPrefix} if the passed value is {@code null}. - */ - public synchronized void start(HttpServer server, HttpServer dashboardServer, - String contextPrefix) throws IOException, InterruptedException { + public synchronized AdaptorContext setup(HttpServer server, + HttpServer dashboardServer, String contextPrefix) throws IOException, + InterruptedException { if (this.scope != null) { throw new IllegalStateException("Already listening"); } @@ -149,17 +136,12 @@ throw new IllegalArgumentException( "Both servers must be HttpServers or both HttpsServers"); } - shuttingDownLatch = new CountDownLatch(1); - if (shutdownCount.get() > 0) { - shuttingDownLatch = null; - return; - } boolean secure = server instanceof HttpsServer; if (secure != config.isServerSecure()) { config.setValue("server.secure", "" + secure); } - KeyPair keyPair = null; + keyPair = null; try { keyPair = getKeyPair(config.getServerKeyAlias()); } catch (IOException ex) { @@ -192,8 +174,6 @@ 30 * 60 * 1000 /* session lifetime: 30 minutes */, 5 * 60 * 1000 /* max cleanup frequency: 5 minutes */); - config.addConfigModificationListener(gsaConfigModListener); - URI baseUri = config.getServerBaseUri(); URI docUri; try { @@ -206,52 +186,29 @@ GsaFeedFileSender fileSender = new GsaFeedFileSender( config.getGsaHostname(), config.isServerSecure(), // use secure bool? config.getGsaCharacterEncoding()); - AclTransform aclTransform = createAclTransform(); + aclTransform = createAclTransform(); GsaFeedFileMaker fileMaker = new GsaFeedFileMaker(docIdCodec, aclTransform, config.isGsa614FeedWorkaroundEnabled(), config.isGsa70AuthMethodWorkaroundEnabled()); docIdSender = new DocIdSender(fileMaker, fileSender, journal, config, adaptor); + // Could be done during start(), but then we would have to save + // dashboardServer and contextPrefix. + dashboardScope = new HttpServerScope(dashboardServer, contextPrefix); + // We are about to start the Adaptor, so anything available through // AdaptorContext or other means must be initialized at this point. Any // reference to 'adaptor' before this point must be done very carefully to // ensure it doesn't call the adaptor until after Adaptor.init() completes. + return adaptorContext = new AdaptorContextImpl(); + } - long sleepDurationMillis = 8000; - - // An hour. - long maxSleepDurationMillis = 60 * 60 * 1000; - // Loop until 1) the adaptor starts successfully, 2) stop() is called, or - // 3) Thread.interrupt() is called on this thread (which we don't do). - // Retrying to start the adaptor is helpful in cases where it needs - // initialization data from a repository that is temporarily down; if the - // adaptor is running as a service, we don't want to stop starting simply - // because another computer is down while we start (which would easily be - // the case after a power failure). - while (true) { - try { - tryToPutVersionIntoConfig(secure); - adaptorContext = new AdaptorContextImpl(); - String adaptorType = adaptor.getClass().getName(); - log.log(Level.INFO, "about to init {0}", adaptorType); - adaptor.init(adaptorContext); - break; - } catch (InterruptedException ex) { - throw ex; - } catch (Exception ex) { - log.log(Level.WARNING, "Failed to initialize adaptor", ex); - if (shuttingDownLatch.await(sleepDurationMillis, - TimeUnit.MILLISECONDS)) { - // Shutdown initiated. - break; - } - sleepDurationMillis - = Math.min(sleepDurationMillis * 2, maxSleepDurationMillis); - ensureLatestConfigLoaded(); - } - } - + /** + * Start servicing incoming requests. This makes use of the + * previously-provided HttpServers and configuration. + */ + public synchronized void start() { // Since the Adaptor has been started, we can now issue other calls to it. // Usages of 'adaptor' are completely safe after this point. adaptorContext.freeze(); @@ -263,10 +220,8 @@ (ConfigModificationListener) adaptor); }*/ - dashboardScope = new HttpServerScope(dashboardServer, contextPrefix); - SamlServiceProvider samlServiceProvider = null; - if (secure) { + if (config.isServerSecure()) { bootstrapOpenSaml(); SamlMetadata metadata = new SamlMetadata(config.getServerHostname(), config.getServerPort(), config.getGsaHostname(), @@ -358,14 +313,13 @@ dashboard = new Dashboard(config, this, journal, sessionManager, secureValueCodec, adaptor, adaptorContext.statusSources); dashboard.start(dashboardScope); - - shuttingDownLatch = null; } - private void tryToPutVersionIntoConfig(boolean secure) throws IOException { + void tryToPutVersionIntoConfig() throws IOException { try { if ("GENERATE".equals(config.getGsaVersion())) { // is not set - GsaVersion ver = GsaVersion.get(config.getGsaHostname(), secure); + GsaVersion ver = GsaVersion.get(config.getGsaHostname(), + config.isServerSecure()); config.overrideKey("gsa.version", "" + ver); } } catch (FileNotFoundException fne) { @@ -596,38 +550,28 @@ } /** - * Stop the current services, allowing up to {@code maxDelay} seconds for - * things to shutdown. + * Stops servicing incoming requests, allowing up to {@code maxDelay} seconds + * for things to shutdown. After called, no requests will be sent to the + * Adaptor. + * + * @return {@code true} if shutdown cleanly, {@code false} if requests may + * still be processing */ - public void stop(long time, TimeUnit unit) { - // Prevent new start()s. - shutdownCount.incrementAndGet(); - try { - CountDownLatch latch = shuttingDownLatch; - if (latch != null) { - // Cause existing start() to begin cancelling. - latch.countDown(); - } - realStop(time, unit); - } finally { - // Permit new start()s. - shutdownCount.decrementAndGet(); - } - } - - private synchronized void realStop(long time, TimeUnit unit) { + public synchronized boolean stop(long time, TimeUnit unit) { + boolean clean = true; if (adaptorContext != null) { adaptorContext.freeze(); } - config.removeConfigModificationListener(gsaConfigModListener); if (scope != null) { scope.close(); - scope = null; + scope = new HttpServerScope( + scope.getHttpServer(), scope.getContextPrefix()); } - if (dashboardScope != null) { + if (scheduleExecutor != null) { // Post-Adaptor.init() resources need to be stopped. dashboardScope.close(); - dashboardScope = null; + dashboardScope = new HttpServerScope( + dashboardScope.getHttpServer(), dashboardScope.getContextPrefix()); scheduleExecutor.shutdownNow(); scheduleExecutor = null; @@ -648,24 +592,35 @@ docIdIncrementalPusher = null; try { - waiter.shutdown(time, unit); + clean = clean & waiter.shutdown(time, unit); } catch (InterruptedException ex) { Thread.currentThread().interrupt(); + clean = false; } - waiter = null; + waiter = new ShutdownWaiter(); } - try { - adaptor.destroy(); - } finally { - // Wait until after adaptor.destroy() to shutdown things accessible by - // AdaptorContext, so that the AdaptorContext is usable until the very - // end. - secureValueCodec = null; - sessionManager = null; - docIdCodec = null; - docIdSender = null; - adaptorContext = null; - } + return clean; + } + + /** + * Stop services necessary for handling outgoing requests. This call + * invalidates the {@link AdaptorContext} returned from {@link #setup}. + */ + public synchronized void teardown() { + scope = null; + dashboardScope = null; + keyPair = null; + aclTransform = null; + waiter = null; + + // Wait until after adaptor.destroy() to shutdown things accessible by + // AdaptorContext, so that the AdaptorContext is usable until the very + // end. + secureValueCodec = null; + sessionManager = null; + docIdCodec = null; + docIdSender = null; + adaptorContext = null; } /** @@ -721,6 +676,17 @@ } } + synchronized void rescheduleFullListing(String schedule) { + if (sendDocIdsFuture == null) { + return; + } + try { + scheduler.reschedule(sendDocIdsFuture, schedule); + } catch (IllegalArgumentException ex) { + log.log(Level.WARNING, "Invalid schedule pattern", ex); + } + } + /** The adaptor instance being used. */ public Adaptor getAdaptor() { return adaptor; @@ -810,47 +776,6 @@ } } - private class GsaConfigModListener implements ConfigModificationListener { - @Override - public void configModified(ConfigModificationEvent ev) { - Set<String> modifiedKeys = ev.getModifiedKeys(); - synchronized (GsaCommunicationHandler.this) { - if (modifiedKeys.contains("adaptor.fullListingSchedule") - && sendDocIdsFuture != null) { - String schedule = ev.getNewConfig().getAdaptorFullListingSchedule(); - try { - scheduler.reschedule(sendDocIdsFuture, schedule); - } catch (IllegalArgumentException ex) { - log.log(Level.WARNING, "Invalid schedule pattern", ex); - } - } - } - - // List of "safe" keys that can be updated without a restart. - List<String> safeKeys = Arrays.asList("adaptor.fullListingSchedule"); - // Set of "unsafe" keys that have been modified. - Set<String> modifiedKeysRequiringRestart - = new HashSet<String>(modifiedKeys); - modifiedKeysRequiringRestart.removeAll(safeKeys); - // If there are modified "unsafe" keys, then we restart things to make - // sure all the code is up-to-date with the new values. - if (!modifiedKeysRequiringRestart.isEmpty()) { - log.warning("Unsafe configuration keys modified. To ensure a sane " - + "state, the adaptor is restarting."); - HttpServer existingServer = scope.getHttpServer(); - HttpServer existingDashboardServer - = dashboardScope.getHttpServer(); - stop(3, TimeUnit.SECONDS); - try { - start(existingServer, existingDashboardServer); - } catch (Exception ex) { - log.log(Level.SEVERE, "Automatic restart failed", ex); - throw new RuntimeException(ex); - } - } - } - } - /** * This class is thread-safe. */
diff --git a/src/com/google/enterprise/adaptor/HttpServerScope.java b/src/com/google/enterprise/adaptor/HttpServerScope.java index f429331..72af660 100644 --- a/src/com/google/enterprise/adaptor/HttpServerScope.java +++ b/src/com/google/enterprise/adaptor/HttpServerScope.java
@@ -65,4 +65,8 @@ public HttpServer getHttpServer() { return server; } + + public String getContextPrefix() { + return contextPrefix; + } }
diff --git a/test/com/google/enterprise/adaptor/ApplicationTest.java b/test/com/google/enterprise/adaptor/ApplicationTest.java index f2e059a..3813d94 100644 --- a/test/com/google/enterprise/adaptor/ApplicationTest.java +++ b/test/com/google/enterprise/adaptor/ApplicationTest.java
@@ -142,6 +142,27 @@ conn.getContent(); } + @Test + public void testFailOnceInitAdaptor() throws Exception { + class FailFirstAdaptor extends NullAdaptor { + private int count = 0; + public boolean started = false; + + @Override + public void init(AdaptorContext context) { + if (count == 0) { + count++; + throw new RuntimeException(); + } + started = true; + } + } + FailFirstAdaptor adaptor = new FailFirstAdaptor(); + app = new Application(adaptor, config); + app.start(); + assertTrue(adaptor.started); + } + private static class NullAdaptor extends AbstractAdaptor { private boolean inited;
diff --git a/test/com/google/enterprise/adaptor/GsaCommunicationHandlerTest.java b/test/com/google/enterprise/adaptor/GsaCommunicationHandlerTest.java index 7db5785..a673980 100644 --- a/test/com/google/enterprise/adaptor/GsaCommunicationHandlerTest.java +++ b/test/com/google/enterprise/adaptor/GsaCommunicationHandlerTest.java
@@ -68,78 +68,50 @@ @After public void teardown() { gsa.stop(0, TimeUnit.SECONDS); + gsa.teardown(); } @Test public void testAdaptorContext() throws Exception { - final AtomicReference<Throwable> error = new AtomicReference<Throwable>(); - class PollingIncrNullAdaptor extends NullAdaptor { + gsa = new GsaCommunicationHandler(new NullAdaptor(), config); + AdaptorContext context = gsa.setup(mockServer, mockServer, null); + assertSame(config, context.getConfig()); + assertNotNull(context.getDocIdPusher()); + assertNotNull(context.getDocIdEncoder()); + assertNotNull(context.getSensitiveValueDecoder()); + ExceptionHandler originalHandler + = context.getGetDocIdsFullErrorHandler(); + ExceptionHandler replacementHandler + = ExceptionHandlers.exponentialBackoffHandler( + 1, 1, TimeUnit.SECONDS); + assertNotNull(originalHandler); + context.setGetDocIdsFullErrorHandler(replacementHandler); + assertSame(replacementHandler, + context.getGetDocIdsFullErrorHandler()); + + StatusSource source = new MockStatusSource("test", + new MockStatus(Status.Code.NORMAL)); + context.addStatusSource(source); + + assertNotNull(context.createHttpContext("/test", new HttpHandler() { @Override - public void init(AdaptorContext context) { - try { - assertSame(config, context.getConfig()); - assertNotNull(context.getDocIdPusher()); - assertNotNull(context.getDocIdEncoder()); - assertNotNull(context.getSensitiveValueDecoder()); - ExceptionHandler originalHandler - = context.getGetDocIdsFullErrorHandler(); - ExceptionHandler replacementHandler - = ExceptionHandlers.exponentialBackoffHandler( - 1, 1, TimeUnit.SECONDS); - assertNotNull(originalHandler); - context.setGetDocIdsFullErrorHandler(replacementHandler); - assertSame(replacementHandler, - context.getGetDocIdsFullErrorHandler()); - - StatusSource source = new MockStatusSource("test", - new MockStatus(Status.Code.NORMAL)); - context.addStatusSource(source); - - assertNotNull(context.createHttpContext("/test", new HttpHandler() { - @Override - public void handle(HttpExchange ex) {} - })); - } catch (Throwable t) { - error.set(t); - } - } - } - PollingIncrNullAdaptor adaptor = new PollingIncrNullAdaptor(); - gsa = new GsaCommunicationHandler(adaptor, config); - gsa.start(mockServer, mockServer); - Throwable t = error.get(); - if (t != null) { - if (t instanceof Exception) { - throw (Exception) t; - } else if (t instanceof Error) { - throw (Error) t; - } else { - throw new AssertionError(); - } - } + public void handle(HttpExchange ex) {} + })); } @Test public void testPollingIncrementalAdaptor() throws Exception { - class PollingIncrNullAdaptor extends NullAdaptor - implements PollingIncrementalLister { - public final ArrayBlockingQueue<Object> queue - = new ArrayBlockingQueue<Object>(1); - - @Override - public void init(AdaptorContext context) { - context.setPollingIncrementalLister(this); - } - + gsa = new GsaCommunicationHandler(new NullAdaptor(), config); + AdaptorContext context = gsa.setup(mockServer, mockServer, null); + final ArrayBlockingQueue<Object> queue = new ArrayBlockingQueue<Object>(1); + context.setPollingIncrementalLister(new PollingIncrementalLister() { @Override public void getModifiedDocIds(DocIdPusher pusher) { queue.offer(new Object()); } - } - PollingIncrNullAdaptor adaptor = new PollingIncrNullAdaptor(); - gsa = new GsaCommunicationHandler(adaptor, config); - gsa.start(mockServer, mockServer); - assertNotNull(adaptor.queue.poll(1, TimeUnit.SECONDS)); + }); + gsa.start(); + assertNotNull(queue.poll(1, TimeUnit.SECONDS)); } @Test @@ -147,93 +119,21 @@ NullAdaptor adaptor = new NullAdaptor(); config.setValue("adaptor.pushDocIdsOnStartup", "false"); gsa = new GsaCommunicationHandler(adaptor, config); - gsa.start(mockServer, mockServer); + gsa.setup(mockServer, mockServer, null); + gsa.start(); gsa.stop(1, TimeUnit.SECONDS); - gsa.start(mockServer, mockServer); + gsa.start(); assertTrue(gsa.checkAndScheduleImmediatePushOfDocIds()); } - @Test - public void testFailOnceInitAdaptor() throws Exception { - class FailFirstAdaptor extends NullAdaptor { - private int count = 0; - public boolean started = false; - - @Override - public void init(AdaptorContext context) { - if (count == 0) { - count++; - throw new RuntimeException(); - } - started = true; - } - } - FailFirstAdaptor adaptor = new FailFirstAdaptor(); - gsa = new GsaCommunicationHandler(adaptor, config); - gsa.start(mockServer, mockServer); - assertTrue(adaptor.started); - } - /** - * Tests that Adaptor is properly initialized before HTTP serving is started. + * Tests that HTTP serving not is started during setup(). */ @Test - public void testInitBeforeServing() throws Exception { - class SlowAdaptor extends NullAdaptor { - public AtomicBoolean initHasFinished = new AtomicBoolean(); - public AtomicBoolean getCalledBeforeInitFinished = new AtomicBoolean(); - - @Override - public void init(AdaptorContext context) { - try { - Thread.sleep(500); // the slowness of this adaptor - initHasFinished.set(true); - } catch (InterruptedException e) { - throw new AssertionError(e); - } - } - - @Override - public void getDocContent(Request req, Response resp) - throws IOException { - if (!initHasFinished.get()) { - getCalledBeforeInitFinished.set(true); - } - resp.respondNotFound(); - } - } - SlowAdaptor adaptor = new SlowAdaptor(); + public void testNoServingBeforeStart() throws Exception { gsa = new GsaCommunicationHandler(adaptor, config); - - Thread tryFetch = new Thread(new Runnable() { - @Override - public void run() { - while (true) { - HttpExchange ex = mockServer.createExchange("GET", "/doc/1"); - if (ex != null) { - try { - mockServer.handle(ex); - } catch (IOException e) { - // We will already be retrying. - } - } - - try { - Thread.sleep(20); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - return; - } - } - } - }); - tryFetch.start(); - - gsa.start(mockServer, mockServer); - tryFetch.interrupt(); - tryFetch.join(); - - assertEquals(false, adaptor.getCalledBeforeInitFinished.get()); + gsa.setup(mockServer, mockServer, null); + assertEquals(0, mockServer.contexts.size()); } @Test @@ -249,9 +149,11 @@ }; config.setValue("adaptor.pushDocIdsOnStartup", "false"); gsa = new GsaCommunicationHandler(adaptor, config); - gsa.start(mockServer, mockServer, "/path"); + gsa.setup(mockServer, mockServer, "/path"); + gsa.start(); GsaCommunicationHandler gsa2 = new GsaCommunicationHandler(adaptor, config); - gsa2.start(mockServer, mockServer, "/path2"); + gsa2.setup(mockServer, mockServer, "/path2"); + gsa2.start(); try { MockHttpExchange ex = mockServer.createExchange("GET", "/path/doc/1"); @@ -262,6 +164,7 @@ assertEquals("2", new String(ex.getResponseBytes(), charset)); } finally { gsa2.stop(0, TimeUnit.SECONDS); + gsa2.teardown(); // gsa.stop() is called in @After, so no need for second finally for // shutting 'gsa' down. }
diff --git a/test/com/google/enterprise/adaptor/MockHttpServer.java b/test/com/google/enterprise/adaptor/MockHttpServer.java index c324d5c..4e91988 100644 --- a/test/com/google/enterprise/adaptor/MockHttpServer.java +++ b/test/com/google/enterprise/adaptor/MockHttpServer.java
@@ -26,7 +26,7 @@ */ public class MockHttpServer extends HttpServer { private final InetSocketAddress addr; - private final List<HttpContext> contexts = new ArrayList<HttpContext>(); + final List<HttpContext> contexts = new ArrayList<HttpContext>(); public MockHttpServer() { this(new InetSocketAddress(80));