Handle HttpURLConnection redirect (NASA open API) - java

I'm writing a Java application that communicates with the NASA open apis. I've a class named NASAClient which exposes the required interface to achieve this task, but I'm facing with the following problem when dealing with the Earth Imagery API.
public EarthImageryResponseObject earthImagery(double lon, double lat, String year, String mon, String day, boolean cloud_score) {
String cs;
if(cloud_score) cs = "True";
else cs = "False";
String url = "https://api.nasa.gov/planetary/earth/imagery?lon="
+ lon
+ "&lat="
+ lat
+ "&date="
+ year+"-"+mon+"-"+day
+ "&cloud_score="
+ cs
+ "&api_key=" + api_key;
try {
String json = (read(getConnection(url)));
return gson.fromJson(json, EarthImageryResponseObject.class);
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
private String read(HttpURLConnection conn) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader((conn.getInputStream())));
return readFromBufferedReader(br);
}
private String readFromBufferedReader(BufferedReader br) throws IOException {
StringBuilder sb = new StringBuilder();
String line = br.readLine();
while (line != null) {
sb.append(line);
line = br.readLine();
}
return sb.toString();
}
private HttpURLConnection getConnection(String url) throws IOException {
HttpURLConnection connection;
connection = createConnection(url);
this.availability = connection.getHeaderField("X-RateLimit-Remaining");
connection.setConnectTimeout(2000);
this.last_resp = connection.getResponseCode();
return connection;
}
private HttpURLConnection createConnection(String url) throws IOException {
URL UniformResourceLocation = new URL(url);
return (HttpURLConnection) UniformResourceLocation.openConnection();
}
When I call earthImagery with some standard parameters, I get the following url:
https://api.nasa.gov/planetary/earth/imagery?lon=100.75&lat=1.5&date=2017-01-01&cloud_score=False&api_key=[MY_KEY]
If I navigate to this url using curl or the browser, I get the expected Json serialized object, but when invoked within my application, I get a redirection link (with 301 response code) to an heroku app:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"><title>Redirecting...</title><h1>Redirecting...</h1><p>You should be redirected automatically to target URL: http://earth-imagery-api.herokuapp.com/earth/imagery/?lon=100.75&lat=1.5&date=2017-01-01&cloud_score=False. If not click the link.
If I try to open a connection to such link, as explained here ,or open it in a browser it returns an error message (parameters are wrong). Really don't know what I am doing wrong. This strategy is the same for the other API I'm using in the application, and they work. Here it follows the stacktrace
Exception in thread "Thread-1" com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 1 path $
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:226)
at com.google.gson.Gson.fromJson(Gson.java:927)
at com.google.gson.Gson.fromJson(Gson.java:892)
at com.google.gson.Gson.fromJson(Gson.java:841)
at com.google.gson.Gson.fromJson(Gson.java:813)
at com.alexfoglia.nasaapi.NASAClient.earthImagery(NASAClient.java:151)
at com.alexfoglia.nasaapi.gui.EarthPanel.lambda$2(EarthPanel.java:127)
at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 1 path $
at com.google.gson.stream.JsonReader.beginObject(JsonReader.java:385)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:215)
... 7 more
And that's because i'm expecting a Json object, but I get a String (html redirect page).

If you want your code to automatically follow redirects then set this flag
connection.setFollowRedirects (true);
https://developer.android.com/reference/java/net/HttpURLConnection.html#setFollowRedirects(boolean)

I solved using both HttpURLConnection and SSLSocket. The first connection obtain the redirect link, that is reached not by another httpurlconnection, instead, a SSLSocket is used to get to this link.
public EarthImageryResponseObject earthImagery(double lon, double lat, String year, String mon, String day, boolean cloud_score) {
String cs;
if(cloud_score) cs = "True";
else cs = "False";
String url = "https://api.nasa.gov/planetary/earth/imagery?lon="
+ lon
+ "&lat="
+ lat
+ "&date="
+ year+"-"+mon+"-"+day
+ "&cloud_score="
+ cs
+ "&api_key=" + api_key;
try {
HttpURLConnection conn = getConnection(url);
String new_url = conn.getHeaderField("Location");
SSLSocketFactory factory =
(SSLSocketFactory)SSLSocketFactory.getDefault();
SSLSocket socket =
(SSLSocket)factory.createSocket("api.nasa.gov", 443);
socket.startHandshake();
socket.getOutputStream().write(("GET "+new_url+"\n").getBytes());
BufferedReader in = new BufferedReader(
new InputStreamReader(
socket.getInputStream()));
StringBuilder sb = new StringBuilder();
String inputLine;
while ((inputLine = in.readLine()) != null)
sb.append(inputLine);
in.close();
socket.close();
String json = sb.toString();
System.out.println(json);
return gson.fromJson(json, EarthImageryResponseObject.class);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}

Related

Decode google translate API response in JAVA

I need to write a small tool in JAVA which will translate text from English to French using the Google translate API. Everything works but I have an apostrophe decoding problem.
Original text:
Inherit Tax Rate
Text translated with Google translate API:
Taux d' imposition hérité
How it should be:
Taux d'imposition hérité
This is my translate method(sorry for the long method):
private String translate(String text, String from, String to) {
StringBuilder result = new StringBuilder();
try {
String encodedText = URLEncoder.encode(text, "UTF-8");
String urlStr = "https://www.googleapis.com/language/translate/v2?key=" + sKey + "&q=" + encodedText + "&target=" + to + "&source=" + from;
URL url = new URL(urlStr);
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
InputStream googleStream;
if (conn.getResponseCode() == 200) {
googleStream = conn.getInputStream(); //success
} else
googleStream = conn.getErrorStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(googleStream));
String line;
while ((line = reader.readLine()) != null) {
result.append(line);
}
JsonParser parser = new JsonParser();
JsonElement element = parser.parse(result.toString());
if (element.isJsonObject()) {
JsonObject obj = element.getAsJsonObject();
if (obj.get("error") == null) {
String translatedText = obj.get("data").getAsJsonObject().
get("translations").getAsJsonArray().
get(0).getAsJsonObject().
get("translatedText").getAsString();
return translatedText;
}
}
if (conn.getResponseCode() != 200) {
System.err.println(result);
}
} catch (IOException | JsonSyntaxException ex) {
System.err.println(ex.getMessage());
}
return null;
}
I'm using an XML writer to write the text and first I though that this has a problem, but I observed that the text is returned like this in the stream so I introduced the encoding parameter when I initialise the InputStreamReader:
BufferedReader reader = new BufferedReader(new InputStreamReader(googleStream, "UTF-8"));
But I receive the string with the same problem. Any ideas about what I can do?
I think this problem is solved by using the format parameter (docs). It defaults to html, but you can change it to text to receive unencoded data. Your request should look like this:
String urlStr = "https://www.googleapis.com/language/translate/v2?key=" + sKey + "&q=" + encodedText + "&target=" + to + "&source=" + from + "&format=text";

Sending two values into php webpage with java

I am trying to send two string values from my java program into my php page and i seem to be having some difficulty figuring out how this all works.
public static void main (String args[]) throws IOException{
Scanner input = new Scanner (System.in);
String sampleValue = input.next();
String sampleValue1 = input.next();
URL url = new URL("http://woah.x10host.com/randomfact.php");
String result = "";
String data = "fName=" + URLEncoder.encode(sampleValue, "UTF-8");
String id = "lName=" + URLEncoder.encode(sampleValue1, "UTF-8");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
try {
connection.setDoInput(true);
connection.setDoOutput(true);
connection.setUseCaches(false);
connection.setRequestMethod("POST");
connection.setRequestProperty("Content-Type","application/x-www-form-urlencoded");
// Send the POST data
DataOutputStream dataOut = new DataOutputStream(connection.getOutputStream());
dataOut.writeBytes(id);
dataOut.writeBytes(data);
dataOut.flush();
System.out.println("Data has been posted");
dataOut.close();
BufferedReader in = null;
try {
String line;
in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
while ((line = in.readLine()) != null) {
result += line;
}
} finally {
if (in != null) {
in.close();
}
}
} finally {
connection.disconnect();
System.out.println(result);
}
}
}
my PHP code is
<?php
$conn = mysqli_connect("localhost","woahx10h_funfact","spk","woahx10h_woah");
// CHECK CONNECTION
if (mysqli_connect_errno())
{
echo "Failed to connect to MySQL: " . mysqli_connect_error();
}
$value_data = $_POST['fName'];
$value_id = $_POST['lName'];
echo $value_data;
echo $value_id;
$conn->close();
?>
However, every time i run the java program, values from both data and id seem to be stored in the $value. I want the value from data to be stored in $value_data while the value from x to be stored in $value_id
You have to add ampersand (&) between each two parameters. So in this case all you have to do is edit your code like this:
...
dataOut.writeBytes(id);
dataOut.writeBytes("&");
dataOut.writeBytes(data);
...
If you would want to add another parameter to the request you would have to add another ampersand and then the parameter, for example:
...
dataOut.writeBytes(id);
dataOut.writeBytes("&");
dataOut.writeBytes(data);
dataOut.writeBytes("&");
dataOut.writeBytes(parameter3);
...

String passed from Android POST request to PHP in server not being recognized properly in MySQL SELECT query

I've been hacking at this one for a while, but I can't find a good reason for the following behavior:
I have an Android app from which I send a multipart/form-data http post. The request has the following form:
private final String delimiter = "--";
private final String boundary = "SwA"
+ Long.toString(System.currentTimeMillis()) + "SwA";
private final String charset = "UTF-8";
private final String lineSpace = "\r\n";
private final String domain = (domain);
private HttpURLConnection configureConnectionForMultipart(String url)
throws MalformedURLException, IOException {
HttpURLConnection con = (HttpURLConnection) (new URL(url))
.openConnection();
con.setRequestMethod("POST");
con.setDoInput(true);
con.setDoOutput(true);
con.setRequestProperty("Connection", "Keep-Alive");
con.setRequestProperty("Content-Type", "multipart/form-data;boundary="
+ boundary);
return con;
}
private void addFormPart(String paramName, String value, DataOutputStream os)
throws IOException {
os.writeBytes(lineSpace + delimiter + boundary + lineSpace);
os.writeBytes("Content-Disposition: form-data; name=\"" + paramName
+ "\"" + lineSpace);
os.writeBytes("Content-Type: text/plain; charset=" + charset + lineSpace);
os.writeBytes(lineSpace + value + lineSpace);
os.flush();
}
private void addFilePart(String paramName, File data, DataOutputStream os)
throws IOException {
os.writeBytes(lineSpace + delimiter + boundary + lineSpace);
os.writeBytes("Content-Disposition: form-data; name=\"" + paramName
+ "\"" + lineSpace);
os.writeBytes("Content-Type: application/octet-stream\r\n");
os.writeBytes("Content-Transfer-Encoding: binary" + lineSpace);
os.writeBytes(lineSpace);
os.flush();
FileInputStream fis = new FileInputStream(data);
byte[] buffer = new byte[4096];
int bytesRead = -1;
while ((bytesRead = fis.read(buffer)) != -1) {
os.write(buffer, 0, bytesRead);
}
os.writeBytes(lineSpace);
os.flush();
fis.close();
}
private void finishMultipart(DataOutputStream os) throws IOException {
os.writeBytes(lineSpace);
os.flush();
os.writeBytes(delimiter + boundary + delimiter + lineSpace);
os.close();
}
private String getResponse(HttpURLConnection con) throws IOException {
String response = "";
int status = con.getResponseCode();
if (status == HttpURLConnection.HTTP_OK) {
BufferedReader reader = new BufferedReader(new InputStreamReader(
con.getInputStream()));
String line = "";
while ((line = reader.readLine()) != null) {
response += line;
}
reader.close();
} else {
BufferedReader reader = new BufferedReader(new InputStreamReader(con.getErrorStream()));
String line = "";
while ((line = reader.readLine()) != null) {
response += line;
}
reader.close();
throw new IOException("Server returned non-OK status: " + status+", "+response);
}
return response;
}
public SearchQueryRunnable initSearchQueryRunnable(String query) {
return new SearchQueryRunnable(query);
}
private class SearchQueryRunnable implements Runnable {
private final String _query;
private final String _url = domain + "search_query.php";
public SearchQueryRunnable(String query) {
_query = query;
}
#Override
public void run() {
try {
HttpURLConnection con = configureConnectionForMultipart(_url);
DataOutputStream os = new DataOutputStream(
con.getOutputStream());
addFormPart("tag", _query, os);
finishMultipart(os);
String result = getResponse(con);
Log.i("SearchQuery", result);
con.disconnect();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
In search_query.php, I have the following:
include 'hashtags_table_api.php';
$tag = $_POST["tag"];
$res = queryHashTagsTable($tag);
In hashtags_table_api.php, there is:
include 'connect.php';
function queryHashTagsTable($hashtag) {
global $pdo;
$sql = "SELECT * FROM `tbl_hashtags` WHERE hashtag = :hashtag";
$stmt = $pdo->prepare ( $sql );
echo $hashtag;
$stmt->bindValue(':hashtag', $hashtag);
if ($stmt->execute()) {
$result = $stmt->fetchAll();
echo count($result);
}
}
connect.php is (omitted important variables):
try {
$pdo = new PDO ( "mysql:host=$host;dbname=$dbname;charset=utf8", $username, $password );
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
echo 'Connected to database';
} catch(PDOException $e) {
echo $e->getMessage();
}
When I run this code and pass the value "hash" to initSearchQueryRunnable, I get a result of 0, even though I have a row with the value "hash" in the column hashtag. What's weird is that when I hardcode the following in search_query.php:
include 'hashtags_table_api.php';
$tag = 'hash';
$res = queryHashTagsTable($tag);
I get the desired result of 1 from my query. I double checked and the $_POST["tag"] is passing the value 'hash' to my server, but for some reason my SQL query will not recognize this as being equal to the value in my DB, even though the exact same hardcoded value is recognized as being equal.
Any clue what else I need to do in order to have my dynamically-passed parameter recognized as an equal value to my MySQL data?
EDIT
After some testing, I realized that the $_POST["tag"] is coming through with quotes, and thus strlen[$tag] = 6. I think this might be the reason why the SQL isn't matching the query string up with what's in the db. Sure enough strlen['hash'] = 4, which the SQL successfully matches up with my query. How can I effectively remove the quotation marks from the value of of $tag, so that my query works? FYI, my server is running PHP 5.4.24, if that's relevant.
I solved my own problem here. It turns out that the text being sent through my http post had two extra invisible whitespaces that I could only detect by some tests using strlen and using a for statement to see where the spaces were being appended. My solution was to use the trim() function (http://php.net/manual/en/function.trim.php) and just simply add $tag = trim($tag) to search_query.php before running queryHashTagsTable($tag). I hope this helps someone else that doesn't realize their http post request is being sent with extra invisible characters and needs the exact string for an sql query.

Sugar get_entry_list returns "Invalid Session ID" when querying Meetings for a specific user

I try to query Sugar through its REST API using Java for entries in Meetings module for a specific user, namely the one who is logged in currently.
I am trying this a few days already while googling around for asolution.
I made a login() call, where I got a session ID, than I make a call to get_user_id(). With the returned user ID I try to query the Meetings module by using get_entry_list().
To get the Meetings assigned to the UserID it works with following query string, where mUserId holds the returned user id of get_user_id():
queryString = "meetings.assigned_user_id='"+mUserId+"'";
But I not only want to get the meetings, where a user is assigned to, but all Meetings where he participates. For that I try a subquery on meetings_users table in my query.
Here is a query strings I tried, which os working on MySQL prompt. But when I try this over REST, it returns "Invalid Session ID":
queryString = "meetings.id IN ( SELECT meetings_users.meeting_id FROM meetings_users WHERE meetings_users.user_id = '"+mUserId+"' )";
Does anyone have a hint on this? Which conditions lead to an "Invalid Session ID" at all?
What also does not work e.g. is appending "and deleted = '0'" to the first stated query:
queryString = "meetings.assigned_user_id='"+mUserId+"' and deleted = '0'";
also fails.
As requested here is the full code example, platform is Android, API Level 8:
private JSONArray getEntryList(String moduleName,
String selectFields[], String queryString, String orderBy, int max_results) throws JSONException, IOException, KeyManagementException, NoSuchAlgorithmException
{
JSONArray jsoSub = new JSONArray();
if (selectFields.length > 0)
{
for (int i = 0; i < selectFields.length; i++)
{
jsoSub.put(selectFields[i]);
}
}
// get_entry_list expects parameters to be ordered, JSONObject does
// not provide this, so I built my JSON String on my own
String sessionIDPrefix = "{\"session\":\""+ mSessionId+ "\"," +
"\"modulename\":\""+ moduleName+ "\"," +
"\"query\":\""+ queryString + "\"," +
"\"order_by\":\""+ orderBy + "\"," +
"\"offset\":\""+ mNextOffset+ "\"," +
"\"select_fields\":["+ jsoSub.toString().substring(
1, jsoSub.toString().length()-2)+ "\"],"+
"\"max_results\":\""+ 20 + "\"}";
String restData = sessionIDPrefix;
Log.d(TAG, restData);
String data = null;
String baseurl = mUrl + REST_URI_APPEND;
data = httpPost(baseurl+"?method=get_entry_list&input_type=json&response_type=json&rest_data="+restData);
Log.d(TAG, data);
JSONObject jsondata = new JSONObject(data);
mResultCount = jsondata.getInt("result_count");
mNextOffset = jsondata.getInt("next_offset");
return jsondata.getJSONArray("entry_list");
}
private String httpPost(String urlStr) throws IOException{
String urlSplitted [] = urlStr.split("/", 4);
String hostPort[] = urlSplitted[2].split(":");
String hostname = hostPort[0];
int port = 80;
if (hostPort.length > 1)
port = new Integer(hostPort[1]);
String file = "/"+urlSplitted[3];
Log.d(TAG, hostname + ", " + port + ", " +file);
URL url = null;
try {
url = new URL("http", hostname, port, file);
} catch (MalformedURLException e) {
throw new IOException(mContext.getText(R.string.error_malformed_url).toString());
}
Log.d(TAG, "URL "+url.toString());
HttpURLConnection conn = null;
try {
conn = (HttpURLConnection) url.openConnection();
} catch (IOException e) {
throw new IOException(mContext.getText(R.string.error_conn_creat).toString());
}
conn.setConnectTimeout(60 * 1000);
conn.setReadTimeout(60 * 1000);
try {
conn.setRequestMethod("POST");
} catch (ProtocolException e) {
throw new IOException(mContext.getText(R.string.error_post).toString());
}
conn.setDoOutput(true);
conn.setDoInput(true);
conn.setUseCaches(false);
conn.setAllowUserInteraction(false);
conn.setRequestProperty("Content-Type",
"application/x-www-form-urlencoded");
try {
conn.connect();
} catch (IOException e) {
throw new IOException(mContext.getText(R.string.error_conn_open).toString()
+ "\n" + e.getMessage());
}
int response = 0;
String responseMessage = null;
try {
response = conn.getResponseCode();
responseMessage = conn.getResponseMessage();
} catch (IOException e) {
conn.disconnect();
throw new IOException(mContext.getText(R.string.error_resp_io).toString());
}
Log.d(TAG, "Exception Response "+ response);
if (response != 200) {
conn.disconnect();
throw new IOException(mContext.getText(R.string.error_http).toString()
+ "\n" + response + " " + responseMessage);
}
StringBuilder sb = null;
try {
BufferedReader rd = new BufferedReader(
new InputStreamReader(conn.getInputStream()));
sb = new StringBuilder();
String line;
while ((line = rd.readLine()) != null) {
Log.d(TAG,"line " + line);
sb.append(line);
}
rd.close();
} catch (IOException e) {
conn.disconnect();
throw new IOException(mContext.getText(R.string.error_resp_read).toString());
}
conn.disconnect();
if (sb.toString() == null)
{
throw new IOException(mContext.getText(R.string.error_resp_empty).toString());
}
return sb.toString();
}
Calling the code above:
if (login() != OK)
return null;
mResultCount = -1;
mNextOffset = 0;
mUserId = getUserId();
String fields[] = new String [] {
"id",
"name",
"description",
"location",
"date_start",
"date_end",
"status",
"type",
"reminder_time",
"parent_type",
"parent_id",
"deleted",
"date_modified"
};
String queryString = null;
if (syncAllUsers)
queryString = "";
else
{
queryString = "meetings.assigned_user_id = 'seed_sarah_id' and meetings.deleted = '0'";
//queryString = "meetings.id IN ( SELECT meeting_id FROM meetings_users WHERE user_id ='"+mUserId+"'";
}
entryList.clear();
while (mResultCount != 0)
{
if (!seamless_login())
return null;
JSONArray serverEntryList = getEntryList(
"Meetings", fields, queryString, "date_start", 0);
//... do sth with data
}
totalContactsResults += mResultCount;
}
logout();
login() returns valid session id, and getUserId() returns right id. The whole code is already working for fetching contacts, and also working for a simple query as stated above.
Thanks in advance
Marc
After further testing, I realized, that whitespaces in the query string are the problem. They lead to an URL containing whitespace. To avoid that some kind of URL encoding has to be done.
I had not success in encoding the whole URL in my httpPost methods (seems not to be necessary). But replacing spaces with '+' in the query string works for me:
queryString = "meetings.id+IN+(SELECT+meetings_users.meeting_id+FROM meetings_users+WHERE+meetings_users.user_id='"+mUserId+"')";
If anyone has a more elegant method of doing this, please let me know.
You would probably be better off using the get_relationships web service call.
SugarRest.call('get_relationships', [SugarRest.session, 'Users', SugarRest.user_id, 'meetings', '', ['name'], 0, ''])
That should be all you need. In the parameter after 'meetings' you can also pass in an additional filter.

Error when using java.net to connect to a website

I am trying to login to a website using java.net in Google App Engine for Java.
The login id and password are stored in variables 'loginid' and 'password' respectively.
The code that I have created is given below:
public Integer login()
{
String param1="", param2="", query="";
String charset = "UTF-8";
String loginurl = "https://website.com/login";
try {
param1 = URLEncoder.encode(loginid, "UTF-8");
param2 = URLEncoder.encode(password, "UTF-8");
query = String.format("username=%s&password=%s",
URLEncoder.encode(param1, charset),
URLEncoder.encode(param2, charset));
} catch (UnsupportedEncodingException ex) {
// ...
}
try {
URL url = new URL(loginurl + "?" + query);
BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream()));
String line="";
String resp="";
while ((line = reader.readLine()) != null) {
resp=resp + line;
}
actionmessage=" Response-" + resp;
return(1);
}
catch (Exception e) {
// ...
}
}
I want to know more about a couple of things with ref to above code.
I am sure that I have entered correct ID and password, but still I am getting login failure. What is wrong with the above code?
How do I check if a submission as made by above code is successful or if there is an error? If there is an error, how do I get the error stream?
It was an error on my part- I encoded the 2 parameters passed to input url, twice!! So the site where I was authenticating, received a garbled value for user id and password!

Categories