Archived
0

обновляем алгоритмы + добавляем асинхранной вызов

This commit is contained in:
2018-12-10 10:31:16 +03:00
parent 2a414b0740
commit c147a613cb
7 changed files with 162 additions and 110 deletions

View File

@@ -0,0 +1,8 @@
package ru.dmitriymx.vkapi;
import com.google.gson.JsonObject;
public interface Callback {
void call(JsonObject jsonObject);
void error(Throwable throwable);
}

View File

@@ -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<String, String> postData;
private final Callback callback;
}

View File

@@ -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;
}
}

View File

@@ -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<TaskMethod> 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<String, String> 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<String, String> 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<String, String> 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<String, String> 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();
}
}

View File

@@ -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());
}
}

View File

@@ -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<Class<? extends Event>, List<EventListener>> 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);
}
}
}

View File

@@ -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<? extends Event> clazz, EventListener listener) {
List<EventListener> eventListeners = lpRunner.mapListeners.computeIfAbsent(clazz, list -> new ArrayList<>());
eventListeners.add(listener);