From 9eb3bb248c18d70b74dd3fc09d3b16f37d28dd96 Mon Sep 17 00:00:00 2001 From: shuzijun Date: Mon, 16 Aug 2021 09:16:56 +0800 Subject: [PATCH] fix compatibility --- .../leetcode/plugin/model/PluginConstant.java | 2 +- .../plugin/setting/PersistentConfig.java | 27 +- .../plugin/setting/ProjectConfig.java | 19 +- .../leetcode/plugin/setting/SettingUI.java | 6 +- .../leetcode/plugin/timer/TimerBarWidget.java | 13 - .../timer/TimerStatusBarWidgetProvider.java | 48 +- .../leetcode/plugin/utils/FileUtils.java | 41 +- .../plugin/utils/HttpRequestUtils.java | 2 +- .../leetcode/plugin/utils/MTAUtils.java | 14 +- .../leetcode/plugin/utils/MessageUtils.java | 19 +- .../leetcode/plugin/utils/SentryUtils.java | 4 +- .../leetcode/plugin/utils/UpdateUtils.java | 4 +- .../plugin/utils/io/HttpRequests.java | 695 ------------------ .../utils/io/HttpUrlConnectionUtil.java | 61 -- .../utils/io/ProgressMonitorInputStream.java | 70 -- .../plugin/utils/io/RequestBuilder.java | 109 --- .../leetcode/plugin/window/HttpLogin.java | 4 +- .../leetcode/plugin/window/JcefLogin.java | 166 +++-- src/main/resources/META-INF/plugin.xml | 269 +------ 19 files changed, 176 insertions(+), 1397 deletions(-) delete mode 100644 src/main/java/com/shuzijun/leetcode/plugin/utils/io/HttpRequests.java delete mode 100644 src/main/java/com/shuzijun/leetcode/plugin/utils/io/HttpUrlConnectionUtil.java delete mode 100644 src/main/java/com/shuzijun/leetcode/plugin/utils/io/ProgressMonitorInputStream.java delete mode 100644 src/main/java/com/shuzijun/leetcode/plugin/utils/io/RequestBuilder.java diff --git a/src/main/java/com/shuzijun/leetcode/plugin/model/PluginConstant.java b/src/main/java/com/shuzijun/leetcode/plugin/model/PluginConstant.java index b7a2d9d4..40375f60 100644 --- a/src/main/java/com/shuzijun/leetcode/plugin/model/PluginConstant.java +++ b/src/main/java/com/shuzijun/leetcode/plugin/model/PluginConstant.java @@ -55,5 +55,5 @@ public class PluginConstant { public static final String LEETCODE_EDITOR_GROUP = ACTION_PREFIX + ".editor.group"; - + public static final String LEETCODE_EDITOR_TIMER_STATUS_BAR_ID = PLUGIN_ID + "-TimerStatusBar"; } diff --git a/src/main/java/com/shuzijun/leetcode/plugin/setting/PersistentConfig.java b/src/main/java/com/shuzijun/leetcode/plugin/setting/PersistentConfig.java index 137ecb92..810d9ef7 100644 --- a/src/main/java/com/shuzijun/leetcode/plugin/setting/PersistentConfig.java +++ b/src/main/java/com/shuzijun/leetcode/plugin/setting/PersistentConfig.java @@ -1,7 +1,8 @@ package com.shuzijun.leetcode.plugin.setting; +import com.intellij.credentialStore.CredentialAttributes; +import com.intellij.credentialStore.Credentials; import com.intellij.ide.passwordSafe.PasswordSafe; -import com.intellij.ide.passwordSafe.PasswordSafeException; import com.intellij.openapi.components.*; import com.intellij.util.xmlb.XmlSerializerUtil; import com.shuzijun.leetcode.plugin.model.Config; @@ -18,7 +19,7 @@ /** * @author shuzijun */ -@State(name = "PersistentConfig" + PluginConstant.ACTION_SUFFIX, storages = {@Storage(value = PluginConstant.ACTION_PREFIX+"-config.xml", roamingType = RoamingType.DISABLED)}) +@State(name = "PersistentConfig" + PluginConstant.ACTION_SUFFIX, storages = {@Storage(value = PluginConstant.ACTION_PREFIX + "-config.xml", roamingType = RoamingType.DISABLED)}) public class PersistentConfig implements PersistentStateComponent { public static String PATH = "leetcode" + File.separator + "editor"; @@ -67,24 +68,16 @@ public String getTempFilePath() { return getConfig().getFilePath() + File.separator + PATH + File.separator + initConfig.get(INITNAME).getAlias() + File.separator; } - public void savePassword(String password) { - try { - PasswordSafe.getInstance().storePassword - (null, this.getClass(), PluginConstant.PLUGIN_ID, password != null ? password : ""); - } catch (PasswordSafeException exception) { - MessageUtils.showAllWarnMsg("warning", "Failed to save password"); + public void savePassword(String password, String username) { + if(username == null || password == null){ + return; } + PasswordSafe.getInstance().set(new CredentialAttributes(PluginConstant.PLUGIN_ID, username, this.getClass()), new Credentials(username, password==null?"":password)); } - public String getPassword() { - if (getConfig().getVersion() != null) { - try { - return PasswordSafe.getInstance().getPassword(null, this.getClass(), PluginConstant.PLUGIN_ID); - } catch (PasswordSafeException exception) { - MessageUtils.showAllWarnMsg("warning", "Password acquisition failed"); - return null; - } - + public String getPassword(String username) { + if (getConfig().getVersion() != null && username != null) { + return PasswordSafe.getInstance().getPassword(new CredentialAttributes(PluginConstant.PLUGIN_ID, username, this.getClass())); } return null; diff --git a/src/main/java/com/shuzijun/leetcode/plugin/setting/ProjectConfig.java b/src/main/java/com/shuzijun/leetcode/plugin/setting/ProjectConfig.java index e1505412..1ffa246e 100644 --- a/src/main/java/com/shuzijun/leetcode/plugin/setting/ProjectConfig.java +++ b/src/main/java/com/shuzijun/leetcode/plugin/setting/ProjectConfig.java @@ -16,17 +16,13 @@ * @author shuzijun */ @State(name = "LeetcodeEditor" + PluginConstant.ACTION_SUFFIX, storages = {@Storage(value = PluginConstant.ACTION_PREFIX+"/editor.xml")}) -public class ProjectConfig implements ProjectComponent, PersistentStateComponent { +public class ProjectConfig implements PersistentStateComponent { public Map idProjectConfig = new HashMap<>(); - public ProjectConfig(Project project) { - - } - @Nullable public static ProjectConfig getInstance(Project project) { - return ServiceManager.getService(project, ProjectConfig.class); + return project.getService(ProjectConfig.class); } private InnerState innerState = new InnerState(); @@ -81,15 +77,4 @@ public String getComponentName() { return this.getClass().getName(); } - public void initComponent() { - } - - public void disposeComponent() { - } - - public void projectOpened() { - } - - public void projectClosed() { - } } diff --git a/src/main/java/com/shuzijun/leetcode/plugin/setting/SettingUI.java b/src/main/java/com/shuzijun/leetcode/plugin/setting/SettingUI.java index 45cb6fd3..ab471f45 100644 --- a/src/main/java/com/shuzijun/leetcode/plugin/setting/SettingUI.java +++ b/src/main/java/com/shuzijun/leetcode/plugin/setting/SettingUI.java @@ -163,7 +163,7 @@ private void loadSetting() { Config config = PersistentConfig.getInstance().getInitConfig(); if (config != null) { userNameField.setText(config.getLoginName()); - passwordField.setText(PersistentConfig.getInstance().getPassword()); + passwordField.setText(PersistentConfig.getInstance().getPassword(config.getLoginName())); if (StringUtils.isNotBlank(config.getFilePath())) { fileFolderBtn.setText(config.getFilePath()); } @@ -216,7 +216,7 @@ public boolean isModified() { Config currentState = new Config(); process(currentState); if (currentState.isModified(config)) { - if (passwordField.getText() != null && passwordField.getText().equals(PersistentConfig.getInstance().getPassword())) { + if (passwordField.getText() != null && passwordField.getText().equals(PersistentConfig.getInstance().getPassword(config.getLoginName()))) { return false; } else { return true; @@ -239,7 +239,7 @@ public void apply() { file.mkdirs(); } PersistentConfig.getInstance().setInitConfig(config); - PersistentConfig.getInstance().savePassword(passwordField.getText()); + PersistentConfig.getInstance().savePassword(passwordField.getText(),config.getLoginName()); CustomTreeCellRenderer.loaColor(); TimerBarWidget.loaColor(); } diff --git a/src/main/java/com/shuzijun/leetcode/plugin/timer/TimerBarWidget.java b/src/main/java/com/shuzijun/leetcode/plugin/timer/TimerBarWidget.java index a9b81818..4c60bd8d 100644 --- a/src/main/java/com/shuzijun/leetcode/plugin/timer/TimerBarWidget.java +++ b/src/main/java/com/shuzijun/leetcode/plugin/timer/TimerBarWidget.java @@ -1,16 +1,13 @@ package com.shuzijun.leetcode.plugin.timer; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.wm.CustomStatusBarWidget; import com.intellij.openapi.wm.StatusBar; -import com.intellij.openapi.wm.StatusBarWidget; import com.shuzijun.leetcode.plugin.model.Config; import com.shuzijun.leetcode.plugin.model.PluginConstant; import com.shuzijun.leetcode.plugin.setting.PersistentConfig; import org.apache.commons.lang.StringUtils; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.awt.*; @@ -147,15 +144,5 @@ public void reset() { label.setVisible(false); } - @Nullable - public StatusBarWidget.WidgetPresentation getPresentation() { - return this.getPresentation(SystemInfo.isMac ? StatusBarWidget.PlatformType.MAC : StatusBarWidget.PlatformType.DEFAULT); - } - - @Nullable - public StatusBarWidget.WidgetPresentation getPresentation(@NotNull StatusBarWidget.PlatformType type) { - return null; - } - } diff --git a/src/main/java/com/shuzijun/leetcode/plugin/timer/TimerStatusBarWidgetProvider.java b/src/main/java/com/shuzijun/leetcode/plugin/timer/TimerStatusBarWidgetProvider.java index 9cf0b2e2..098a7a46 100644 --- a/src/main/java/com/shuzijun/leetcode/plugin/timer/TimerStatusBarWidgetProvider.java +++ b/src/main/java/com/shuzijun/leetcode/plugin/timer/TimerStatusBarWidgetProvider.java @@ -1,19 +1,55 @@ package com.shuzijun.leetcode.plugin.timer; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Disposer; +import com.intellij.openapi.wm.StatusBar; import com.intellij.openapi.wm.StatusBarWidget; -import com.intellij.openapi.wm.StatusBarWidgetProvider; +import com.intellij.openapi.wm.StatusBarWidgetFactory; +import com.shuzijun.leetcode.plugin.model.PluginConstant; +import org.jetbrains.annotations.Nls; +import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; /** * @author shuzijun */ -public class TimerStatusBarWidgetProvider implements StatusBarWidgetProvider { - @Nullable +public class TimerStatusBarWidgetProvider implements StatusBarWidgetFactory { + + private TimerBarWidget timerBarWidget; + + @Override + public @NonNls @NotNull String getId() { + return PluginConstant.LEETCODE_EDITOR_TIMER_STATUS_BAR_ID; + } + + @Override + public @Nls @NotNull String getDisplayName() { + return PluginConstant.LEETCODE_EDITOR_TIMER_STATUS_BAR_ID; + } + + @Override + public boolean isAvailable(@NotNull Project project) { + return true; + } + + @Override + public @NotNull StatusBarWidget createWidget(@NotNull Project project) { + if (timerBarWidget == null) { + timerBarWidget = new TimerBarWidget(project); + } + return timerBarWidget; + } + + @Override + public void disposeWidget(@NotNull StatusBarWidget widget) { + if (timerBarWidget != null) { + Disposer.dispose(timerBarWidget); + } + } + @Override - public StatusBarWidget getWidget(@NotNull Project project) { - return new TimerBarWidget(project); + public boolean canBeEnabledOn(@NotNull StatusBar statusBar) { + return false; } } diff --git a/src/main/java/com/shuzijun/leetcode/plugin/utils/FileUtils.java b/src/main/java/com/shuzijun/leetcode/plugin/utils/FileUtils.java index e6424d33..5e0203d8 100644 --- a/src/main/java/com/shuzijun/leetcode/plugin/utils/FileUtils.java +++ b/src/main/java/com/shuzijun/leetcode/plugin/utils/FileUtils.java @@ -1,19 +1,14 @@ package com.shuzijun.leetcode.plugin.utils; -import com.intellij.openapi.application.Application; import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.application.TransactionGuard; -import com.intellij.openapi.application.WriteAction; import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.fileEditor.FileEditorManager; import com.intellij.openapi.fileEditor.OpenFileDescriptor; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.ThrowableComputable; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VfsUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.newvfs.RefreshQueue; -import com.intellij.util.ExceptionUtil; import com.shuzijun.leetcode.plugin.model.CodeTypeEnum; import com.shuzijun.leetcode.plugin.model.Constant; import com.shuzijun.leetcode.plugin.model.LeetcodeEditor; @@ -25,7 +20,6 @@ import java.util.ArrayList; import java.util.LinkedList; import java.util.List; -import java.util.concurrent.atomic.AtomicReference; import java.util.function.BiConsumer; /** @@ -244,40 +238,11 @@ public static void openFileEditorAndSaveState(File file, Project project, Questi public static void saveEditDocument(VirtualFile file){ if (FileDocumentManager.getInstance().isFileModified(file)) { try { - ThrowableComputable action = new ThrowableComputable() { - @Override - public Boolean compute() throws Throwable { + ApplicationManager.getApplication().invokeLaterOnWriteThread((() -> { + ApplicationManager.getApplication().runWriteAction(() -> { FileDocumentManager.getInstance().saveDocument(FileDocumentManager.getInstance().getDocument(file)); - return true; - } - }; - - - Application application = ApplicationManager.getApplication(); - if (application.isDispatchThread()) { - ApplicationManager.getApplication().runWriteAction(action); - } else { - if (application.isReadAccessAllowed()) { - LogUtils.LOG.error("Must not start write action from within read action in the other thread - deadlock is coming"); - } - - AtomicReference result = new AtomicReference(); - AtomicReference exception = new AtomicReference(); - TransactionGuard.getInstance().submitTransactionAndWait(() -> { - try { - result.set(WriteAction.compute(action)); - } catch (Throwable var4) { - exception.set(var4); - } - }); - Throwable t = (Throwable) exception.get(); - if (t != null) { - t.addSuppressed(new RuntimeException()); - ExceptionUtil.rethrowUnchecked(t); - throw t; - } - } + })); } catch (Throwable ignore) { LogUtils.LOG.error("自动保存文件错误", ignore); } diff --git a/src/main/java/com/shuzijun/leetcode/plugin/utils/HttpRequestUtils.java b/src/main/java/com/shuzijun/leetcode/plugin/utils/HttpRequestUtils.java index b1d9d501..f1e8d407 100644 --- a/src/main/java/com/shuzijun/leetcode/plugin/utils/HttpRequestUtils.java +++ b/src/main/java/com/shuzijun/leetcode/plugin/utils/HttpRequestUtils.java @@ -1,6 +1,6 @@ package com.shuzijun.leetcode.plugin.utils; -import com.shuzijun.leetcode.plugin.utils.io.HttpRequests; +import com.intellij.util.io.HttpRequests; import org.apache.commons.lang3.StringUtils; import org.apache.http.HttpHeaders; import org.jetbrains.annotations.NotNull; diff --git a/src/main/java/com/shuzijun/leetcode/plugin/utils/MTAUtils.java b/src/main/java/com/shuzijun/leetcode/plugin/utils/MTAUtils.java index 82b119f8..834111a7 100644 --- a/src/main/java/com/shuzijun/leetcode/plugin/utils/MTAUtils.java +++ b/src/main/java/com/shuzijun/leetcode/plugin/utils/MTAUtils.java @@ -1,17 +1,7 @@ package com.shuzijun.leetcode.plugin.utils; -import com.intellij.ide.plugins.PluginManager; -import com.intellij.openapi.extensions.PluginId; -import com.intellij.openapi.util.SystemInfo; import com.shuzijun.leetcode.plugin.model.Config; -import com.shuzijun.leetcode.plugin.model.PluginConstant; -import com.shuzijun.leetcode.plugin.utils.io.HttpRequests; -import org.apache.http.client.utils.URIBuilder; -import java.awt.*; -import java.net.URI; -import java.util.Calendar; -import java.util.Locale; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -59,7 +49,7 @@ public ClickTask(Config config, String actionsId) { @Override public void run() { - try { + /* try { if (version == null) { version = PluginManager.getPlugin(PluginId.getId(PluginConstant.PLUGIN_ID)).getVersion() .replace("v", "").replaceAll("-|_", "."); @@ -98,7 +88,7 @@ public void run() { HttpRequests.request(uri.toURL().toString()).userAgent(userAgent).tryConnect(); } catch (Exception e) { - } + }*/ } } } diff --git a/src/main/java/com/shuzijun/leetcode/plugin/utils/MessageUtils.java b/src/main/java/com/shuzijun/leetcode/plugin/utils/MessageUtils.java index 6fc4eaaa..371d4853 100644 --- a/src/main/java/com/shuzijun/leetcode/plugin/utils/MessageUtils.java +++ b/src/main/java/com/shuzijun/leetcode/plugin/utils/MessageUtils.java @@ -3,8 +3,7 @@ import com.intellij.notification.Notification; import com.intellij.notification.NotificationType; import com.intellij.notification.Notifications; -import com.intellij.openapi.components.ProjectComponent; -import com.intellij.openapi.components.ServiceManager; +import com.intellij.openapi.components.Service; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.MessageType; import com.intellij.openapi.ui.popup.Balloon; @@ -12,7 +11,6 @@ import com.intellij.openapi.ui.popup.JBPopupFactory; import com.intellij.ui.JBColor; import com.intellij.ui.awt.RelativePoint; -import com.shuzijun.leetcode.plugin.model.Constant; import com.shuzijun.leetcode.plugin.model.PluginConstant; import org.jetbrains.annotations.Nullable; @@ -22,7 +20,8 @@ /** * @author shuzijun */ -public class MessageUtils implements ProjectComponent { +@Service +public final class MessageUtils { private Project project; @@ -32,7 +31,7 @@ public MessageUtils(Project project) { @Nullable public static MessageUtils getInstance(Project project) { - return ServiceManager.getService(project, MessageUtils.class); + return project.getService(MessageUtils.class); } @@ -67,15 +66,5 @@ public String getComponentName() { return this.getClass().getName(); } - public void initComponent() { - } - - public void disposeComponent() { - } - public void projectOpened() { - } - - public void projectClosed() { - } } diff --git a/src/main/java/com/shuzijun/leetcode/plugin/utils/SentryUtils.java b/src/main/java/com/shuzijun/leetcode/plugin/utils/SentryUtils.java index f41ff3a3..a1b5a9e3 100644 --- a/src/main/java/com/shuzijun/leetcode/plugin/utils/SentryUtils.java +++ b/src/main/java/com/shuzijun/leetcode/plugin/utils/SentryUtils.java @@ -1,6 +1,6 @@ package com.shuzijun.leetcode.plugin.utils; -import com.intellij.ide.plugins.PluginManager; +import com.intellij.ide.plugins.PluginManagerCore; import com.intellij.openapi.application.ApplicationInfo; import com.intellij.openapi.application.impl.ApplicationInfoImpl; import com.intellij.openapi.extensions.PluginId; @@ -79,7 +79,7 @@ public void helpBuildingEvent(EventBuilder eventBuilder) { } context.addTag("javaVersion", SystemInfo.JAVA_RUNTIME_VERSION); - context.addTag("pluginVersion", PluginManager.getPlugin(PluginId.getId(PluginConstant.PLUGIN_ID)).getVersion()); + context.addTag("pluginVersion", PluginManagerCore.getPlugin(PluginId.getId(PluginConstant.PLUGIN_ID)).getVersion()); if(error == null){ sentry.sendMessage(description); }else { diff --git a/src/main/java/com/shuzijun/leetcode/plugin/utils/UpdateUtils.java b/src/main/java/com/shuzijun/leetcode/plugin/utils/UpdateUtils.java index cade7271..1b77ef3d 100644 --- a/src/main/java/com/shuzijun/leetcode/plugin/utils/UpdateUtils.java +++ b/src/main/java/com/shuzijun/leetcode/plugin/utils/UpdateUtils.java @@ -2,7 +2,7 @@ import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; -import com.intellij.ide.plugins.PluginManager; +import com.intellij.ide.plugins.PluginManagerCore; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.extensions.PluginId; import com.intellij.openapi.project.Project; @@ -33,7 +33,7 @@ public void run() { CloseableHttpClient httpClient = HttpClients.custom().build(); HttpGet httpget = null; try { - String[] version = PluginManager.getPlugin(PluginId.getId(PluginConstant.PLUGIN_ID)).getVersion().replace("v", "").split("\\.|-"); + String[] version = PluginManagerCore.getPlugin(PluginId.getId(PluginConstant.PLUGIN_ID)).getVersion().replace("v", "").split("\\.|-"); httpget = new HttpGet("https://plugins.jetbrains.com/api/plugins/"+PluginConstant.WEB_ID+"/updates"); CloseableHttpResponse response = httpClient.execute(httpget); String body = EntityUtils.toString(response.getEntity(), "UTF-8"); diff --git a/src/main/java/com/shuzijun/leetcode/plugin/utils/io/HttpRequests.java b/src/main/java/com/shuzijun/leetcode/plugin/utils/io/HttpRequests.java deleted file mode 100644 index 9df852a4..00000000 --- a/src/main/java/com/shuzijun/leetcode/plugin/utils/io/HttpRequests.java +++ /dev/null @@ -1,695 +0,0 @@ -// Copyright 2000-2019 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.shuzijun.leetcode.plugin.utils.io; - -import com.intellij.Patches; -import com.intellij.ide.IdeBundle; -import com.intellij.openapi.application.Application; -import com.intellij.openapi.application.ApplicationInfo; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.application.ApplicationNamesInfo; -import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.progress.ProgressIndicator; -import com.intellij.openapi.util.io.BufferExposingByteArrayOutputStream; -import com.intellij.openapi.util.io.FileUtilRt; -import com.intellij.openapi.util.io.StreamUtil; -import com.intellij.openapi.util.text.StringUtil; -import com.intellij.util.ArrayUtil; -import com.intellij.util.SystemProperties; -import com.intellij.util.Url; -import com.intellij.util.io.CountingGZIPInputStream; -import com.intellij.util.net.HttpConfigurable; -import com.intellij.util.net.NetUtils; -import com.intellij.util.net.ssl.CertificateManager; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import javax.net.ssl.*; -import java.io.*; -import java.net.*; -import java.nio.ByteBuffer; -import java.nio.charset.Charset; -import java.nio.charset.StandardCharsets; -import java.util.List; -import java.util.Map; - -/** - *

Handy class for reading data from URL connections with built-in support for HTTP redirects and gzipped content and automatic cleanup.

- * - *

Examples

- * - *

Reading the whole response into a string:
- * {@code HttpRequests.request("https://example.com").readString(progressIndicator)}

- * - *

Downloading a file:
- * {@code HttpRequests.request("https://example.com/file.zip").saveToFile(new File(downloadDir, "temp.zip"), progressIndicator)}

- * - *

Tuning a connection:
- * {@code HttpRequests.request(url).userAgent("IntelliJ").readString()}
- * {@code HttpRequests.request(url).tuner(connection -> connection.setRequestProperty("X-Custom", value)).readString()}

- * - *

Using the input stream to implement custom reading logic:
- * {@code int firstByte = HttpRequests.request("file:///dev/random").connect(request -> request.getInputStream().read())}
- * {@code String firstLine = HttpRequests.request("https://example.com").connect(request -> new BufferedReader(request.getReader()).readLine())}

- * - * @see HttpStatusException a sublass of IOException, which includes an actual URL and HTTP response code - * @see - */ -public final class HttpRequests { - private static final Logger LOG = Logger.getInstance(HttpRequests.class); - - public static final String JSON_CONTENT_TYPE = "application/json; charset=utf-8"; - - public static final int CONNECTION_TIMEOUT = SystemProperties.getIntProperty("idea.connection.timeout", 10000); - public static final int READ_TIMEOUT = SystemProperties.getIntProperty("idea.read.timeout", 60000); - public static final int REDIRECT_LIMIT = SystemProperties.getIntProperty("idea.redirect.limit", 10); - - private static final int[] REDIRECTS = { - // temporary redirects - HttpURLConnection.HTTP_MOVED_TEMP, 307 /* temporary redirect */, - // permanent redirects - HttpURLConnection.HTTP_MOVED_PERM, HttpURLConnection.HTTP_SEE_OTHER, 308 /* permanent redirect */ - }; - - private HttpRequests() { } - - public interface Request { - @NotNull - String getURL(); - - @NotNull - URLConnection getConnection() throws IOException; - - @NotNull - InputStream getInputStream() throws IOException; - - @NotNull - BufferedReader getReader() throws IOException; - - @NotNull - BufferedReader getReader(@Nullable ProgressIndicator indicator) throws IOException; - - /** @deprecated Called automatically on open connection. Use {@link RequestBuilder#tryConnect()} to get response code */ - @Deprecated - boolean isSuccessful() throws IOException; - - @NotNull - File saveToFile(@NotNull File file, @Nullable ProgressIndicator indicator) throws IOException; - - byte[] readBytes(@Nullable ProgressIndicator indicator) throws IOException; - - @NotNull - String readString(@Nullable ProgressIndicator indicator) throws IOException; - - @NotNull - default String readString() throws IOException { - return readString(null); - } - - @NotNull - CharSequence readChars(@Nullable ProgressIndicator indicator) throws IOException; - - default void write(@NotNull String data) throws IOException { - write(data.getBytes(StandardCharsets.UTF_8)); - } - - default void write(byte[] data) throws IOException { - HttpURLConnection connection = (HttpURLConnection)getConnection(); - connection.setFixedLengthStreamingMode(data.length); - try (OutputStream stream = connection.getOutputStream()) { - stream.write(data); - } - } - } - - public interface RequestProcessor { - T process(@NotNull Request request) throws IOException; - } - - public interface ConnectionTuner { - void tune(@NotNull URLConnection connection) throws IOException; - } - - public static class HttpStatusException extends IOException { - private final int myStatusCode; - private final String myUrl; - - public HttpStatusException(@NotNull String message, int statusCode, @NotNull String url) { - super(message); - myStatusCode = statusCode; - myUrl = url; - } - - public int getStatusCode() { - return myStatusCode; - } - - @NotNull - public String getUrl() { - return myUrl; - } - - @Override - public String toString() { - return super.toString() + ". Status=" + myStatusCode + ", Url=" + myUrl; - } - } - - @NotNull - public static RequestBuilder request(@NotNull Url url) { - return request(url.toExternalForm()); - } - - @NotNull - public static RequestBuilder request(@NotNull String url) { - return new RequestBuilderImpl(url, null); - } - - @NotNull - public static RequestBuilder head(@NotNull String url) { - return new RequestBuilderImpl(url, connection -> ((HttpURLConnection)connection).setRequestMethod("HEAD")); - } - - @NotNull - public static RequestBuilder delete(@NotNull String url) { - return new RequestBuilderImpl(url, connection -> ((HttpURLConnection)connection).setRequestMethod("DELETE")); - } - - @NotNull - public static RequestBuilder delete(@NotNull String url, @Nullable String contentType) { - return requestWithBody(url, "DELETE", contentType, null); - } - - @NotNull - public static RequestBuilder post(@NotNull String url, @Nullable String contentType) { - return requestWithBody(url, "POST", contentType, null); - } - - @NotNull - public static RequestBuilder put(@NotNull String url, @Nullable String contentType) { - return requestWithBody(url, "PUT", contentType, null); - } - - /** - * Java does not support "newer" HTTP methods, so we have to rely on server-side support of `X-HTTP-Method-Override` header to invoke PATCH - * For reasoning see {@link HttpURLConnection#setRequestMethod(String)} - *

- * TODO: either fiddle with reflection or patch JDK to avoid server reliance - */ - @NotNull - public static RequestBuilder patch(@NotNull String url, @Nullable String contentType) { - return requestWithBody(url, "POST", contentType, - connection -> connection.setRequestProperty("X-HTTP-Method-Override", "PATCH")); - } - - @NotNull - private static RequestBuilder requestWithBody(@NotNull String url, - @NotNull String requestMethod, - @Nullable String contentType, - @Nullable ConnectionTuner tuner) { - return new RequestBuilderImpl(url, rawConnection -> { - HttpURLConnection connection = (HttpURLConnection)rawConnection; - connection.setRequestMethod(requestMethod); - connection.setDoOutput(true); - if (contentType != null) { - connection.setRequestProperty("Content-Type", contentType); - } - if (tuner != null) { - tuner.tune(connection); - } - }); - } - - @NotNull - public static String createErrorMessage(@NotNull IOException e, @NotNull Request request, boolean includeHeaders) { - StringBuilder builder = new StringBuilder(); - - builder.append("Cannot download '").append(request.getURL()).append("': ").append(e.getMessage()); - - try { - URLConnection connection = request.getConnection(); - if (includeHeaders) { - builder.append("\n, headers: ").append(connection.getHeaderFields()); - } - if (connection instanceof HttpURLConnection) { - HttpURLConnection httpConnection = (HttpURLConnection)connection; - builder.append("\n, response: ").append(httpConnection.getResponseCode()).append(' ').append(httpConnection.getResponseMessage()); - } - } - catch (Throwable ignored) { } - - return builder.toString(); - } - - private static class RequestBuilderImpl extends RequestBuilder { - private final String myUrl; - private int myConnectTimeout = CONNECTION_TIMEOUT; - private int myTimeout = READ_TIMEOUT; - private int myRedirectLimit = REDIRECT_LIMIT; - private boolean myGzip = true; - private boolean myForceHttps; - private boolean myUseProxy = true; - private boolean myIsReadResponseOnError; - private HostnameVerifier myHostnameVerifier; - private String myUserAgent; - private String myAccept; - private ConnectionTuner myTuner; - private final ConnectionTuner myInternalTuner; - private boolean myThrowStatusCodeException = true; - - private RequestBuilderImpl(@NotNull String url, @Nullable ConnectionTuner internalTuner) { - myUrl = url; - myInternalTuner = internalTuner; - } - - @Override - public RequestBuilder connectTimeout(int value) { - myConnectTimeout = value; - return this; - } - - @Override - public RequestBuilder readTimeout(int value) { - myTimeout = value; - return this; - } - - @Override - public RequestBuilder redirectLimit(int redirectLimit) { - myRedirectLimit = redirectLimit; - return this; - } - - @Override - public RequestBuilder gzip(boolean value) { - myGzip = value; - return this; - } - - @Override - public RequestBuilder forceHttps(boolean forceHttps) { - myForceHttps = forceHttps; - return this; - } - - @Override - public RequestBuilder useProxy(boolean useProxy) { - myUseProxy = useProxy; - return this; - } - - @Override - public RequestBuilder isReadResponseOnError(boolean isReadResponseOnError) { - myIsReadResponseOnError = isReadResponseOnError; - return this; - } - - @Override - public RequestBuilder hostNameVerifier(@Nullable HostnameVerifier hostnameVerifier) { - myHostnameVerifier = hostnameVerifier; - return this; - } - - @Override - public RequestBuilder userAgent(@Nullable String userAgent) { - myUserAgent = userAgent; - return this; - } - - @Override - public RequestBuilder productNameAsUserAgent() { - Application app = ApplicationManager.getApplication(); - if (app != null && !app.isDisposed()) { - String productName = ApplicationNamesInfo.getInstance().getFullProductName(); - String version = ApplicationInfo.getInstance().getBuild().asStringWithoutProductCode(); - return userAgent(productName + '/' + version); - } - else { - return userAgent("IntelliJ"); - } - } - - @Override - public RequestBuilder accept(@Nullable String mimeType) { - myAccept = mimeType; - return this; - } - - @Override - public RequestBuilder tuner(@Nullable ConnectionTuner tuner) { - myTuner = tuner; - return this; - } - - @NotNull - @Override - public RequestBuilder throwStatusCodeException(boolean shouldThrow) { - myThrowStatusCodeException = shouldThrow; - return this; - } - - @Override - public T connect(@NotNull HttpRequests.RequestProcessor processor) throws IOException { - return process(this, processor); - } - } - - private static class RequestImpl implements Request, AutoCloseable { - private final RequestBuilderImpl myBuilder; - private String myUrl; - private URLConnection myConnection; - private InputStream myInputStream; - private BufferedReader myReader; - - private RequestImpl(RequestBuilderImpl builder) { - myBuilder = builder; - myUrl = myBuilder.myUrl; - } - - @NotNull - @Override - public String getURL() { - return myUrl; - } - - @NotNull - @Override - public URLConnection getConnection() throws IOException { - if (myConnection == null) { - myConnection = openConnection(myBuilder, this); - } - return myConnection; - } - - @NotNull - @Override - public InputStream getInputStream() throws IOException { - if (myInputStream == null) { - if(((HttpURLConnection)getConnection()).getResponseCode()>=400){ - myInputStream = ((HttpURLConnection)getConnection()).getErrorStream(); - }else { - myInputStream = getConnection().getInputStream(); - } - if (myBuilder.myGzip && "gzip".equalsIgnoreCase(getConnection().getContentEncoding())) { - myInputStream = CountingGZIPInputStream.create(myInputStream); - } - } - return myInputStream; - } - - @NotNull - @Override - public BufferedReader getReader() throws IOException { - return getReader(null); - } - - @NotNull - @Override - public BufferedReader getReader(@Nullable ProgressIndicator indicator) throws IOException { - if (myReader == null) { - InputStream inputStream = getInputStream(); - if (indicator != null) { - int contentLength = getConnection().getContentLength(); - if (contentLength > 0) { - inputStream = new ProgressMonitorInputStream(indicator, inputStream, contentLength); - } - } - myReader = new BufferedReader(new InputStreamReader(inputStream, getCharset())); - } - return myReader; - } - - @NotNull - private Charset getCharset() throws IOException { - return HttpUrlConnectionUtil.getCharset(getConnection()); - } - - @Override - public boolean isSuccessful() throws IOException { - URLConnection connection = getConnection(); - return !(connection instanceof HttpURLConnection) || ((HttpURLConnection)connection).getResponseCode() == 200; - } - - @Override - public byte[] readBytes(@Nullable ProgressIndicator indicator) throws IOException { - return doReadBytes(indicator).toByteArray(); - } - - @NotNull - private BufferExposingByteArrayOutputStream doReadBytes(@Nullable ProgressIndicator indicator) throws IOException { - return HttpUrlConnectionUtil.readBytes(getInputStream(), getConnection(), indicator); - } - - @NotNull - @Override - public String readString(@Nullable ProgressIndicator indicator) throws IOException { - return HttpUrlConnectionUtil.readString(getInputStream(), getConnection(), indicator); - } - - @NotNull - @Override - public CharSequence readChars(@Nullable ProgressIndicator indicator) throws IOException { - BufferExposingByteArrayOutputStream byteStream = doReadBytes(indicator); - if (byteStream.size() == 0) { - return ArrayUtil.EMPTY_CHAR_SEQUENCE; - } - else { - return getCharset().decode(ByteBuffer.wrap(byteStream.getInternalBuffer(), 0, byteStream.size())); - } - } - - @Override - @NotNull - public File saveToFile(@NotNull File file, @Nullable ProgressIndicator indicator) throws IOException { - FileUtilRt.createParentDirs(file); - - boolean deleteFile = true; - try (OutputStream out = new BufferedOutputStream(new FileOutputStream(file))) { - NetUtils.copyStreamContent(indicator, getInputStream(), out, getConnection().getContentLength()); - deleteFile = false; - } - catch (HttpStatusException e) { - throw e; - } - catch (IOException e) { - throw new IOException(createErrorMessage(e, this, false), e); - } - finally { - if (deleteFile) { - FileUtilRt.delete(file); - } - } - - return file; - } - - @Override - public void close() { - StreamUtil.closeStream(myInputStream); - StreamUtil.closeStream(myReader); - if (myConnection instanceof HttpURLConnection) { - ((HttpURLConnection)myConnection).disconnect(); - } - } - } - - private static T process(RequestBuilderImpl builder, RequestProcessor processor) throws IOException { - Application app = ApplicationManager.getApplication(); - LOG.assertTrue(app == null || app.isUnitTestMode() || app.isHeadlessEnvironment() || !app.isReadAccessAllowed(), - "Network shouldn't be accessed in EDT or inside read action"); - - ClassLoader contextLoader = Thread.currentThread().getContextClassLoader(); - if (contextLoader != null && shouldOverrideContextClassLoader()) { - // hack-around for class loader lock in sun.net.www.protocol.http.NegotiateAuthentication (IDEA-131621) - try (URLClassLoader cl = new URLClassLoader(new URL[0], contextLoader)) { - Thread.currentThread().setContextClassLoader(cl); - return doProcess(builder, processor); - } - finally { - Thread.currentThread().setContextClassLoader(contextLoader); - } - } - else { - return doProcess(builder, processor); - } - } - - private static boolean shouldOverrideContextClassLoader() { - return Patches.JDK_BUG_ID_8032832 && - SystemProperties.getBooleanProperty("http.requests.override.context.classloader", true); - } - - private static T doProcess(RequestBuilderImpl builder, RequestProcessor processor) throws IOException { - try (RequestImpl request = new RequestImpl(builder)) { - T result = processor.process(request); - - if (builder.myThrowStatusCodeException) { - URLConnection connection = request.myConnection; - if (connection != null && connection.getDoOutput()) { - // getResponseCode is not checked on connect, because write must be performed before read - HttpURLConnection urlConnection = (HttpURLConnection)connection; - int responseCode = urlConnection.getResponseCode(); - if (responseCode >= 400) { - throwHttpStatusError(urlConnection, request, builder, responseCode); - } - } - } - - return result; - } - } - - private static URLConnection openConnection(RequestBuilderImpl builder, RequestImpl request) throws IOException { - if (builder.myForceHttps && StringUtil.startsWith(request.myUrl, "http:")) { - request.myUrl = "https:" + request.myUrl.substring(5); - } - - for (int i = 0; i < builder.myRedirectLimit; i++) { - String url = request.myUrl; - - final URLConnection connection; - if (!builder.myUseProxy) { - connection = new URL(url).openConnection(Proxy.NO_PROXY); - } - else if (ApplicationManager.getApplication() == null) { - connection = new URL(url).openConnection(); - } - else { - connection = HttpConfigurable.getInstance().openConnection(url); - } - - if (connection instanceof HttpsURLConnection) { - configureSslConnection(url, (HttpsURLConnection)connection); - } - - connection.setConnectTimeout(builder.myConnectTimeout); - connection.setReadTimeout(builder.myTimeout); - - if (builder.myUserAgent != null) { - connection.setRequestProperty("User-Agent", builder.myUserAgent); - } - - if (builder.myHostnameVerifier != null && connection instanceof HttpsURLConnection) { - ((HttpsURLConnection)connection).setHostnameVerifier(builder.myHostnameVerifier); - } - - if (builder.myGzip) { - connection.setRequestProperty("Accept-Encoding", "gzip"); - } - - if (builder.myAccept != null) { - connection.setRequestProperty("Accept", builder.myAccept); - } - - connection.setUseCaches(false); - - if (builder.myInternalTuner != null) { - builder.myInternalTuner.tune(connection); - } - - if (builder.myTuner != null) { - builder.myTuner.tune(connection); - } - - checkRequestHeadersForNulBytes(connection); - - if (!(connection instanceof HttpURLConnection)) { - return connection; - } - - if (connection.getDoOutput()) { - return connection; - } - - HttpURLConnection httpURLConnection = (HttpURLConnection)connection; - String method = httpURLConnection.getRequestMethod(); - - LOG.assertTrue(method.equals("GET") || method.equals("HEAD") || method.equals("DELETE"), - "'" + method + "' not supported; please use GET, HEAD, DELETE, PUT or POST"); - - if (LOG.isDebugEnabled()) { - LOG.debug("connecting to " + url); - } - int responseCode; - try { - responseCode = httpURLConnection.getResponseCode(); - } - catch (SSLHandshakeException e) { - throw !NetUtils.isSniEnabled() ? new SSLException("SSL error probably caused by disabled SNI", e) : e; - } - if (LOG.isDebugEnabled()) { - LOG.debug("response from " + url + ": " + responseCode); - } - - if (responseCode < 200 || responseCode >= 300 && responseCode != HttpURLConnection.HTTP_NOT_MODIFIED) { - if (ArrayUtil.indexOf(REDIRECTS, responseCode) >= 0) { - httpURLConnection.disconnect(); - url = connection.getHeaderField("Location"); - if (LOG.isDebugEnabled()) { - LOG.debug("redirect from " + url + ": " + url); - } - if (url != null) { - request.myUrl = url; - continue; - } - } - - if(builder.myThrowStatusCodeException) { - throwHttpStatusError(httpURLConnection, request, builder, responseCode); - } - } - - return connection; - } - - throw new IOException(IdeBundle.message("error.connection.failed.redirects")); - } - - private static void throwHttpStatusError(HttpURLConnection connection, RequestImpl request, RequestBuilderImpl builder, int responseCode) throws IOException { - String message = null; - if (builder.myIsReadResponseOnError) { - message = HttpUrlConnectionUtil.readString(connection.getErrorStream(), connection, null); - } - if (StringUtil.isEmpty(message)) { - message = "Request failed with status code " + responseCode; - } - connection.disconnect(); - throw new HttpStatusException(message, responseCode, StringUtil.notNullize(request.myUrl, "Empty URL")); - } - - private static void configureSslConnection(@NotNull String url, @NotNull HttpsURLConnection connection) { - if (ApplicationManager.getApplication() == null) { - LOG.info("Application is not initialized yet; Using default SSL configuration to connect to " + url); - return; - } - - try { - SSLSocketFactory factory = CertificateManager.getInstance().getSslContext().getSocketFactory(); - if (factory == null) { - LOG.info("SSLSocketFactory is not defined by the IDE Certificate Manager; Using default SSL configuration to connect to " + url); - } - else { - connection.setSSLSocketFactory(factory); - } - } - catch (Throwable e) { - LOG.info("Problems configuring SSL connection to " + url, e); - } - } - - /* - * Many servers would not process a request and just return 400 (Bad Request) response if any of request headers contains NUL byte. - * This method checks the request and removes invalid headers. - */ - private static void checkRequestHeadersForNulBytes(URLConnection connection) { - for (Map.Entry> header : connection.getRequestProperties().entrySet()) { - for (String headerValue : header.getValue()) { - if (headerValue.indexOf('\0') >= 0) { - connection.setRequestProperty(header.getKey(), null); - LOG.error(String.format("Problem during request to '%s'. Header's '%s' value contains NUL bytes: '%s'. Omitting this header.", - connection.getURL().toString(), header.getKey(), headerValue)); - break; - } - } - } - } -} \ No newline at end of file diff --git a/src/main/java/com/shuzijun/leetcode/plugin/utils/io/HttpUrlConnectionUtil.java b/src/main/java/com/shuzijun/leetcode/plugin/utils/io/HttpUrlConnectionUtil.java deleted file mode 100644 index 3496d0de..00000000 --- a/src/main/java/com/shuzijun/leetcode/plugin/utils/io/HttpUrlConnectionUtil.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.shuzijun.leetcode.plugin.utils.io; - - -import com.intellij.openapi.progress.ProcessCanceledException; -import com.intellij.openapi.progress.ProgressIndicator; -import com.intellij.openapi.util.io.BufferExposingByteArrayOutputStream; -import com.intellij.openapi.util.text.StringUtil; -import com.intellij.util.net.NetUtils; -import kotlin.text.Charsets; -import org.apache.commons.lang3.StringUtils; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.URLConnection; -import java.nio.charset.Charset; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -public class HttpUrlConnectionUtil { - - private static final int BLOCK_SIZE = 16 * 1024; - private static final Pattern CHARSET_PATTERN = Pattern.compile("charset=([^;]+)"); - - public static BufferExposingByteArrayOutputStream readBytes( InputStream inputStream, @NotNull URLConnection connection, @Nullable ProgressIndicator progressIndicator) throws IOException, ProcessCanceledException { - int contentLength = connection.getContentLength(); - BufferExposingByteArrayOutputStream out = new BufferExposingByteArrayOutputStream(contentLength > 0 ? contentLength : BLOCK_SIZE); - NetUtils.copyStreamContent(progressIndicator, inputStream, (OutputStream)out, contentLength); - return out; - } - public static String readString(@NotNull InputStream inputStream, @NotNull URLConnection connection, @Nullable ProgressIndicator progressIndicator) throws IOException, ProcessCanceledException { - BufferExposingByteArrayOutputStream byteStream = readBytes(inputStream, connection, progressIndicator); - if(byteStream.size()== 0){ - return ""; - }else { - return new String(byteStream.getInternalBuffer(), 0, byteStream.size(), getCharset(connection)); - } - } - - - public static Charset getCharset(@NotNull URLConnection urlConnection) throws IOException { - String contentType = urlConnection.getContentType(); - if (StringUtils.isBlank(contentType)) { - Matcher m = CHARSET_PATTERN.matcher(contentType); - if (m.find()) { - try { - Charset charset = Charset.forName(StringUtil.unquoteString(m.group(1))); - return charset; - } catch (IllegalArgumentException var5) { - throw new IOException("unknown charset (" + contentType + ')', var5); - } - } - } - return Charsets.UTF_8; - } - - - -} diff --git a/src/main/java/com/shuzijun/leetcode/plugin/utils/io/ProgressMonitorInputStream.java b/src/main/java/com/shuzijun/leetcode/plugin/utils/io/ProgressMonitorInputStream.java deleted file mode 100644 index f6797a4a..00000000 --- a/src/main/java/com/shuzijun/leetcode/plugin/utils/io/ProgressMonitorInputStream.java +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2000-2019 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.shuzijun.leetcode.plugin.utils.io; - -import com.intellij.openapi.progress.ProgressIndicator; -import org.jetbrains.annotations.NotNull; - -import java.io.IOException; -import java.io.InputStream; - -final class ProgressMonitorInputStream extends InputStream { - private final ProgressIndicator indicator; - private final InputStream in; - - private final double available; - private long count; - - ProgressMonitorInputStream(@NotNull ProgressIndicator indicator, @NotNull InputStream in, int length) { - this.indicator = indicator; - this.in = in; - available = length; - } - - @Override - public int read() throws IOException { - int c = in.read(); - updateProgress(c >= 0 ? 1 : 0); - return c; - } - - private void updateProgress(long increment) { - indicator.checkCanceled(); - if (increment > 0) { - count += increment; - if(!indicator.isIndeterminate()) { - indicator.setFraction((double)count / available); - } - } - } - - @Override - public int read(byte[] b) throws IOException { - int r = in.read(b); - updateProgress(r); - return r; - } - - @Override - public int read(byte[] b, int off, int len) throws IOException { - int r = in.read(b, off, len); - updateProgress(r); - return r; - } - - @Override - public long skip(long n) throws IOException { - long r = in.skip(n); - updateProgress(r); - return r; - } - - @Override - public void close() throws IOException { - in.close(); - } - - @Override - public int available() throws IOException { - return in.available(); - } -} \ No newline at end of file diff --git a/src/main/java/com/shuzijun/leetcode/plugin/utils/io/RequestBuilder.java b/src/main/java/com/shuzijun/leetcode/plugin/utils/io/RequestBuilder.java deleted file mode 100644 index 2a3947fe..00000000 --- a/src/main/java/com/shuzijun/leetcode/plugin/utils/io/RequestBuilder.java +++ /dev/null @@ -1,109 +0,0 @@ -// Copyright 2000-2019 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.shuzijun.leetcode.plugin.utils.io; - -import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.progress.ProgressIndicator; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import javax.net.ssl.HostnameVerifier; -import java.io.File; -import java.io.IOException; -import java.net.HttpURLConnection; -import java.net.URLConnection; - -@SuppressWarnings("BoundedWildcard") -public abstract class RequestBuilder { - public abstract RequestBuilder connectTimeout(int value); - public abstract RequestBuilder readTimeout(int value); - public abstract RequestBuilder redirectLimit(int redirectLimit); - - /** - * Whether gzip encoding supported. Defaults to {@code true}. - */ - public abstract RequestBuilder gzip(boolean value); - - public abstract RequestBuilder forceHttps(boolean forceHttps); - public abstract RequestBuilder useProxy(boolean useProxy); - public abstract RequestBuilder hostNameVerifier(@Nullable HostnameVerifier hostnameVerifier); - public abstract RequestBuilder userAgent(@Nullable String userAgent); - public abstract RequestBuilder productNameAsUserAgent(); - public abstract RequestBuilder accept(@Nullable String mimeType); - public abstract RequestBuilder tuner(@Nullable HttpRequests.ConnectionTuner tuner); - - /** - * Whether to read server response on error. Error message available as {@link HttpRequests.HttpStatusException#getMessage()}. - * Defaults to false. - */ - public abstract RequestBuilder isReadResponseOnError(boolean isReadResponseOnError); - - /** - * Whether to analyze response status code and throw an exception if it's an "error" code. - * Defaults to true. - */ - @NotNull - public abstract RequestBuilder throwStatusCodeException(boolean shouldThrow); - - public abstract T connect(@NotNull HttpRequests.RequestProcessor processor) throws IOException; - - public int tryConnect() throws IOException { - return connect(request -> { - URLConnection connection = request.getConnection(); - return connection instanceof HttpURLConnection ? ((HttpURLConnection)connection).getResponseCode() : -1; - }); - } - - public T connect(@NotNull HttpRequests.RequestProcessor processor, T errorValue, @Nullable Logger logger) { - try { - return connect(processor); - } - catch (Throwable e) { - if (logger != null) { - logger.warn(e); - } - return errorValue; - } - } - - public void saveToFile(@NotNull File file, @Nullable ProgressIndicator indicator) throws IOException { - connect(request -> request.saveToFile(file, indicator)); - } - - public byte[] readBytes(@Nullable ProgressIndicator indicator) throws IOException { - return connect(request -> request.readBytes(indicator)); - } - - @NotNull - public String readString(@Nullable ProgressIndicator indicator) throws IOException { - return connect(request -> request.readString(indicator)); - } - - @NotNull - public String readString() throws IOException { - return readString(null); - } - - @NotNull - public CharSequence readChars(@Nullable ProgressIndicator indicator) throws IOException { - return connect(request -> request.readChars(indicator)); - } - - @NotNull - public CharSequence readChars() throws IOException { - return readChars(null); - } - - public void write(@NotNull String data) throws IOException { - connect(request -> { - request.write(data); - return null; - }); - } - - public void write(byte[] data) throws IOException { - connect(request -> { - request.write(data); - return null; - }); - } -} \ No newline at end of file diff --git a/src/main/java/com/shuzijun/leetcode/plugin/window/HttpLogin.java b/src/main/java/com/shuzijun/leetcode/plugin/window/HttpLogin.java index 66717772..13ef196f 100644 --- a/src/main/java/com/shuzijun/leetcode/plugin/window/HttpLogin.java +++ b/src/main/java/com/shuzijun/leetcode/plugin/window/HttpLogin.java @@ -33,7 +33,7 @@ public static boolean ajaxLogin(Config config, JTree tree, Project project) { return Boolean.FALSE; } - if (StringUtils.isBlank(PersistentConfig.getInstance().getPassword())) { + if (StringUtils.isBlank(PersistentConfig.getInstance().getPassword(config.getLoginName()))) { return Boolean.FALSE; } @@ -41,7 +41,7 @@ public static boolean ajaxLogin(Config config, JTree tree, Project project) { HttpEntity ent = MultipartEntityBuilder.create() .addTextBody("csrfmiddlewaretoken", HttpRequestUtils.getToken()) .addTextBody("login", config.getLoginName()) - .addTextBody("password", PersistentConfig.getInstance().getPassword()) + .addTextBody("password", PersistentConfig.getInstance().getPassword(config.getLoginName())) .addTextBody("next", "/problems") .build(); HttpRequest httpRequest = HttpRequest.post(URLUtils.getLeetcodeLogin(), ent.getContentType().getValue()); diff --git a/src/main/java/com/shuzijun/leetcode/plugin/window/JcefLogin.java b/src/main/java/com/shuzijun/leetcode/plugin/window/JcefLogin.java index 5c41cc2c..0ec06d8a 100644 --- a/src/main/java/com/shuzijun/leetcode/plugin/window/JcefLogin.java +++ b/src/main/java/com/shuzijun/leetcode/plugin/window/JcefLogin.java @@ -1,9 +1,11 @@ package com.shuzijun.leetcode.plugin.window; import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.util.Disposer; -import com.intellij.openapi.wm.impl.IdeFrameImpl; -import com.intellij.ui.jcef.JBCefBrowser; +import com.intellij.ui.components.JBPanel; +import com.intellij.ui.components.JBScrollPane; +import com.intellij.ui.jcef.JCEFHtmlPanel; import com.shuzijun.leetcode.plugin.model.PluginConstant; import com.shuzijun.leetcode.plugin.utils.HttpRequestUtils; import com.shuzijun.leetcode.plugin.utils.LogUtils; @@ -15,11 +17,11 @@ import org.cef.handler.CefLoadHandlerAdapter; import org.cef.misc.BoolRef; import org.cef.network.CefCookie; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.awt.*; -import java.awt.event.WindowAdapter; -import java.awt.event.WindowEvent; import java.net.HttpCookie; import java.util.ArrayList; import java.util.List; @@ -40,88 +42,120 @@ public JcefLogin(Project project, JTree tree) { @Override public void loadComponent() { - Window activeFrame = IdeFrameImpl.getActiveFrame(); - if (activeFrame == null) { - return; + CookiePanel cookiePanel = new CookiePanel(project); + cookiePanel.show(); + } + + + private class CookiePanel extends DialogWrapper { + + private JPanel jpanel; + private LoginJCEFPanel loginJCEFPanel; + + public CookiePanel(Project project) { + super(project, Boolean.TRUE); + + jpanel = new JBPanel(); + jpanel.setLayout(new BorderLayout()); + loginJCEFPanel = new LoginJCEFPanel(); + loginJCEFPanel.getComponent().setMinimumSize(new Dimension(1000, 500)); + loginJCEFPanel.getComponent().setPreferredSize(new Dimension(1000, 500)); + jpanel.add(new JBScrollPane(loginJCEFPanel.getComponent(), JBScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JBScrollPane.HORIZONTAL_SCROLLBAR_NEVER), BorderLayout.CENTER); + setModal(true); + init(); + setTitle("login"); + loginJCEFPanel.loadURL(URLUtils.getLeetcodeLogin()); } - Rectangle bounds = activeFrame.getGraphicsConfiguration().getBounds(); - final JFrame frame = new IdeFrameImpl(); - frame.setTitle("JCEF login"); - frame.setAlwaysOnTop(true); - frame.setDefaultCloseOperation(2); - frame.setBounds(bounds.width / 4, bounds.height / 4, bounds.width / 2, bounds.height / 2); - frame.setLayout(new BorderLayout()); - loadJCEFComponent(frame); + @NotNull + @Override + protected Action getOKAction() { - frame.setVisible(true); + return null; + } - } + @NotNull + @Override + protected Action[] createActions() { + return new Action[]{}; + } - private void loadJCEFComponent(JFrame frame) { - final JBCefBrowser jbCefBrowser = new JBCefBrowser(URLUtils.getLeetcodeLogin()); + @Nullable + @Override + protected JComponent createCenterPanel() { + return jpanel; + } - frame.addWindowListener(new WindowAdapter() { - @Override - public void windowClosed(WindowEvent e) { - jbCefBrowser.getJBCefCookieManager().deleteCookies(URLUtils.leetcode, false); - jbCefBrowser.getJBCefCookieManager().deleteCookies(URLUtils.leetcodecn, false); - Disposer.dispose(jbCefBrowser); - } - }); + @Override + protected void dispose() { + Disposer.dispose(loginJCEFPanel); + super.dispose(); + } + } - jbCefBrowser.getJBCefClient().addLoadHandler(new CefLoadHandlerAdapter() { + private class LoginJCEFPanel extends JCEFHtmlPanel { - boolean successDispose = false; + private CefLoadHandlerAdapter cefLoadHandler; - @Override - public void onLoadError(CefBrowser browser, CefFrame frame, CefLoadHandler.ErrorCode errorCode, String errorText, String failedUrl) { - if (!successDispose) { - browser.executeJavaScript("alert('The page failed to load, please check the network and open it again')", PluginConstant.PLUGIN_ID, 0); + public LoginJCEFPanel() { + super("about:blank"); + getJBCefClient().addLoadHandler(cefLoadHandler = new CefLoadHandlerAdapter() { + + boolean successDispose = false; + + @Override + public void onLoadError(CefBrowser browser, CefFrame frame, CefLoadHandler.ErrorCode errorCode, String errorText, String failedUrl) { + if (!successDispose) { + browser.executeJavaScript("alert('The page failed to load, please check the network and open it again')", PluginConstant.PLUGIN_ID, 0); + } } - } - @Override - public void onLoadingStateChange(CefBrowser browser, boolean isLoading, boolean canGoBack, boolean canGoForward) { + @Override + public void onLoadingStateChange(CefBrowser browser, boolean isLoading, boolean canGoBack, boolean canGoForward) { - jbCefBrowser.getJBCefCookieManager().getCefCookieManager().visitAllCookies(new CefCookieVisitor() { + getJBCefBrowser(getCefBrowser()).getJBCefCookieManager().getCefCookieManager().visitAllCookies(new CefCookieVisitor() { - private List cookieList = new ArrayList<>(); + private List cookieList = new ArrayList<>(); - @Override - public boolean visit(CefCookie cefCookie, int count, int total, BoolRef boolRef) { + @Override + public boolean visit(CefCookie cefCookie, int count, int total, BoolRef boolRef) { - boolean isSession = Boolean.FALSE; - if (cefCookie.domain.contains("leetcode")) { - HttpCookie cookie = new HttpCookie(cefCookie.name, cefCookie.value); - cookie.setDomain(cefCookie.domain); - cookie.setPath(cefCookie.path); - cookieList.add(cookie); - if ("LEETCODE_SESSION".equals(cefCookie.name)) { - isSession = Boolean.TRUE; + boolean isSession = Boolean.FALSE; + if (cefCookie.domain.contains("leetcode")) { + HttpCookie cookie = new HttpCookie(cefCookie.name, cefCookie.value); + cookie.setDomain(cefCookie.domain); + cookie.setPath(cefCookie.path); + cookieList.add(cookie); + if ("LEETCODE_SESSION".equals(cefCookie.name)) { + isSession = Boolean.TRUE; + } } - } - if (count == total - 1 && isSession) { - HttpRequestUtils.setCookie(cookieList); - if (HttpRequestUtils.isLogin()) { - HttpLogin.loginSuccess(tree, project, cookieList); - //browser.executeJavaScript("alert('Login is successful. close the window')", "leetcode-editor", 0); - successDispose = true; - frame.dispose(); - } else { - cookieList.clear(); - LogUtils.LOG.info("login failure"); + if (count == total - 1 && isSession) { + HttpRequestUtils.setCookie(cookieList); + if (HttpRequestUtils.isLogin()) { + HttpLogin.loginSuccess(tree, project, cookieList); + browser.executeJavaScript("alert('Login is successful. close the window')", "leetcode-editor", 0); + successDispose = true; + } else { + cookieList.clear(); + LogUtils.LOG.info("login failure"); + } } + return true; } - return true; - } - }); - } - }, jbCefBrowser.getCefBrowser()); - - frame.add(jbCefBrowser.getComponent(), BorderLayout.CENTER); + }); + } + }, getCefBrowser()); + } + @Override + public void dispose() { + getJBCefClient().removeLoadHandler(cefLoadHandler,getCefBrowser()); + getJBCefBrowser(getCefBrowser()).getJBCefCookieManager().deleteCookies(URLUtils.leetcode, false); + getJBCefBrowser(getCefBrowser()).getJBCefCookieManager().deleteCookies(URLUtils.leetcodecn, false); + super.dispose(); + } } } diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index a2927dcb..480265fa 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -117,276 +117,13 @@ -

  • v6.9
    - 1.修复兼容性.
    -
  • -
  • v6.9
    - 1.fix Compatibility.
    -
  • -
  • v6.8
    - 1.优化登录提示.
    - 2.支持2020.2+提供的JCEF.
    - 3.修复查看提交.
    -
  • -
  • v6.8
    - 1.Optimize login prompts.
    - 2.Support for 2020.2+ provided JCEF.
    - 3.fix submission.
    -
  • -
  • v6.7
    - 1.优化答案视图.
    - 2.增加支持多提交区域.
    - 3.固定每日一题.
    -
  • -
  • v6.7
    - 1.optimize solution view.
    - 2.Added support for multiple submission ranges.
    - 3.pin day question.
    -
  • -
  • v6.6
    - 1.修复图标兼容性.
    -
  • -
  • v6.6
    - 1.Fix icon compatibility.
    -
  • -
  • v6.5
    - 1.更换图标.
    - 2.增加定位题目功能.
    - 3.增加运行失败打印测试用例.
    - 4.增加获取当前时间工具.
    - 5.增加展示赞同数.
    - 6.增加筛选选项.
    - 7.修复探索题目题目号.
    -
  • -
  • v6.5
    - 1.Change the icon.
    - 2.Add positioning function.
    - 3.Add running errors increase the print test case.
    - 4.Add current time function.
    - 5.Add show likes dislikes.
    - 6.Add screening criteria.
    - 7.Fix explore question.
    -
  • -
  • v6.4
    - 1.替换代理方式,与IDE保持一致.
    - 2.增加转换蛇形命名.
    - 3.导航栏增加展示登录人名称.
    - 4.增加查看解决方案.
    - 5.增加查看进展.
    -
  • -
  • v6.4
    - 1.Replace the proxy mode.
    - 2.add snakeCaseName.
    - 3.Navigation bar displays login information.
    - 4.Add open solution.
    - 5.add Progress.
    -
  • -
    -
  • v6.3
    - 1.修复Python提交代码区域错误问题.
    - 2.修复使用快捷键时提示问题.(建议将快捷键绑定到editor右键菜单上)
    -
  • -
  • v6.3
    - 1.Fixed Python commit code area error.
    - 2.Fix the prompt problem when using shortcut keys. (It is recommended to bind shortcut keys to the editor right-click menu)
    -
  • -
  • v6.2
    - 1.修复计时器显示错误.
    - 2.修复提交问题时异常.
    - 3.修复IDE低版本部分功能不支持出现异常
    -
  • -
  • v6.2
    - 1.fix wrong minute elapsed of timer.
    - 2.Fix the submit failure prompt.
    - 3.Fix IDE low version exception
    -
  • -
  • v6.1
    - 1.增加 Editor 右键菜单.
    - 2.增加 Bash SQL 支持.
    - 3.增加支持展示英文描述(leetcode-cn.com).
    - 4.增加计时器,统计做题时间.
    - 5.修复通知范围问题. -
  • -
  • v6.1
    - 1.Add editor menu.
    - 2.Add Bash SQL support.
    - 3.Add English description(leetcode-cn.com).
    - 4.Add timer.
    - 5.Repair notification scope. -
  • -
  • v6.0
    - 1.修改leetcode.com登录方式, 参考
    - 2.修复快捷键无法使用问题
    - 3.修复题目名称不展示中文问题
    - 4.增加提交时自动保存
    - 5.增加结果stdout信息
    - 6.增加自定义题目颜色
    - 7.优化题目描述超长
    -
  • -
  • v6.0
    - 1.Modify leetcode.com login method, help
    - 2.Fix the problem that the shortcut keys cannot be used
    - 3.Fix the problem that the title name does not show Chinese
    - 4.Automatically save when adding commits
    - 5.Increase result stdout information
    - 6.Add custom title color
    - 7.Optimize the title description
    -
  • -
  • v5.4
    - 1.修复leetcode.com Run Code 超时问题
    -
  • -
  • v5.4
    - 1.fix leetcode.com RunCode Wait result timeout
    -
  • -
  • v5.2
    - 1.修复登陆提示
    - 2.修复leetcode-cn列表无法展示问题
    -
  • -
  • v5.2
    - 1.fix login warn
    - 2.fix leetcode-cn list
    -
  • -
  • v5.1
    - 1.修复登陆提示
    - 2.修复favorite图标
    -
  • -
  • v5.1
    - 1.fix login warn
    - 2.fix favorite icon
    -
  • -
  • v5.0
    - 1.增加代码自定义生成(详细介绍)(示例)
    - 2.增加查看题目描述(依赖 Markdown)
    -
  • -
  • v5.0
    - 1.add code custom(details)(demo)
    - 2.add show content(Rely on Markdown) -
  • -
  • v4.3
    - 1.修复付费用户订阅题目展示
    - 2.修复leetcode-cn.com登陆问题
    - 3.增加跳转到题目对应的leetcode官网地址的功能 @zzdcon
    -
  • -
  • v4.3
    - 1.Fix subscription view
    - 2.fix leetcode-cn.com login
    - 3.add a new feature of browsing certain question on the website of leetcode @zzdcon
    -
  • - -
  • v4.2
    - 1.增加代理
    - 2.修复订阅题目展示
    -
  • -
  • v4.2
    - 1.proxy
    - 2.Fix subscription view
    -
  • - -
  • v4.1
    - 1.修复部分bug
    - 2.增加电子邮件未验证提醒
    -
  • -
  • v4.1
    - 1.Fix bugs
    - 2.add verify email info
    -
  • - -
  • v4.0
    - 1.增加tag筛选
    - 2.添加收藏功能
    -
  • -
  • v4.0
    - 1.add tag filtrate
    - 2.add Favorite
    -
  • -
  • v3.5
    - 1.修复运行错误提示
    -
  • -
  • v3.5
    - 1.Fix compile error info
    -
  • - -
  • v3.4
    - 1.修复题目编号与网页显示不一致
    -
  • -
  • v3.4
    - 1.Fix question number is inconsistent with web page display
    -
  • - -
  • v3.3
    - 1.解决UI阻塞问题
    -
  • -
  • v3.3
    - 1.Fix UI blocking
    -
  • - -
  • v3.2
    - 1.修复搜索定位问题
    -
  • -
  • v3.2
    - 1.Fix search positioning
    -
  • -
  • v3.1
    - 1.添加列表查看节点
    -
  • -
  • v3.1
    - 1.add Lists
    -
  • - -
  • v3.0
    - 1.增加查看提交记录
    - 2.增加自定义测试用例
    - 3.更换UI
    -
  • -
  • v3.0
    - 1.add show Submissions
    - 2.add custom test case
    - 3.update UI
    -
  • - -
  • v2.1
    - 1.增加英语提示
    -
  • -
  • v2.1
    - 1.add English tips
    -
  • -
    -
  • v2.0
    - 1.增加探索分类,只支持题目
    - 2.增加难度与标签分类
    - 3.增加搜索功能
    - 4.增加支持语言类型
    - 5.优化题目描述展示
    -
  • -
  • v2.0
    - 1.add explore
    - 2.add difficulty and tags
    - 3.add query
    - 4.add code type
    - 5.optimize comment
    -
  • -
    -
    -
  • v1.0
    - 1.支持leetcode.com和leetcode-cn.com
    - 2.支持获取题目与编辑
    - 3.支持测试
    - 4.支持提交
    -
  • - -
  • v1.0
    - 1.Support leetcode.com and leetcode-cn.com
    - 2.pull and edit
    - 3.test
    - 4.submit
    -
  • ]]> - + @@ -407,11 +144,9 @@ serviceImplementation="com.shuzijun.leetcode.plugin.setting.PersistentConfig"/> - - +