Skip to content

Commit

Permalink
Cache unavailable URI's
Browse files Browse the repository at this point in the history
Fixes eclipse-lemminx#201

Signed-off-by: Nikolas Komonen <[email protected]>
  • Loading branch information
NikolasKomonen committed Jan 25, 2019
1 parent 02d9713 commit 55078ec
Show file tree
Hide file tree
Showing 9 changed files with 319 additions and 16 deletions.
10 changes: 4 additions & 6 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,12 @@
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"configurations": [{
"type": "java",
"name": "Debug (Attach)",
"name": "Debug (Attach) - Remote",
"request": "attach",
"hostName": "localhost",
"port": 1054,
"projectName": "org.eclipse.lsp4xml"
}
]
}
}]
}
17 changes: 17 additions & 0 deletions org.eclipse.lsp4xml/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -69,13 +69,24 @@
</plugins>
</build>
<dependencies>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>27.0.1-jre</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.lsp4j</groupId>
<artifactId>org.eclipse.lsp4j</artifactId>
<exclusions>
<exclusion>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.eclipse.lsp4j</groupId>
Expand All @@ -102,5 +113,11 @@
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
<version>9.4.14.v20181114</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,14 @@
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;


import org.eclipse.lsp4xml.utils.FilesUtils;
import org.eclipse.lsp4xml.utils.URIUtils;

Expand All @@ -38,6 +43,7 @@
*
*/
public class CacheResourcesManager {
protected final Cache<String, Boolean> unavailableURICache;

private static final String CACHE_PATH = "cache";
private static final Logger LOGGER = Logger.getLogger(CacheResourcesManager.class.getName());
Expand Down Expand Up @@ -79,14 +85,25 @@ public String getResourceFromClasspath() {
}

public CacheResourcesManager() {
this(CacheBuilder.newBuilder().maximumSize(100).expireAfterWrite(30, TimeUnit.SECONDS).build());
}

public CacheResourcesManager(Cache<String, Boolean> cache) {
resourcesLoading = new HashMap<>();
unavailableURICache = cache;
}

public Path getResource(final String resourceURI) throws IOException {
Path resourceCachePath = getResourceCachePath(resourceURI);
if (Files.exists(resourceCachePath)) {
return resourceCachePath;
}

if(unavailableURICache.getIfPresent(resourceURI) != null) {
LOGGER.info("Ignored unavailable schema URI: " + resourceURI + "\n");
return null;
}

CompletableFuture<Path> f = null;
synchronized (resourcesLoading) {
if (resourcesLoading.containsKey(resourceURI)) {
Expand Down Expand Up @@ -139,11 +156,12 @@ private CompletableFuture<Path> downloadResource(final String resourceURI, Path
LOGGER.info("Downloaded " + resourceURI + " to " + resourceCachePath + " in " + elapsed + "ms");
} catch (Exception e) {
// Do nothing
unavailableURICache.put(resourceURI, true);
Throwable rootCause = getRootCause(e);
String error = "[" + rootCause.getClass().getTypeName() + "] " + rootCause.getMessage();
LOGGER.log(Level.SEVERE,
"Error while downloading " + resourceURI + " to " + resourceCachePath + " : " + error);
throw new CacheResourceDownloadedException("Error while downloading '" + resourceURI + "'.", e);
throw new CacheResourceDownloadedException("Error while downloading '" + resourceURI + "' to " + resourceCachePath + ".", e);
} finally {
synchronized (resourcesLoading) {
resourcesLoading.remove(resourceURI);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,37 @@
import java.nio.file.Paths;
import java.util.Scanner;

import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;

/**
* Files utilities.
*
*/
public class FilesUtils {

private FilesUtils(){}
public static final String LSP4XML_WORKDIR_KEY = "lsp4xml.workdir";

private FilesUtils() {
}

private static final Path DEPLOYED_BASE_PATH = getDeployedBasePath();
public static Supplier<Path> DEPLOYED_BASE_PATH;

static {
resetDeployPath();
}

/** Public for test purposes */
public static void resetDeployPath() {
DEPLOYED_BASE_PATH = Suppliers.memoize(() -> getDeployedBasePath());
}

private static Path getDeployedBasePath() {
String dir = System.getProperty("user.home");
String dir = System.getProperty(LSP4XML_WORKDIR_KEY);
if (dir != null) {
return Paths.get(dir);
}
dir = System.getProperty("user.home");
if (dir == null) {
dir = System.getProperty("user.dir");
}
Expand All @@ -48,7 +67,7 @@ private static Path getDeployedBasePath() {
* @throws IOException
*/
public static Path getDeployedPath(Path path) throws IOException {
return DEPLOYED_BASE_PATH.resolve(path);
return DEPLOYED_BASE_PATH.get().resolve(path);
}

/**
Expand Down Expand Up @@ -86,5 +105,4 @@ static String toString(InputStream is) {
return s.hasNext() ? s.next() : "";
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*******************************************************************************
* Copyright (c) 2019 Red Hat Inc. and others.
* All rights reserved. This program and the accompanying materials
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v20.html
*
* Contributors:
* Red Hat Inc. - initial API and implementation
*******************************************************************************/
package org.eclipse.lsp4xml;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;

import com.google.common.io.MoreFiles;
import com.google.common.io.RecursiveDeleteOption;

import org.eclipse.lsp4xml.utils.FilesUtils;
import org.eclipse.lsp4xml.utils.ProjectUtils;
import org.junit.After;
import org.junit.Before;

/**
* AbstractCacheBasedTest
*/
public abstract class AbstractCacheBasedTest {

protected static Path TEST_WORK_DIRECTORY = ProjectUtils.getProjectDirectory().resolve("target/test-cache");

@Before
public final void setupCache() throws Exception {
clearCache();
System.setProperty(FilesUtils.LSP4XML_WORKDIR_KEY, TEST_WORK_DIRECTORY.toAbsolutePath().toString());
}

@After
public final void clearCache() throws IOException {
if (Files.exists(TEST_WORK_DIRECTORY)) {
MoreFiles.deleteDirectoryContents(TEST_WORK_DIRECTORY,RecursiveDeleteOption.ALLOW_INSECURE);
}
System.clearProperty(FilesUtils.LSP4XML_WORKDIR_KEY);
FilesUtils.resetDeployPath();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import org.eclipse.lsp4j.Diagnostic;
import org.eclipse.lsp4j.DiagnosticSeverity;
import org.eclipse.lsp4xml.AbstractCacheBasedTest;
import org.eclipse.lsp4xml.XMLAssert;
import org.eclipse.lsp4xml.extensions.contentmodel.model.ContentModelManager;
import org.eclipse.lsp4xml.extensions.contentmodel.participants.XMLSchemaErrorCode;
Expand All @@ -27,7 +28,7 @@
* Test with published diagnostics.
*
*/
public class XMLSchemaPublishDiagnosticsTest {
public class XMLSchemaPublishDiagnosticsTest extends AbstractCacheBasedTest {

@Test
public void schemaWithUrlWithoutCache() throws Exception {
Expand Down Expand Up @@ -77,11 +78,13 @@ public void schemaWithUrlWithCache() throws Exception {
" xsi:noNamespaceSchemaLocation=\"http://invoice.xsd\">\r\n" + //
"</invoice> \r\n" + //
"";

String expectedLocation = TEST_WORK_DIRECTORY.resolve("cache/http/invoice.xsd").toString();
XMLAssert.testPublishDiagnosticsFor(xml, fileURI, configuration,
pd(fileURI,
new Diagnostic(r(1, 1, 1, 8), "The resource 'http://invoice.xsd' is downloading.",
DiagnosticSeverity.Information, "XML")),
pd(fileURI, new Diagnostic(r(1, 1, 1, 8), "Error while downloading 'http://invoice.xsd'.",
pd(fileURI, new Diagnostic(r(1, 1, 1, 8), "Error while downloading 'http://invoice.xsd' to "+expectedLocation+".",
DiagnosticSeverity.Error, "XML")));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,39 @@

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.fail;

import java.util.concurrent.TimeUnit;

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;

import org.eclipse.lsp4xml.AbstractCacheBasedTest;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

public class CacheResourcesManagerTest {
public class CacheResourcesManagerTest extends AbstractCacheBasedTest {

private CacheResourcesManager cacheResourcesManager;

private FileServer server;


@Before
public void setup() throws Exception {
cacheResourcesManager = new CacheResourcesManager(testingCache());
cacheResourcesManager.setUseCache(true);
}

@After
public void stopServer() throws Exception {
if (server != null) {
server.stop();
}
}

@Test
public void testCanUseCache() {
Expand All @@ -24,12 +53,60 @@ public void testCanUseCache() {
}

private void testCanUseCache(boolean useCacheEnabled) {
CacheResourcesManager cacheResourcesManager = new CacheResourcesManager();
cacheResourcesManager.setUseCache(useCacheEnabled);
assertEquals(useCacheEnabled, cacheResourcesManager.canUseCache("http://foo"));
assertEquals(useCacheEnabled, cacheResourcesManager.canUseCache("ftp://foo"));
assertEquals(useCacheEnabled, cacheResourcesManager.canUseCache("https://foo"));
assertFalse(cacheResourcesManager.canUseCache("file:///foo"));
}

@Test
public void testUnavailableCache() throws Exception {
FileServer server = new FileServer();
server.start();
String uri = server.getUri("bad/url");
try {
cacheResourcesManager.getResource(uri);
fail("cacheResourcesManager should be busy downloading the url");
} catch (CacheResourceDownloadingException ignored) {
}
TimeUnit.MILLISECONDS.sleep(200);
//failed to download so returns null
assertNull(cacheResourcesManager.getResource(uri));

TimeUnit.SECONDS.sleep(1);//wait past the cache expiration date

//Manager should retry downloading
try {
cacheResourcesManager.getResource(uri);
fail("cacheResourcesManager should be busy re-downloading the url");
} catch (CacheResourceDownloadingException ignored) {
}
}

@Test
public void testAvailableCache() throws Exception {
FileServer server = new FileServer();
server.start();
String uri = server.getUri("/dtd/web-app_2_3.dtd");
try {
cacheResourcesManager.getResource(uri);
fail("cacheResourcesManager should be busy downloading the url");
} catch (CacheResourceDownloadingException ignored) {
}
TimeUnit.MILLISECONDS.sleep(200);

assertNotNull(cacheResourcesManager.getResource(uri));

server.stop();
TimeUnit.SECONDS.sleep(1);//wait past the cache expiration date

//Manager should return cached content, even if server is offline
assertNotNull(cacheResourcesManager.getResource(uri));
}

private Cache<String, Boolean> testingCache() {
return CacheBuilder.newBuilder().expireAfterWrite(1, TimeUnit.SECONDS).maximumSize(1).build();
}

}
Loading

0 comments on commit 55078ec

Please sign in to comment.