From c147a613cbd8382b41ea0a0dffff829736ca594a Mon Sep 17 00:00:00 2001 From: DmitriyMX Date: Mon, 10 Dec 2018 10:31:16 +0300 Subject: [PATCH] =?UTF-8?q?=D0=BE=D0=B1=D0=BD=D0=BE=D0=B2=D0=BB=D1=8F?= =?UTF-8?q?=D0=B5=D0=BC=20=D0=B0=D0=BB=D0=B3=D0=BE=D1=80=D0=B8=D1=82=D0=BC?= =?UTF-8?q?=D1=8B=20+=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D1=8F=D0=B5?= =?UTF-8?q?=D0=BC=20=D0=B0=D1=81=D0=B8=D0=BD=D1=85=D1=80=D0=B0=D0=BD=D0=BD?= =?UTF-8?q?=D0=BE=D0=B9=20=D0=B2=D1=8B=D0=B7=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/ru/dmitriymx/vkapi/Callback.java | 8 + .../java/ru/dmitriymx/vkapi/TaskMethod.java | 16 ++ src/main/java/ru/dmitriymx/vkapi/Timer.java | 21 +++ src/main/java/ru/dmitriymx/vkapi/VkApi.java | 164 ++++++++++-------- .../ru/dmitriymx/vkapi/VkApiException.java | 13 -- .../ru/dmitriymx/vkapi/longpoll/LPRunner.java | 18 +- .../vkapi/longpoll/LongPollListener.java | 32 ++-- 7 files changed, 162 insertions(+), 110 deletions(-) create mode 100644 src/main/java/ru/dmitriymx/vkapi/Callback.java create mode 100644 src/main/java/ru/dmitriymx/vkapi/TaskMethod.java create mode 100644 src/main/java/ru/dmitriymx/vkapi/Timer.java delete mode 100644 src/main/java/ru/dmitriymx/vkapi/VkApiException.java diff --git a/src/main/java/ru/dmitriymx/vkapi/Callback.java b/src/main/java/ru/dmitriymx/vkapi/Callback.java new file mode 100644 index 0000000..70d43d3 --- /dev/null +++ b/src/main/java/ru/dmitriymx/vkapi/Callback.java @@ -0,0 +1,8 @@ +package ru.dmitriymx.vkapi; + +import com.google.gson.JsonObject; + +public interface Callback { + void call(JsonObject jsonObject); + void error(Throwable throwable); +} diff --git a/src/main/java/ru/dmitriymx/vkapi/TaskMethod.java b/src/main/java/ru/dmitriymx/vkapi/TaskMethod.java new file mode 100644 index 0000000..5aee269 --- /dev/null +++ b/src/main/java/ru/dmitriymx/vkapi/TaskMethod.java @@ -0,0 +1,16 @@ +package ru.dmitriymx.vkapi; + +import lombok.Builder; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import java.util.Map; + +@Builder +@RequiredArgsConstructor +@Getter +class TaskMethod { + private final String method; + private final Map postData; + private final Callback callback; +} diff --git a/src/main/java/ru/dmitriymx/vkapi/Timer.java b/src/main/java/ru/dmitriymx/vkapi/Timer.java new file mode 100644 index 0000000..7abc8ca --- /dev/null +++ b/src/main/java/ru/dmitriymx/vkapi/Timer.java @@ -0,0 +1,21 @@ +package ru.dmitriymx.vkapi; + +class Timer { + private long lastTime; + + void reset() { + this.lastTime = System.currentTimeMillis(); + } + + boolean after(long expendedDiffTime) { + return diff() >= expendedDiffTime; + } + + boolean before(long expendedDiffTime) { + return diff() < expendedDiffTime; + } + + long diff() { + return System.currentTimeMillis() - lastTime; + } +} diff --git a/src/main/java/ru/dmitriymx/vkapi/VkApi.java b/src/main/java/ru/dmitriymx/vkapi/VkApi.java index 7542d7e..a02828c 100644 --- a/src/main/java/ru/dmitriymx/vkapi/VkApi.java +++ b/src/main/java/ru/dmitriymx/vkapi/VkApi.java @@ -5,106 +5,130 @@ import com.google.gson.JsonObject; import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; +import ru.dmitriymx.vkapi.browser.ApacheBroswe; import ru.dmitriymx.vkapi.browser.Browser; import ru.dmitriymx.vkapi.browser.Response; +import ru.dmitriymx.vkapi.longpoll.LongPollListener; import java.io.IOException; +import java.text.MessageFormat; +import java.util.Collections; import java.util.Map; -import java.util.stream.Collectors; +import java.util.StringJoiner; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.SynchronousQueue; @Slf4j -public class VkApi { +public class VkApi implements Runnable { private static final String VKAPI_URL = "https://api.vk.com/method/"; private final String accessToken; private final Browser browser; - private final Gson gson; + private final Gson gson = new Gson(); @Getter @Setter - private String apiVersion = "5.62"; - @Getter - @Setter - private long callPause = 1200L; - private long lastTime; - private int call = 0; + private String apiVersion; + + private Timer timer = new Timer(); + private int countCallApiPerSecond = 0; + + private BlockingQueue queueExecute = new SynchronousQueue<>(); + + public VkApi(String apiVersion, String accessToken) { + this(apiVersion, accessToken, new ApacheBroswe()); + } + + public VkApi(String apiVersion, String accessToken, Browser browser) { + if (stringIsNullOrEmpty(apiVersion)) throw new IllegalArgumentException("API version don't be NULL or EMPTY!"); + if (stringIsNullOrEmpty(accessToken)) throw new IllegalArgumentException("Access token don't be NULL or EMPTY!"); + if (browser == null) throw new IllegalArgumentException("Browser don't be NULL or EMPTY!"); - public VkApi(String accessToken, Browser browser) { - if (accessToken == null || accessToken.trim().isEmpty()) { - throw new RuntimeException("Access token don't be NULL or EMPTY!"); - } this.accessToken = accessToken; this.browser = browser; - this.gson = new Gson(); + + timer.reset(); } - public JsonObject execApi(String methodApi, Map params) throws VkApiException { - checkCalls(); - String url = VKAPI_URL + methodApi; - String postData = paramsToString(params); - - Response response; - try { - response = browser.post(url, postData); - } catch (IOException e) { - throw new RuntimeException(e); + public JsonObject execApi(String methodApi, Map params) throws IOException { + if (timer.before(1000L) && countCallApiPerSecond >= 3) { + safeSleep(timer.diff() + 100); + timer.reset(); + countCallApiPerSecond = 0; + } else if (timer.after(1000L)) { + timer.reset(); + countCallApiPerSecond = 0; } + countCallApiPerSecond++; + Response response = browser.post(VKAPI_URL + methodApi, paramsToString(params)); chechResponse(response); - JsonObject jsonObject = gson.fromJson(response.getContent(), JsonObject.class); - if (jsonObject.has("error")) { - throw new VkApiException(jsonObject); - } - - return jsonObject; + return gson.fromJson(response.getContent(), JsonObject.class); } - public JsonObject longExecApi(String server, String key, long ts, long wait) throws VkApiException { + public void execApiAsync(String apiMethod, Map params, Callback callback) { + queueExecute.offer(TaskMethod.builder() + .method(apiMethod) + .postData(params) + .callback(callback) + .build()); + } + + public LongPollListener getLongPollListener() throws IOException { + JsonObject jsonObject = execApi("messages.getLongPollServer", Collections.singletonMap("need_pts", "1")); + jsonObject = jsonObject.getAsJsonObject("response"); + + return new LongPollListener( + this, + jsonObject.get("server").getAsString(), + jsonObject.get("key").getAsString(), + jsonObject.get("ts").getAsLong() + ); + } + + @Override + public void run() { + while(!Thread.currentThread().isInterrupted()) { + try { + TaskMethod task = queueExecute.take(); + + try { + JsonObject jsonObject = execApi(task.getMethod(), task.getPostData()); + + if (task.getCallback() != null) { + task.getCallback().call(jsonObject); + } + } catch (IOException e) { + if (task.getCallback() != null) { + task.getCallback().error(e); + } else { + log.error(MessageFormat.format( + "Error execute method \"{0}\"", + task.getMethod()), e); + } + } + } catch (InterruptedException e) { + break; + } + } + } + + public JsonObject longExecApi(String server, String key, long ts, long wait) throws IOException { String url = String.format("https://%s?act=a_check&key=%s&ts=%d&wait=%d&mode=2&version=1", - server, key, ts, wait + server, key, ts, wait ); - Response response; - try { - response = browser.get(url); - } catch (IOException e) { - throw new RuntimeException(e); - } - + Response response = browser.get(url); chechResponse(response); - JsonObject jsonObject = gson.fromJson(response.getContent(), JsonObject.class); - if (jsonObject.has("error")) { - throw new VkApiException(jsonObject); - } - - return jsonObject; + return gson.fromJson(response.getContent(), JsonObject.class); } private String paramsToString(Map params) { - String collect = ""; - if (params != null && params.size() > 0) { - collect = "&" + params.entrySet().stream() - .map(entry -> entry.getKey() + "=" + entry.getValue()) - .collect(Collectors.joining("&")); - } - return "access_token=" + accessToken + "&v=" + apiVersion + collect; - } + final StringJoiner sj = new StringJoiner("&"); + sj.add("access_token=" + accessToken).add("v=" + apiVersion); + params.forEach((key, value) -> sj.add(key + "=" + value)); - private void checkCalls() { - if (call >= 3) { - long currTime = System.currentTimeMillis(); - long diff = currTime - lastTime; - - if (diff <= callPause) { - safeSleep(callPause); - } - - lastTime = System.currentTimeMillis(); - call = 0; - } else { - safeSleep(callPause/3); - call++; - } + return sj.toString(); } private void chechResponse(Response response) { @@ -131,4 +155,8 @@ public class VkApi { // ignore } } + + private static boolean stringIsNullOrEmpty(String string) { + return string == null || string.isEmpty(); + } } diff --git a/src/main/java/ru/dmitriymx/vkapi/VkApiException.java b/src/main/java/ru/dmitriymx/vkapi/VkApiException.java deleted file mode 100644 index d732bbd..0000000 --- a/src/main/java/ru/dmitriymx/vkapi/VkApiException.java +++ /dev/null @@ -1,13 +0,0 @@ -package ru.dmitriymx.vkapi; - -import com.google.gson.JsonObject; - -public class VkApiException extends Exception { - public VkApiException(String message) { - super(message); - } - - public VkApiException(JsonObject jsonObject) { - this(jsonObject.getAsJsonObject("error").get("error_msg").getAsString()); - } -} diff --git a/src/main/java/ru/dmitriymx/vkapi/longpoll/LPRunner.java b/src/main/java/ru/dmitriymx/vkapi/longpoll/LPRunner.java index 854b8a3..5338656 100644 --- a/src/main/java/ru/dmitriymx/vkapi/longpoll/LPRunner.java +++ b/src/main/java/ru/dmitriymx/vkapi/longpoll/LPRunner.java @@ -2,12 +2,11 @@ package ru.dmitriymx.vkapi.longpoll; import com.google.gson.JsonArray; import com.google.gson.JsonObject; +import lombok.Setter; import lombok.extern.slf4j.Slf4j; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import ru.dmitriymx.vkapi.VkApi; -import ru.dmitriymx.vkapi.VkApiException; +import java.io.IOException; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -19,6 +18,8 @@ class LPRunner implements Runnable { private final String server; private final String key; private long ts; + @Setter + private long timeOut; Map, List> mapListeners = new HashMap<>(); LPRunner(VkApi vkApi, String server, String key, long ts) { @@ -31,13 +32,10 @@ class LPRunner implements Runnable { @Override public void run() { while (!Thread.currentThread().isInterrupted()) { - log.debug("next loop..."); + log.debug("next loop"); try { - JsonObject jsonObject = vkApi.longExecApi( - this.server, this.key, this.ts, - 25L - ); + JsonObject jsonObject = vkApi.longExecApi(server, key, ts, timeOut); JsonArray updates = jsonObject.getAsJsonArray("updates"); if (updates.size() > 0) { @@ -47,8 +45,8 @@ class LPRunner implements Runnable { } this.ts = jsonObject.get("ts").getAsLong(); - } catch (VkApiException e) { - log.error("Oops!", e); + } catch (IOException e) { + log.error("Execute long poll error", e); } } } diff --git a/src/main/java/ru/dmitriymx/vkapi/longpoll/LongPollListener.java b/src/main/java/ru/dmitriymx/vkapi/longpoll/LongPollListener.java index c0b8e8b..1e1631a 100644 --- a/src/main/java/ru/dmitriymx/vkapi/longpoll/LongPollListener.java +++ b/src/main/java/ru/dmitriymx/vkapi/longpoll/LongPollListener.java @@ -1,33 +1,23 @@ package ru.dmitriymx.vkapi.longpoll; -import com.google.gson.JsonObject; import ru.dmitriymx.vkapi.VkApi; -import ru.dmitriymx.vkapi.VkApiException; -import java.util.*; +import java.util.ArrayList; +import java.util.List; public class LongPollListener { - private Thread thread; - private LPRunner lpRunner; + private final Thread thread; + private final LPRunner lpRunner; - public static LongPollListener create(VkApi vkApi) throws VkApiException { - JsonObject jsonObject = vkApi.execApi("messages.getLongPollServer", - Collections.singletonMap("need_pts", "1")); - jsonObject = jsonObject.getAsJsonObject("response"); - - return new LongPollListener( - vkApi, - jsonObject.get("server").getAsString(), - jsonObject.get("key").getAsString(), - jsonObject.get("ts").getAsLong() - ); - } - - private LongPollListener(VkApi vkApi, String server, String key, long ts) { + public LongPollListener(VkApi vkApi, String server, String key, long ts) { this.lpRunner = new LPRunner(vkApi, server, key, ts); this.thread = new Thread(this.lpRunner, "Long poll listener"); } + public void setTimeOut(long timeOut) { + lpRunner.setTimeOut(timeOut); + } + public void start() { start(false); } @@ -43,6 +33,10 @@ public class LongPollListener { } } + public void stop() { + thread.interrupt(); + } + public void register(Class clazz, EventListener listener) { List eventListeners = lpRunner.mapListeners.computeIfAbsent(clazz, list -> new ArrayList<>()); eventListeners.add(listener);