HTML Requests in Android - java

so here is my issue: I am working on a mobile App, that requires a login. I am programming in Android Studio / Java. Got a quite good experience in Java but i've never done networking.. There is a .asp script on a server that processes the login, which i need to send the login data to. The best way to solve this i think is a HTTP - Request, because if you enter the url of the script into the browser, followed by the query string containing the login data, you already get a response.
http://sampleurl.info/actions/checklogin.asp?userName=klingenhaeger&password=droid&device=android
returns a Json String containing a profile token and a timestamp and the profile name.Like:
{"profil_token":"qn2hJcRQixYjG7yyW956g1407921902","profil_name":"Marc Klingenhäger","timestamp":"1407921902"}
This profile token is then attached to every url the user requests, and in that way the user gains permission to all the websites.
I read that you can do the same thing with a http GET request, but me and my coworker worked on this
(such a simple thing) for nine ours and didn't get our code working...
We tried out heaps of snippets, this is our most simple attempt:
In the Main activity, on clicking the button that leads to the login, LoginActivity.class is called using a Intent.
Intent intent = new Intent(this, LoginActivity.class);
startActivity(intent);
After entering the user data, the user clicks the login button, and the method attemptLogin(); gets called.
public void attemptLogin() {
if (mAuthTask != null) {
return;
}
// Reset errors.
mEmailView.setError(null);
mPasswordView.setError(null);
// Store values at the time of the login attempt.
String email = mEmailView.getText().toString();
String password = mPasswordView.getText().toString();
boolean cancel = false;
View focusView = null;
// Check for a valid password, if the user entered one.
if (!TextUtils.isEmpty(password) && !isPasswordValid(password)) {
mPasswordView.setError(getString(R.string.error_invalid_password));
focusView = mPasswordView;
cancel = true;
}
// Check for a valid email address.
if (TextUtils.isEmpty(email)) {
mEmailView.setError(getString(R.string.error_field_required));
focusView = mEmailView;
cancel = true;
} else if (!isEmailValid(email)) {
mEmailView.setError(getString(R.string.error_invalid_email));
focusView = mEmailView;
cancel = true;
}
if (cancel) {
// There was an error; don't attempt login and focus the first
// form field with an error.
focusView.requestFocus();
} else {
// Show a progress spinner, and kick off a background task to
// perform the user login attempt.
showProgress(true);
mAuthTask = new UserLoginTask(email, password);
mAuthTask.execute((Void) null);
}
}
So after some error detection, the class userLoginTask (subclass of AsyncTask) is initialized to handle the networking stuff, because initializing the http request in the main thread seems to cause an exception. We didn't manage to code a HTTP - Request here so far.. (thats the main problem)
public class UserLoginTask extends AsyncTask {
private final String mEmail;
private final String mPassword;
UserLoginTask(String email, String password) {
mEmail = email;
mPassword = password;
}
#Override
protected Boolean doInBackground(Void... params) {
// TODO: attempt authentication against a network service.
try {
// Simulate network access.
Thread.sleep(2000);
} catch (InterruptedException e) {
return false;
}
for (String credential : DUMMY_CREDENTIALS) {
String[] pieces = credential.split(":");
if (pieces[0].equals(mEmail)) {
// Account exists, return true if the password matches.
return pieces[1].equals(mPassword);
}
}
// TODO: register the new account here.
return true;
}
#Override
protected void onPostExecute(final Boolean success) {
mAuthTask = null;
showProgress(false);
if (success) {
finish();
} else {
mPasswordView.setError(getString(R.string.error_incorrect_password));
mPasswordView.requestFocus();
}
}
#Override
protected void onCancelled() {
mAuthTask = null;
showProgress(false);
}
}
So my question is basically, how can i initialize a HTTP - Request in the UserLoginTask class. Any Ideas? Thanks in advance! :)
Falco

The easiest way is to use a URL object and open a stream to your HTTP server.
The server response can be read through this stream:
String url = "http://sampleurl.info/actions/checklogin.asp?userName=klingenhaeger&password=droid&device=android";
try {
URL u = new URL(url);
InputStream is = u.openStream(); // Opens streaming connection to url
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
StringBuffer result = new StringBuffer(); // Buffer to store saved data
String input = null;
while((input = reader.readLine()) != null) {
// Read data until the end of the stream
result.append(input);
}
// Do something with result here
} catch (IOException e) {
e.printStackTrace();
}
When you have retrieved the data as a string, you can parse the JSON to get the profile_token

Use Android's Volley http://developer.android.com/training/volley/index.html and issue an HTTP POST request, sending username / password.
I advise hashing the password (MD5 or something else - depends on what the back-end handles to decrypt).

Google suggests using HttpUrlConnection.
An example that should do what you want is very simple, especially when using GET. First, construct an URL from String. Your response is InputStream, which you parse to JSONObject and obtain your token.
URL url = new URL("http://sampleurl.info/actions/checklogin.asp?userName=klingenhaeger&password=droid&device=android");
//later:
URL url = new URL("http://sampleurl.info/actions/checklogin.asp?token=abcde");
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
try {
InputStream in = new BufferedInputStream(urlConnection.getInputStream());
readStream(in);
finally {
urlConnection.disconnect();
}
}
This is recommended approach, since it does not require any external libraries, and it can easily be converted to use POST instead of GET and HTTPS instead of HTTP.

Related

Null Pointer Exception When Trying to Open Webpage That Requires Authentication Java

I'm trying to write a code that will open multiple URLs that require authentication and parse the text of the webpage for a SUCCESS message. If I supply the username and password in the authentication pop up, then the rest will go through without having to supply authentication every time. Right now, my authentication class looks like so:
public class CustomAuthenticator extends Authenticator {
protected PasswordAuthentication getPasswordAuthentication() {
String username = "********";
String password = "********";
return new PasswordAuthentication(username, password.toCharArray());
}
}
And my URL parsing method is:
public static String urlSuccessUnknown(String url) {
Document doc;
String res = null;
Authenticator.setDefault(new CustomAuthenticator());
try {
doc = Jsoup.connect(url).get();
res = doc.body().text();
if(res.indexOf("SUCCESS") >= 0)
return "SUCCESS";
else if(!(res.indexOf("SUCCESS") >= 0))
return "FLAG";
}
catch (Exception e) { e.printStackTrace(); }
return "Unable to correctly parse";
}
Whenever I test this with one URL, I receive a "java.lang.NullPointerException" at the line "res = doc.body().text();" and two other lines in other parts of my code. What am I doing wrong?
Also, when the webpage is opened, it asks for the username and password in a pop-up window instead of embedded in the page. I just wanted to include this in case a CustomAuthenticator is not the correct way to supply the username and password. Thank you for your time.

Retrofit Error :500 Internal server error in Android

Am Using retrofit for my connection with server,My app has signin page and signout page During Login i get the value from text box and send using POST request to the server It works fine,
public void LoginUser(View v)
{RestAdapter adapter = new RestAdapter.Builder()
.setEndpoint(ROOT_URL)
.build();
WashAPI api = adapter.create(WashAPI.class);
api.LoginUser(
Email.getText().toString(),
Password.getText().toString(),
//Creating an anonymous callback
new Callback<Response>() {
#Override
public void success(Response result, Response response) {
//On success we will read the server's output using bufferedreader
//Creating a bufferedreader object
BufferedReader reader = null;
//An string to store output from the server
String output = "";
try {
//Initializing buffered reader
reader = new BufferedReader(new InputStreamReader(result.getBody().in()));
//Reading the output in the string
output = reader.readLine();
} catch (IOException e) {
e.printStackTrace();
}
Interface for signin
public interface WashAPI {
#FormUrlEncoded
#POST("/xxx/yyy/signin")
public void LoginUser(
#Field("email") String email,
#Field("password") String password,
Callback<Response> callback);
}
This works good
After login with my server API it returns me a token, At the time of signout i need to send the token so my session get experied.
code for signout
public void signout(View v)
{
Log.d("insidetoken",t);
RestAdapter adapter = new RestAdapter.Builder()
.setEndpoint(ROOT_URL)
.build();
SignoutAPI api = adapter.create(SignoutAPI.class);
api.signout(t,
new Callback<Response>() {
#Override
public void success(Response result, Response response) {
BufferedReader reader = null;
String output = "";
try {
reader = new BufferedReader(new InputStreamReader(result.getBody().in()));
output = reader.readLine();
} catch (IOException e) {
e.printStackTrace();
}
Interface for signout
public interface SignoutAPI {
#FormUrlEncoded
#POST("/xxx/yyy/zzz/signout")
public void signout(
#Field("token") String token,
Callback<Response> callback);
}
My code is same for both signin and sigout
but for signin it works and for signout it gives me RETROFIT ERROR : 500 INTERNAL SERVER ERROR
But Using POSTMAN It works fine
500 INTERNAL SERVER ERROR means that there is problem in the server side you should have to check it using postman.
I am damn sure that there will be problem in the web service response not of your code at android side.
As mentioned in the comments too, it appears you are doing something in the login service that is not being done in the sign out service.
To fix this, make sure in you sign out service, you are checking for a POST parameter named token.
Maybe you are giving token in wrong way
In my way It was "Bearer TOKEN" -> for Authorization in Header
In my case I changed my parameters to that
fun setProfileAvatar(token: String,#Part image: MultipartBody.Part) : Single<Message> {
return apiService.setProfileAvatar("Bearer ${token}",image)
}
You can enable logging in retrofit to show you server-side errors and messages.
use this link to understand how to enable it.
https://stackoverflow.com/a/33256827/9474700

Stop an async Spring method from caller class

I've a class that call a Rest web service to receive a file from server. While bytes are transferred, I've created an Async task, it checks if connection with server is fine to allow the stop connection if an error appears.
This async task has a loop that I have to stop:
#Component
public class ConnectionTest {
#Async
//Check connection with the server, if for three attemp it failes, throw exception
public void checkServerConnection(String serverIp) throws Exception{
int count=0;
for(;;Thread.sleep(7000)){
try{
System.out.println("TEST");
URL url = new URL(serverIp);
HttpURLConnection con = (HttpURLConnection) url
.openConnection();
con.connect();
if (con.getResponseCode() == 200){
System.out.println("Connection established!!");
}
if (count>0) count=0;
}catch(Exception e){
count++;
if (count==3)
throw new Exception("Connection error");
}
}
}
}
but how can I stop this method from the caller?
#Autowired
private ConnectionTest connectionTest;
#Override
public Response getFile(String username, String password, String serverIp, String toStorePath, String filePath){
ResponseEntity<byte[]> responseEntity = null;
try{
//it is used to check if connection of the client with the server goes down
connectionTest.checkServerConnection();
RestClient restClient = new RestClient(username, password);
// SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
// requestFactory.setBufferRequestBody(false);
// restClient.setRequestFactory(requestFactory);
// RestTemplate restClient = new RestTemplate();
responseEntity = restClient.getForEntity(serverIp + "client/file/?filePath={filePath}", byte[].class, filePath);
//TODO kill async task and return false
UPDATE: as #Thomas has suggested I've used a boolean variable in ConnectionTest, I changed for cycle with while (!stop) and after the web service call I set ConnectionTest.setStop(true).
Pay attention to set stop=false before loop (and not as instance field) otherwise only the first request has this value and goes inside the while.
UPDATE 2
This is the my last code, it seems to work, maybe I should change while loop with wait-notify:
public Response getFile(String username, String password, String serverIp, String toStorePath, String filePath){
try{
//it is used to check if connection of the client with the server goes down
Future<Boolean> isConnect = connectionTest.checkServerConnection(serverIp);
Future<ResponseEntity<byte[]>> downloadResult = downloadAsync.makeRequest(username, password, serverIp, filePath);
while(!isConnect.isDone() && !downloadResult.isDone()){
}
if (isConnect.isDone()){
downloadResult.cancel(true);
return new Response(false, false, "Error with server connection!", null);
}else{
connectionTest.setStop(true);
ResponseEntity<byte[]> responseEntity = downloadResult.get();
if (MediaType.TEXT_PLAIN.toString().equals(responseEntity.getHeaders().getContentType().toString())){
ErrorResponse errorResponse= ErrorResponseBuilder.buildErrorResponse(new FileException("Error with file transfert!"));
return new Response(false, false, new String(Base64.decodeBase64(responseEntity.getBody()),Charset.forName("UTF-8")), errorResponse);
}else{
Path p = Paths.get(filePath);
String fileName = p.getFileName().toString();
FileOutputStream fos = new FileOutputStream(toStorePath+"\\"+ fileName);
fos.write(responseEntity.getBody());
fos.close();
return new Response(true, true, "Your file has been downloaded!", null);
}
}
}catch(Exception e){
ErrorResponse errorResponse= ErrorResponseBuilder.buildErrorResponse(e);
return new Response(false, false, "Error on the client side!" , errorResponse);
}
}
connection check async:
#Component
public class ConnectionTest {
private boolean stop;
#Async
//Check connection with the server, if for three attemp it failes, throw exception
/**
*
* #param serverIp
* #throws IOException
*/
public Future<Boolean> checkServerConnection(String serverIp) throws IOException {
int count=0;
stop = false;
while (!stop){
try{
Thread.sleep(7000);
System.out.println("TEST");
//java.net.InetAddress.getByName(SERVER_ADDRESSS);
URL url = new URL(serverIp);
HttpURLConnection con = (HttpURLConnection) url
.openConnection();
con.connect();
if (count>0) count=0;
}catch(Exception e){
count++;
System.out.println(count);
if (count==3)
return new AsyncResult<Boolean>(stop);
}
}
return new AsyncResult<Boolean>(stop);
}
/**
* #return the stop
*/
public boolean isStop() {
return stop;
}
/**
* #param stop the stop to set
*/
public void setStop(boolean stop) {
this.stop = stop;
}
}
download async:
#Component
public class DownloadAsync {
#Async
public Future<ResponseEntity<byte[]>> makeRequest(String username, String password, String serverIp, String filePath){
RestClient restClient = new RestClient(username, password);
ResponseEntity<byte[]> response= restClient.getForEntity(serverIp + "client/file/?filePath={filePath}", byte[].class, filePath);
return new AsyncResult<ResponseEntity<byte[]>>(response);
}
}
When you deal with an #Async method, a good practice is to return a Future object from it because you need a connection point between the client and task code.
Let's make your task method return a Future:
public Future<Integer> checkServerConnection(String serverIp) {
// other code here
return new AsyncResult<>(count);
}
You'll need to add a couple of imports:
import java.util.concurrent.Future;
import org.springframework.scheduling.annotation.AsyncResult;
Finally, in the client code let's get the Future:
Future<Integer> checkTask = connectionTest.checkServerConnection();
Now, you can do some useful things with the checkTask. For example:
// Check if the task was completed including by an exception being thrown.
checkTask.isDone();
// Get the task result.
Integer count = checkTask.get(); // Note: this is a blocking method.
// If the task was finished by throwing an exception,
// get() method will also throw an exception.
// You can get the cause exception like this:
if (checkTask.isDone()) {
try {
checkTask.get();
} catch(Exception e) {
Exception cause = e.getCause(); // this will be your new Exception("Connection error")
}
}
// Not recommended, but you can also cancel the task:
checkTask.cancel(mayInterruptIfRunning);
first off I don't want to perplex the issue any further so I am going to give you a high level description for doing this. Particularly, look how this is done very elegantly in android, using publish delegates.
Basically, a publish delegate consists of 2 portions. First, an overridden method to publish changes, and another method to receive changes. The time interval in which changes are received, depend on the "CHUNK" size currently in the queue and the data size, but generally, you can think of this as a best effort attempt to receive publish events.
So a big high level picture is this.
ASYNCTASK
IN BACKGROUND (DOWNLOAD OVER TIME)
IN BACKGROUND (PUBLISH DOWNLOAD PROGRESS)
PUBLISH RECEIVER ( RECEIVE UPDATE OF THE DOWNLOAD [perhaps in percent]
MAKE A DECISION FROM HERE.
I am not neglecting the importance of the Spring context here, but I think once you receive this post, you will accept it's applicability, regardless of framework.
Best,
Mobile Dev
AT

GWT Facebook Integration

I am trying to write a server side Facebook Notification service in my GWT app. The idea is that I will run this as a timertask or cron job sort of.
With the code below, I get a login URL, I want to be able to Login programmatically as this is intended to be automated (Headless sort of way). I was gonna try do a submit with HTMLunit but I thought the FB API should cater for this.
Please advice.
public class NotificationServiceImpl extends RemoteServiceServlet implements NotificationService {
/**serialVersionUID*/
private static final long serialVersionUID = 6893572879522128833L;
private static final String FACEBOOK_USER_CLIENT = "facebook.user.client";
long facebookUserID;
public String sendMessage(Notification notification) throws IOException {
String api_key = notification.getApi_key();
String secret = notification.getSecret_key();
try {
// MDC.put(ipAddress, req.getRemoteAddr());
HttpServletRequest request = getThreadLocalRequest();
HttpServletResponse response = getThreadLocalResponse();
HttpSession session = getThreadLocalRequest().getSession(true);
// session.setAttribute("api_key", api_key);
IFacebookRestClient<Document> userClient = getUserClient(session);
if(userClient == null) {
System.out.println("User session doesn't have a Facebook API client setup yet. Creating one and storing it in the user's session.");
userClient = new FacebookXmlRestClient(api_key, secret);
session.setAttribute(FACEBOOK_USER_CLIENT, userClient);
}
System.out.println("Creating a FacebookWebappHelper, which copies fb_ request param data into the userClient");
FacebookWebappHelper<Document> facebook = new FacebookWebappHelper<Document>(request, response, api_key, secret, userClient);
String nextPage = request.getRequestURI();
nextPage = nextPage.substring(nextPage.indexOf("/", 1) + 1); //cut out the first /, the context path and the 2nd /
System.out.println(nextPage);
boolean redirectOccurred = facebook.requireLogin(nextPage);
if(redirectOccurred) {
return null;
}
redirectOccurred = facebook.requireFrame(nextPage);
if(redirectOccurred) {
return null;
}
try {
facebookUserID = userClient.users_getLoggedInUser();
if (userClient.users_hasAppPermission(Permission.STATUS_UPDATE)) {
userClient.users_setStatus("Im testing Facebook With Java! This status is written using my Java code! Can you see it? Cool :D", false);
}
} catch(FacebookException ex) {
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Error while fetching user's facebook ID");
System.out.println("Error while getting cached (supplied by request params) value " +
"of the user's facebook ID or while fetching it from the Facebook service " +
"if the cached value was not present for some reason. Cached value = {}" + userClient.getCacheUserId());
return null;
}
// MDC.put(facebookUserId, String.valueOf(facebookUserID));
// chain.doFilter(request, response);
} finally {
// MDC.remove(ipAddress);
// MDC.remove(facebookUserId);
}
return String.valueOf(facebookUserID);
}
public static FacebookXmlRestClient getUserClient(HttpSession session) {
return (FacebookXmlRestClient)session.getAttribute(FACEBOOK_USER_CLIENT);
}
}
Error message:
[ERROR] com.google.gwt.user.client.rpc.InvocationException: <script type="text/javascript">
[ERROR] top.location.href = "http://www.facebook.com/login.php?v=1.0&api_key=MY_KEY&next=notification";
[ERROR] </script>

HtmlUnit's WebClient fails on second identical loadWebResponse() call

I'm attempting to write tests for a very long and kludgy "getPost" method in a webapp I'm working on for my job. I'm using JUnit, HtmlUnit, and Jetty's ServletTester to approximate sending a request to a servlet and receiving a response. I've managed to get it mostly working, but I'm having a problem. I'm trying to test the login functionality. If the user logs in successfully, the server should send some JSON back to the client with the user's information. If the user is already logged in, the server should send back "result": "failure" and an error message.
My problem comes when I try to test the second requirement. I can log in successfully, and get the correct data back. However, when I try to send the request again, it returns 404: not found. I tried breaking the code up into different tests, but I have to be able to call login twice in order to test the second requirement. Later tests in the JUnit file run just fine, and the servlet is staying connected the same time. I tried making a second, identical request, but that also failed. I've searched the internet to no avail. In short, I'm stumped.
Here's what I'm working with (unnecessary code has been edited out):
//In MyFunServlet class:
private final static String USERID_ATTRIBUTENAME = "userid";
void doPost(HttpServletRequest request, HttpServletResponse response) {
String action = request.getParameter("opt");
final HttpSession session = request.getSession();
if(action != null){
Long userId = (Long)session.getAttribute(USERID_ATTRIBUTENAME);
if(userId != null){
//do stuffz
} else {
if(action.equals("login")) {
User user = LoginUser(request, response);
try{
JSONObject json = new JSONObject();
if(request.getAttribute("result") == "success"){
json.put("result", "success");
json.put("id", user.getId());
json.put("name", user.getName());
} else {
json.put("result", "failure");
json.put("message", request.getAttribute("message"));
}
SendJSONResponse(json, request, response);
}catch(Exception e){
}
} else {
System.out.print("Unknown opt: " + action);
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
}
}
}
}
private void LoginUser(HttpServletRequest request, HttpServletResponse response){
final HttpSession session = request.getSession();
User user = null;
Long userId = (Long)session.getAttribute(USERID_ATTRIBUTENAME);
if(userId != null){
request.setAttribute("result", "failure");
request.setAttribute("message", "The user is already logged in.");
} else {
final String email = request.getParameter("accountEmail");
final String password = request.getParameter("accountPassword");
if(email != null) {
user = helperClass.magicallyGetUserByEmail(email);
if(user != null){
if(user.getPassword().equals(password)){
session.setAttribute(USERID_ATTRIBUTENAME, user.getId();
request.setAttribute("result", "success");
}
}
} else {
request.setAttribute("result", "failure");
}
}
return user;
}
private void SendJSONResponse(JSONObject json, HttpServletRequest request,
HttpServletResponse response) {
String contentStr = json.toString();
response.setContentType("application/json");
response.setStatus( HttpServletResponse.SC_OK);
response.setContentLength(contentStr.length());
response.getWriter().print(contentStr);
response.flushBuffer();
}
For reference purposes, this file is 1084 lines long. The doPost method is about 900 of those. Disclaimer: this is not my code. I did not write it. I only have to test it.
Now for the test:
//In MyFunServletTest.java:
//using JUnit 4
public class MyFunServletTest {
static ServletTester tester;
static String baseUrl;
WebClient webClient = new WebClient();
User user;
WebRequest loginRequest;
#BeforeClass
public static void initClass(){
tester = new ServletTester;
tester.setContextPath("/");
tester.addServlet(MyFunServlet.class, "/fun.service");
baseUrl = tester.createSocketConnector(true);
tester.start();
}
#AfterClass
public static void cleanClass() {
tester.stop();
}
#Before
public void preTest(){
//taking values from our magical test user
user = new User();
user.setEmail("blah#blah.com");
user.setPassword("secure");
loginRequest = new WebRequest(baseUrl + "/fun.service", HttpMethod.POST);
List<NameValuePair> params = new ArrayList<NameValuePair>();
params.add(new NameValuePair("opt","login"));
params.add(new NameValuePair("accountEmail", user.getEmail());
params.add(new NameValuePair("accountPassword", user.getPassword());
loginRequest.setRequestParameters(params);
}
#Test
public void testLogin() {
WebResponse response = webClient.loadWebResponse(loginRequest);
JSONObject responseJSON = new JSONObject(response.getContentAsString());
//this test passes
assertEquals("success", responseJSON.getString("result"));
response = webClient.loadWebResponse(loginRequest);
//this test fails
assertTrue(404 != response.getStatusCode());
//this then causes an error, as response.getContentAsString() is null.
esponseJSON = new JSONObject(response.getContentAsString());
}
}
Help? Where am I missing something?
Thanks.
Without the ability to run the test myself, I can only offer some approaches:
Try creating two JSONObject objects to store the two responses separately, and compare the two (either print them or using the debugger), see if anything looks odd there.
If that doesn't tell you anything, create two separate identical request instances and use each.
Then try tracing through the call to loadWebResponse to see exactly what URL is being requested (cranking up the log level might tell you this, too).
If the 404 is correct, then the second request is somehow being mangled, but the question would be WHERE.

Categories