Make Dashboard directly immutable
Dashboard still contains references to non-immutable structures, but
the main internals no longer change.
diff --git a/src/com/google/enterprise/adaptor/Dashboard.java b/src/com/google/enterprise/adaptor/Dashboard.java
index 80163dc..3064091 100644
--- a/src/com/google/enterprise/adaptor/Dashboard.java
+++ b/src/com/google/enterprise/adaptor/Dashboard.java
@@ -33,55 +33,47 @@
private final Config config;
private final Journal journal;
- private HttpServerScope scope;
- private CircularLogRpcMethod circularLogRpcMethod;
- private final StatusMonitor monitor = new StatusMonitor();
+ private final CircularLogRpcMethod circularLogRpcMethod
+ = new CircularLogRpcMethod();
private final GsaCommunicationHandler gsaCommHandler;
private final SessionManager<HttpExchange> sessionManager;
private final RpcHandler rpcHandler;
- private final Adaptor adaptor;
public Dashboard(Config config, GsaCommunicationHandler gsaCommHandler,
Journal journal, SessionManager<HttpExchange> sessionManager,
- SensitiveValueCodec secureValueCodec, Adaptor adaptor) {
+ SensitiveValueCodec secureValueCodec, Adaptor adaptor,
+ List<StatusSource> adaptorSources) {
this.config = config;
this.gsaCommHandler = gsaCommHandler;
this.journal = journal;
this.sessionManager = sessionManager;
- this.adaptor = adaptor;
+
+ List<StatusSource> sources = new LinkedList<StatusSource>();
+ sources.add(new LastPushStatusSource(journal));
+ sources.add(new RetrieverStatusSource(journal));
+ sources.add(new GsaCrawlingStatusSource(journal));
+ sources.addAll(adaptorSources);
rpcHandler = new RpcHandler(sessionManager);
rpcHandler.registerRpcMethod("startFeedPush", new StartFeedPushRpcMethod());
rpcHandler.registerRpcMethod("startIncrementalFeedPush",
new StartIncrementalFeedPushRpcMethod());
- circularLogRpcMethod = new CircularLogRpcMethod();
rpcHandler.registerRpcMethod("getLog", circularLogRpcMethod);
rpcHandler.registerRpcMethod("getConfig", new ConfigRpcMethod(config));
- rpcHandler.registerRpcMethod("getStatuses", new StatusRpcMethod(monitor));
+ rpcHandler.registerRpcMethod("getStatuses", new StatusRpcMethod(sources));
rpcHandler.registerRpcMethod("checkForUpdatedConfig",
new CheckForUpdatedConfigRpcMethod(gsaCommHandler));
rpcHandler.registerRpcMethod("encodeSensitiveValue",
new EncodeSensitiveValueMethod(secureValueCodec));
-
- monitor.addSource(new LastPushStatusSource(journal));
- monitor.addSource(new RetrieverStatusSource(journal));
- monitor.addSource(new GsaCrawlingStatusSource(journal));
+ rpcHandler.registerRpcMethod("getStats", new StatRpcMethod(journal, adaptor,
+ gsaCommHandler.isAdaptorIncremental()));
}
/** Starts listening for connections to the dashboard. */
- public void start(HttpServer dashboardServer, String contextPrefix)
- throws IOException {
- rpcHandler.registerRpcMethod("getStats", new StatRpcMethod(journal, adaptor,
- gsaCommHandler.isAdaptorIncremental()));
-
- this.scope = new HttpServerScope(dashboardServer, contextPrefix);
- int dashboardPort = dashboardServer.getAddress().getPort();
- if (dashboardPort != config.getServerDashboardPort()) {
- config.setValue("server.dashboardPort", "" + dashboardPort);
- }
+ public void start(HttpServerScope scope) {
boolean secure = config.isServerSecure();
HttpHandler dashboardHandler = new DashboardHandler();
- addFilters(scope.createContext("/dashboard",
+ HttpContext dashboardContext = addFilters(scope.createContext("/dashboard",
createAdminSecurityHandler(dashboardHandler, config, sessionManager,
secure)));
addFilters(scope.createContext("/rpc", createAdminSecurityHandler(
@@ -91,7 +83,9 @@
config.getFeedName().replace('_', '-')),
config, sessionManager, secure)));
addFilters(scope.createContext("/",
- new RedirectHandler(contextPrefix + "/dashboard")));
+ new RedirectHandler(dashboardContext.getPath())));
+
+ circularLogRpcMethod.start();
}
private AdministratorSecurityHandler createAdminSecurityHandler(
@@ -102,26 +96,7 @@
}
public void stop() {
- if (circularLogRpcMethod != null) {
- circularLogRpcMethod.close();
- circularLogRpcMethod = null;
- }
- if (scope != null) {
- scope.close();
- scope = null;
- }
- }
-
- public HttpServerScope getScope() {
- return scope;
- }
-
- public void addStatusSource(StatusSource source) {
- monitor.addSource(source);
- }
-
- public void clearStatusSources() {
- monitor.clearSources();
+ circularLogRpcMethod.close();
}
private HttpContext addFilters(HttpContext context) {
@@ -152,7 +127,7 @@
/**
* Installs a log handler; to uninstall handler, call {@link #close}.
*/
- public CircularLogRpcMethod() {
+ public void start() {
LogManager.getLogManager().getLogger("").addHandler(circularLog);
}
@@ -185,15 +160,25 @@
}
static class StatusRpcMethod implements RpcHandler.RpcMethod {
- private final StatusMonitor monitor;
+ private final List<StatusSource> sources;
- public StatusRpcMethod(StatusMonitor monitor) {
- this.monitor = monitor;
+ public StatusRpcMethod(List<StatusSource> sources) {
+ this.sources = Collections.unmodifiableList(
+ new ArrayList<StatusSource>(sources));
+ }
+
+ public Map<StatusSource, Status> retrieveStatuses() {
+ Map<StatusSource, Status> statuses
+ = new LinkedHashMap<StatusSource, Status>(sources.size() * 2);
+ for (StatusSource source : sources) {
+ statuses.put(source, source.retrieveStatus());
+ }
+ return statuses;
}
@Override
public Object run(List request) {
- Map<StatusSource, Status> statuses = monitor.retrieveStatuses();
+ Map<StatusSource, Status> statuses = retrieveStatuses();
List<Object> flatStatuses = new ArrayList<Object>(statuses.size());
// TODO(ejona): choose locale based on Accept-Languages.
Locale locale = Locale.ENGLISH;
diff --git a/src/com/google/enterprise/adaptor/GsaCommunicationHandler.java b/src/com/google/enterprise/adaptor/GsaCommunicationHandler.java
index 34b95db..fa66cbc 100644
--- a/src/com/google/enterprise/adaptor/GsaCommunicationHandler.java
+++ b/src/com/google/enterprise/adaptor/GsaCommunicationHandler.java
@@ -94,7 +94,10 @@
private ExecutorService backgroundExecutor;
private DocIdCodec docIdCodec;
private DocIdSender docIdSender;
+ private HttpServerScope dashboardScope;
private Dashboard dashboard;
+ private final List<StatusSource> statusSources
+ = new CopyOnWriteArrayList<StatusSource>();
private SensitiveValueCodec secureValueCodec;
private SamlIdentityProvider samlIdentityProvider;
/**
@@ -181,8 +184,13 @@
if (port != config.getServerPort()) {
config.setValue("server.port", "" + port);
}
+ int dashboardPort = dashboardServer.getAddress().getPort();
+ if (dashboardPort != config.getServerDashboardPort()) {
+ config.setValue("server.dashboardPort", "" + dashboardPort);
+ }
scope = new HttpServerScope(server, contextPrefix);
+ dashboardScope = new HttpServerScope(dashboardServer, contextPrefix);
waiter = new ShutdownWaiter();
scheduleExecutor = Executors.newSingleThreadScheduledExecutor(
@@ -221,9 +229,6 @@
docIdSender
= new DocIdSender(fileMaker, fileSender, journal, config, adaptor);
- dashboard = new Dashboard(config, this, journal, sessionManager,
- secureValueCodec, adaptor);
-
// 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
@@ -349,7 +354,9 @@
TimeUnit.MILLISECONDS);
}
- dashboard.start(dashboardServer, contextPrefix);
+ dashboard = new Dashboard(config, this, journal, sessionManager,
+ secureValueCodec, adaptor, statusSources);
+ dashboard.start(dashboardScope);
shuttingDownLatch = null;
}
@@ -610,10 +617,13 @@
private synchronized void realStop(long time, TimeUnit unit) {
if (scope != null) {
scope.close();
- }
- if (dashboard != null) {
- dashboard.clearStatusSources();
+ scope = null;
+
+ dashboardScope.close();
+ dashboardScope = null;
+
dashboard.stop();
+ dashboard = null;
}
if (scheduleExecutor != null) {
scheduleExecutor.shutdownNow();
@@ -635,8 +645,6 @@
// AdaptorContext is usable until the very end.
sendDocIdsFuture = null;
scheduler = null;
- scope = null;
- dashboard = null;
scheduleExecutor = null;
backgroundExecutor = null;
waiter = null;
@@ -835,7 +843,7 @@
+ "state, the adaptor is restarting.");
HttpServer existingServer = scope.getHttpServer();
HttpServer existingDashboardServer
- = dashboard.getScope().getHttpServer();
+ = dashboardScope.getHttpServer();
stop(3, TimeUnit.SECONDS);
try {
start(existingServer, existingDashboardServer);
@@ -871,7 +879,7 @@
if (afterInit) {
throw new IllegalStateException("After init()");
}
- dashboard.addStatusSource(source);
+ statusSources.add(source);
}
@Override
diff --git a/src/com/google/enterprise/adaptor/StatusMonitor.java b/src/com/google/enterprise/adaptor/StatusMonitor.java
deleted file mode 100644
index 7d4454c..0000000
--- a/src/com/google/enterprise/adaptor/StatusMonitor.java
+++ /dev/null
@@ -1,56 +0,0 @@
-// Copyright 2011 Google Inc. All Rights Reserved.
-//
-// Licensed 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 com.google.enterprise.adaptor;
-
-import java.util.*;
-import java.util.concurrent.CopyOnWriteArrayList;
-
-/**
- * Collection of various status LED sources.
- *
- * <p>This class is thread-safe.
- */
-class StatusMonitor {
- private List<StatusSource> sources = new CopyOnWriteArrayList<StatusSource>();
-
- public Map<StatusSource, Status> retrieveStatuses() {
- Map<StatusSource, Status> statuses
- = new LinkedHashMap<StatusSource, Status>(sources.size() * 2);
- for (StatusSource source : sources) {
- statuses.put(source, source.retrieveStatus());
- }
- return statuses;
- }
-
- /**
- * Add a {@code StatusSource}.
- *
- * @throws NullPointerException when {@code source} is {@code null}
- */
- public void addSource(StatusSource source) {
- if (source == null) {
- throw new NullPointerException();
- }
- sources.add(source);
- }
-
- public void removeSource(StatusSource source) {
- sources.remove(source);
- }
-
- public void clearSources() {
- sources.clear();
- }
-}
diff --git a/test/com/google/enterprise/adaptor/DashboardTest.java b/test/com/google/enterprise/adaptor/DashboardTest.java
index e0e0b7f..3ee23d9 100644
--- a/test/com/google/enterprise/adaptor/DashboardTest.java
+++ b/test/com/google/enterprise/adaptor/DashboardTest.java
@@ -33,6 +33,7 @@
String golden = "Testing\n";
Dashboard.CircularLogRpcMethod method
= new Dashboard.CircularLogRpcMethod();
+ method.start();
try {
Logger logger = Logger.getLogger("");
Level origLevel = logger.getLevel();
@@ -72,11 +73,11 @@
golden.add(goldenObj);
}
- StatusMonitor monitor = new StatusMonitor();
+ List<StatusSource> sources = new ArrayList<StatusSource>();
Status status = new MockStatus(Status.Code.NORMAL, "fine");
MockStatusSource source = new MockStatusSource("mock", status);
- monitor.addSource(source);
- Dashboard.StatusRpcMethod method = new Dashboard.StatusRpcMethod(monitor);
+ sources.add(source);
+ Dashboard.StatusRpcMethod method = new Dashboard.StatusRpcMethod(sources);
List list = (List) method.run(null);
assertEquals(golden, list);
}