Search code examples
javaapipdfhttpurlconnectionmendeley

Receiving 500 error when trying to upload PDF to Mendeley using Java


I'm attempting to write Java code to upload an article PDF to Mendeley using its document API, but I keep receiving a 500 error. I'm new to Java, so I might just be using the wrong code or libraries. Ultimately, the goal is to send an article PDF to Mendeley through its document API so that I can retrieve metadata about that article.

For reference, here's curl code provided in the Mendeley API docs that I'm trying to replicate in Java:

curl 'https://api.mendeley.com/documents' \
    -X POST \
    -H 'Authorization: Bearer ACCESS_TOKEN' \
    -H 'Content-Type: application/pdf' \
    -H 'Content-Disposition: attachment; filename="example.pdf"' \
    --data-binary @example.pdf   

I was able to get it to work using Python and the requests library. When I use the wrong access token I receive a 401 error, so I know the API is receiving my query. The returned 500 error does not include additional error text.

// setup connection
String url = "https://api.mendeley.com/documents";
URL obj = new URL(url);
HttpURLConnection con = (HttpURLConnection) obj.openConnection();
con.setRequestMethod("POST");
con.setDoOutput(true);

// set headers
String ACCESS_TOKEN = getApiValue("api_token");        
con.setRequestProperty("Authorization", "Bearer " + ACCESS_TOKEN);
con.setRequestProperty("Content-Type", "application/pdf");
con.setRequestProperty("Content-Disposition", "attachment; filename=\"" + fileName + "\"");

// send PDF
DataOutputStream wr = new DataOutputStream(con.getOutputStream());
File pdfFile = new File(fileName);
byte[] buf = new byte[8192];
InputStream pdfIS = new FileInputStream(pdfFile);
int c = 0;

while ((c = pdfIS.read(buf, 0, buf.length)) > 0) {
    wr.write(buf, 0, c);
    wr.flush();
}

wr.close();
pdfIS.close();

// get results
int responseCode = con.getResponseCode();
BufferedReader in = new BufferedReader(
    new InputStreamReader(con.getInputStream()));
String inputLine;
StringBuffer response = new StringBuffer();

while ((inputLine = in.readLine()) != null) {
    response.append(inputLine);
}
in.close();


Solution

  • I'm a software engineer working at Mendeley using Java, so hopefully I'm well-placed to answer this.

    As an aside, your original question stated that you were getting 501 response codes - however, after looking at the Mendeley logs for the past 7 days and not seeing any 501 response codes, and after trying out the code you've posted, I assumed that you meant that you got 500 response codes :)

    It turns out there are two bugs at play here. The first is a bug in the OpenJDK (see https://bugs.openjdk.java.net/browse/JDK-8163921 and https://bugs.openjdk.java.net/browse/JDK-8177439) - for some reason, if you use an HttpUrlConnection but don't set an Accept header, then a "default" Accept header is inserted when you make the request.

    Unfortunately, the default header provided is invalid! So you should have been returned a 400 error (Bad Request). To get around this, add the following line of code, which adds a valid Accept header to your request, thus preventing a malformed default header from being inserted:

    con.setRequestProperty("Accept", "*/*");
    

    Now everything should work as if you had made a curl request (or at least, that made it work for me)!

    Notice that I mentioned that a 400 error should have been returned (along with a helpful message of sort which would have helped you debug what was going wrong). Unfortunately, there was a bug on our side which meant that when the malformed, default Accept header was parsed, an Exception was thrown which wasn't handled correctly. Since we didn't handle the exception, the framework we use returned a 500 by default. I've raised a ticket internally for that, so hopefully that'll be fixed in the future.

    Lastly, the way you're sending requests (ie with the HttpUrlConnection class), while it works, is quite low-level - this makes things a bit more complicated for you than it has to be. I would suggest using a library which allows you to send requests without worrying about some of the nitty-gritty details, for example the Jetty HttpClient or the Apache HttpComponents library

    I hope that helps!