Example usage for com.fasterxml.jackson.core JsonProcessingException toString

List of usage examples for com.fasterxml.jackson.core JsonProcessingException toString

Introduction

In this page you can find the example usage for com.fasterxml.jackson.core JsonProcessingException toString.

Prototype

@Override
    public String toString() 

Source Link

Usage

From source file:com.smartsheet.tin.filters.common.ORCFormatter.java

/**
 * Create the JSON entry for a row from a OneRowChange.
 * Whether the row_vals are ColumnVals or KeyVals depends on whether the
 * OneRowChange was for an INSERT/UPDATE (Column values) or a DELETE (Key
 * values).//  ww  w.j a  v a 2 s.c  o  m
 * 
 * @param msg_pojo The partially filled out object to add the key to.
 * @param tki Specifies which columns are keys.
 * @param pos2idx Mapping between key columns and the indexes of row_vals.
 * @param row_vals These can be the Column or Key values.
 * @return The JSON string representation of the msg_pojo.
 */
private String makeRowEntry(ORCPrimaryKeyInfo msg_pojo, TableKeyInfo tki, Position2Index pos2idx,
        ArrayList<ColumnVal> row_vals) {
    try {
        // Discard the key values for any previous row.
        msg_pojo.resetKey();

        for (TableKeyInfo.KeyPair kp : tki.getKeys()) {
            int idx = kp.getIndex();
            logger.debug(String.format("Key column '%s' at idx: %d pos2idx.get(%d)=%d", kp.getColumnName(), idx,
                    idx, pos2idx.getIndex(idx)));
            msg_pojo.addKey(kp.getColumnName(), kp.getColumnType(), kp.getColumnTypeDesc(),
                    row_vals.get(pos2idx.getIndex(idx)).getValue());
        }
        try {
            return this.mapper.writeValueAsString(msg_pojo);
        } catch (JsonProcessingException e) {
            logger.error("Failed using mapper to write JSON:", e);
            return msg_pojo.toBasicJSON();
        }
    } catch (Exception e) {
        logger.error("Failed creating JSON row entry", e);
        // NOTE: This might not be valid JSON, depends on the error msg.
        return "{\"ERROR\": \"" + e.toString() + "\" }";
    }

}

From source file:org.wikidata.wdtk.wikibaseapi.WikibaseDataFetcher.java

/**
 * Creates a map of identifiers or page titles to documents retrieved via
 * the API URL.//from  w  w w. j  a  v  a  2 s.  c om
 *
 * @param numOfEntities
 *            number of entities that should be retrieved
 * @param url
 *            the API URL (with parameters)
 * @param siteKey
 *            null if the map keys should be document ids; siite key (e.g.,
 *            "enwiki") if the map should use page titles of a linked site
 *            as keys
 * @return map of document identifiers or titles to documents retrieved via
 *         the API URL
 */
Map<String, EntityDocument> getStringEntityDocumentMap(int numOfEntities, String url, String siteKey) {
    if (numOfEntities == 0 || url == null) {
        return Collections.<String, EntityDocument>emptyMap();
    }
    Map<String, EntityDocument> result = new HashMap<>(numOfEntities);

    try (InputStream inStream = this.webResourceFetcher.getInputStreamForUrl(url)) {

        JsonNode root = mapper.readTree(inStream);

        if (root.has("error")) {
            JsonNode errorNode = root.path("error");
            logger.error(
                    "Error when reading data from API: " + errorNode.path("info").asText("DESCRIPTION MISSING")
                            + " [" + errorNode.path("code").asText("UNKNOWN ERROR CODE") + "]");
        } // fall through: maybe there are some entities anyway

        JsonNode entities = root.path("entities");
        for (JsonNode entityNode : entities) {
            if (!entityNode.has("missing")) {
                try {
                    JacksonTermedStatementDocument ed = mapper.treeToValue(entityNode,
                            JacksonTermedStatementDocument.class);
                    ed.setSiteIri(this.siteIri);

                    if (siteKey == null) {
                        result.put(ed.getEntityId().getId(), ed);
                    } else {
                        if (ed instanceof JacksonItemDocument
                                && ((JacksonItemDocument) ed).getSiteLinks().containsKey(siteKey)) {
                            result.put(((JacksonItemDocument) ed).getSiteLinks().get(siteKey).getPageTitle(),
                                    ed);
                        }
                    }
                } catch (JsonProcessingException e) {
                    logger.error("Error when reading JSON for entity " + entityNode.path("id").asText("UNKNOWN")
                            + ": " + e.toString());
                }
            }
        }

    } catch (IOException e) {
        logger.error("Could not retrieve data from " + url + ". Error:\n" + e.toString());
    }

    return result;
}

From source file:io.github.retz.web.WebConsole.java

public WebConsole(ServerConfiguration config) {

    if (config.isTLS()) {
        LOG.info(// w  ww.ja  v  a2s .  c o m
                "HTTPS enabled. Keystore file={}, keystore pass={} chars, Truststore file={}, Truststorepass={} chars",
                config.getKeystoreFile(), config.getKeystorePass().length(), config.getTruststoreFile(),
                "(not printed)");
        secure(config.getKeystoreFile(), config.getKeystorePass(), config.getTruststoreFile(),
                config.getTruststorePass());
    } else {
        LOG.info("HTTPS disabled. Scheme: {}", config.getUri().getScheme());
    }

    port(config.getUri().getPort());
    ipAddress(config.getUri().getHost());
    staticFileLocation("/public");

    before((req, res) -> {
        res.header("Server", RetzScheduler.HTTP_SERVER_NAME);

        String resource;

        if (req.raw().getQueryString() != null) {
            resource = new StringBuilder().append(new URI(req.url()).getPath()).append("?")
                    .append(req.raw().getQueryString()).toString();
        } else {
            resource = new URI(req.url()).getPath();
        }
        LOG.info("{} {} from {} {}", req.requestMethod(), resource, req.ip(), req.userAgent());

        // TODO: authenticator must be per each user and single admin user
        Optional<Authenticator> adminAuthenticator = Optional.ofNullable(config.getAuthenticator());
        if (!adminAuthenticator.isPresent()) {
            // No authentication required
            return;
        }

        String verb = req.requestMethod();
        String md5 = req.headers("content-md5");
        if (md5 == null) {
            md5 = "";
        }
        String date = req.headers("date");

        LOG.debug("req={}, res={}, resource=", req, res, resource);
        // These don't require authentication to simplify operation
        if (NO_AUTH_PAGES.contains(resource)) {
            return;
        }

        Optional<AuthHeader> authHeaderValue = getAuthInfo(req);
        if (!authHeaderValue.isPresent()) {
            halt(401, "Bad Authorization header: " + req.headers(AuthHeader.AUTHORIZATION));
        }

        Authenticator authenticator;
        if (adminAuthenticator.get().getKey().equals(authHeaderValue.get().key())) {
            // Admin
            authenticator = adminAuthenticator.get();
        } else {
            // Not admin
            Optional<User> u = Database.getInstance().getUser(authHeaderValue.get().key());
            if (!u.isPresent()) {
                halt(403, "No such user");
            }
            if (config.authenticationEnabled()) {
                authenticator = new HmacSHA256Authenticator(u.get().keyId(), u.get().secret());
            } else {
                authenticator = new NoopAuthenticator(u.get().keyId());
            }
        }

        if (!authenticator.authenticate(verb, md5, date, resource, authHeaderValue.get().key(),
                authHeaderValue.get().signature())) {
            String string2sign = authenticator.string2sign(verb, md5, date, resource);
            if (LOG.isDebugEnabled()) {
                LOG.debug("Auth failed. Calculated signature={}, Given signature={}",
                        authenticator.signature(verb, md5, date, resource), authHeaderValue.get().signature());
            }
            halt(401, "Authentication failed. String to sign: " + string2sign);
        }
    });

    after((req, res) -> {
        LOG.debug("{} {} {} {} from {} {}", res.raw().getStatus(), req.requestMethod(), req.url(),
                req.raw().getQueryString(), req.ip(), req.userAgent());
    });

    exception(JobNotFoundException.class, (exception, request, response) -> {
        LOG.debug("Exception: {}", exception.toString(), exception);
        response.status(404);
        ErrorResponse errorResponse = new ErrorResponse(exception.toString());
        try {
            response.body(MAPPER.writeValueAsString(errorResponse));
        } catch (JsonProcessingException e) {
            LOG.error(e.toString(), e);
            response.body(e.toString());
        }
    });

    // APIs to be in vanilla HTTP
    get("/ping", (req, res) -> "OK");
    get("/status", WebConsole::status);

    // TODO: XXX: validate application owner at ALL job-related APIs
    // /jobs GET -> list
    get(ListJobRequest.resourcePattern(), (req, res) -> {
        Optional<AuthHeader> authHeaderValue = getAuthInfo(req);
        LOG.debug("list jobs owned by {}", authHeaderValue.get().key());

        ListJobResponse listJobResponse = WebConsole.list(authHeaderValue.get().key(), -1);
        listJobResponse.ok();
        res.status(200);
        res.type("application/json");
        return MAPPER.writeValueAsString(listJobResponse);
    });
    // /job  PUT -> schedule, GET -> get-job, DELETE -> kill
    get(GetJobRequest.resourcePattern(), JobRequestRouter::getJob);
    // Get a file
    get(GetFileRequest.resourcePattern(), JobRequestRouter::getFile);
    // Get file list
    get(ListFilesRequest.resourcePattern(), JobRequestRouter::getDir);

    post(ScheduleRequest.resourcePattern(), WebConsole::schedule);

    delete(KillRequest.resourcePattern(), (req, res) -> {
        LOG.debug("kill", req.params(":id"));
        int id = Integer.parseInt(req.params(":id")); // or 400 when failed?
        WebConsole.kill(id);
        res.status(200);
        KillResponse response = new KillResponse();
        response.ok();
        return MAPPER.writeValueAsString(response);
    });

    // /apps GET -> list-app
    get(ListAppRequest.resourcePattern(), (req, res) -> {
        Optional<AuthHeader> authHeaderValue = getAuthInfo(req);
        LOG.info("Listing all apps owned by {}", authHeaderValue.get().key());
        ListAppResponse response = new ListAppResponse(Applications.getAll(authHeaderValue.get().key()));
        response.ok();
        return MAPPER.writeValueAsString(response);
    });

    // /app  PUT -> load, GET -> get-app, DELETE -> unload-app
    put(LoadAppRequest.resourcePattern(), (req, res) -> {
        LOG.debug(LoadAppRequest.resourcePattern());
        Optional<AuthHeader> authHeaderValue = getAuthInfo(req);
        res.type("application/json");

        // TODO: check key from Authorization header matches a key in Application object
        LoadAppRequest loadAppRequest = MAPPER.readValue(req.bodyAsBytes(), LoadAppRequest.class);
        LOG.debug("app (id={}, owner={}), requested by {}", loadAppRequest.application().getAppid(),
                loadAppRequest.application().getOwner(), authHeaderValue.get().key());

        // Compare application owner and requester
        validateOwner(req, loadAppRequest.application());

        if (!(loadAppRequest.application().container() instanceof DockerContainer)) {
            if (loadAppRequest.application().getUser().isPresent()
                    && loadAppRequest.application().getUser().get().equals("root")) {
                res.status(400);
                return MAPPER.writeValueAsString(
                        new ErrorResponse("root user is only allowed with Docker container"));
            }
        }

        Application app = loadAppRequest.application();
        LOG.info("Registering application name={} owner={}", app.getAppid(), app.getOwner());
        boolean result = Applications.load(app);

        if (result) {
            res.status(200);
            LoadAppResponse response = new LoadAppResponse();
            response.ok();
            return MAPPER.writeValueAsString(response);
        } else {
            res.status(400);
            return MAPPER.writeValueAsString(new ErrorResponse("cannot load application"));
        }
    });

    get(GetAppRequest.resourcePattern(), (req, res) -> {
        LOG.debug(LoadAppRequest.resourcePattern());
        Optional<AuthHeader> authHeaderValue = getAuthInfo(req);

        String appname = req.params(":name");
        LOG.debug("deleting app {} requested by {}", appname, authHeaderValue.get().key());
        Optional<Application> maybeApp = Applications.get(appname);
        res.type("application/json");
        if (maybeApp.isPresent()) {
            // Compare application owner and requester
            validateOwner(req, maybeApp.get());

            res.status(200);
            GetAppResponse getAppResponse = new GetAppResponse(maybeApp.get());
            getAppResponse.ok();
            return MAPPER.writeValueAsString(getAppResponse);

        } else {
            ErrorResponse response = new ErrorResponse("No such application: " + appname);
            res.status(404);
            return MAPPER.writeValueAsString(response);
        }
    });

    delete(UnloadAppRequest.resourcePattern(), (req, res) -> {
        String appname = req.params(":name");
        LOG.warn("deleting app {} (This API is deprecated)", appname);
        WebConsole.unload(appname);
        UnloadAppResponse response = new UnloadAppResponse();
        response.ok();
        return MAPPER.writeValueAsString(response);
    });

    init();
}

From source file:com.streamsets.datacollector.event.handler.remote.RemoteDataCollector.java

private String getSourceOffset(String pipelineId, Map<String, String> offset) {
    SourceOffset sourceOffset = new SourceOffset(SourceOffset.CURRENT_VERSION, offset);
    try {//from   w  w  w.j  av a  2s .  co m
        return ObjectMapperFactory.get().writeValueAsString(new SourceOffsetJson(sourceOffset));
    } catch (JsonProcessingException e) {
        throw new IllegalStateException(
                Utils.format("Failed to fetch source offset for pipeline: {} due to error: {}", pipelineId,
                        e.toString()),
                e);
    }
}

From source file:com.rusticisoftware.tincan.RemoteLRS.java

@Override
public StatementsResultLRSResponse saveStatements(List<Statement> statements) {
    StatementsResultLRSResponse lrsResponse = new StatementsResultLRSResponse();
    if (statements.size() == 0) {
        lrsResponse.setSuccess(true);//from w ww. j a  v  a 2  s. c  o m
        return lrsResponse;
    }

    ArrayNode rootNode = Mapper.getInstance().createArrayNode();
    for (Statement statement : statements) {
        rootNode.add(statement.toJSONNode(version));
    }

    lrsResponse.setRequest(new HTTPRequest());
    lrsResponse.getRequest().setResource("statements");
    lrsResponse.getRequest().setMethod(HttpMethods.POST);
    lrsResponse.getRequest().setContentType("application/json");
    try {
        lrsResponse.getRequest().setContent(Mapper.getWriter(this.usePrettyJSON()).writeValueAsBytes(rootNode));
    } catch (JsonProcessingException ex) {
        lrsResponse.setErrMsg("Exception: " + ex.toString());
        return lrsResponse;
    }

    HTTPResponse response = makeSyncRequest(lrsResponse.getRequest());
    int status = response.getStatus();

    lrsResponse.setResponse(response);

    if (status == 200) {
        lrsResponse.setSuccess(true);
        lrsResponse.setContent(new StatementsResult());
        try {
            Iterator it = Mapper.getInstance().readValue(response.getContent(), ArrayNode.class).elements();
            for (int i = 0; it.hasNext(); ++i) {
                lrsResponse.getContent().getStatements().add(statements.get(i));
                lrsResponse.getContent().getStatements().get(i)
                        .setId(UUID.fromString(((JsonNode) it.next()).textValue()));
            }
        } catch (Exception ex) {
            lrsResponse.setErrMsg("Exception: " + ex.toString());
            lrsResponse.setSuccess(false);
        }
    } else {
        lrsResponse.setSuccess(false);
    }

    return lrsResponse;
}

From source file:io.github.retz.db.Database.java

public void updateJobs(List<Job> list) {
    try (Connection conn = dataSource.getConnection()) {
        conn.setAutoCommit(false);/*  ww  w .j a  v a  2s .  c  o m*/
        Jobs jobs = new Jobs(conn, MAPPER);
        for (Job job : list) {
            jobs.updateJob(job);
        }
        conn.commit();
    } catch (JsonProcessingException e) {
        LOG.error(e.toString());
    } catch (SQLException e) {
        LOG.error(e.toString());
    }
}

From source file:com.streamsets.datacollector.execution.runner.cluster.ClusterRunner.java

@VisibleForTesting
void validateAndSetStateTransition(PipelineStatus toStatus, String message, Map<String, Object> attributes)
        throws PipelineStoreException, PipelineRunnerException {
    Utils.checkState(attributes != null, "Attributes cannot be set to null");
    PipelineState fromState = getState();
    if (fromState.getStatus() == toStatus && toStatus != PipelineStatus.STARTING) {
        LOG.debug(//from  w ww  . j a  va2s  . c  o m
                Utils.format("Ignoring status '{}' as this is same as current status", fromState.getStatus()));
    } else {
        PipelineState pipelineState;
        synchronized (this) {
            fromState = getState();
            checkState(VALID_TRANSITIONS.get(fromState.getStatus()).contains(toStatus),
                    ContainerError.CONTAINER_0102, fromState.getStatus(), toStatus);
            long nextRetryTimeStamp = fromState.getNextRetryTimeStamp();
            int retryAttempt = fromState.getRetryAttempt();
            if (toStatus == PipelineStatus.RUN_ERROR && shouldRetry) {
                toStatus = PipelineStatus.RETRY;
                checkState(VALID_TRANSITIONS.get(fromState.getStatus()).contains(toStatus),
                        ContainerError.CONTAINER_0102, fromState.getStatus(), toStatus);
            }
            if (toStatus == PipelineStatus.RETRY && fromState.getStatus() != PipelineStatus.CONNECTING) {
                retryAttempt = fromState.getRetryAttempt() + 1;
                if (retryAttempt > maxRetries && maxRetries != -1) {
                    LOG.info("Retry attempt '{}' is greater than max no of retries '{}'", retryAttempt,
                            maxRetries);
                    toStatus = PipelineStatus.RUN_ERROR;
                    retryAttempt = 0;
                    nextRetryTimeStamp = 0;
                } else {
                    nextRetryTimeStamp = RetryUtils.getNextRetryTimeStamp(retryAttempt,
                            getState().getTimeStamp());
                    long delay = 0;
                    long currentTime = System.currentTimeMillis();
                    if (nextRetryTimeStamp > currentTime) {
                        delay = nextRetryTimeStamp - currentTime;
                    }
                    retryFuture = scheduleForRetries(runnerExecutor, delay);
                }
            } else if (!toStatus.isActive()) {
                retryAttempt = 0;
                nextRetryTimeStamp = 0;
            }
            ObjectMapper objectMapper = ObjectMapperFactory.get();
            String metricsJSONStr = null;
            if (!toStatus.isActive() || toStatus == PipelineStatus.DISCONNECTED) {
                Object metrics = getMetrics();
                if (metrics != null) {
                    try {
                        metricsJSONStr = objectMapper.writer().writeValueAsString(metrics);
                    } catch (JsonProcessingException e) {
                        throw new PipelineStoreException(ContainerError.CONTAINER_0210, e.toString(), e);
                    }
                }
                if (metricsJSONStr == null) {
                    metricsJSONStr = getState().getMetrics();
                }
            }
            pipelineState = pipelineStateStore.saveState(user, name, rev, toStatus, message, attributes,
                    getState().getExecutionMode(), metricsJSONStr, retryAttempt, nextRetryTimeStamp);
        }
        // This should be out of sync block
        if (eventListenerManager != null) {
            eventListenerManager.broadcastStateChange(fromState, pipelineState, ThreadUsage.CLUSTER);
        }
    }
}

From source file:org.metis.sql.SqlJob.java

/**
 * Polls the DB and notifies clients of a change in the DB or a fatal error.
 * //w ww.j av a  2s.  c  om
 * @return
 * @throws Exception
 */
private String pollDB() throws Exception {

    SqlResult sqlResult = null;

    try {

        // execute the sql statement. if a sqlResult was not returned,
        // then an error occurred and this job must be considered
        // defunct.
        if ((sqlResult = sqlStmnt.execute(getlParams())) == null) {
            // execute will have logged the necessary debug/error info.
            // notify all subscribed clients, that an error has occurred
            // and that this job is being stopped
            LOG.error(getThreadName() + ":ERROR, execute did not return a sqlResult object");
            sendInternalServerError("");
            throw new Exception("execute returns null sqlResult");
        }

        // sqlResult was returned, but it may not contain a result set
        List<Map<String, Object>> listMap = sqlResult.getResultSet();
        String jsonOutput = null;
        if (listMap == null || listMap.isEmpty()) {
            LOG.trace(getThreadName() + ":sqlResult did not contain a result set");
        } else {
            // convert the result set to a json object
            jsonOutput = Utils.generateJson(listMap);
            if (LOG.isTraceEnabled()) {
                if (jsonOutput.length() > 100) {
                    LOG.trace(getThreadName() + ": first 100 bytes of acquired result set = "
                            + jsonOutput.substring(0, 100));
                } else {
                    LOG.trace(getThreadName() + ": acquired this result set - " + jsonOutput);
                }
            }
        }

        // get the digital signature of the json object (if any) that
        // represents the result set
        String dSign = (jsonOutput != null) ? getHashOf(jsonOutput) : WS_DFLT_SIGNATURE;
        LOG.trace(getThreadName() + ": acquired digital signature = " + dSign);
        LOG.trace(getThreadName() + ": current  digital signature = " + getDigitalSignature());

        // determine if a change has occurred
        if (getDigitalSignature() == null) {
            // first time, so just update the current digital signature
            setDigitalSignature(dSign);
        } else if (!dSign.equals(getDigitalSignature())) {
            // update the current digital signature
            setDigitalSignature(dSign);
            // ... and send the notification
            LOG.debug(getThreadName() + ": sending notification");
            sendChangeNotification(dSign);
            return getDigitalSignature();
        }

    } catch (JsonProcessingException exc) {
        LOG.error(getThreadName() + ":ERROR, caught this " + "JsonProcessingException while trying to gen json "
                + "message: " + exc.toString());
        LOG.error(getThreadName() + ": exception stack trace follows:");
        dumpStackTrace(exc.getStackTrace());
        if (exc.getCause() != null) {
            LOG.error(getThreadName() + ": Caused by " + exc.getCause().toString());
            LOG.error(getThreadName() + ": causing exception stack trace follows:");
            dumpStackTrace(exc.getCause().getStackTrace());
        }
        sendInternalServerError("");
        throw exc;

    } catch (Exception exc) {
        LOG.error(getThreadName() + ":ERROR, caught this " + "Exception while trying to gen json " + "message: "
                + exc.toString());
        LOG.error(getThreadName() + ": exception stack trace follows:");
        dumpStackTrace(exc.getStackTrace());
        if (exc.getCause() != null) {
            LOG.error(getThreadName() + ": Caused by " + exc.getCause().toString());
            LOG.error(getThreadName() + ": causing exception stack trace follows:");
            dumpStackTrace(exc.getCause().getStackTrace());
        }
        sendInternalServerError("");
        throw exc;
    } finally {
        if (sqlResult != null) {
            SqlResult.enqueue(sqlResult);
        }
    }

    return null;
}

From source file:com.dnanexus.DXHTTPRequest.java

/**
 * Issues a request against the specified resource and returns either the text of the response
 * or the parsed JSON of the response (depending on whether parseResponse is set).
 *
 * @throws DXAPIException If the server returns a complete response with an HTTP status code
 *         other than 200 (OK).//www.j a v a 2s .  co m
 * @throws DXHTTPException If an error occurs while making the HTTP request or obtaining the
 *         response (includes HTTP protocol errors).
 * @throws InternalError If the server returns an HTTP status code 500 and the environment
 *         specifies that retries are disabled.
 * @throws ServiceUnavailableException If the server returns an HTTP status code 503 and
 *         indicates that the client should retry the request at a later time, and the
 *         environment specifies that retries are disabled.
 */
private ParsedResponse requestImpl(String resource, String data, boolean parseResponse,
        RetryStrategy retryStrategy) {

    HttpPost request = new HttpPost(apiserver + resource);

    if (securityContext == null || securityContext.isNull()) {
        throw new DXHTTPException(new IOException("No security context was set"));
    }

    request.setHeader("Content-Type", "application/json");
    request.setHeader("Connection", "close");
    request.setHeader("Authorization", securityContext.get("auth_token_type").textValue() + " "
            + securityContext.get("auth_token").textValue());
    request.setEntity(new StringEntity(data, Charset.forName("UTF-8")));

    // Retry with exponential backoff
    int timeoutSeconds = 1;
    int attempts = 0;

    while (true) {
        Integer statusCode = null;
        String requestId = "";

        // This guarantees that we get at least one iteration around this loop before running
        // out of retries, so we can check at the bottom of the loop instead of the top.
        assert NUM_RETRIES > 0;

        // By default, our conservative strategy is to retry if the route permits it. Later we
        // may update this to unconditionally retry if we can definitely determine that the
        // server never saw the request.
        boolean retryRequest = (retryStrategy == RetryStrategy.SAFE_TO_RETRY);
        int retryAfterSeconds = 60;

        try {
            // In this block, any IOException will cause the request to be retried (up to a
            // total of NUM_RETRIES retries). RuntimeException (including DXAPIException)
            // instances are not caught and will immediately return control to the caller.

            // TODO: distinguish between errors during connection init and socket errors while
            // sending or receiving data. The former can always be retried, but the latter can
            // only be retried if the request is idempotent.
            HttpResponse response = httpclient.execute(request);

            statusCode = response.getStatusLine().getStatusCode();
            requestId = getHeader(response, "X-Request-ID");
            HttpEntity entity = response.getEntity();

            if (statusCode == null) {
                throw new DXHTTPException();
            } else if (statusCode == HttpStatus.SC_OK) {
                // 200 OK
                byte[] value = EntityUtils.toByteArray(entity);
                int realLength = value.length;
                if (entity.getContentLength() >= 0 && realLength != entity.getContentLength()) {
                    // Content length mismatch. Retry is possible (if the route permits it).
                    throw new IOException("Received response of " + realLength
                            + " bytes but Content-Length was " + entity.getContentLength());
                } else if (parseResponse) {
                    JsonNode responseJson = null;
                    try {
                        responseJson = DXJSON.parseJson(new String(value, "UTF-8"));
                    } catch (JsonProcessingException e) {
                        if (entity.getContentLength() < 0) {
                            // content-length was not provided, and the JSON could not be
                            // parsed. Retry (if the route permits it) since this is probably
                            // just a streaming request that encountered a transient error.
                            throw new IOException(
                                    "Content-length was not provided and the response JSON could not be parsed.");
                        }
                        // This is probably a real problem (the request
                        // is complete but doesn't parse), so avoid
                        // masking it as an IOException (which is
                        // rethrown as DXHTTPException below). If it
                        // comes up frequently we can revisit how these
                        // should be handled.
                        throw new RuntimeException("Request is of the correct length but is unparseable", e);
                    } catch (IOException e) {
                        // TODO: characterize what kinds of errors
                        // DXJSON.parseJson can emit, determine how we can
                        // get here and what to do about it.
                        throw new RuntimeException(e);
                    }
                    return new ParsedResponse(null, responseJson);
                } else {
                    return new ParsedResponse(new String(value, Charset.forName("UTF-8")), null);
                }
            } else if (statusCode < 500) {
                // 4xx errors should be considered not recoverable.
                String responseStr = EntityUtils.toString(entity);
                String errorType = null;
                String errorMessage = responseStr;
                try {
                    JsonNode responseJson = DXJSON.parseJson(responseStr);
                    JsonNode errorField = responseJson.get("error");
                    if (errorField != null) {
                        JsonNode typeField = errorField.get("type");
                        if (typeField != null) {
                            errorType = typeField.asText();
                        }
                        JsonNode messageField = errorField.get("message");
                        if (messageField != null) {
                            errorMessage = messageField.asText();
                        }
                    }
                } catch (IOException e) {
                    // Just fall back to reproducing the entire response
                    // body.
                }
                logError(errorType + ": " + errorMessage + ". Code: " + Integer.toString(statusCode)
                        + " Request ID: " + requestId);
                throw DXAPIException.getInstance(errorType, errorMessage, statusCode);
            } else {
                // Propagate 500 error to caller
                if (this.disableRetry && statusCode != 503) {
                    logError("POST " + resource + ": " + statusCode + " Internal Server Error, try "
                            + String.valueOf(attempts + 1) + "/" + NUM_RETRIES + " Request ID: " + requestId);
                    throw new InternalErrorException("Internal Server Error", statusCode);
                }
                // If retries enabled, 500 InternalError should get retried unconditionally
                retryRequest = true;
                if (statusCode == 503) {
                    Header retryAfterHeader = response.getFirstHeader("retry-after");
                    // Consume the response to avoid leaking resources
                    EntityUtils.consume(entity);
                    if (retryAfterHeader != null) {
                        try {
                            retryAfterSeconds = Integer.parseInt(retryAfterHeader.getValue());
                        } catch (NumberFormatException e) {
                            // Just fall back to the default
                        }
                    }
                    throw new ServiceUnavailableException("503 Service Unavailable", statusCode,
                            retryAfterSeconds);
                }
                throw new IOException(EntityUtils.toString(entity));
            }
        } catch (ServiceUnavailableException e) {
            int secondsToWait = retryAfterSeconds;

            if (this.disableRetry) {
                logError("POST " + resource + ": 503 Service Unavailable, suggested wait " + secondsToWait
                        + " seconds" + ". Request ID: " + requestId);
                throw e;
            }

            // Retries due to 503 Service Unavailable and Retry-After do NOT count against the
            // allowed number of retries.
            logError("POST " + resource + ": 503 Service Unavailable, waiting for "
                    + Integer.toString(secondsToWait) + " seconds" + " Request ID: " + requestId);
            sleep(secondsToWait);
            continue;
        } catch (IOException e) {
            // Note, this catches both exceptions directly thrown from httpclient.execute (e.g.
            // no connectivity to server) and exceptions thrown by our code above after parsing
            // the response.
            logError(errorMessage("POST", resource, e.toString(), timeoutSeconds, attempts + 1, NUM_RETRIES));
            if (attempts == NUM_RETRIES || !retryRequest) {
                if (statusCode == null) {
                    throw new DXHTTPException();
                }
                throw new InternalErrorException("Maximum number of retries reached, or unsafe to retry",
                        statusCode);
            }
        }

        assert attempts < NUM_RETRIES;
        assert retryRequest;

        attempts++;

        // The number of failed attempts is now no more than NUM_RETRIES, and the total number
        // of attempts allowed is NUM_RETRIES + 1 (the first attempt, plus up to NUM_RETRIES
        // retries). So there is at least one more retry left; sleep before we retry.
        assert attempts <= NUM_RETRIES;

        sleep(timeoutSeconds);
        timeoutSeconds *= 2;
    }
}