Use REST API to query server's "interest" in an error report
If the server announces a query URL in its discovery document, this
enables a REST-based remote history which takes precendence over the
problems.zip-based history.
To reduce network traffic, it caches HTTP requests in a Lucene index.
Bug: 508320
Change-Id: I98fd4e59f01820af6f7e27c44a487c735e55bae3
Signed-off-by: Andreas Sewe <andreas.sewe@codetrails.com>
diff --git a/bundles/org.eclipse.epp.logging.aeri.ide/META-INF/MANIFEST.MF b/bundles/org.eclipse.epp.logging.aeri.ide/META-INF/MANIFEST.MF
index f22469d..26adaba 100644
--- a/bundles/org.eclipse.epp.logging.aeri.ide/META-INF/MANIFEST.MF
+++ b/bundles/org.eclipse.epp.logging.aeri.ide/META-INF/MANIFEST.MF
@@ -45,10 +45,15 @@
org.apache.http;version="[4.3.0,5.0.0)",
org.apache.http.auth;version="[4.3.0,5.0.0)",
org.apache.http.client;version="[4.3.0,5.0.0)",
+ org.apache.http.client.cache;version="[4.3.0,5.0.0)",
org.apache.http.client.entity;version="[4.3.0,5.0.0)",
org.apache.http.client.fluent;version="[4.3.0,5.0.0)",
+ org.apache.http.client.utils;version="[4.3.0,5.0.0)",
org.apache.http.conn;version="[4.3.0,5.0.0)",
org.apache.http.entity;version="[4.3.0,5.0.0)",
+ org.apache.http.impl.client;version="[4.3.0,5.0.0)",
+ org.apache.http.impl.client.cache;version="[4.3.0,5.0.0)",
+ org.apache.http.message;version="[4.3.0,5.0.0)",
org.apache.http.util;version="[4.3.0,5.0.0)"
Export-Package: org.eclipse.epp.internal.logging.aeri.ide;x-internal:=true,
org.eclipse.epp.internal.logging.aeri.ide.server;x-internal:=true,
diff --git a/bundles/org.eclipse.epp.logging.aeri.ide/src/org/eclipse/epp/internal/logging/aeri/ide/l10n/LogMessages.java b/bundles/org.eclipse.epp.logging.aeri.ide/src/org/eclipse/epp/internal/logging/aeri/ide/l10n/LogMessages.java
index 586c3d9..005e0dc 100644
--- a/bundles/org.eclipse.epp.logging.aeri.ide/src/org/eclipse/epp/internal/logging/aeri/ide/l10n/LogMessages.java
+++ b/bundles/org.eclipse.epp.logging.aeri.ide/src/org/eclipse/epp/internal/logging/aeri/ide/l10n/LogMessages.java
@@ -7,9 +7,7 @@
*/
package org.eclipse.epp.internal.logging.aeri.ide.l10n;
-import static org.eclipse.core.runtime.IStatus.ERROR;
-import static org.eclipse.core.runtime.IStatus.INFO;
-import static org.eclipse.core.runtime.IStatus.WARNING;
+import static org.eclipse.core.runtime.IStatus.*;
import org.eclipse.epp.logging.aeri.core.util.Logs.DefaultLogMessage;
import org.osgi.framework.Bundle;
@@ -35,6 +33,8 @@
public static final LogMessages ERROR_SAVE_PREFERENCES_FAILED = new LogMessages(ERROR, Messages.LOG_ERROR_SAVE_PREFERENCES_FAILED);
public static final LogMessages INFO_SERVER_NOT_AVAILABLE = new LogMessages(INFO, Messages.LOG_INFO_SERVER_NOT_AVAILABLE);
+ public static final LogMessages INFO_TEMPORARILY_DISABLED_REST_QUERIES = new LogMessages(INFO,
+ Messages.LOG_INFO_TEMPORARILY_DISABLED_REST_QUERIES);
public static final LogMessages WARN_HISTORY_NOT_AVAILABLE = new LogMessages(WARNING, Messages.LOG_WARN_HISTORY_NOT_AVAILABLE);
public static final LogMessages WARN_HISTORY_STOP_FAILED = new LogMessages(WARNING, Messages.LOG_WARN_HISTORY_STOP_FAILED);
@@ -51,6 +51,8 @@
Messages.LOG_WARN_RESPONSE_UPLOAD_REPORT_FAILED);
public static final LogMessages WARN_UNEXPECTED_SERVER_RESPONSE = new LogMessages(WARNING, Messages.LOG_WARN_UNEXECTED_SERVER_RESPONSE);
public static final LogMessages WARN_REPORT_PROCESSOR_FAILED = new LogMessages(WARNING, Messages.LOG_WARN_REPORT_PROCESSOR_FAILED);
+ public static final LogMessages WARN_REST_QUERY_FAILED = new LogMessages(WARNING,
+ Messages.LOG_WARN_REST_QUERY_FAILED);
public LogMessages(int severity, String message) {
super(severity, code++, String.format("%s %s", message, VERSION)); //$NON-NLS-1$
diff --git a/bundles/org.eclipse.epp.logging.aeri.ide/src/org/eclipse/epp/internal/logging/aeri/ide/l10n/Messages.java b/bundles/org.eclipse.epp.logging.aeri.ide/src/org/eclipse/epp/internal/logging/aeri/ide/l10n/Messages.java
index f3ef37d..3254539 100644
--- a/bundles/org.eclipse.epp.logging.aeri.ide/src/org/eclipse/epp/internal/logging/aeri/ide/l10n/Messages.java
+++ b/bundles/org.eclipse.epp.logging.aeri.ide/src/org/eclipse/epp/internal/logging/aeri/ide/l10n/Messages.java
@@ -111,6 +111,7 @@
public static String LOG_HISTORY_ABBREVIATION;
public static String LOG_INFO_SERVER_NOT_AVAILABLE;
+ public static String LOG_INFO_TEMPORARILY_DISABLED_REST_QUERIES;
public static String LOG_WARN_EXTENSION_FAILED;
public static String LOG_WARN_FORMATTING_FAILED;
@@ -123,6 +124,7 @@
public static String LOG_WARN_REPORT_PROCESSOR_FAILED;
public static String LOG_WARN_REPORTING_ERROR;
public static String LOG_WARN_RESPONSE_UPLOAD_REPORT_FAILED;
+ public static String LOG_WARN_REST_QUERY_FAILED;
public static String LOG_WARN_SERVER_FAILURE;
public static String LOG_WARN_UNEXECTED_SERVER_RESPONSE;
public static String LOG_WARN_URL_VALIDATION_FAILED;
diff --git a/bundles/org.eclipse.epp.logging.aeri.ide/src/org/eclipse/epp/internal/logging/aeri/ide/l10n/messages.properties b/bundles/org.eclipse.epp.logging.aeri.ide/src/org/eclipse/epp/internal/logging/aeri/ide/l10n/messages.properties
index 01a95d9..4294684 100644
--- a/bundles/org.eclipse.epp.logging.aeri.ide/src/org/eclipse/epp/internal/logging/aeri/ide/l10n/messages.properties
+++ b/bundles/org.eclipse.epp.logging.aeri.ide/src/org/eclipse/epp/internal/logging/aeri/ide/l10n/messages.properties
@@ -101,6 +101,7 @@
LOG_ERROR_SAVE_PREFERENCES_FAILED=Saving preferences failed: {0}={1}
LOG_INFO_SERVER_NOT_AVAILABLE=The Error Reporting server is not available. Error Reporting will be disabled until the next restart.
+LOG_INFO_TEMPORARILY_DISABLED_REST_QUERIES=Temporarily disabled REST queries for server \u2018{0}\u2019.
LOG_WARN_EXTENSION_FAILED=Extension \u2018{0}\u2019 failed: {1}.
LOG_WARN_FORMATTING_FAILED=Formatting \u2018{0}\u2019 with arguments \u2018{1}\u2019 failed: {2}.
@@ -113,6 +114,7 @@
LOG_WARN_REPORT_PROCESSOR_FAILED=Report processor \u2018{0}\u2019 failed with exception
LOG_WARN_REPORTING_ERROR=Unexpected Error occurred while processing a log event. Please open a bug at https://bugs.eclipse.org/bugs/enter_bug.cgi?product=EPP&component=logging
LOG_WARN_RESPONSE_UPLOAD_REPORT_FAILED=Sending report to \u2018{0}\u2019 failed: {1}
+LOG_WARN_REST_QUERY_FAILED=REST query for server \u2018{0}\u2019 failed: \u2018{1}\u2019
LOG_WARN_SERVER_FAILURE=Server \u2018{0}\u2019 failed with exception: {1}.
LOG_WARN_UNEXECTED_SERVER_RESPONSE=Unexpected response : {0} \u2010 {1}
LOG_WARN_URL_VALIDATION_FAILED=Cannot open browser for URL \u2018{0}\u2019: {1}
diff --git a/bundles/org.eclipse.epp.logging.aeri.ide/src/org/eclipse/epp/internal/logging/aeri/ide/server/mars/IO.java b/bundles/org.eclipse.epp.logging.aeri.ide/src/org/eclipse/epp/internal/logging/aeri/ide/server/mars/IO.java
index a17fb84..5a25f4d 100644
--- a/bundles/org.eclipse.epp.logging.aeri.ide/src/org/eclipse/epp/internal/logging/aeri/ide/server/mars/IO.java
+++ b/bundles/org.eclipse.epp.logging.aeri.ide/src/org/eclipse/epp/internal/logging/aeri/ide/server/mars/IO.java
@@ -101,21 +101,24 @@
.socketTimeout(configuration.getSocketTimeoutMs());
String response = proxyAuthentication(executor, target).execute(request).returnContent().asString();
ServerResponse raw = Json.deserialize(response, ServerResponse.class);
+ return extractProblemState(raw);
+ }
+ public static IProblemState extractProblemState(ServerResponse serverResponse) {
IProblemState problemState = IModelFactory.eINSTANCE.createProblemState();
// this looks a bit weird: it's not a bug id but the public id of the submission...
- String submissionUrl = raw.getSubmissionUrl().orNull();
+ String submissionUrl = serverResponse.getSubmissionUrl().orNull();
if (submissionUrl != null) {
Links.addLink(problemState, Links.REL_SUBMISSION, submissionUrl, Messages.LINK_TEXT_SUBMISSION);
}
- if (raw.hasBug()) {
- Links.addLink(problemState, REL_BUG, raw.getBugUrl().orNull(),
- Formats.format(Messages.LINK_TEXT_BUG, raw.getBugId().or(Messages.LINK_TEXT_BUG_ID_NULL)));
+ if (serverResponse.hasBug()) {
+ Links.addLink(problemState, REL_BUG, serverResponse.getBugUrl().orNull(),
+ Formats.format(Messages.LINK_TEXT_BUG, serverResponse.getBugId().or(Messages.LINK_TEXT_BUG_ID_NULL)));
}
- problemState.setStatus(tryParse(raw));
- String message = raw.getInformation().orNull();
+ problemState.setStatus(tryParse(serverResponse));
+ String message = serverResponse.getInformation().orNull();
if (message != null && !StringUtils.contains(message, "</a>") && !StringUtils.contains(message, "{link")) { //$NON-NLS-1$
// TODO temporary for Mars.1 servers in Neon:
// Server sends an 'additional' status message which may not contain any links. Let's append them in a generic way for Neon.M4
@@ -123,7 +126,7 @@
}
problemState.setMessage(message);
- String[] keywords = raw.getKeywords().orNull();
+ String[] keywords = serverResponse.getKeywords().orNull();
if (keywords != null) {
for (String keyword : keywords) {
problemState.getNeedinfo().add(keyword);
@@ -134,7 +137,7 @@
}
// returns a string with all links. Separated by ' ' and with a leading ' ' if the list of links is not empty.
- private String appendLinks(EMap<String, ILink> links) {
+ private static String appendLinks(EMap<String, ILink> links) {
if (links.isEmpty()) {
return ""; //$NON-NLS-1$
}
diff --git a/bundles/org.eclipse.epp.logging.aeri.ide/src/org/eclipse/epp/internal/logging/aeri/ide/server/mars/IProblemsHistory.java b/bundles/org.eclipse.epp.logging.aeri.ide/src/org/eclipse/epp/internal/logging/aeri/ide/server/mars/IProblemsHistory.java
new file mode 100644
index 0000000..15500a7
--- /dev/null
+++ b/bundles/org.eclipse.epp.logging.aeri.ide/src/org/eclipse/epp/internal/logging/aeri/ide/server/mars/IProblemsHistory.java
@@ -0,0 +1,23 @@
+/**
+ * Copyright (c) 2016 Codetrails GmbH.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.eclipse.epp.internal.logging.aeri.ide.server.mars;
+
+import java.io.Closeable;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.epp.logging.aeri.core.IProblemState;
+import org.eclipse.epp.logging.aeri.core.ISystemSettings;
+
+import com.google.common.base.Optional;
+
+public interface IProblemsHistory extends Closeable {
+
+ Optional<IProblemState> seen(IStatus status);
+
+ void sync(IO io, ISystemSettings systemSettings);
+}
diff --git a/bundles/org.eclipse.epp.logging.aeri.ide/src/org/eclipse/epp/internal/logging/aeri/ide/server/mars/ServerProblemsHistory.java b/bundles/org.eclipse.epp.logging.aeri.ide/src/org/eclipse/epp/internal/logging/aeri/ide/server/mars/ProblemsDatabaseProblemsHistory.java
similarity index 86%
rename from bundles/org.eclipse.epp.logging.aeri.ide/src/org/eclipse/epp/internal/logging/aeri/ide/server/mars/ServerProblemsHistory.java
rename to bundles/org.eclipse.epp.logging.aeri.ide/src/org/eclipse/epp/internal/logging/aeri/ide/server/mars/ProblemsDatabaseProblemsHistory.java
index 94c21d0..8e840b6 100644
--- a/bundles/org.eclipse.epp.logging.aeri.ide/src/org/eclipse/epp/internal/logging/aeri/ide/server/mars/ServerProblemsHistory.java
+++ b/bundles/org.eclipse.epp.logging.aeri.ide/src/org/eclipse/epp/internal/logging/aeri/ide/server/mars/ProblemsDatabaseProblemsHistory.java
@@ -64,14 +64,14 @@
import org.eclipse.epp.logging.aeri.core.util.Statuses;
import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Predicate;
+import com.google.common.base.Optional;
import com.google.common.io.Files;
import com.google.common.util.concurrent.AbstractIdleService;
/**
* A history of remotely known problems that are worth knowing on the client side.
*/
-public class ServerProblemsHistory extends AbstractIdleService {
+public class ProblemsDatabaseProblemsHistory extends AbstractIdleService implements IProblemsHistory {
// values for problem databases
public static final String F_VERSION = "version"; //$NON-NLS-1$
@@ -85,11 +85,13 @@
public static final String F_FINGERPRINT = "fingerprint"; //$NON-NLS-1$
public static final String F_NEEDINFOS = "needinfos"; //$NON-NLS-1$
- private File stateLocation;
+ private final File stateLocation;
+
+ private UpdateIndexJob updateIndexJob;
private Directory index;
private SearcherManager manager;
- public ServerProblemsHistory(File stateLocation) {
+ public ProblemsDatabaseProblemsHistory(File stateLocation) {
this.stateLocation = stateLocation;
}
@@ -108,7 +110,7 @@
}
@VisibleForTesting
- protected Directory createIndexDirectory() throws IOException {
+ Directory createIndexDirectory() throws IOException {
stateLocation.mkdirs();
FSDirectory directory = FSDirectory.open(stateLocation);
@@ -126,14 +128,15 @@
}
}
- public IProblemState seen(IStatus status) {
+ @Override
+ public Optional<IProblemState> seen(IStatus status) {
checkNotNull(status);
checkState(isRunning());
String fingerprint = Statuses.traceIdentityHash(status);
return seen(new TermQuery(new Term(F_FINGERPRINT, fingerprint)));
}
- private IProblemState seen(Query q) {
+ private Optional<IProblemState> seen(Query q) {
IndexSearcher searcher = manager.acquire();
try {
TopDocs results = searcher.search(q, 1);
@@ -142,7 +145,7 @@
int doc = results.scoreDocs[0].doc;
Document d = searcher.doc(doc);
IProblemState status = loadStatus(d);
- return status;
+ return Optional.of(status);
}
} catch (Exception e) {
log(WARN_INDEX_NOT_AVAILABLE, e);
@@ -153,9 +156,7 @@
log(WARN_INDEX_NOT_AVAILABLE, e);
}
}
- IProblemState state = IModelFactory.eINSTANCE.createProblemState();
- state.setStatus(ProblemStatus.UNCONFIRMED);
- return state;
+ return Optional.absent();
}
private IProblemState loadStatus(Document d) {
@@ -227,47 +228,13 @@
manager.close();
}
- public static class RemoteProblemsHistoryFilter implements Predicate<IStatus> {
-
- private ServerProblemsHistory index;
-
- public RemoteProblemsHistoryFilter(ServerProblemsHistory index) {
- this.index = index;
- }
-
- @Override
- public boolean apply(IStatus input) {
- if (!index.isRunning()) {
- // if the database is not (yet) set up, let everything pass
- return true;
- }
-
- IProblemState status = index.seen(input);
- switch (status.getStatus()) {
- case IGNORED:
- case INVALID:
- case FAILURE:
- return false;
- case NEW:
- case UNCONFIRMED:
- case CONFIRMED:
- case FIXED:
- case NEEDINFO:
- return true;
- default:
- Logs.log(LogMessages.DEBUG_UNKNOWN_SERVER_STATUS, status.getStatus());
- return true;
- }
- }
- }
-
public static class UpdateIndexJob extends Job {
private IO io;
private ISystemSettings settings;
- private ServerProblemsHistory history;
+ private ProblemsDatabaseProblemsHistory history;
- public UpdateIndexJob(IO io, ISystemSettings settings, ServerProblemsHistory history) {
+ public UpdateIndexJob(IO io, ISystemSettings settings, ProblemsDatabaseProblemsHistory history) {
super(Formats.format(Messages.JOB_NAME_UPDATE_INDEX, io.getConfiguration().getProblemsUrl()));
this.io = io;
@@ -327,4 +294,29 @@
}
}
+
+ @Override
+ public void sync(IO io, ISystemSettings systemSettings) {
+ if (shouldUpdateProblemDatabase(io, systemSettings)) {
+ updateProblemDatabase(io, systemSettings);
+ }
+ }
+
+ private boolean shouldUpdateProblemDatabase(IO io, ISystemSettings systemSettings) {
+ return !isUpdating() && io.isProblemsDatabaseOutdated();
+ }
+
+ private boolean isUpdating() {
+ return updateIndexJob != null && updateIndexJob.getResult() == null;
+ }
+
+ @VisibleForTesting
+ protected void updateProblemDatabase(IO io, ISystemSettings systemSettings) {
+ updateIndexJob = new UpdateIndexJob(io, systemSettings, this);
+ updateIndexJob.schedule();
+ }
+
+ @Override
+ public void close() throws IOException {
+ }
}
diff --git a/bundles/org.eclipse.epp.logging.aeri.ide/src/org/eclipse/epp/internal/logging/aeri/ide/server/mars/ServerConfiguration.java b/bundles/org.eclipse.epp.logging.aeri.ide/src/org/eclipse/epp/internal/logging/aeri/ide/server/mars/ServerConfiguration.java
index 3d3c66c..7c204ee 100644
--- a/bundles/org.eclipse.epp.logging.aeri.ide/src/org/eclipse/epp/internal/logging/aeri/ide/server/mars/ServerConfiguration.java
+++ b/bundles/org.eclipse.epp.logging.aeri.ide/src/org/eclipse/epp/internal/logging/aeri/ide/server/mars/ServerConfiguration.java
@@ -37,7 +37,7 @@
// in minutes
private long problemsTtl;
- private String queryUrl;
+ private String interestUrl;
// max time in seconds until a connection to the server has to be established.
private int connectTimeout = (int) TimeUnit.SECONDS.toMillis(3);;
@@ -213,12 +213,12 @@
this.problemsTtl = problemsTtlInMinutes;
}
- public String getQueryUrl() {
- return queryUrl;
+ public String getInterestUrl() {
+ return interestUrl;
}
- public void setQueryUrl(String queryUrl) {
- this.queryUrl = queryUrl;
+ public void setInterestUrl(String interestUrl) {
+ this.interestUrl = interestUrl;
}
public List<String> getAcceptedProducts() {
diff --git a/bundles/org.eclipse.epp.logging.aeri.ide/src/org/eclipse/epp/internal/logging/aeri/ide/server/mars/ServerConnection.java b/bundles/org.eclipse.epp.logging.aeri.ide/src/org/eclipse/epp/internal/logging/aeri/ide/server/mars/ServerConnection.java
index d8684c1..3495c72 100644
--- a/bundles/org.eclipse.epp.logging.aeri.ide/src/org/eclipse/epp/internal/logging/aeri/ide/server/mars/ServerConnection.java
+++ b/bundles/org.eclipse.epp.logging.aeri.ide/src/org/eclipse/epp/internal/logging/aeri/ide/server/mars/ServerConnection.java
@@ -19,6 +19,8 @@
import java.io.File;
import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
import java.util.concurrent.TimeoutException;
import javax.annotation.PostConstruct;
@@ -39,7 +41,7 @@
import org.eclipse.epp.internal.logging.aeri.ide.l10n.Messages;
import org.eclipse.epp.internal.logging.aeri.ide.server.LocalReportsHistory;
import org.eclipse.epp.internal.logging.aeri.ide.server.LocalReportsHistory.LocalHistorySeenFilter;
-import org.eclipse.epp.internal.logging.aeri.ide.server.mars.ServerProblemsHistory.UpdateIndexJob;
+import org.eclipse.epp.internal.logging.aeri.ide.server.rest.RestBasedProblemsHistory;
import org.eclipse.epp.logging.aeri.core.IModelFactory;
import org.eclipse.epp.logging.aeri.core.IProblemState;
import org.eclipse.epp.logging.aeri.core.IReport;
@@ -79,8 +81,11 @@
public void notifyChanged(Notification msg) {
int featureID = msg.getFeatureID(featureClass);
if (msg.getEventType() == Notification.SET && ArrayUtils.contains(observedFeatureIds, featureID)) {
- if (shouldUpdateProblemDatabase(io, systemSettings, remoteHistory)) {
- updateProblemDatabase(io, systemSettings, remoteHistory);
+ if (!shouldUse()) {
+ return;
+ }
+ for (IProblemsHistory remoteHistory : remoteHistories) {
+ remoteHistory.sync(io, systemSettings);
}
}
}
@@ -91,9 +96,8 @@
private final File configurationArea;
private Predicate<IStatus> statusFilters = alwaysFalse();
private IO io;
- private ServerProblemsHistory remoteHistory;
private LocalReportsHistory localHistory;
- private UpdateIndexJob updateIndexJob;
+ private final List<IProblemsHistory> remoteHistories = new ArrayList<>();
@Inject
public ServerConnection(IServerDescriptor descriptor, ISystemSettings system, File configurationArea) {
@@ -111,17 +115,6 @@
protected void startUp() throws Exception {
try {
{
- File remoteHistoryStateLocation = new File(configurationArea, "remote-history"); //$NON-NLS-1$
- remoteHistory = new ServerProblemsHistory(remoteHistoryStateLocation);
- remoteHistory.startAsync();
- }
- {
- File localHistoryStateLocation = new File(configurationArea, "local-history"); //$NON-NLS-1$
- localHistoryStateLocation.mkdirs();
- localHistory = new LocalReportsHistory(localHistoryStateLocation);
- localHistory.startAsync();
- }
- {
File file = new File(configurationArea, "server-config.json"); //$NON-NLS-1$
io = createIO(file);
if (file.exists()) {
@@ -131,16 +124,37 @@
io.refreshConfiguration(checkNotNull(getLink(server, REL_DISCOVERY)).getHref(), new NullProgressMonitor());
io.saveConfiguration();
}
- if (shouldUpdateProblemDatabase(io, systemSettings, remoteHistory)) {
- updateProblemDatabase(io, systemSettings, remoteHistory);
- }
}
+
{
+ File localHistoryStateLocation = new File(configurationArea, "local-history"); //$NON-NLS-1$
+ localHistoryStateLocation.mkdirs();
+ localHistory = new LocalReportsHistory(localHistoryStateLocation);
+ localHistory.startAsync();
+ }
+
+ {
+ if (io.getConfiguration().getInterestUrl() != null) {
+ File cacheDir = new File(configurationArea, "http-cache");
+ cacheDir.mkdirs();
+ remoteHistories.add(createRestBasedProblemsHistory(cacheDir));
+ }
+ if (io.getConfiguration().getProblemsUrl() != null) {
+ remoteHistories.add(createServerProblemsHistory());
+ }
+
+ if (shouldUse()) {
+ for (IProblemsHistory remoteHistory : remoteHistories) {
+ remoteHistory.sync(io, systemSettings);
+ }
+ }
+
server.eAdapters().add(new UpdateDatabaseOnFeatureChangeAdapter(IServerDescriptor.class, SERVER_DESCRIPTOR__ENABLED,
SERVER_DESCRIPTOR__CONFIGURED));
systemSettings.eAdapters().add(new UpdateDatabaseOnFeatureChangeAdapter(ISystemSettings.class, SYSTEM_SETTINGS__CONFIGURED,
SYSTEM_SETTINGS__SEND_MODE));
}
+
{
// make sure we can operate before removing the AlwaysFalse filter...
ServerConfiguration configuration = io.getConfiguration();
@@ -168,13 +182,26 @@
}
}
- private boolean shouldUpdateProblemDatabase(IO io, ISystemSettings systemSettings, ServerProblemsHistory remoteHistory) {
- return !isUpdating() && server.isConfigured() && server.isEnabled() && systemSettings.isConfigured()
- && systemSettings.getSendMode() != SendMode.NEVER && io.isProblemsDatabaseOutdated();
+ private IProblemsHistory createRestBasedProblemsHistory(File cacheDir) throws IOException {
+ return new RestBasedProblemsHistory(io.getConfiguration(), cacheDir);
}
- private boolean isUpdating() {
- return updateIndexJob != null && updateIndexJob.getResult() == null;
+ @VisibleForTesting
+ protected IProblemsHistory createServerProblemsHistory() {
+ File remoteHistoryStateLocation = new File(configurationArea, "remote-history"); //$NON-NLS-1$
+ ProblemsDatabaseProblemsHistory remoteHistory = new ProblemsDatabaseProblemsHistory(remoteHistoryStateLocation);
+ remoteHistory.startAsync();
+ return remoteHistory;
+ }
+
+ private boolean shouldUse() {
+ if (!systemSettings.isConfigured() || systemSettings.getSendMode() == SendMode.NEVER) {
+ return false;
+ }
+ if (!server.isConfigured() || !server.isEnabled()) {
+ return false;
+ }
+ return true;
}
@VisibleForTesting
@@ -182,48 +209,66 @@
return new IO(Executor.newInstance(), file);
}
- @VisibleForTesting
- protected void updateProblemDatabase(IO io, ISystemSettings systemSettings, ServerProblemsHistory remoteHistory) {
- updateIndexJob = new UpdateIndexJob(io, systemSettings, remoteHistory);
- updateIndexJob.schedule();
- }
-
@Override
public IProblemState interested(IStatus status, IEclipseContext context, IProgressMonitor monitor) {
- if (!isRunning() || !statusFilters.apply(status)) {
+ if (!shouldUse()) {
+ // FIXME
+ // This prevents requests to the server before Aeri has been enabled by the user.
+ // The downside is that problems that occur before Aeri is enabled will be shown as unconfirmed.
+ // Ideally, once Aeri is enabled, the server is queried and the response is used. This requires bigger changes in the control
+ // flow however.
+ return problemStateUnconfirmedBeforeSend();
+ } else if (!isRunning() || !statusFilters.apply(status)) {
IProblemState res = IModelFactory.eINSTANCE.createProblemState();
res.setStatus(ProblemStatus.IGNORED);
return res;
} else {
- IProblemState seen = remoteHistory.seen(status);
- String message = seen.getMessage();
- if (seen.getMessage() != null) {
- return seen;
- }
- switch (seen.getStatus()) {
- case IGNORED:
- return seen;
- case NEEDINFO:
- message = msgNeedinfoBeforeSend(seen);
- break;
- case WONTFIX:
- message = msgWontFixBeforeSend(seen);
- break;
+ IProblemState seen = null;
+ for (IProblemsHistory remoteHistory : remoteHistories) {
+ seen = remoteHistory.seen(status).orNull();
+ if (seen == null) {
+ continue;
+ }
- case FIXED:
- message = msgFixedBeforeSend(seen);
+ String message = seen.getMessage();
+ if (seen.getMessage() != null) {
+ return seen;
+ }
+ switch (seen.getStatus()) {
+ case IGNORED:
+ return seen;
+ case NEEDINFO:
+ message = msgNeedinfoBeforeSend(seen);
+ break;
+ case WONTFIX:
+ message = msgWontFixBeforeSend(seen);
+ break;
+ case FIXED:
+ message = msgFixedBeforeSend(seen);
+ break;
+ case UNCONFIRMED:
+ message = msgUnconfirmedBeforeSend();
+ break;
+ default:
+ message = Formats.format("Unexpected state {0}", seen.getStatus()); //$NON-NLS-1$
+ }
+ seen.setMessage(message);
break;
- case UNCONFIRMED:
- message = msgUnconfirmedBeforeSend(seen);
- break;
- default:
- message = Formats.format("Unexpected state {0}", seen.getStatus()); //$NON-NLS-1$
}
- seen.setMessage(message);
+ if (seen == null) {
+ return problemStateUnconfirmedBeforeSend();
+ }
return seen;
}
}
+ private IProblemState problemStateUnconfirmedBeforeSend() {
+ IProblemState res = IModelFactory.eINSTANCE.createProblemState();
+ res.setStatus(ProblemStatus.UNCONFIRMED);
+ res.setMessage(msgUnconfirmedBeforeSend());
+ return res;
+ }
+
@Override
public IReport transform(IStatus status, IEclipseContext context) {
// set the accepted patterns for the AnonymizeStackTracesProcessor
@@ -301,8 +346,8 @@
return format(Messages.PROBLEM_MESSAGES_NEW_AFTER_SEND, server.getName(), response);
}
- protected String msgUnconfirmedBeforeSend(IProblemState cachedState) {
- return format(Messages.PROBLEM_MESSAGES_UNCONFIRMED_BEFORE_SEND, server.getName(), cachedState);
+ protected String msgUnconfirmedBeforeSend() {
+ return format(Messages.PROBLEM_MESSAGES_UNCONFIRMED_BEFORE_SEND, server.getName());
}
protected String msgUnconfirmedAfterSend(IProblemState response) {
@@ -359,11 +404,13 @@
@Override
protected void shutDown() throws Exception {
+ for (IProblemsHistory remoteHistory : remoteHistories) {
+ remoteHistory.close();
+ }
}
@Override
public String toString() {
return server.getId() + " " + super.toString(); //$NON-NLS-1$
}
-
}
diff --git a/bundles/org.eclipse.epp.logging.aeri.ide/src/org/eclipse/epp/internal/logging/aeri/ide/server/rest/LuceneHttpCacheStorage.java b/bundles/org.eclipse.epp.logging.aeri.ide/src/org/eclipse/epp/internal/logging/aeri/ide/server/rest/LuceneHttpCacheStorage.java
new file mode 100644
index 0000000..cfa53fe
--- /dev/null
+++ b/bundles/org.eclipse.epp.logging.aeri.ide/src/org/eclipse/epp/internal/logging/aeri/ide/server/rest/LuceneHttpCacheStorage.java
@@ -0,0 +1,256 @@
+/**
+ * Copyright (c) 2016 Codetrails GmbH.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.eclipse.epp.internal.logging.aeri.ide.server.rest;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.http.Header;
+import org.apache.http.ProtocolVersion;
+import org.apache.http.StatusLine;
+import org.apache.http.client.cache.HttpCacheEntry;
+import org.apache.http.client.cache.HttpCacheStorage;
+import org.apache.http.client.cache.HttpCacheUpdateCallback;
+import org.apache.http.client.cache.HttpCacheUpdateException;
+import org.apache.http.client.cache.Resource;
+import org.apache.http.impl.client.cache.HeapResource;
+import org.apache.http.message.BasicHeader;
+import org.apache.http.message.BasicStatusLine;
+import org.apache.lucene.analysis.KeywordAnalyzer;
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.Field;
+import org.apache.lucene.document.Field.Index;
+import org.apache.lucene.document.Field.Store;
+import org.apache.lucene.document.Fieldable;
+import org.apache.lucene.index.IndexWriter;
+import org.apache.lucene.index.IndexWriterConfig;
+import org.apache.lucene.index.IndexWriterConfig.OpenMode;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.search.IndexSearcher;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.ScoreDoc;
+import org.apache.lucene.search.SearcherManager;
+import org.apache.lucene.search.TermQuery;
+import org.apache.lucene.search.TopDocs;
+import org.apache.lucene.store.Directory;
+import org.apache.lucene.util.Version;
+
+class LuceneHttpCacheStorage implements HttpCacheStorage, Closeable {
+
+ private static final String KEY_FIELD_NAME = "key";
+ private static final String REQUEST_DATE_FIELD_NAME = "requestDate";
+ private static final String RESPONSE_DATE_FIELD_NAME = "responseDate";
+ private static final String STATUS_CODE_FIELD_NAME = "statusLine/statusCode";
+ private static final String REASON_PHRASE_FIELD_NAME = "statusLine/reasonPhrase";
+ private static final String PROTOCOL_FIELD_NAME = "statusLine/protocolVersion/protocol";
+ private static final String MINOR_PROTOCOL_VERSION_FIELD_NAME = "statusLine/protocolVersion/minor";
+ private static final String MAJOR_PROTOCAL_VERSION_FIELD_NAME = "statusLine/protocolVersion/major";
+ private static final String HEADER_FIELD_NAMES = "header";
+ private static final int HEADER_FIELD_NAMES_LENGTH = HEADER_FIELD_NAMES.length();
+ private static final String BODY_FIELD_NAME = "body";
+ private static final String VARIANT_FIELD_NAMES = "variant";
+ private static final int VARIANT_FIELD_NAMES_LENGTH = VARIANT_FIELD_NAMES.length();
+
+ private final IndexWriter writer;
+ private final SearcherManager searcherManager;
+
+ LuceneHttpCacheStorage(Directory directory) throws IOException {
+ IndexWriterConfig writerConfig = new IndexWriterConfig(Version.LUCENE_35, new KeywordAnalyzer());
+ writerConfig.setOpenMode(OpenMode.CREATE_OR_APPEND);
+ writer = new IndexWriter(directory, writerConfig);
+ searcherManager = new SearcherManager(writer, true, null, null);
+ }
+
+ @Override
+ public HttpCacheEntry getEntry(String key) throws IOException {
+ searcherManager.maybeReopen();
+ IndexSearcher searcher = searcherManager.acquire();
+ try {
+ Query query = new TermQuery(new Term(KEY_FIELD_NAME, key));
+ TopDocs topDocs = searcher.search(query, 1);
+ if (topDocs.totalHits > 1) {
+ throw new IOException("Corrupt index (cache key is not unique)");
+ }
+
+ ScoreDoc[] scoreDocs = topDocs.scoreDocs;
+ if (scoreDocs.length > 0) {
+ ScoreDoc scoreDoc = scoreDocs[0];
+ Document document = searcher.doc(scoreDoc.doc);
+ return fromLuceneFields(document.getFields());
+ } else {
+ return null;
+ }
+ } finally {
+ searcherManager.release(searcher);
+ }
+ }
+
+ @Override
+ public void putEntry(String key, HttpCacheEntry entry) throws IOException {
+ Document document = toLuceneDocument(key, entry);
+ synchronized (this) {
+ writer.updateDocument(new Term(KEY_FIELD_NAME, key), document);
+ }
+ }
+
+ @Override
+ public void updateEntry(String key, HttpCacheUpdateCallback callback) throws IOException, HttpCacheUpdateException {
+ synchronized (this) {
+ HttpCacheEntry existingEntry = getEntry(key);
+ HttpCacheEntry newEntry = callback.update(existingEntry);
+ Document newDocument = toLuceneDocument(key, newEntry);
+ writer.updateDocument(new Term(KEY_FIELD_NAME, key), newDocument);
+ }
+ }
+
+ @Override
+ public void removeEntry(String key) throws IOException {
+ Query query = new TermQuery(new Term(KEY_FIELD_NAME, key));
+ synchronized (this) {
+ writer.deleteDocuments(query);
+ }
+ }
+
+ @Override
+ public void close() throws IOException {
+ searcherManager.close();
+ writer.close();
+ }
+
+ private Document toLuceneDocument(String key, HttpCacheEntry entry) throws IOException {
+ Document document = new Document();
+ document.add(new Field(KEY_FIELD_NAME, key, Store.NO, Index.NOT_ANALYZED_NO_NORMS));
+ for (Fieldable field : toLuceneFields(entry)) {
+ document.add(field);
+ }
+ return document;
+ }
+
+ private List<Fieldable> toLuceneFields(HttpCacheEntry entry) throws IOException {
+ List<Fieldable> fields = new ArrayList<>();
+
+ fields.add(new Field(REQUEST_DATE_FIELD_NAME, Long.toString(entry.getRequestDate().getTime()), Store.YES, Index.NO));
+
+ fields.add(new Field(RESPONSE_DATE_FIELD_NAME, Long.toString(entry.getResponseDate().getTime()), Store.YES, Index.NO));
+
+ StatusLine statusLine = entry.getStatusLine();
+ fields.add(new Field(STATUS_CODE_FIELD_NAME, Integer.toString(statusLine.getStatusCode()), Store.YES, Index.NO));
+ fields.add(new Field(REASON_PHRASE_FIELD_NAME, statusLine.getReasonPhrase(), Store.YES, Index.NO));
+
+ ProtocolVersion protocolVersion = statusLine.getProtocolVersion();
+ fields.add(new Field(PROTOCOL_FIELD_NAME, protocolVersion.getProtocol(), Store.YES, Index.NO));
+ fields.add(new Field(MAJOR_PROTOCAL_VERSION_FIELD_NAME, Integer.toString(protocolVersion.getMajor()), Store.YES, Index.NO));
+ fields.add(new Field(MINOR_PROTOCOL_VERSION_FIELD_NAME, Integer.toString(protocolVersion.getMinor()), Store.YES, Index.NO));
+
+ Header[] headers = entry.getAllHeaders();
+ for (int index = 0; index < headers.length; index++) {
+ Header header = headers[index];
+ fields.add(new Field(HEADER_FIELD_NAMES + '/' + index + '/' + header.getName(), header.getValue(), Store.YES, Index.NO));
+ }
+
+ Resource body = entry.getResource();
+ if (body != null) {
+ fields.add(new Field(BODY_FIELD_NAME, IOUtils.toByteArray(body.getInputStream())));
+ }
+
+ if (entry.hasVariants()) {
+ for (Entry<String, String> variant : entry.getVariantMap().entrySet()) {
+ fields.add(new Field(VARIANT_FIELD_NAMES + '/' + variant.getKey(), variant.getValue(), Store.YES, Index.NO));
+ }
+ }
+
+ return fields;
+ }
+
+ private HttpCacheEntry fromLuceneFields(List<Fieldable> fields) throws IOException {
+ Date requestDate = null;
+ Date responseDate = null;
+ int statusCode = Integer.MIN_VALUE;
+ String reasonPhrase = null;
+ String protocol = null;
+ int majorProtocolVersion = Integer.MIN_VALUE;
+ int minorProtocolVersion = Integer.MIN_VALUE;
+ List<Header> responseHeaders = new ArrayList<>();
+ Resource body = null;
+ Map<String, String> variantMap = new HashMap<>();
+
+ for (Fieldable field : fields) {
+ String fieldName = field.name();
+ if (REQUEST_DATE_FIELD_NAME.equals(fieldName)) {
+ requestDate = parseDateField(field);
+ } else if (RESPONSE_DATE_FIELD_NAME.equals(fieldName)) {
+ responseDate = parseDateField(field);
+ } else if (STATUS_CODE_FIELD_NAME.equals(fieldName)) {
+ statusCode = parseIntField(field);
+ } else if (REASON_PHRASE_FIELD_NAME.equals(fieldName)) {
+ reasonPhrase = field.stringValue();
+ } else if (PROTOCOL_FIELD_NAME.equals(fieldName)) {
+ protocol = field.stringValue();
+ } else if (MAJOR_PROTOCAL_VERSION_FIELD_NAME.equals(fieldName)) {
+ majorProtocolVersion = parseIntField(field);
+ } else if (MINOR_PROTOCOL_VERSION_FIELD_NAME.equals(fieldName)) {
+ minorProtocolVersion = parseIntField(field);
+ } else if (fieldName.startsWith(HEADER_FIELD_NAMES)) {
+ try {
+ int secondSlash = fieldName.indexOf('/', HEADER_FIELD_NAMES_LENGTH + 1);
+ String indexString = fieldName.substring(HEADER_FIELD_NAMES_LENGTH + 1, secondSlash);
+ int index = Integer.parseInt(indexString);
+ String headerName = fieldName.substring(secondSlash + 1);
+ String headerValue = field.stringValue();
+ Header header = new BasicHeader(headerName, headerValue);
+ responseHeaders.add(index, header);
+ } catch (NumberFormatException e) {
+ throw new IOException(e);
+ }
+ } else if (BODY_FIELD_NAME.equals(fieldName)) {
+ body = new HeapResource(field.getBinaryValue());
+ } else if (fieldName.startsWith(VARIANT_FIELD_NAMES)) {
+ String variantKey = fieldName.substring(VARIANT_FIELD_NAMES_LENGTH + 1);
+ String cacheKey = field.stringValue();
+ variantMap.put(variantKey, cacheKey);
+ } else {
+ throw new IOException("Corrupt index (unknown field: " + fieldName + ")");
+ }
+ }
+
+ try {
+ ProtocolVersion protocolVersion = new ProtocolVersion(protocol, majorProtocolVersion, minorProtocolVersion);
+ StatusLine statusLine = new BasicStatusLine(protocolVersion, statusCode, reasonPhrase);
+ return new HttpCacheEntry(requestDate, responseDate, statusLine, responseHeaders.toArray(new Header[responseHeaders.size()]),
+ body, variantMap);
+ } catch (IllegalArgumentException e) {
+ throw new IOException("Corrupt index", e);
+ }
+ }
+
+ private Date parseDateField(Fieldable field) throws IOException {
+ try {
+ String stringValue = field.stringValue();
+ long longValue = Long.parseLong(stringValue);
+ return new Date(longValue);
+ } catch (NumberFormatException e) {
+ throw new IOException(field.name(), e);
+ }
+ }
+
+ private int parseIntField(Fieldable field) throws IOException {
+ try {
+ String stringValue = field.stringValue();
+ return Integer.parseInt(stringValue);
+ } catch (NumberFormatException e) {
+ throw new IOException(field.name(), e);
+ }
+ }
+}
diff --git a/bundles/org.eclipse.epp.logging.aeri.ide/src/org/eclipse/epp/internal/logging/aeri/ide/server/rest/RestBasedProblemsHistory.java b/bundles/org.eclipse.epp.logging.aeri.ide/src/org/eclipse/epp/internal/logging/aeri/ide/server/rest/RestBasedProblemsHistory.java
new file mode 100644
index 0000000..7c3cc07
--- /dev/null
+++ b/bundles/org.eclipse.epp.logging.aeri.ide/src/org/eclipse/epp/internal/logging/aeri/ide/server/rest/RestBasedProblemsHistory.java
@@ -0,0 +1,239 @@
+/**
+ * Copyright (c) 2016 Codetrails GmbH.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.eclipse.epp.internal.logging.aeri.ide.server.rest;
+
+import static org.apache.http.HttpStatus.SC_NOT_FOUND;
+import static org.eclipse.epp.internal.logging.aeri.ide.l10n.LogMessages.WARN_REST_QUERY_FAILED;
+import static org.eclipse.epp.internal.logging.aeri.ide.server.Proxies.*;
+import static org.eclipse.epp.logging.aeri.core.util.Logs.log;
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Date;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.http.Header;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpHeaders;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.StatusLine;
+import org.apache.http.client.ClientProtocolException;
+import org.apache.http.client.HttpResponseException;
+import org.apache.http.client.ResponseHandler;
+import org.apache.http.client.cache.HttpCacheStorage;
+import org.apache.http.client.fluent.Executor;
+import org.apache.http.client.fluent.Request;
+import org.apache.http.client.utils.DateUtils;
+import org.apache.http.client.utils.URIBuilder;
+import org.apache.http.entity.ContentType;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.cache.CachingHttpClientBuilder;
+import org.apache.http.impl.client.cache.CachingHttpClients;
+import org.apache.lucene.store.Directory;
+import org.apache.lucene.store.FSDirectory;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.epp.internal.logging.aeri.ide.l10n.LogMessages;
+import org.eclipse.epp.internal.logging.aeri.ide.server.json.Json;
+import org.eclipse.epp.internal.logging.aeri.ide.server.mars.IO;
+import org.eclipse.epp.internal.logging.aeri.ide.server.mars.IProblemsHistory;
+import org.eclipse.epp.internal.logging.aeri.ide.server.mars.ServerConfiguration;
+import org.eclipse.epp.logging.aeri.core.ILink;
+import org.eclipse.epp.logging.aeri.core.IModelFactory;
+import org.eclipse.epp.logging.aeri.core.IProblemState;
+import org.eclipse.epp.logging.aeri.core.ISystemSettings;
+import org.eclipse.epp.logging.aeri.core.ProblemStatus;
+import org.eclipse.epp.logging.aeri.core.util.Statuses;
+
+import com.google.common.base.Optional;
+import com.google.gson.reflect.TypeToken;
+
+public class RestBasedProblemsHistory implements IProblemsHistory {
+
+ private static final ContentType STATUS_REPONSES_CONTENT_TYPE = ContentType.create("application/x.aer.status-reponses+json");
+ private static final String STACK_TRACE_FINGERPRINT__QUERY = "stackTraceFingerprint";
+
+ private final ServerConfiguration config;
+ private final URI baseURI;
+ private final LuceneHttpCacheStorage storage;
+ private final CloseableHttpClient client;
+ private final Executor executor;
+
+ private Date embargoDate = new Date();
+
+ public RestBasedProblemsHistory(ServerConfiguration config, File cacheDir) throws IOException {
+ this.config = config;
+ try {
+ this.baseURI = new URI(config.getInterestUrl());
+ } catch (URISyntaxException e) {
+ throw new IllegalArgumentException(e);
+ }
+
+ Directory directory = FSDirectory.open(cacheDir);
+ storage = new LuceneHttpCacheStorage(directory);
+ client = createClient(storage);
+ executor = Executor.newInstance(client);
+ }
+
+ @Override
+ public Optional<IProblemState> seen(IStatus status) {
+ Date now = new Date();
+ if (now.before(embargoDate)) {
+ log(LogMessages.INFO_TEMPORARILY_DISABLED_REST_QUERIES, config.getTitle());
+ return Optional.absent();
+ }
+
+ String fingerprint = Statuses.traceIdentityHash(status);
+ URI restURI;
+ try {
+ restURI = new URIBuilder(baseURI).addParameter(STACK_TRACE_FINGERPRINT__QUERY, fingerprint).build();
+ } catch (URISyntaxException e1) {
+ return Optional.absent();
+ }
+
+ try {
+ Request request = Request.Get(restURI).addHeader(HttpHeaders.ACCEPT, STATUS_REPONSES_CONTENT_TYPE.toString())
+ .viaProxy(getProxyHost(restURI).orNull()).connectTimeout(config.getConnectTimeoutMs()).staleConnectionCheck(true)
+ .socketTimeout(config.getSocketTimeoutMs());
+ StatusReponse statusReponse = proxyAuthentication(executor, restURI).execute(request).handleResponse(new RestReponseHandler());
+
+ if (statusReponse != null) {
+ return Optional.of(toProblemState(statusReponse));
+ } else {
+ return Optional.absent();
+ }
+ } catch (IOException e) {
+ log(WARN_REST_QUERY_FAILED, e, config.getTitle(), restURI);
+ return Optional.absent();
+ }
+ }
+
+ @Override
+ public void sync(IO io, ISystemSettings systemSettings) {
+ // No-op
+ }
+
+ @Override
+ public void close() throws IOException {
+ client.close();
+ storage.close();
+ }
+
+ /**
+ * For legacy reasons, {@link IProblemState} is not a perfect match for {@link StatusReponse}, but this hopefully will change in the
+ * future.
+ */
+ private IProblemState toProblemState(StatusReponse serverResponse) {
+ IProblemState mProblemState = IModelFactory.eINSTANCE.createProblemState();
+ mProblemState.setStatus(toProblemStatus(serverResponse.getSituation()));
+ mProblemState.setMessage(serverResponse.getMessage());
+ for (String auxiliaryInformationRequest : serverResponse.getAuxiliaryInformationRequests()) {
+ mProblemState.getNeedinfo().add(auxiliaryInformationRequest);
+ }
+ for (Link link : serverResponse.getLinks()) {
+ ILink mLink = IModelFactory.eINSTANCE.createLink();
+ mLink.setHref(link.getHref().toString());
+ mLink.setRel(link.getRel());
+ mLink.setTitle(link.getTitle());
+ mProblemState.getLinks().put(link.getRel(), mLink);
+ }
+ return mProblemState;
+ }
+
+ private ProblemStatus toProblemStatus(ProblemSituation situation) {
+ switch (situation) {
+ case FAILURE:
+ return ProblemStatus.FAILURE;
+ case FIXED:
+ return ProblemStatus.FIXED;
+ case IGNORE:
+ return ProblemStatus.IGNORED;
+ case OPEN:
+ return ProblemStatus.CONFIRMED;
+ case WONTFIX:
+ return ProblemStatus.WONTFIX;
+ default:
+ throw new IllegalArgumentException(situation.toString());
+ }
+ }
+
+ private final class RestReponseHandler implements ResponseHandler<StatusReponse> {
+ @Override
+ public StatusReponse handleResponse(HttpResponse response) throws IOException {
+ StatusLine statusLine = response.getStatusLine();
+ HttpEntity entity = response.getEntity();
+ if (statusLine.getStatusCode() == SC_NOT_FOUND) {
+ return null;
+ } else if (statusLine.getStatusCode() >= HttpStatus.SC_INTERNAL_SERVER_ERROR) {
+ Header retryAfterHeader = response.getFirstHeader(HttpHeaders.RETRY_AFTER);
+ if (retryAfterHeader != null) {
+ embargoDate = parseRetryAfter(retryAfterHeader.getValue());
+ }
+ throw new HttpResponseException(statusLine.getStatusCode(), statusLine.getReasonPhrase());
+ } else if (statusLine.getStatusCode() >= HttpStatus.SC_MULTIPLE_CHOICES) {
+ throw new HttpResponseException(statusLine.getStatusCode(), statusLine.getReasonPhrase());
+ }
+
+ if (entity == null) {
+ throw new ClientProtocolException("Response contains no content");
+ }
+ ContentType contentType = ContentType.getOrDefault(entity);
+ // Ignore any parameters sent by the server, as application/json doesn't have any as per RFC 7159
+ if (!contentType.getMimeType().equals(STATUS_REPONSES_CONTENT_TYPE.getMimeType())) {
+ throw new ClientProtocolException("Unexpected content type: " + contentType);
+ }
+
+ try {
+ List<StatusReponse> statusResponses = Json.deserialize(entity.getContent(), new TypeToken<List<StatusReponse>>() {
+ }.getType());
+ if (statusResponses.isEmpty()) {
+ throw new IOException("Expected at least one status response");
+ }
+ return statusResponses.get(0);
+ } catch (Exception e) {
+ throw new ClientProtocolException("Cannot parse content", e);
+ }
+ }
+ }
+
+ /**
+ * Workaround for <a href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=508270">Bug 508270</a>
+ */
+ private CloseableHttpClient createClient(HttpCacheStorage storage) {
+ CachingHttpClientBuilder builder = CachingHttpClients.custom().setHttpCacheStorage(storage);
+ try {
+ Method buildMethod = CachingHttpClientBuilder.class.getMethod("build");
+ return (CloseableHttpClient) buildMethod.invoke(builder);
+ } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException
+ | InvocationTargetException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private Date parseRetryAfter(String retryAfter) {
+ if (retryAfter != null) {
+ try {
+ return new Date(System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(Long.parseLong(retryAfter)));
+ } catch (NumberFormatException ignore) {
+ Date date = DateUtils.parseDate(retryAfter);
+ return date != null ? date : tomorrow();
+ }
+ } else {
+ return tomorrow();
+ }
+ }
+
+ private Date tomorrow() {
+ return new Date(System.currentTimeMillis() + TimeUnit.DAYS.toMillis(1));
+ }
+}
diff --git a/bundles/org.eclipse.epp.logging.aeri.ide/src/org/eclipse/epp/internal/logging/aeri/ide/server/rest/StatusReponse.java b/bundles/org.eclipse.epp.logging.aeri.ide/src/org/eclipse/epp/internal/logging/aeri/ide/server/rest/StatusReponse.java
new file mode 100644
index 0000000..5108d72
--- /dev/null
+++ b/bundles/org.eclipse.epp.logging.aeri.ide/src/org/eclipse/epp/internal/logging/aeri/ide/server/rest/StatusReponse.java
@@ -0,0 +1,109 @@
+/**
+ * Copyright (c) 2016 Codetrails GmbH.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.eclipse.epp.internal.logging.aeri.ide.server.rest;
+
+import java.net.URI;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+import com.google.gson.annotations.SerializedName;
+
+final class StatusReponse {
+
+ @SerializedName("situation")
+ private ProblemSituation situation;
+
+ /**
+ * Inline-level HTML
+ */
+ @SerializedName("message")
+ private String message;
+
+ @SerializedName("links")
+ private List<Link> links;
+
+ @SerializedName("auxiliaryInformationRequests")
+ private Set<String> auxiliaryInformationRequests;
+
+ public ProblemSituation getSituation() {
+ return situation;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public List<Link> getLinks() {
+ return links;
+ }
+
+ public Set<String> getAuxiliaryInformationRequests() {
+ return auxiliaryInformationRequests;
+ }
+}
+
+final class Link {
+
+ @SerializedName("href")
+ private URI href;
+
+ @SerializedName("rel")
+ private String rel;
+
+ @SerializedName("title")
+ private String title;
+
+ public URI getHref() {
+ return href;
+ }
+
+ public String getRel() {
+ return rel;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+ if (other == null) {
+ return false;
+ }
+ if (getClass() != other.getClass()) {
+ return false;
+ }
+ Link that = (Link) other;
+ return Objects.equals(this.href, that.href) && Objects.equals(this.rel, that.rel) && Objects.equals(this.title, that.title);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(href, rel, title);
+ }
+}
+
+enum ProblemSituation {
+
+ OPEN("Open"), FIXED("Fixed"), WONTFIX("Won't Fix"), IGNORE("Ignored"), FAILURE("Failure");
+
+ private final String label;
+
+ ProblemSituation(String label) {
+ this.label = label;
+ }
+
+ @Override
+ public String toString() {
+ return label;
+ }
+}
diff --git a/tests/org.eclipse.epp.logging.aeri.ide.tests/src/org/eclipse/epp/internal/logging/aeri/ide/server/mars/ProblemsDatabaseProblemsHistoryTest.java b/tests/org.eclipse.epp.logging.aeri.ide.tests/src/org/eclipse/epp/internal/logging/aeri/ide/server/mars/ProblemsDatabaseProblemsHistoryTest.java
new file mode 100644
index 0000000..cd458f4
--- /dev/null
+++ b/tests/org.eclipse.epp.logging.aeri.ide.tests/src/org/eclipse/epp/internal/logging/aeri/ide/server/mars/ProblemsDatabaseProblemsHistoryTest.java
@@ -0,0 +1,221 @@
+package org.eclipse.epp.internal.logging.aeri.ide.server.mars;
+
+import static com.google.common.base.Optional.absent;
+import static org.eclipse.epp.logging.aeri.core.ResetSendMode.RESTART;
+import static org.eclipse.epp.logging.aeri.core.SendMode.NEVER;
+import static org.hamcrest.Matchers.*;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.*;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.apache.http.HttpStatus;
+import org.apache.lucene.analysis.KeywordAnalyzer;
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.Field;
+import org.apache.lucene.document.Field.Index;
+import org.apache.lucene.document.Field.Store;
+import org.apache.lucene.index.IndexWriter;
+import org.apache.lucene.index.IndexWriterConfig;
+import org.apache.lucene.index.IndexWriterConfig.OpenMode;
+import org.apache.lucene.store.Directory;
+import org.apache.lucene.store.RAMDirectory;
+import org.apache.lucene.util.Version;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.epp.internal.logging.aeri.ide.server.mars.ProblemsDatabaseProblemsHistory.UpdateIndexJob;
+import org.eclipse.epp.internal.logging.aeri.ide.utils.Zips;
+import org.eclipse.epp.logging.aeri.core.ISystemSettings;
+import org.eclipse.epp.logging.aeri.core.ProblemStatus;
+import org.eclipse.epp.logging.aeri.core.util.Statuses;
+import org.eclipse.epp.logging.aeri.tests.util.TestStatuses;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+public class ProblemsDatabaseProblemsHistoryTest {
+
+ private ProblemsDatabaseProblemsHistory sut;
+
+ private RAMDirectory directory;
+
+ @Before
+ public void setup() {
+ sut = new ProblemsDatabaseProblemsHistory(null) {
+
+ @Override
+ Directory createIndexDirectory() throws IOException {
+ directory = new RAMDirectory();
+ return directory;
+ }
+ };
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void testSeenWithHistoryNotRunning() {
+ sut.seen(TestStatuses.coreExceptionStatus());
+ }
+
+ @Test
+ public void testSeenOnEmptyProblemsDatabase() {
+ sut.startAsync().awaitRunning();
+
+ assertThat(sut.seen(TestStatuses.coreExceptionStatus()), is(absent()));
+ }
+
+ @Test
+ public void testIndexProblemStatus() throws Exception {
+ sut.startAsync().awaitRunning();
+
+ Status status = TestStatuses.thirdPartyStatus();
+ addToIndex(status, "NEEDINFO");
+
+ assertThat(sut.seen(status).get().getStatus(), is(ProblemStatus.NEEDINFO));
+ }
+
+ @Test
+ public void testIndexMatchStatus() throws Exception {
+ sut.startAsync().awaitRunning();
+
+ Status status = TestStatuses.multiStatus();
+ addToIndex(status, "FIXED");
+ // should not match
+ addToIndex(TestStatuses.coreExceptionStatus(), "NONE");
+
+ assertThat(sut.seen(status).get().getStatus(), is(ProblemStatus.FIXED));
+ }
+
+ public static class UpdateIndexJobTest {
+
+ @Rule
+ public TemporaryFolder temporaryFolder = new TemporaryFolder();
+
+ @Test
+ public void testUpdateLocalNotOutdated() {
+ IO io = mock(IO.class);
+ when(io.getConfiguration()).thenReturn(new ServerConfiguration());
+ ISystemSettings settings = mock(ISystemSettings.class);
+ ProblemsDatabaseProblemsHistory history = mock(ProblemsDatabaseProblemsHistory.class);
+
+ when(io.isProblemsDatabaseOutdated()).thenReturn(false);
+ UpdateIndexJob job = new UpdateIndexJob(io, settings, history);
+
+ job.run(mock(IProgressMonitor.class));
+ verify(io).getConfiguration();
+ verify(io).isProblemsDatabaseOutdated();
+ verifyNoMoreInteractions(io);
+ verifyNoMoreInteractions(settings);
+ verifyNoMoreInteractions(history);
+ }
+
+ @Test
+ public void testUpdateRemoteNotModified() throws IOException {
+ IO io = mock(IO.class);
+ when(io.getConfiguration()).thenReturn(new ServerConfiguration());
+ ISystemSettings settings = mock(ISystemSettings.class);
+ ProblemsDatabaseProblemsHistory history = mock(ProblemsDatabaseProblemsHistory.class);
+
+ when(io.isProblemsDatabaseOutdated()).thenReturn(true);
+ when(io.downloadDatabase(any(File.class), any(IProgressMonitor.class))).thenReturn(HttpStatus.SC_NOT_MODIFIED);
+ UpdateIndexJob job = new UpdateIndexJob(io, settings, history);
+
+ job.run(mock(IProgressMonitor.class));
+
+ verify(io).getConfiguration();
+ verify(io).isProblemsDatabaseOutdated();
+ verify(io).downloadDatabase(any(File.class), any(IProgressMonitor.class));
+ verifyNoMoreInteractions(io);
+ verifyNoMoreInteractions(settings);
+ verifyNoMoreInteractions(history);
+ }
+
+ @Test
+ public void testUpdateFromRemoteFailure() throws IOException {
+ IO io = mock(IO.class);
+ when(io.getConfiguration()).thenReturn(new ServerConfiguration());
+
+ ISystemSettings settings = mock(ISystemSettings.class);
+ ProblemsDatabaseProblemsHistory history = mock(ProblemsDatabaseProblemsHistory.class);
+
+ when(io.isProblemsDatabaseOutdated()).thenReturn(true);
+ when(io.downloadDatabase(any(File.class), any(IProgressMonitor.class))).thenReturn(HttpStatus.SC_INTERNAL_SERVER_ERROR);
+ UpdateIndexJob job = new UpdateIndexJob(io, settings, history);
+
+ job.run(mock(IProgressMonitor.class));
+
+ verify(io).getConfiguration();
+ verify(io).isProblemsDatabaseOutdated();
+ verify(io).downloadDatabase(any(File.class), any(IProgressMonitor.class));
+ verifyNoMoreInteractions(io);
+ verify(settings).setSendMode(NEVER);
+ verify(settings).setResetSendMode(RESTART);
+ verifyNoMoreInteractions(settings);
+ verifyNoMoreInteractions(history);
+ }
+
+ @Test
+ public void testUpdateFromRemoteSuccess() throws IOException {
+ IO io = mock(IO.class);
+ when(io.getConfiguration()).thenReturn(new ServerConfiguration());
+
+ ISystemSettings settings = mock(ISystemSettings.class);
+ ProblemsDatabaseProblemsHistory history = mock(ProblemsDatabaseProblemsHistory.class);
+
+ when(io.isProblemsDatabaseOutdated()).thenReturn(true);
+ // the job will try to unzip the index for the service
+ when(io.downloadDatabase(any(File.class), any(IProgressMonitor.class))).then(new Answer<Integer>() {
+
+ @Override
+ public Integer answer(InvocationOnMock invocation) throws Throwable {
+ File file = (File) invocation.getArguments()[0];
+ createMinimalZipFile(file);
+ return HttpStatus.SC_OK;
+ }
+
+ });
+
+ UpdateIndexJob job = new UpdateIndexJob(io, settings, history);
+
+ long downloadTimestampBefore = io.getConfiguration().getProblemsZipLastDownloadTimestamp();
+
+ job.run(mock(IProgressMonitor.class));
+
+ long downloadedTimestampNow = io.getConfiguration().getProblemsZipLastDownloadTimestamp();
+
+ assertThat(downloadedTimestampNow, is(not(downloadTimestampBefore)));
+
+ verify(io, atLeastOnce()).getConfiguration();
+ verify(io).isProblemsDatabaseOutdated();
+ verify(io).downloadDatabase(any(File.class), any(IProgressMonitor.class));
+ verify(io).saveConfiguration();
+ verifyNoMoreInteractions(io);
+ verifyNoMoreInteractions(settings);
+ verify(history).replaceContent(any(File.class));
+ }
+
+ private void createMinimalZipFile(File file) throws IOException {
+ File folder = temporaryFolder.newFolder();
+ File f = new File(folder, "empty");
+ f.createNewFile();
+ Zips.zip(folder, file);
+ }
+ }
+
+ private void addToIndex(Status status, String problemStatus) throws Exception {
+ IndexWriterConfig conf = new IndexWriterConfig(Version.LUCENE_35, new KeywordAnalyzer());
+ conf.setOpenMode(OpenMode.CREATE_OR_APPEND);
+ try (IndexWriter writer = new IndexWriter(directory, conf)) {
+ Document doc = new Document();
+ doc.add(new Field(ProblemsDatabaseProblemsHistory.F_FINGERPRINT, Statuses.traceIdentityHash(status), Store.NO, Index.NOT_ANALYZED));
+ doc.add(new Field(ProblemsDatabaseProblemsHistory.F_ACTION, problemStatus, Store.YES, Index.NO));
+ writer.addDocument(doc);
+ writer.commit();
+ sut.indexChanged();
+ }
+ }
+}
diff --git a/tests/org.eclipse.epp.logging.aeri.ide.tests/src/org/eclipse/epp/internal/logging/aeri/ide/server/mars/RemoteProblemsHistoryTest.java b/tests/org.eclipse.epp.logging.aeri.ide.tests/src/org/eclipse/epp/internal/logging/aeri/ide/server/mars/RemoteProblemsHistoryTest.java
deleted file mode 100644
index d362f59..0000000
--- a/tests/org.eclipse.epp.logging.aeri.ide.tests/src/org/eclipse/epp/internal/logging/aeri/ide/server/mars/RemoteProblemsHistoryTest.java
+++ /dev/null
@@ -1,227 +0,0 @@
-package org.eclipse.epp.internal.logging.aeri.ide.server.mars;
-
-import static org.eclipse.epp.logging.aeri.core.SendMode.NEVER;
-import static org.hamcrest.Matchers.*;
-import static org.junit.Assert.*;
-import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.*;
-
-import java.io.File;
-import java.io.IOException;
-
-import org.apache.http.HttpStatus;
-import org.apache.lucene.analysis.KeywordAnalyzer;
-import org.apache.lucene.document.Document;
-import org.apache.lucene.document.Field;
-import org.apache.lucene.document.Field.Index;
-import org.apache.lucene.document.Field.Store;
-import org.apache.lucene.index.CorruptIndexException;
-import org.apache.lucene.index.IndexWriter;
-import org.apache.lucene.index.IndexWriterConfig;
-import org.apache.lucene.index.IndexWriterConfig.OpenMode;
-import org.apache.lucene.store.Directory;
-import org.apache.lucene.store.LockObtainFailedException;
-import org.apache.lucene.store.RAMDirectory;
-import org.apache.lucene.util.Version;
-import org.eclipse.core.runtime.IProgressMonitor;
-import org.eclipse.core.runtime.Status;
-import org.eclipse.epp.internal.logging.aeri.ide.server.mars.ServerProblemsHistory.RemoteProblemsHistoryFilter;
-import org.eclipse.epp.internal.logging.aeri.ide.server.mars.ServerProblemsHistory.UpdateIndexJob;
-import org.eclipse.epp.internal.logging.aeri.ide.utils.Zips;
-import org.eclipse.epp.logging.aeri.core.ISystemSettings;
-import org.eclipse.epp.logging.aeri.core.ProblemStatus;
-import org.eclipse.epp.logging.aeri.core.ResetSendMode;
-import org.eclipse.epp.logging.aeri.core.util.Statuses;
-import org.eclipse.epp.logging.aeri.tests.util.TestStatus;
-import org.eclipse.epp.logging.aeri.tests.util.TestStatuses;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
-
-public class RemoteProblemsHistoryTest {
-
- private ServerProblemsHistory sut;
- private RemoteProblemsHistoryFilter filter;
- private TestStatus status;
-
- private RAMDirectory directory;
-
- @Rule
- public TemporaryFolder temporaryFolder = new TemporaryFolder();
-
- @Before
- public void setup() {
- sut = new ServerProblemsHistory(null) {
-
- @Override
- protected Directory createIndexDirectory() throws IOException {
- directory = new RAMDirectory();
- return directory;
- }
-
- };
- filter = new RemoteProblemsHistoryFilter(sut);
- status = new TestStatus();
- status.setException(new RuntimeException().fillInStackTrace());
- }
-
- @Test
- public void testNotRunning() {
- // not started should not fail anything
- assertTrue(filter.apply(status));
- }
-
- @Test
- public void testEmptyProblemsDatabase() {
- sut.startAsync().awaitRunning();
- assertTrue(filter.apply(status));
- }
-
- @Test
- public void testIndexProblemStatus() throws Exception {
- sut.startAsync().awaitRunning();
- Status status = TestStatuses.thirdPartyStatus();
-
- index(status, "NEEDINFO");
-
- assertThat(sut.seen(status).getStatus(), is(ProblemStatus.NEEDINFO));
- }
-
- @Test
- public void testIndexMatchStatus() throws Exception {
- sut.startAsync().awaitRunning();
- Status status = TestStatuses.multiStatus();
-
- index(status, "FIXED");
- // should not match
- index(TestStatuses.coreExceptionStatus(), "NONE");
-
- assertThat(sut.seen(status).getStatus(), is(ProblemStatus.FIXED));
- }
-
- @Test
- public void testUpdateLocalNotOutdated() {
- IO io = mock(IO.class);
- when(io.getConfiguration()).thenReturn(new ServerConfiguration());
- ISystemSettings settings = mock(ISystemSettings.class);
- ServerProblemsHistory history = mock(ServerProblemsHistory.class);
-
- when(io.isProblemsDatabaseOutdated()).thenReturn(false);
- UpdateIndexJob job = new UpdateIndexJob(io, settings, history);
-
- job.run(mock(IProgressMonitor.class));
- verify(io).getConfiguration();
- verify(io).isProblemsDatabaseOutdated();
- verifyNoMoreInteractions(io);
- verifyNoMoreInteractions(settings);
- verifyNoMoreInteractions(history);
- }
-
- @Test
- public void testUpdateRemoteNotModified() throws IOException {
- IO io = mock(IO.class);
- when(io.getConfiguration()).thenReturn(new ServerConfiguration());
- ISystemSettings settings = mock(ISystemSettings.class);
- ServerProblemsHistory history = mock(ServerProblemsHistory.class);
-
- when(io.isProblemsDatabaseOutdated()).thenReturn(true);
- when(io.downloadDatabase(any(File.class), any(IProgressMonitor.class))).thenReturn(HttpStatus.SC_NOT_MODIFIED);
- UpdateIndexJob job = new UpdateIndexJob(io, settings, history);
-
- job.run(mock(IProgressMonitor.class));
-
- verify(io).getConfiguration();
- verify(io).isProblemsDatabaseOutdated();
- verify(io).downloadDatabase(any(File.class), any(IProgressMonitor.class));
- verifyNoMoreInteractions(io);
- verifyNoMoreInteractions(settings);
- verifyNoMoreInteractions(history);
- }
-
- @Test
- public void testUpdateFromRemoteFailure() throws IOException {
- IO io = mock(IO.class);
- when(io.getConfiguration()).thenReturn(new ServerConfiguration());
-
- ISystemSettings settings = mock(ISystemSettings.class);
- ServerProblemsHistory history = mock(ServerProblemsHistory.class);
-
- when(io.isProblemsDatabaseOutdated()).thenReturn(true);
- when(io.downloadDatabase(any(File.class), any(IProgressMonitor.class))).thenReturn(HttpStatus.SC_INTERNAL_SERVER_ERROR);
- UpdateIndexJob job = new UpdateIndexJob(io, settings, history);
-
- job.run(mock(IProgressMonitor.class));
-
- verify(io).getConfiguration();
- verify(io).isProblemsDatabaseOutdated();
- verify(io).downloadDatabase(any(File.class), any(IProgressMonitor.class));
- verifyNoMoreInteractions(io);
- verify(settings).setSendMode(NEVER);
- verify(settings).setResetSendMode(ResetSendMode.RESTART);
- verifyNoMoreInteractions(settings);
- verifyNoMoreInteractions(history);
- }
-
- @Test
- public void testUpdateFromRemoteSuccess() throws IOException {
- IO io = mock(IO.class);
- when(io.getConfiguration()).thenReturn(new ServerConfiguration());
-
- ISystemSettings settings = mock(ISystemSettings.class);
- ServerProblemsHistory history = mock(ServerProblemsHistory.class);
-
- when(io.isProblemsDatabaseOutdated()).thenReturn(true);
- // the job will try to unzip the index for the service
- when(io.downloadDatabase(any(File.class), any(IProgressMonitor.class))).then(new Answer<Integer>() {
-
- @Override
- public Integer answer(InvocationOnMock invocation) throws Throwable {
- File file = (File) invocation.getArguments()[0];
- createMinimalZipFile(file);
- return HttpStatus.SC_OK;
- }
-
- });
-
- UpdateIndexJob job = new UpdateIndexJob(io, settings, history);
-
- long downloadTimestampBefore = io.getConfiguration().getProblemsZipLastDownloadTimestamp();
-
- job.run(mock(IProgressMonitor.class));
-
- long downloadedTimestampNow = io.getConfiguration().getProblemsZipLastDownloadTimestamp();
-
- assertThat(downloadedTimestampNow, is(not(downloadTimestampBefore)));
-
- verify(io, atLeastOnce()).getConfiguration();
- verify(io).isProblemsDatabaseOutdated();
- verify(io).downloadDatabase(any(File.class), any(IProgressMonitor.class));
- verify(io).saveConfiguration();
- verifyNoMoreInteractions(io);
- verifyNoMoreInteractions(settings);
- verify(history).replaceContent(any(File.class));
- }
-
- private void index(Status status, String problemStatus) throws CorruptIndexException, LockObtainFailedException, IOException {
- IndexWriterConfig conf = new IndexWriterConfig(Version.LUCENE_35, new KeywordAnalyzer());
- conf.setOpenMode(OpenMode.CREATE_OR_APPEND);
- try (IndexWriter writer = new IndexWriter(directory, conf)) {
- Document doc = new Document();
- doc.add(new Field(ServerProblemsHistory.F_FINGERPRINT, Statuses.traceIdentityHash(status), Store.NO, Index.NOT_ANALYZED));
- doc.add(new Field(ServerProblemsHistory.F_ACTION, problemStatus, Store.YES, Index.NO));
- writer.addDocument(doc);
- writer.commit();
- sut.indexChanged();
- }
- }
-
- private void createMinimalZipFile(File file) throws IOException {
- File folder = temporaryFolder.newFolder();
- File f = new File(folder, "empty");
- f.createNewFile();
- Zips.zip(folder, file);
- }
-}
diff --git a/tests/org.eclipse.epp.logging.aeri.ide.tests/src/org/eclipse/epp/internal/logging/aeri/ide/server/mars/ServerConnectionDatabaseDownloadTest.java b/tests/org.eclipse.epp.logging.aeri.ide.tests/src/org/eclipse/epp/internal/logging/aeri/ide/server/mars/ServerConnectionDatabaseDownloadTest.java
index a510fd6..5eae48b 100644
--- a/tests/org.eclipse.epp.logging.aeri.ide.tests/src/org/eclipse/epp/internal/logging/aeri/ide/server/mars/ServerConnectionDatabaseDownloadTest.java
+++ b/tests/org.eclipse.epp.logging.aeri.ide.tests/src/org/eclipse/epp/internal/logging/aeri/ide/server/mars/ServerConnectionDatabaseDownloadTest.java
@@ -1,8 +1,6 @@
package org.eclipse.epp.internal.logging.aeri.ide.server.mars;
-import static org.eclipse.epp.logging.aeri.core.SendMode.BACKGROUND;
-import static org.eclipse.epp.logging.aeri.core.SendMode.NEVER;
-import static org.eclipse.epp.logging.aeri.core.SendMode.NOTIFY;
+import static org.eclipse.epp.logging.aeri.core.SendMode.*;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.*;
@@ -25,10 +23,6 @@
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameter;
import org.junit.runners.Parameterized.Parameters;
-import org.mockito.Mockito;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
-import org.mockito.verification.VerificationMode;
@RunWith(Parameterized.class)
public class ServerConnectionDatabaseDownloadTest {
@@ -37,113 +31,63 @@
private static final boolean NOT_ENABLED = false;
private static final boolean CONFIGURED = true;
private static final boolean NOT_CONFIGURED = false;
- private static final boolean DB_OUTDATED = true;
- private static final boolean NOT_DB_OUTDATED = false;
private static final boolean ENABLED_AFTER_START = true;
private static final boolean NOT_ENABLED_AFTER_START = false;
private static final boolean CONFIGURED_AFTER_START = true;
private static final boolean NOT_CONFIGURED_AFTER_START = false;
- @Parameters(name = "{0},{1},{2},{3},{4},{5}")
+ @Parameters(name = "{0}, {1}, {2}, {3}, {4}, {5}")
public static Collection<Object[]> data() {
return Arrays.asList(new Object[][] {
//
- { BACKGROUND, ENABLED, CONFIGURED, DB_OUTDATED, ENABLED_AFTER_START, CONFIGURED_AFTER_START, times(1) },
- { BACKGROUND, ENABLED, CONFIGURED, DB_OUTDATED, ENABLED_AFTER_START, NOT_CONFIGURED_AFTER_START, times(1) },
- { BACKGROUND, ENABLED, CONFIGURED, DB_OUTDATED, NOT_ENABLED_AFTER_START, CONFIGURED_AFTER_START, times(1) },
- { BACKGROUND, ENABLED, CONFIGURED, DB_OUTDATED, NOT_ENABLED_AFTER_START, NOT_CONFIGURED_AFTER_START, times(1) },
- { BACKGROUND, ENABLED, CONFIGURED, NOT_DB_OUTDATED, ENABLED_AFTER_START, CONFIGURED_AFTER_START, never() },
- { BACKGROUND, ENABLED, CONFIGURED, NOT_DB_OUTDATED, ENABLED_AFTER_START, NOT_CONFIGURED_AFTER_START, never() },
- { BACKGROUND, ENABLED, CONFIGURED, NOT_DB_OUTDATED, NOT_ENABLED_AFTER_START, CONFIGURED_AFTER_START, never() },
- { BACKGROUND, ENABLED, CONFIGURED, NOT_DB_OUTDATED, NOT_ENABLED_AFTER_START, NOT_CONFIGURED_AFTER_START, never() },
- { BACKGROUND, ENABLED, NOT_CONFIGURED, DB_OUTDATED, ENABLED_AFTER_START, CONFIGURED_AFTER_START, times(1) },
- { BACKGROUND, ENABLED, NOT_CONFIGURED, DB_OUTDATED, ENABLED_AFTER_START, NOT_CONFIGURED_AFTER_START, never() },
- { BACKGROUND, ENABLED, NOT_CONFIGURED, DB_OUTDATED, NOT_ENABLED_AFTER_START, CONFIGURED_AFTER_START, never() },
- { BACKGROUND, ENABLED, NOT_CONFIGURED, DB_OUTDATED, NOT_ENABLED_AFTER_START, NOT_CONFIGURED_AFTER_START, never() },
- { BACKGROUND, ENABLED, NOT_CONFIGURED, NOT_DB_OUTDATED, ENABLED_AFTER_START, CONFIGURED_AFTER_START, never() },
- { BACKGROUND, ENABLED, NOT_CONFIGURED, NOT_DB_OUTDATED, ENABLED_AFTER_START, NOT_CONFIGURED_AFTER_START, never() },
- { BACKGROUND, ENABLED, NOT_CONFIGURED, NOT_DB_OUTDATED, NOT_ENABLED_AFTER_START, CONFIGURED_AFTER_START, never() },
- { BACKGROUND, ENABLED, NOT_CONFIGURED, NOT_DB_OUTDATED, NOT_ENABLED_AFTER_START, NOT_CONFIGURED_AFTER_START, never() },
- { BACKGROUND, NOT_ENABLED, CONFIGURED, DB_OUTDATED, ENABLED_AFTER_START, CONFIGURED_AFTER_START, times(1) },
- { BACKGROUND, NOT_ENABLED, CONFIGURED, DB_OUTDATED, ENABLED_AFTER_START, NOT_CONFIGURED_AFTER_START, times(1) },
- { BACKGROUND, NOT_ENABLED, CONFIGURED, DB_OUTDATED, NOT_ENABLED_AFTER_START, CONFIGURED_AFTER_START, never() },
- { BACKGROUND, NOT_ENABLED, CONFIGURED, DB_OUTDATED, NOT_ENABLED_AFTER_START, NOT_CONFIGURED_AFTER_START, never() },
- { BACKGROUND, NOT_ENABLED, CONFIGURED, NOT_DB_OUTDATED, ENABLED_AFTER_START, CONFIGURED_AFTER_START, never() },
- { BACKGROUND, NOT_ENABLED, CONFIGURED, NOT_DB_OUTDATED, ENABLED_AFTER_START, NOT_CONFIGURED_AFTER_START, never() },
- { BACKGROUND, NOT_ENABLED, CONFIGURED, NOT_DB_OUTDATED, NOT_ENABLED_AFTER_START, CONFIGURED_AFTER_START, never() },
- { BACKGROUND, NOT_ENABLED, CONFIGURED, NOT_DB_OUTDATED, NOT_ENABLED_AFTER_START, NOT_CONFIGURED_AFTER_START, never() },
- { BACKGROUND, NOT_ENABLED, NOT_CONFIGURED, DB_OUTDATED, ENABLED_AFTER_START, CONFIGURED_AFTER_START, times(1) },
- { BACKGROUND, NOT_ENABLED, NOT_CONFIGURED, DB_OUTDATED, ENABLED_AFTER_START, NOT_CONFIGURED_AFTER_START, never() },
- { BACKGROUND, NOT_ENABLED, NOT_CONFIGURED, DB_OUTDATED, NOT_ENABLED_AFTER_START, CONFIGURED_AFTER_START, never() },
- { BACKGROUND, NOT_ENABLED, NOT_CONFIGURED, DB_OUTDATED, NOT_ENABLED_AFTER_START, NOT_CONFIGURED_AFTER_START, never() },
- { BACKGROUND, NOT_ENABLED, NOT_CONFIGURED, NOT_DB_OUTDATED, ENABLED_AFTER_START, CONFIGURED_AFTER_START, never() },
- { BACKGROUND, NOT_ENABLED, NOT_CONFIGURED, NOT_DB_OUTDATED, ENABLED_AFTER_START, NOT_CONFIGURED_AFTER_START, never() },
- { BACKGROUND, NOT_ENABLED, NOT_CONFIGURED, NOT_DB_OUTDATED, NOT_ENABLED_AFTER_START, CONFIGURED_AFTER_START, never() },
- { BACKGROUND, NOT_ENABLED, NOT_CONFIGURED, NOT_DB_OUTDATED, NOT_ENABLED_AFTER_START, NOT_CONFIGURED_AFTER_START, never() },
- { NEVER, ENABLED, CONFIGURED, DB_OUTDATED, ENABLED_AFTER_START, CONFIGURED_AFTER_START, never() },
- { NEVER, ENABLED, CONFIGURED, DB_OUTDATED, ENABLED_AFTER_START, NOT_CONFIGURED_AFTER_START, never() },
- { NEVER, ENABLED, CONFIGURED, DB_OUTDATED, NOT_ENABLED_AFTER_START, CONFIGURED_AFTER_START, never() },
- { NEVER, ENABLED, CONFIGURED, DB_OUTDATED, NOT_ENABLED_AFTER_START, NOT_CONFIGURED_AFTER_START, never() },
- { NEVER, ENABLED, CONFIGURED, NOT_DB_OUTDATED, ENABLED_AFTER_START, CONFIGURED_AFTER_START, never() },
- { NEVER, ENABLED, CONFIGURED, NOT_DB_OUTDATED, ENABLED_AFTER_START, NOT_CONFIGURED_AFTER_START, never() },
- { NEVER, ENABLED, CONFIGURED, NOT_DB_OUTDATED, NOT_ENABLED_AFTER_START, CONFIGURED_AFTER_START, never() },
- { NEVER, ENABLED, CONFIGURED, NOT_DB_OUTDATED, NOT_ENABLED_AFTER_START, NOT_CONFIGURED_AFTER_START, never() },
- { NEVER, ENABLED, NOT_CONFIGURED, DB_OUTDATED, ENABLED_AFTER_START, CONFIGURED_AFTER_START, never() },
- { NEVER, ENABLED, NOT_CONFIGURED, DB_OUTDATED, ENABLED_AFTER_START, NOT_CONFIGURED_AFTER_START, never() },
- { NEVER, ENABLED, NOT_CONFIGURED, DB_OUTDATED, NOT_ENABLED_AFTER_START, CONFIGURED_AFTER_START, never() },
- { NEVER, ENABLED, NOT_CONFIGURED, DB_OUTDATED, NOT_ENABLED_AFTER_START, NOT_CONFIGURED_AFTER_START, never() },
- { NEVER, ENABLED, NOT_CONFIGURED, NOT_DB_OUTDATED, ENABLED_AFTER_START, CONFIGURED_AFTER_START, never() },
- { NEVER, ENABLED, NOT_CONFIGURED, NOT_DB_OUTDATED, ENABLED_AFTER_START, NOT_CONFIGURED_AFTER_START, never() },
- { NEVER, ENABLED, NOT_CONFIGURED, NOT_DB_OUTDATED, NOT_ENABLED_AFTER_START, CONFIGURED_AFTER_START, never() },
- { NEVER, ENABLED, NOT_CONFIGURED, NOT_DB_OUTDATED, NOT_ENABLED_AFTER_START, NOT_CONFIGURED_AFTER_START, never() },
- { NEVER, NOT_ENABLED, CONFIGURED, DB_OUTDATED, ENABLED_AFTER_START, CONFIGURED_AFTER_START, never() },
- { NEVER, NOT_ENABLED, CONFIGURED, DB_OUTDATED, ENABLED_AFTER_START, NOT_CONFIGURED_AFTER_START, never() },
- { NEVER, NOT_ENABLED, CONFIGURED, DB_OUTDATED, NOT_ENABLED_AFTER_START, CONFIGURED_AFTER_START, never() },
- { NEVER, NOT_ENABLED, CONFIGURED, DB_OUTDATED, NOT_ENABLED_AFTER_START, NOT_CONFIGURED_AFTER_START, never() },
- { NEVER, NOT_ENABLED, CONFIGURED, NOT_DB_OUTDATED, ENABLED_AFTER_START, CONFIGURED_AFTER_START, never() },
- { NEVER, NOT_ENABLED, CONFIGURED, NOT_DB_OUTDATED, ENABLED_AFTER_START, NOT_CONFIGURED_AFTER_START, never() },
- { NEVER, NOT_ENABLED, CONFIGURED, NOT_DB_OUTDATED, NOT_ENABLED_AFTER_START, CONFIGURED_AFTER_START, never() },
- { NEVER, NOT_ENABLED, CONFIGURED, NOT_DB_OUTDATED, NOT_ENABLED_AFTER_START, NOT_CONFIGURED_AFTER_START, never() },
- { NEVER, NOT_ENABLED, NOT_CONFIGURED, DB_OUTDATED, ENABLED_AFTER_START, CONFIGURED_AFTER_START, never() },
- { NEVER, NOT_ENABLED, NOT_CONFIGURED, DB_OUTDATED, ENABLED_AFTER_START, NOT_CONFIGURED_AFTER_START, never() },
- { NEVER, NOT_ENABLED, NOT_CONFIGURED, DB_OUTDATED, NOT_ENABLED_AFTER_START, CONFIGURED_AFTER_START, never() },
- { NEVER, NOT_ENABLED, NOT_CONFIGURED, DB_OUTDATED, NOT_ENABLED_AFTER_START, NOT_CONFIGURED_AFTER_START, never() },
- { NEVER, NOT_ENABLED, NOT_CONFIGURED, NOT_DB_OUTDATED, ENABLED_AFTER_START, CONFIGURED_AFTER_START, never() },
- { NEVER, NOT_ENABLED, NOT_CONFIGURED, NOT_DB_OUTDATED, ENABLED_AFTER_START, NOT_CONFIGURED_AFTER_START, never() },
- { NEVER, NOT_ENABLED, NOT_CONFIGURED, NOT_DB_OUTDATED, NOT_ENABLED_AFTER_START, CONFIGURED_AFTER_START, never() },
- { NEVER, NOT_ENABLED, NOT_CONFIGURED, NOT_DB_OUTDATED, NOT_ENABLED_AFTER_START, NOT_CONFIGURED_AFTER_START, never() },
- { NOTIFY, ENABLED, CONFIGURED, DB_OUTDATED, ENABLED_AFTER_START, CONFIGURED_AFTER_START, times(1) },
- { NOTIFY, ENABLED, CONFIGURED, DB_OUTDATED, ENABLED_AFTER_START, NOT_CONFIGURED_AFTER_START, times(1) },
- { NOTIFY, ENABLED, CONFIGURED, DB_OUTDATED, NOT_ENABLED_AFTER_START, CONFIGURED_AFTER_START, times(1) },
- { NOTIFY, ENABLED, CONFIGURED, DB_OUTDATED, NOT_ENABLED_AFTER_START, NOT_CONFIGURED_AFTER_START, times(1) },
- { NOTIFY, ENABLED, CONFIGURED, NOT_DB_OUTDATED, ENABLED_AFTER_START, CONFIGURED_AFTER_START, never() },
- { NOTIFY, ENABLED, CONFIGURED, NOT_DB_OUTDATED, ENABLED_AFTER_START, NOT_CONFIGURED_AFTER_START, never() },
- { NOTIFY, ENABLED, CONFIGURED, NOT_DB_OUTDATED, NOT_ENABLED_AFTER_START, CONFIGURED_AFTER_START, never() },
- { NOTIFY, ENABLED, CONFIGURED, NOT_DB_OUTDATED, NOT_ENABLED_AFTER_START, NOT_CONFIGURED_AFTER_START, never() },
- { NOTIFY, ENABLED, NOT_CONFIGURED, DB_OUTDATED, ENABLED_AFTER_START, CONFIGURED_AFTER_START, times(1) },
- { NOTIFY, ENABLED, NOT_CONFIGURED, DB_OUTDATED, ENABLED_AFTER_START, NOT_CONFIGURED_AFTER_START, never() },
- { NOTIFY, ENABLED, NOT_CONFIGURED, DB_OUTDATED, NOT_ENABLED_AFTER_START, CONFIGURED_AFTER_START, never() },
- { NOTIFY, ENABLED, NOT_CONFIGURED, DB_OUTDATED, NOT_ENABLED_AFTER_START, NOT_CONFIGURED_AFTER_START, never() },
- { NOTIFY, ENABLED, NOT_CONFIGURED, NOT_DB_OUTDATED, ENABLED_AFTER_START, CONFIGURED_AFTER_START, never() },
- { NOTIFY, ENABLED, NOT_CONFIGURED, NOT_DB_OUTDATED, ENABLED_AFTER_START, NOT_CONFIGURED_AFTER_START, never() },
- { NOTIFY, ENABLED, NOT_CONFIGURED, NOT_DB_OUTDATED, NOT_ENABLED_AFTER_START, CONFIGURED_AFTER_START, never() },
- { NOTIFY, ENABLED, NOT_CONFIGURED, NOT_DB_OUTDATED, NOT_ENABLED_AFTER_START, NOT_CONFIGURED_AFTER_START, never() },
- { NOTIFY, NOT_ENABLED, CONFIGURED, DB_OUTDATED, ENABLED_AFTER_START, CONFIGURED_AFTER_START, times(1) },
- { NOTIFY, NOT_ENABLED, CONFIGURED, DB_OUTDATED, ENABLED_AFTER_START, NOT_CONFIGURED_AFTER_START, times(1) },
- { NOTIFY, NOT_ENABLED, CONFIGURED, DB_OUTDATED, NOT_ENABLED_AFTER_START, CONFIGURED_AFTER_START, never() },
- { NOTIFY, NOT_ENABLED, CONFIGURED, DB_OUTDATED, NOT_ENABLED_AFTER_START, NOT_CONFIGURED_AFTER_START, never() },
- { NOTIFY, NOT_ENABLED, CONFIGURED, NOT_DB_OUTDATED, ENABLED_AFTER_START, CONFIGURED_AFTER_START, never() },
- { NOTIFY, NOT_ENABLED, CONFIGURED, NOT_DB_OUTDATED, ENABLED_AFTER_START, NOT_CONFIGURED_AFTER_START, never() },
- { NOTIFY, NOT_ENABLED, CONFIGURED, NOT_DB_OUTDATED, NOT_ENABLED_AFTER_START, CONFIGURED_AFTER_START, never() },
- { NOTIFY, NOT_ENABLED, CONFIGURED, NOT_DB_OUTDATED, NOT_ENABLED_AFTER_START, NOT_CONFIGURED_AFTER_START, never() },
- { NOTIFY, NOT_ENABLED, NOT_CONFIGURED, DB_OUTDATED, ENABLED_AFTER_START, CONFIGURED_AFTER_START, times(1) },
- { NOTIFY, NOT_ENABLED, NOT_CONFIGURED, DB_OUTDATED, ENABLED_AFTER_START, NOT_CONFIGURED_AFTER_START, never() },
- { NOTIFY, NOT_ENABLED, NOT_CONFIGURED, DB_OUTDATED, NOT_ENABLED_AFTER_START, CONFIGURED_AFTER_START, never() },
- { NOTIFY, NOT_ENABLED, NOT_CONFIGURED, DB_OUTDATED, NOT_ENABLED_AFTER_START, NOT_CONFIGURED_AFTER_START, never() },
- { NOTIFY, NOT_ENABLED, NOT_CONFIGURED, NOT_DB_OUTDATED, ENABLED_AFTER_START, CONFIGURED_AFTER_START, never() },
- { NOTIFY, NOT_ENABLED, NOT_CONFIGURED, NOT_DB_OUTDATED, ENABLED_AFTER_START, NOT_CONFIGURED_AFTER_START, never() },
- { NOTIFY, NOT_ENABLED, NOT_CONFIGURED, NOT_DB_OUTDATED, NOT_ENABLED_AFTER_START, CONFIGURED_AFTER_START, never() },
- { NOTIFY, NOT_ENABLED, NOT_CONFIGURED, NOT_DB_OUTDATED, NOT_ENABLED_AFTER_START, NOT_CONFIGURED_AFTER_START, never() },
+ { BACKGROUND, ENABLED, CONFIGURED, ENABLED_AFTER_START, CONFIGURED_AFTER_START, 3 },
+ { BACKGROUND, ENABLED, CONFIGURED, ENABLED_AFTER_START, NOT_CONFIGURED_AFTER_START, 2 },
+ { BACKGROUND, ENABLED, CONFIGURED, NOT_ENABLED_AFTER_START, CONFIGURED_AFTER_START, 1 },
+ { BACKGROUND, ENABLED, CONFIGURED, NOT_ENABLED_AFTER_START, NOT_CONFIGURED_AFTER_START, 1 },
+ { BACKGROUND, ENABLED, NOT_CONFIGURED, ENABLED_AFTER_START, CONFIGURED_AFTER_START, 1 },
+ { BACKGROUND, ENABLED, NOT_CONFIGURED, ENABLED_AFTER_START, NOT_CONFIGURED_AFTER_START, 0 },
+ { BACKGROUND, ENABLED, NOT_CONFIGURED, NOT_ENABLED_AFTER_START, CONFIGURED_AFTER_START, 0 },
+ { BACKGROUND, ENABLED, NOT_CONFIGURED, NOT_ENABLED_AFTER_START, NOT_CONFIGURED_AFTER_START, 0 },
+ { BACKGROUND, NOT_ENABLED, CONFIGURED, ENABLED_AFTER_START, CONFIGURED_AFTER_START, 2 },
+ { BACKGROUND, NOT_ENABLED, CONFIGURED, ENABLED_AFTER_START, NOT_CONFIGURED_AFTER_START, 1 },
+ { BACKGROUND, NOT_ENABLED, CONFIGURED, NOT_ENABLED_AFTER_START, CONFIGURED_AFTER_START, 0 },
+ { BACKGROUND, NOT_ENABLED, CONFIGURED, NOT_ENABLED_AFTER_START, NOT_CONFIGURED_AFTER_START, 0 },
+ { BACKGROUND, NOT_ENABLED, NOT_CONFIGURED, ENABLED_AFTER_START, CONFIGURED_AFTER_START, 1 },
+ { BACKGROUND, NOT_ENABLED, NOT_CONFIGURED, ENABLED_AFTER_START, NOT_CONFIGURED_AFTER_START, 0 },
+ { BACKGROUND, NOT_ENABLED, NOT_CONFIGURED, NOT_ENABLED_AFTER_START, CONFIGURED_AFTER_START, 0 },
+ { BACKGROUND, NOT_ENABLED, NOT_CONFIGURED, NOT_ENABLED_AFTER_START, NOT_CONFIGURED_AFTER_START, 0 },
+ { NEVER, ENABLED, CONFIGURED, ENABLED_AFTER_START, CONFIGURED_AFTER_START, 0 },
+ { NEVER, ENABLED, CONFIGURED, ENABLED_AFTER_START, NOT_CONFIGURED_AFTER_START, 0 },
+ { NEVER, ENABLED, CONFIGURED, NOT_ENABLED_AFTER_START, CONFIGURED_AFTER_START, 0 },
+ { NEVER, ENABLED, CONFIGURED, NOT_ENABLED_AFTER_START, NOT_CONFIGURED_AFTER_START, 0 },
+ { NEVER, ENABLED, NOT_CONFIGURED, ENABLED_AFTER_START, CONFIGURED_AFTER_START, 0 },
+ { NEVER, ENABLED, NOT_CONFIGURED, ENABLED_AFTER_START, NOT_CONFIGURED_AFTER_START, 0 },
+ { NEVER, ENABLED, NOT_CONFIGURED, NOT_ENABLED_AFTER_START, CONFIGURED_AFTER_START, 0 },
+ { NEVER, ENABLED, NOT_CONFIGURED, NOT_ENABLED_AFTER_START, NOT_CONFIGURED_AFTER_START, 0 },
+ { NEVER, NOT_ENABLED, CONFIGURED, ENABLED_AFTER_START, CONFIGURED_AFTER_START, 0 },
+ { NEVER, NOT_ENABLED, CONFIGURED, ENABLED_AFTER_START, NOT_CONFIGURED_AFTER_START, 0 },
+ { NEVER, NOT_ENABLED, CONFIGURED, NOT_ENABLED_AFTER_START, CONFIGURED_AFTER_START, 0 },
+ { NEVER, NOT_ENABLED, CONFIGURED, NOT_ENABLED_AFTER_START, NOT_CONFIGURED_AFTER_START, 0 },
+ { NEVER, NOT_ENABLED, NOT_CONFIGURED, ENABLED_AFTER_START, CONFIGURED_AFTER_START, 0 },
+ { NEVER, NOT_ENABLED, NOT_CONFIGURED, ENABLED_AFTER_START, NOT_CONFIGURED_AFTER_START, 0 },
+ { NEVER, NOT_ENABLED, NOT_CONFIGURED, NOT_ENABLED_AFTER_START, CONFIGURED_AFTER_START, 0 },
+ { NEVER, NOT_ENABLED, NOT_CONFIGURED, NOT_ENABLED_AFTER_START, NOT_CONFIGURED_AFTER_START, 0 },
+ { NOTIFY, ENABLED, CONFIGURED, ENABLED_AFTER_START, CONFIGURED_AFTER_START, 3 },
+ { NOTIFY, ENABLED, CONFIGURED, ENABLED_AFTER_START, NOT_CONFIGURED_AFTER_START, 2 },
+ { NOTIFY, ENABLED, CONFIGURED, NOT_ENABLED_AFTER_START, CONFIGURED_AFTER_START, 1 },
+ { NOTIFY, ENABLED, CONFIGURED, NOT_ENABLED_AFTER_START, NOT_CONFIGURED_AFTER_START, 1 },
+ { NOTIFY, ENABLED, NOT_CONFIGURED, ENABLED_AFTER_START, CONFIGURED_AFTER_START, 1 },
+ { NOTIFY, ENABLED, NOT_CONFIGURED, ENABLED_AFTER_START, NOT_CONFIGURED_AFTER_START, 0 },
+ { NOTIFY, ENABLED, NOT_CONFIGURED, NOT_ENABLED_AFTER_START, CONFIGURED_AFTER_START, 0 },
+ { NOTIFY, ENABLED, NOT_CONFIGURED, NOT_ENABLED_AFTER_START, NOT_CONFIGURED_AFTER_START, 0 },
+ { NOTIFY, NOT_ENABLED, CONFIGURED, ENABLED_AFTER_START, CONFIGURED_AFTER_START, 2 },
+ { NOTIFY, NOT_ENABLED, CONFIGURED, ENABLED_AFTER_START, NOT_CONFIGURED_AFTER_START, 1 },
+ { NOTIFY, NOT_ENABLED, CONFIGURED, NOT_ENABLED_AFTER_START, CONFIGURED_AFTER_START, 0 },
+ { NOTIFY, NOT_ENABLED, CONFIGURED, NOT_ENABLED_AFTER_START, NOT_CONFIGURED_AFTER_START, 0 },
+ { NOTIFY, NOT_ENABLED, NOT_CONFIGURED, ENABLED_AFTER_START, CONFIGURED_AFTER_START, 1 },
+ { NOTIFY, NOT_ENABLED, NOT_CONFIGURED, ENABLED_AFTER_START, NOT_CONFIGURED_AFTER_START, 0 },
+ { NOTIFY, NOT_ENABLED, NOT_CONFIGURED, NOT_ENABLED_AFTER_START, CONFIGURED_AFTER_START, 0 },
+ { NOTIFY, NOT_ENABLED, NOT_CONFIGURED, NOT_ENABLED_AFTER_START, NOT_CONFIGURED_AFTER_START, 0 },
//
});
}
@@ -158,16 +102,13 @@
public boolean isInitialConfigured;
@Parameter(3)
- public boolean isDatabaseOutdated;
-
- @Parameter(4)
public boolean isEnabledAfterStartup;
- @Parameter(5)
+ @Parameter(4)
public boolean isConfiguredAfterStartup;
- @Parameter(6)
- public VerificationMode timesUpdateCalled;
+ @Parameter(5)
+ public int expectedSyncCalls;
private IModelFactory mFac = IModelFactory.eINSTANCE;
private IIdeFactory iFac = IIdeFactory.eINSTANCE;
@@ -179,34 +120,44 @@
private IServerDescriptor descriptor;
private ISystemSettings settings;
+ private IProblemsHistory serverProblemsHistory;
- private boolean isMockDatabaseDownloaded;
+ private boolean syncCalled = false;
@Before
public void setup() throws IOException {
- descriptor = iFac.createServerDescriptor();
+ descriptor = createServerDescriptor();
+
+ settings = createSystemSettings();
+
+ File configurationArea = temporaryFolder.newFolder();
+
+ sut = spy(new ServerConnection(descriptor, settings, configurationArea));
+
+ ServerConfiguration serverConfiguration = new ServerConfiguration();
+ serverConfiguration.setProblemsUrl("http://example.org/problems.zip");
+
+ io = mock(IO.class);
+ when(io.getConfiguration()).thenReturn(serverConfiguration);
+
+ when(sut.createIO(any())).thenReturn(io);
+
+ serverProblemsHistory = mock(IProblemsHistory.class);
+
+ when(sut.createServerProblemsHistory()).thenReturn(serverProblemsHistory);
+ }
+
+ private IServerDescriptor createServerDescriptor() {
+ IServerDescriptor descriptor = iFac.createServerDescriptor();
descriptor.setEnabled(false);
descriptor.setConfigured(false);
- io = Mockito.mock(IO.class);
- when(io.isProblemsDatabaseOutdated()).thenAnswer(new Answer<Boolean>() {
- @Override
- public Boolean answer(InvocationOnMock invocation) throws Throwable {
- return isDatabaseOutdated && !isMockDatabaseDownloaded;
- }
- });
- File configurationArea = temporaryFolder.newFolder();
- settings = mFac.createSystemSettings();
+ return descriptor;
+ }
+
+ private ISystemSettings createSystemSettings() {
+ ISystemSettings settings = mFac.createSystemSettings();
settings.setConfigured(true);
- ServerConnection serverConnection = new ServerConnection(descriptor, settings, configurationArea);
- sut = Mockito.spy(serverConnection);
- isMockDatabaseDownloaded = false;
- Mockito.doAnswer(new Answer<Void>() {
- public Void answer(InvocationOnMock invocation) {
- isMockDatabaseDownloaded = true;
- return null;
- }
- }).when(sut).updateProblemDatabase(any(), any(), any());
- when(sut.createIO(any())).thenReturn(io);
+ return settings;
}
@After
@@ -217,15 +168,17 @@
@Test
public void test() throws Exception {
settings.setSendMode(sendMode);
+
descriptor.setEnabled(isInitialEnabled);
+
descriptor.setConfigured(isInitialConfigured);
sut.startUp();
descriptor.setEnabled(isEnabledAfterStartup);
+
descriptor.setConfigured(isConfiguredAfterStartup);
- verify(sut, timesUpdateCalled).updateProblemDatabase(any(), any(), any());
+ verify(serverProblemsHistory, times(expectedSyncCalls)).sync(any(), any());
}
-
}
diff --git a/tests/org.eclipse.epp.logging.aeri.ide.tests/src/org/eclipse/epp/internal/logging/aeri/ide/server/mars/ServerConnectionTest.java b/tests/org.eclipse.epp.logging.aeri.ide.tests/src/org/eclipse/epp/internal/logging/aeri/ide/server/mars/ServerConnectionTest.java
index 361b738..c7bcdf5 100644
--- a/tests/org.eclipse.epp.logging.aeri.ide.tests/src/org/eclipse/epp/internal/logging/aeri/ide/server/mars/ServerConnectionTest.java
+++ b/tests/org.eclipse.epp.logging.aeri.ide.tests/src/org/eclipse/epp/internal/logging/aeri/ide/server/mars/ServerConnectionTest.java
@@ -43,7 +43,6 @@
assertFalse(sut.msgNeedinfoBeforeSend(state).contains("{"));
assertFalse(sut.msgNewAfterSend(state).contains("{"));
assertFalse(sut.msgUnconfirmedAfterSend(state).contains("{"));
- assertFalse(sut.msgUnconfirmedBeforeSend(state).contains("{"));
+ assertFalse(sut.msgUnconfirmedBeforeSend().contains("{"));
}
-
}