Search in sources :

Example 1 with DiscordException

use of org.javacord.api.exception.DiscordException in project Javacord by BtoBastian.

the class RestRequest method executeBlocking.

/**
 * Executes the request blocking.
 *
 * @return The result of the request.
 * @throws Exception If something went wrong while executing the request.
 */
public RestRequestResult executeBlocking() throws Exception {
    api.getGlobalRatelimiter().ifPresent(ratelimiter -> {
        try {
            ratelimiter.requestQuota();
        } catch (InterruptedException e) {
            logger.warn("Encountered unexpected ratelimiter interrupt", e);
        }
    });
    Request.Builder requestBuilder = new Request.Builder();
    HttpUrl.Builder httpUrlBuilder = endpoint.getOkHttpUrl(urlParameters).newBuilder();
    queryParameters.forEach(httpUrlBuilder::addQueryParameter);
    requestBuilder.url(httpUrlBuilder.build());
    RequestBody requestBody;
    if (multipartBody != null) {
        requestBody = multipartBody;
    } else if (body != null) {
        requestBody = RequestBody.create(MediaType.parse("application/json"), body);
    } else {
        requestBody = RequestBody.create(null, new byte[0]);
    }
    switch(method) {
        case GET:
            requestBuilder.get();
            break;
        case POST:
            requestBuilder.post(requestBody);
            break;
        case PUT:
            requestBuilder.put(requestBody);
            break;
        case DELETE:
            requestBuilder.delete(requestBody);
            break;
        case PATCH:
            requestBuilder.patch(requestBody);
            break;
        default:
            throw new IllegalArgumentException("Unsupported http method!");
    }
    if (includeAuthorizationHeader) {
        requestBuilder.addHeader("authorization", api.getPrefixedToken());
    }
    headers.forEach(requestBuilder::addHeader);
    logger.debug("Trying to send {} request to {}{}", method::name, () -> endpoint.getFullUrl(urlParameters), () -> body != null ? " with body " + body : "");
    try (Response response = getApi().getHttpClient().newCall(requestBuilder.build()).execute()) {
        RestRequestResult result = new RestRequestResult(this, response);
        logger.debug("Sent {} request to {} and received status code {} with{} body{}", method::name, () -> endpoint.getFullUrl(urlParameters), response::code, () -> result.getBody().map(b -> "").orElse(" empty"), () -> result.getStringBody().map(s -> " " + s).orElse(""));
        if (response.code() >= 300 || response.code() < 200) {
            RestRequestInformation requestInformation = asRestRequestInformation();
            RestRequestResponseInformation responseInformation = new RestRequestResponseInformationImpl(requestInformation, result);
            Optional<RestRequestHttpResponseCode> responseCode = RestRequestHttpResponseCode.fromCode(response.code());
            // Check if the response body contained a know error code
            if (!result.getJsonBody().isNull() && result.getJsonBody().has("code")) {
                int code = result.getJsonBody().get("code").asInt();
                String message = result.getJsonBody().has("message") ? result.getJsonBody().get("message").asText() : null;
                Optional<? extends DiscordException> discordException = RestRequestResultErrorCode.fromCode(code, responseCode.orElse(null)).flatMap(restRequestResultCode -> restRequestResultCode.getDiscordException(origin, (message == null) ? restRequestResultCode.getMeaning() : message, requestInformation, responseInformation));
                // There's an exception for this specific response code
                if (discordException.isPresent()) {
                    throw discordException.get();
                }
            }
            switch(response.code()) {
                case 429:
                    // A 429 will be handled in the RatelimitManager class
                    return result;
                default:
                    // There are specific exceptions for specific response codes (e.g. NotFoundException for 404)
                    Optional<? extends DiscordException> discordException = responseCode.flatMap(restRequestHttpResponseCode -> restRequestHttpResponseCode.getDiscordException(origin, "Received a " + response.code() + " response from Discord with" + (result.getBody().isPresent() ? "" : " empty") + " body" + result.getStringBody().map(s -> " " + s).orElse("") + "!", requestInformation, responseInformation));
                    if (discordException.isPresent()) {
                        throw discordException.get();
                    } else {
                        // No specific exception was defined for the response code, so throw a "normal"
                        throw new DiscordException(origin, "Received a " + response.code() + " response from Discord with" + (result.getBody().isPresent() ? "" : " empty") + " body" + result.getStringBody().map(s -> " " + s).orElse("") + "!", requestInformation, responseInformation);
                    }
            }
        }
        return result;
    }
}
Also used : DiscordException(org.javacord.api.exception.DiscordException) Request(okhttp3.Request) RestRequestResponseInformation(org.javacord.api.util.rest.RestRequestResponseInformation) MalformedURLException(java.net.MalformedURLException) URL(java.net.URL) HashMap(java.util.HashMap) CompletableFuture(java.util.concurrent.CompletableFuture) DiscordApiImpl(org.javacord.core.DiscordApiImpl) Function(java.util.function.Function) RequestBody(okhttp3.RequestBody) LoggerUtil(org.javacord.core.util.logging.LoggerUtil) Logger(org.apache.logging.log4j.Logger) MultipartBody(okhttp3.MultipartBody) DiscordApi(org.javacord.api.DiscordApi) Map(java.util.Map) Response(okhttp3.Response) Optional(java.util.Optional) JsonNode(com.fasterxml.jackson.databind.JsonNode) HttpUrl(okhttp3.HttpUrl) MediaType(okhttp3.MediaType) RestRequestInformation(org.javacord.api.util.rest.RestRequestInformation) Request(okhttp3.Request) RestRequestInformation(org.javacord.api.util.rest.RestRequestInformation) HttpUrl(okhttp3.HttpUrl) Response(okhttp3.Response) DiscordException(org.javacord.api.exception.DiscordException) RestRequestResponseInformation(org.javacord.api.util.rest.RestRequestResponseInformation) RequestBody(okhttp3.RequestBody)

Example 2 with DiscordException

use of org.javacord.api.exception.DiscordException in project Javacord by BtoBastian.

the class RatelimitManager method queueRequest.

/**
 * Queues the given request.
 * This method is automatically called when using {@link RestRequest#execute(Function)}!
 *
 * @param request The request to queue.
 */
public void queueRequest(RestRequest<?> request) {
    final RatelimitBucket bucket;
    final boolean alreadyInQueue;
    synchronized (buckets) {
        // Search for a bucket that fits to this request
        bucket = buckets.stream().filter(b -> b.equals(request.getEndpoint(), request.getMajorUrlParameter().orElse(null))).findAny().orElseGet(() -> new RatelimitBucket(api, request.getEndpoint(), request.getMajorUrlParameter().orElse(null)));
        // Must be executed BEFORE adding the request to the queue
        alreadyInQueue = bucket.peekRequestFromQueue() != null;
        // Add the bucket to the set of buckets (does nothing if it's already in the set)
        buckets.add(bucket);
        // Add the request to the bucket's queue
        bucket.addRequestToQueue(request);
    }
    // If the bucket is already in the queue, there's nothing more to do
    if (alreadyInQueue) {
        return;
    }
    // Start working of the queue
    api.getThreadPool().getExecutorService().submit(() -> {
        RestRequest<?> currentRequest = bucket.peekRequestFromQueue();
        RestRequestResult result = null;
        long responseTimestamp = System.currentTimeMillis();
        while (currentRequest != null) {
            try {
                int sleepTime = bucket.getTimeTillSpaceGetsAvailable();
                if (sleepTime > 0) {
                    logger.debug("Delaying requests to {} for {}ms to prevent hitting ratelimits", bucket, sleepTime);
                }
                // Sleep until space is available
                while (sleepTime > 0) {
                    try {
                        Thread.sleep(sleepTime);
                    } catch (InterruptedException e) {
                        logger.warn("We got interrupted while waiting for a rate limit!", e);
                    }
                    // Update in case something changed (e.g. because we hit a global ratelimit)
                    sleepTime = bucket.getTimeTillSpaceGetsAvailable();
                }
                // Execute the request
                result = currentRequest.executeBlocking();
                // Calculate the time offset, if it wasn't done before
                responseTimestamp = System.currentTimeMillis();
            } catch (Throwable t) {
                responseTimestamp = System.currentTimeMillis();
                if (currentRequest.getResult().isDone()) {
                    logger.warn("Received exception for a request that is already done. " + "This should not be able to happen!", t);
                }
                // Try to get the response from the exception if it exists
                if (t instanceof DiscordException) {
                    result = ((DiscordException) t).getResponse().map(RestRequestResponseInformationImpl.class::cast).map(RestRequestResponseInformationImpl::getRestRequestResult).orElse(null);
                }
                // Complete the request
                currentRequest.getResult().completeExceptionally(t);
            } finally {
                try {
                    // Calculate offset
                    calculateOffset(responseTimestamp, result);
                    // Handle the response
                    handleResponse(currentRequest, result, bucket, responseTimestamp);
                } catch (Throwable t) {
                    logger.warn("Encountered unexpected exception.", t);
                }
                // The request didn't finish, so let's try again
                if (!currentRequest.getResult().isDone()) {
                    continue;
                }
                // Poll a new quest
                synchronized (buckets) {
                    bucket.pollRequestFromQueue();
                    currentRequest = bucket.peekRequestFromQueue();
                    if (currentRequest == null) {
                        buckets.remove(bucket);
                    }
                }
            }
        }
    });
}
Also used : DiscordException(org.javacord.api.exception.DiscordException) Set(java.util.Set) CompletableFuture(java.util.concurrent.CompletableFuture) DiscordApiImpl(org.javacord.core.DiscordApiImpl) Function(java.util.function.Function) RestRequestResponseInformationImpl(org.javacord.core.util.rest.RestRequestResponseInformationImpl) HashSet(java.util.HashSet) LoggerUtil(org.javacord.core.util.logging.LoggerUtil) Logger(org.apache.logging.log4j.Logger) OffsetDateTime(java.time.OffsetDateTime) RestRequestResult(org.javacord.core.util.rest.RestRequestResult) DateTimeFormatter(java.time.format.DateTimeFormatter) RestRequest(org.javacord.core.util.rest.RestRequest) Response(okhttp3.Response) RestRequestResponseInformationImpl(org.javacord.core.util.rest.RestRequestResponseInformationImpl) DiscordException(org.javacord.api.exception.DiscordException) RestRequestResult(org.javacord.core.util.rest.RestRequestResult)

Aggregations

CompletableFuture (java.util.concurrent.CompletableFuture)2 Function (java.util.function.Function)2 Response (okhttp3.Response)2 Logger (org.apache.logging.log4j.Logger)2 DiscordException (org.javacord.api.exception.DiscordException)2 DiscordApiImpl (org.javacord.core.DiscordApiImpl)2 LoggerUtil (org.javacord.core.util.logging.LoggerUtil)2 JsonNode (com.fasterxml.jackson.databind.JsonNode)1 MalformedURLException (java.net.MalformedURLException)1 URL (java.net.URL)1 OffsetDateTime (java.time.OffsetDateTime)1 DateTimeFormatter (java.time.format.DateTimeFormatter)1 HashMap (java.util.HashMap)1 HashSet (java.util.HashSet)1 Map (java.util.Map)1 Optional (java.util.Optional)1 Set (java.util.Set)1 HttpUrl (okhttp3.HttpUrl)1 MediaType (okhttp3.MediaType)1 MultipartBody (okhttp3.MultipartBody)1