summaryrefslogtreecommitdiff
path: root/json/src/com/jetbrains/jsonSchema/remote/JsonSchemaRemoteContentProvider.java
diff options
context:
space:
mode:
Diffstat (limited to 'json/src/com/jetbrains/jsonSchema/remote/JsonSchemaRemoteContentProvider.java')
-rw-r--r--json/src/com/jetbrains/jsonSchema/remote/JsonSchemaRemoteContentProvider.java125
1 files changed, 125 insertions, 0 deletions
diff --git a/json/src/com/jetbrains/jsonSchema/remote/JsonSchemaRemoteContentProvider.java b/json/src/com/jetbrains/jsonSchema/remote/JsonSchemaRemoteContentProvider.java
new file mode 100644
index 00000000..dad1766b
--- /dev/null
+++ b/json/src/com/jetbrains/jsonSchema/remote/JsonSchemaRemoteContentProvider.java
@@ -0,0 +1,125 @@
+// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
+package com.jetbrains.jsonSchema.remote;
+
+import com.intellij.json.JsonFileType;
+import com.intellij.openapi.fileTypes.FileType;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.openapi.vfs.impl.http.DefaultRemoteContentProvider;
+import com.intellij.util.Url;
+import com.intellij.util.containers.ContainerUtil;
+import com.intellij.util.io.HttpRequests;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URLConnection;
+import java.nio.file.Files;
+import java.time.Duration;
+import java.util.List;
+
+public class JsonSchemaRemoteContentProvider extends DefaultRemoteContentProvider {
+ private static final int DEFAULT_CONNECT_TIMEOUT = 10000;
+ private static final long UPDATE_DELAY = Duration.ofHours(4).toMillis();
+ static final String STORE_URL_PREFIX_HTTP = "http://json.schemastore.org";
+ static final String STORE_URL_PREFIX_HTTPS = "https://schemastore.azurewebsites.net";
+ private static final String SCHEMA_URL_PREFIX = "http://json-schema.org/";
+ private static final String ETAG_HEADER = "ETag";
+ private static final String LAST_MODIFIED_HEADER = "Last-Modified";
+
+ private long myLastUpdateTime = 0;
+
+ @Override
+ public boolean canProvideContent(@NotNull Url url) {
+ String externalForm = url.toExternalForm();
+ return externalForm.startsWith(STORE_URL_PREFIX_HTTP)
+ || externalForm.startsWith(STORE_URL_PREFIX_HTTPS)
+ || externalForm.startsWith(SCHEMA_URL_PREFIX)
+ || externalForm.endsWith(".json");
+ }
+
+ @Override
+ protected void saveAdditionalData(@NotNull HttpRequests.Request request, @NotNull File file) throws IOException {
+ URLConnection connection = request.getConnection();
+ if (saveTag(file, connection, ETAG_HEADER)) return;
+ saveTag(file, connection, LAST_MODIFIED_HEADER);
+ }
+
+ @Nullable
+ @Override
+ protected FileType adjustFileType(@Nullable FileType type, @NotNull Url url) {
+ if (type == null && url.toExternalForm().startsWith(SCHEMA_URL_PREFIX)) {
+ // json-schema.org doesn't provide a mime-type for schemas
+ return JsonFileType.INSTANCE;
+ }
+ return super.adjustFileType(type, url);
+ }
+
+ private static boolean saveTag(@NotNull File file, @NotNull URLConnection connection, @NotNull String header) throws IOException {
+ String tag = connection.getHeaderField(header);
+ if (tag != null) {
+ String path = file.getAbsolutePath();
+ if (!path.endsWith(".json")) path += ".json";
+ File tagFile = new File(path + "." + header);
+ saveToFile(tagFile, tag);
+ return true;
+ }
+ return false;
+ }
+
+ private static void saveToFile(@NotNull File tagFile, @NotNull String headerValue) throws IOException {
+ if (!tagFile.exists()) if (!tagFile.createNewFile()) return;
+ Files.write(tagFile.toPath(), ContainerUtil.createMaybeSingletonList(headerValue));
+ }
+
+ @Override
+ public boolean isUpToDate(@NotNull Url url, @NotNull VirtualFile local) {
+ long now = System.currentTimeMillis();
+ // don't update more frequently than once in 4 hours
+ if (now - myLastUpdateTime < UPDATE_DELAY) {
+ return true;
+ }
+
+ myLastUpdateTime = now;
+ String path = local.getPath();
+
+ if (now - new File(path).lastModified() < UPDATE_DELAY) {
+ return true;
+ }
+
+ if (checkUpToDate(url, path, ETAG_HEADER)) return true;
+ if (checkUpToDate(url, path, LAST_MODIFIED_HEADER)) return true;
+
+ return false;
+ }
+
+ private boolean checkUpToDate(@NotNull Url url, @NotNull String path, @NotNull String header) {
+ File file = new File(path + "." + header);
+ try {
+ return isUpToDate(url, file, header);
+ }
+ catch (IOException e) {
+ // in case of an error, don't bother with update for the next UPDATE_DELAY milliseconds
+ //noinspection ResultOfMethodCallIgnored
+ new File(path).setLastModified(System.currentTimeMillis());
+ return true;
+ }
+ }
+
+ @Override
+ protected int getDefaultConnectionTimeout() {
+ return DEFAULT_CONNECT_TIMEOUT;
+ }
+
+ private boolean isUpToDate(@NotNull Url url, @NotNull File file, @NotNull String header) throws IOException {
+ List<String> strings = file.exists() ? Files.readAllLines(file.toPath()) : ContainerUtil.emptyList();
+
+ String currentTag = strings.size() > 0 ? strings.get(0) : null;
+ if (currentTag == null) return false;
+
+ String remoteTag = connect(url, HttpRequests.head(url.toExternalForm()),
+ r -> r.getConnection().getHeaderField(header));
+
+ return currentTag.equals(remoteTag);
+ }
+}