Android Application that connects to remote server - java

I am trying to build an Android App on Platform 2.2 Froyo. The app is supposed to connect to a remote server, fetch data from it and display to user in a different language.
So my question - What technologies I need to learn so that I can
build the above app.
Note - I have already installed the Android platform and have built simple apps like Hello, world. I know Java. Also I am using Eclipse.
Thank you for your answers. No rude comment please...
//--------------Code for connecting to web using HTTP protocol-----------------//
package in.androidbook.Networking;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.os.Bundle;
import android.widget.ImageView;
import android.widget.Toast;
public class MainActivity extends Activity
{
ImageView img;
/* This is for making asynchronous calls to ensure that connection to server will not return until data is received */
private class BackgroundTask extends AsyncTask<String, Void, Bitmap>
{
protected Bitmap doInBackground(String...url)
{
Bitmap bitmap = DownloadImage(url[0]);
return bitmap;
}
protected void onPostExecute(Bitmap bitmap)
{
ImageView img = (ImageView) findViewById(R.id.img);
img.setImageBitmap(bitmap);
}
}
// Code for making HTTP connection
private InputStream OpenHttpConnection(String urlString) throws IOException
{
InputStream in = null;
int response = -1;
URL url = new URL(urlString);//We take an object of class URL
URLConnection conn = url.openConnection(); //Create a connection object and open the connection
if(!(conn instanceof HttpURLConnection)) throw new IOException("Not an Http connection");
try
{
HttpURLConnection httpConn = (HttpURLConnection) conn; //httpConn object is assigned the value of conn. Typecasting is done to avoid conflict.
httpConn.setAllowUserInteraction(false);
httpConn.setInstanceFollowRedirects(true);
httpConn.setRequestMethod("GET");
httpConn.connect();
response = httpConn.getResponseCode();
if(response == HttpURLConnection.HTTP_OK)
in = httpConn.getInputStream();
}
catch (Exception ex)
{
throw new IOException("Error connecting");
}
return in;
}
//------------------------------------------ OpenHttpConnection method completed----------------------------------------------------------//
//----------------------------------------------------------------------------------------------------------------------------------------------------------------//
//-------------------------------Method to download an image--------------------------------------------------------------------------------------//
private Bitmap DownloadImage(String URL)
{
Bitmap bitmap = null;
InputStream in = null;
try
{
in = OpenHttpConnection(URL);
bitmap = BitmapFactory.decodeStream(in);
in.close();
}
catch(IOException e1)
{
Toast.makeText(this, e1.getLocalizedMessage(), Toast.LENGTH_LONG).show();
//Toast displays a short msg to user. this refers to current object.
e1.printStackTrace();
}
return bitmap;
}
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Bitmap bitmap = DownloadImage("http://i.zdnet.com/blogs/3-29-androids.jpg");
img = (ImageView) findViewById(R.id.img);
img.setImageBitmap(bitmap);
}
}

UPDATED (based on comment) : As we are talking about the client side, I confirm my answer. If the site is not yours, the first thing you need to do if check how/if it allows for some kind of communication via API, and in what kind of format (XML and JSON being the most used). With this information, it should be pretty easy. Take a look of the Android example using the Google Map or Twitter, you should find some.
Well, it depends what do you mean exactly : are you asking for the skills needed on the client side (the app) - in the idea that the server is already built, or will be by someone else, or the skills needed for the server ?
In the former case, I would advice to communicate with REST API and JSON. Take a look at apache HTTPGet, HTTPClient and HTTPResponse class and org.json (all are included in Android). If you want to test with them, use some public APIs (so you do not have to worry about the server), such as Google Map API (which is simple and free to use under some limits).
In the latter case, I'm with ColWinters : if you know java, use it there also, with Tomcat as a server and a basic Servlet. You'll find examples aplenty on the Internet.

Look up these technologies,
Apache Tomcat - Java Server Pages (server processing)
MySQL (storage of data)
and thats it. Also make sure to do the request in a seperate thread like an Async Task from an activity.

If you know java, I would suggest servlet as service hosted on server which reads data from mysql or anydatabase and contructs as json. in your android app make http using in buit httpclient to remote servlet and parse json response. So. Servlets,Httpclient,Json covers most if your app is native to phone app.

Related

Why OutputStreamWriter doesn't write data?

I'm very new to android programming and I have a problem with OutputStreamWriter. Now i'm working on simple test appication to send a POST requests to a server. There is a PHP application on the server side, which is designed to write data in a txt file.
When i run my android app it goes through the code and the commands in the "finally" block are performed. The server really writes in the txt file, but it puts empty data.
I've tried also with GET method and tried to check REQUEST array. But no data are sent.
If i delete from my code OutputStreamWriter I obtain the same picture, so it doesn't work at all.
I also tried with using encoding, the same result.
I have done through similar questions:
OutputStreamWriter not writing -
writing to file and problem was with its creating;
Android, Java: HTTP POST Request
- here and a few other questions is used httpClient which is depricated now.
HttpUrlConnection getOutputStream have plroblem, HttpUrlConnection getOutputStream have plroblem - here networking at main thread, but mine is run on the another one;
Android POST request not working, Java HttpURLConnection OutputStreamWriter is empty - is proposed to use reader to receive input stream. I've tried this solution, but the result was the same (no data sent)
My android version 6.0.1.
What is the problem may be in?
package com.victoria.requester;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import java.io.*;
import java.net.*;
import android.widget.TextView;
import java.io.IOException;
import java.net.URL;
import java.net.HttpURLConnection;
public class MainActivity extends AppCompatActivity {
public final static String EXTRA_MESSAGE = "EXTRA_MESSAGE";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
thread.start();
}
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
String url = "http://192.168.56.226:90/wfile.php";
String f_name = "123";
HttpURLConnection httpCon = null;
TextView textView = findViewById(R.id.textView);
textView.setText("OK1!");
try {
textView.setText("OK2!");
URL urlObj = new URL(url);
httpCon = (HttpURLConnection) urlObj.openConnection();
httpCon.setDoInput(true);
httpCon.setDoOutput(true);
//httpCon.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
httpCon.setRequestProperty("Content-Type", "text/html");
httpCon.setRequestMethod("POST");
String parameters = "f_name"+f_name;
//String parameters = URLEncoder.encode("f_name", "UTF-8") + "=" + URLEncoder.encode("123", "UTF-8");
httpCon.getResponseCode();
OutputStreamWriter writer = new OutputStreamWriter(httpCon.getOutputStream());
writer.write(parameters);
writer.close();
} catch (MalformedURLException e) {
textView.setText("bad URL!");
} catch (IOException e) {
textView.setText("network error!");
} finally {
httpCon.disconnect();
textView.setText("All done");
}
}
});
}
So, I've finally solved my problem, which actually was complex.
First of all, thank you, #Stephen C.
When I moved getResponseCode AFTER getOutputStream() my server begun to receive requests.
It's important that without calling getResponseCode the data weren't sent.
But the second trouble was with string:
String parameters = "f_name" + f_name;
As I lost "=" the name of the element in the POST array was 'f_name123' and value was empty))) That's why i couldn't pick the data up from $_POST["f_name"].
Also I'd like to notice that textview doesn't work only if accessing within the connection. After disconnected you can use textview.
Thank to all for help.
The next code works for me now:
package com.victoria.requester;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import java.io.*;
import java.net.*;
import android.widget.TextView;
import java.io.IOException;
import java.net.URL;
import java.net.HttpURLConnection;
public class MainActivity extends AppCompatActivity {
public final static String EXTRA_MESSAGE = "EXTRA_MESSAGE";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
thread.start();
}
Thread thread;
{
thread = new Thread(new Runnable() {
#Override
public void run() {
String url = "http://192.168.56.226:90/wfile.php";
String f_name = "123";
HttpURLConnection httpCon = null;
TextView textView = findViewById(R.id.textView);
try {
URL urlObj = new URL(url);
httpCon = (HttpURLConnection) urlObj.openConnection();
httpCon.setDoOutput(true);
httpCon.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
httpCon.setRequestMethod("POST");
String parameters = "f_name=" + f_name;
OutputStreamWriter writer = new
OutputStreamWriter(httpCon.getOutputStream());
writer.write(parameters);
writer.flush();
writer.close();
httpCon.getResponseCode();
} catch (MalformedURLException e) {
textView.setText("bad URL!");
} catch (IOException e) {
textView.setText("network error!");
} finally {
httpCon.disconnect();
textView.setText("All done");
}
}
});
}
}
The problem is that you are calling httpCon.getOutputStream() and writing the request data after you have called httpCon.getResponseCode().
The correct order is:
Call getConnection() to get the connection object for the URL
In any order:
Set the request method
Set any request properties
Call setDoOutput(true) since you intend to use the output stream.
Call getOutputStream()
Write the data.
Close the stream.
Get the response code.
What you are currently doing will throw an exception when you call getOutputStream. The remote server can only send you a response after it has read the request data. When you call getResponseCode() this tells the client-side stack to indicate to the server that there is no more request data.
In addition, you should not call setDoInput(true) if you don't intend to read data from the connection in some circumstances. At the very least it is redundant.

Upload image twitter4j

I introduced myself to Twitter4j yesterday, and are now testing out features for an upcoming program of mine. As the title suggests, I am trying to upload an image to twitter, without any luck. Here's my code:
import static java.awt.Toolkit.getDefaultToolkit;
import static javax.swing.JOptionPane.ERROR_MESSAGE;
import static javax.swing.JOptionPane.showMessageDialog;
import java.awt.Image;
import java.io.File;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import twitter4j.Status;
import twitter4j.Twitter;
import twitter4j.TwitterException;
import twitter4j.TwitterFactory;
import twitter4j.examples.tweets.UploadMultipleImages;
import twitter4j.media.ImageUpload;
import twitter4j.media.ImageUploadFactory;
public final class UpdateStatus {
static File file = new File("/images/Done.jpg");
public static void main(String[] args) {
for(int i=0;i<2;i++){
Twitter twitter = new TwitterFactory().getInstance();
Status status=null;
try {
ImageUpload.upload(file,"22");
} catch (TwitterException e) {
System.err.println("Shit...");
System.exit(3);
}
}
System.out.println("Done");
}
}
The image I'm trying to upload is Done.jpg, and is in a folder in the package. I've used this method for images in other programs, so I am pretty sure it works. Though, this gives me an error message before I run the code, saying "Cannot make a static reference to the non-static method upload(File, String) from the type ImageUpload". Any ideas that could help me? :D
You need to ensure following before testing your code -
Register your app at https://apps.twitter.com/ and get Oauth tokens to be able to connect your app to Twitter and perform desired action.
You will get a consumerKey,consumerAccessToken, accessKey and accessToken.
If you want to post updates, please ensure you configure your app
permissions to have a Read and Write access, deafult access is Read
Only.
After you have the required access tokens, you need to instantiate a Twitter instance using those tokens. This instance can then be used to perform requisite action. See sample code below to upload an image -
ConfigurationBuilder twitterConfigBuilder = new ConfigurationBuilder();
twitterConfigBuilder.setDebugEnabled(true);
twitterConfigBuilder.setOAuthConsumerKey("consumerkey");
twitterConfigBuilder.setOAuthConsumerSecret("consumersecret");
twitterConfigBuilder.setOAuthAccessToken("accesstoken");
twitterConfigBuilder.setOAuthAccessTokenSecret("accesstokensecret");
Twitter twitter = new TwitterFactory(twitterConfigBuilder.build()).getInstance();
String statusMessage = "Watch out this interesting offer I came across today";
File file = new File("/images/Done.jpg");
StatusUpdate status = new StatusUpdate(statusMessage);
status.setMedia(file); // set the image to be uploaded here.
twitter.updateStatus(status);
Hope this helps.
ImageUpload.upload is not a static method, but an instance method.
You need to create an instance of ImageUpload, and call the method from the instance.
Checking the documentation of ImageUpload, it is an interface. So you'll need to instantiate a class that implements ImageUpload.

Using NTLM authentication in Java applications

I want to use Windows NTLM authentication in my Java application to authenticate intranet users transparently. The users should not notice any authentication if using their browsers (single sign-on).
I've found a few libs with NTLM support, but don't know which one to use:
http://spnego.sourceforge.net/
http://sourceforge.net/projects/ntlmv2auth/
http://jcifs.samba.org/
http://www.ioplex.com/jespa.html
http://www.luigidragone.com/software/ntlm-authentication-in-java/
Any suggestions where to start?
Out of the above list, only ntlmv2-auth and Jespa support NTLMv2. Jespa is workable but commercial. ntlmv2-auth I haven't tried but it's based on the code from Liferay, which I've seen working before.
'ntlm-authentication-in-java' is only NTLMv1, which is old, insecure, and works in a dwindling number of environments as people upgrade to newer Windows versions. JCIFS used to have an NTLMv1 HTTP auth filter, but it was removed in later versions, as the way it was implemented amounts to a man-in-the-middle attack on the insecure protocol. (The same appears to be true of 'ntlm-authentication-in-java'.)
The 'spnego' project is Kerberos not NTLM. If you want to replicate full IWA as IIS does it, you'd need to support both NTLMv2 and Kerberos ('NTLM' auth, 'Negotiate' auth, NTLMSSP-in-SPNego auth and NTLM-masquerading-as-Negotiate auth).
Luigi Dragone's script is really old and seems to always fail.
HttpURLConnection can work with NTLM if you add library jcifs, this example works with latest jcifs-1.3.18 :
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.Map;
import org.apache.http.impl.auth.NTLMEngineException;
public class TestNTLMConnection {
public static void main(String[] args) throws UnknownHostException, IOException, NTLMEngineException {
// Method 1 : authentication in URL
jcifs.Config.registerSmbURLHandler();
URL urlRequest = new URL("http://domain%5Cuser:pass#127.0.0.1/");
// or Method 2 : authentication via System.setProperty()
// System.setProperty("http.auth.ntlm.domain", "domain");
// System.setProperty("jcifs.smb.client.domain", "domain");
// System.setProperty("jcifs.smb.client.username", "user");
// System.setProperty("jcifs.smb.client.password", "pass");
// Not verified // System.setProperty("jcifs.netbios.hostname", "host");
// System.setProperty("java.protocol.handler.pkgs", "jcifs");
// URL urlRequest = new URL("http://127.0.0.1:8180/simulate_get.php");
HttpURLConnection conn = (HttpURLConnection) urlRequest.openConnection();
StringBuilder response = new StringBuilder();
try {
InputStream stream = conn.getInputStream();
BufferedReader in = new BufferedReader(new InputStreamReader(stream));
String str = "";
while ((str = in.readLine()) != null) {
response.append(str);
}
in.close();
System.out.println(response);
} catch(IOException err) {
System.out.println(err);
} finally {
Map<String, String> msgResponse = new HashMap<String, String>();
for (int i = 0;; i++) {
String headerName = conn.getHeaderFieldKey(i);
String headerValue = conn.getHeaderField(i);
if (headerName == null && headerValue == null) {
break;
}
msgResponse.put(headerName == null ? "Method" : headerName, headerValue);
}
System.out.println(msgResponse);
}
}
}
And if you are curious about the content of each handshake, you can find another example using jcifs and Socket on this thread.
Had to recently implement this at work hence here is updated solution with Spring's RestTemplate:
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.NTCredentials;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.HttpClients;
import org.springframework.http.HttpEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
import java.io.IOException;
public class Runner {
public static void main(String[] args) {
var credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(AuthScope.ANY, new NTCredentials("username", "password", "", "someDomain"));
try (var client = HttpClients.custom()
.setDefaultCredentialsProvider(credentialsProvider)
.build();) {
var requestFactory = new HttpComponentsClientHttpRequestFactory();
requestFactory.setHttpClient(client);
RestTemplate restTemplate = new RestTemplate(requestFactory);
ResponseEntity<String> stringResponseEntity = restTemplate.postForEntity("url", new HttpEntity<>("yourDtoObject"), String.class);
} catch (IOException e) {
e.printStackTrace();
}
}
}
dependencies needed are: spring-web and org.apache.httpcomponents
ps: it is important to enter username without domain otherwise it doesn't work. As in if your domain is companyName/username often people just enter that whole thing as username and what you should do is enter them separately where domain="companyName" and username="username"
Ref: https://jcifs.samba.org/src/docs/faq.html#ntlmv2
Q: Does jCIFS support NTLMv2?
A: Yes. As of 1.3.0, JCIFS fully supports NTLMv2 and uses it by default.
Note: The NTLM HTTP SSO Filter that used to be included with JCIFS cannot support NTLMv2.
Relatively from the list you gave,I would go with JCIFS.
The library is mature , and their documentation is good.
To top it off they had fairly regular releases , and the last one being Nov 2011.
Personal Experience : it was fairly easy to get started when compared to others i have tried (spnego and ntmv2auth)

Communication between my Java Server and Local Client

I've been writing easy Java Server. I'm gonna deploy this code to my student's server and run it there.
public class Demo {
public static void main(String[] args) {
String port = "50000";
ServerAttributes attr = new ServerAttributes();
attr.setPort(Integer.parseInt(port));
Socket socket = null;
ServerSocket serverSocket= null;
try {
serverSocket = new ServerSocket(attr.getPort());
System.out.println("Waiting for accept...");
while(true) {
socket = serverSocket.accept();
// TODO
socket.close();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
I wanna create easy Client code which will be 'talking' with my server. Communication Client->Server is easy. My server is visible for client. But what should I do to provide communication in another way?
Maybe REST is good idea? So, how can I 'teach' my server to answer on REST queries?
I've got piece of code which send data to my GAE server:
package enceladus.server.trash.rest;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
public class RESTGAEServer {
static String httpAddress = "http://*********.appspot.com/sign";
public static void main(String[] args) {
HttpClient client = new DefaultHttpClient();
HttpPost post = new HttpPost(httpAddress);
try {
List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>();
nameValuePairs.add(new BasicNameValuePair("guestbookName", "default"));
nameValuePairs.add(new BasicNameValuePair("content", "TEST"));
post.setEntity(new UrlEncodedFormEntity(nameValuePairs));
#SuppressWarnings("unused")
HttpResponse response = client.execute(post);
} catch (IOException e) {
e.printStackTrace();
}
}
}
Thanks in advance
If you are trying to provide RESTFul service from the server, its not an easy task. What you might want to do is user something like Restlet for bootstrapping your RESTFul server and client.
For more information refer to http://wiki.restlet.org/docs_2.0/13-restlet/21-restlet/318-restlet.html
REST is a very simple an easy way of communicating between a client and a server. REST basically says, use HTTP the way it was meant to be used, even when communicating between computer programs.
Read up on HTTP in case you do not have enough knowledge. Here is one good document: http://www.jmarshall.com/easy/http/
Once you understand how to send and receive HTTP messages on the client and on the server, you are ready to develop RESTful server API:s.
What you need to know about REST is that it is mostly a way of thinking when you design your API. Make sure to utilize HTTP to its full extent and send/receive data in whatever format (usually JSON, XML or UrlEncoded key/value pairs).
I would say you are MUCH better off doing this yourself than to try to learn Restlet or some other huge library at the same time you learn REST. REST and HTTP are both easy stuff - once you get down to the "it's just some text going back and fourth". When you understand these things fully, then you could look at some frameworks.
Here is some information about REST:
http://rest.elkstein.org/

Why does this HTTP servlet behave inconsistently?

An intranet site has a search form which uses AJAX to call a servlet on a different domain for search suggestions.
This works in Internet Explorer with the intranet domain being a "trusted site" and with cross-domain requests enabled for trusted sites, but doesn't work in Firefox.
I have tried to work around the problem by creating a servlet on the intranet server, so there's a JS call to my servlet on the same domain, then my servlet calls the suggestions servlet on the other domain. The cross-domain call is server-side, so it should work regardless of browser settings.
The AJAX call and my servlet's call to the other servlet both use a HTTP POST request with arguments in the URL and empty request-content.
The reason I'm sticking with POST requests is that the JS code is all in files on the search server, which I can't modify, and that code uses POST requests.
I've tried calling the customer's existing suggestions servlet with a GET request, and it produces a 404 error.
The problem is that the result is inconsistent.
I've used System.out.println calls to show the full URL and size of the result on the server log.
The output first seemed to change depending on the calling browser and/or website, but now seems to change even between sessions of the same browser.
E.g. entering "g" in the search box, I got this output from the first few tries on the Development environment using Firefox:
Search suggestion URL: http://searchdev.companyname.com.au/suggest?q=g&max=10&site=All&client=ie&access=p&format=rich
Search suggestion result length: 64
Initial tries with Firefox on the Test environment (different intranet server but same search server) produced a result length of 0 for the same search URL.
Initial tries with Internet Explorer produced a result length of 0 in both environments.
Then I tried searching for different letters, and found that "t" produced a result in IE when "g" hadn't.
After closing the browsers and leaving it for a while, I tried again and got different results.
E.g. Using Firefox and trying "g" in the Development environment now produces no result when it was previously producing one.
The inconsistency makes me think something is wrong with my servlet code, which is shown below. What could be causing the problem?
I think the search suggestions are being provided by a Google Search Appliance, and the JS files on the search server all seem to have come from Google.
The actual AJAX call is this line in one file:
XH_XmlHttpPOST(xmlhttp, url, '', handler);
The XH_XmlHttpPOST function is as follows in another file:
function XH_XmlHttpPOST(xmlHttp, url, data, handler) {
xmlHttp.open("POST", url, true);
xmlHttp.onreadystatechange = handler;
xmlHttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xmlHttp.setRequestHeader("Content-Length",
/** #type {string} */ (data.length));
XH_XmlHttpSend(xmlHttp, data);
}
Here is my servlet code:
package com.companyname.theme;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Properties;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class suggest extends HttpServlet {
Properties props=null;
#Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String result = "";
String args = req.getQueryString();
String baseURL = props.getProperty("searchFormBaseURL");
String urlStr = baseURL + "/suggest?" + args;
System.out.println("Search suggestion URL: " + urlStr);
try {
int avail, rCount;
int totalCount = 0;
byte[] ba = null;
byte[] bCopy;
URL url = new URL(urlStr);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("POST");
conn.setDoOutput(true);
OutputStream os = conn.getOutputStream();
os.write("".getBytes());
os.close();
InputStream is = conn.getInputStream();
while ((avail = is.available()) > 0) {
if (ba == null) ba = new byte[avail];
else if (totalCount + avail > ba.length) {
// Resize ba if there's more data available.
bCopy = new byte[totalCount + avail];
System.arraycopy(ba, 0, bCopy, 0, totalCount);
ba = bCopy;
bCopy = null;
}
rCount = is.read(ba, totalCount, avail);
if (rCount < 0) break;
totalCount += rCount;
}
is.close();
conn.disconnect();
result = (ba == null ? "" : new String(ba));
System.out.println("Search suggestion result length: " + Integer.toString(result.length()));
} catch(MalformedURLException e) {
e.printStackTrace();
} catch(IOException e) {
e.printStackTrace();
}
PrintWriter pw = resp.getWriter();
pw.print(result);
}
#Override
public void init() throws ServletException {
super.init();
InputStream stream = this.getClass().getResourceAsStream("/WEB-INF/lib/endeavour.properties");
props = new Properties();
try {
props.load(stream);
stream.close();
} catch (Exception e) {
// TODO: handle exception
}
}
}
Solution: don't rely on InputStream.available().
The JavaDoc for that method says it always returns 0.
HttpURLConnection.getInputStream() actually returns a HttpInputStream, in which available() seems to work but apparently sometimes returns 0 when there is more data.
I changed my read loop to not use available() at all, and now it consistently returns the expected results.
The working servlet is below.
package com.integral.ie.theme;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Properties;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class suggest extends HttpServlet implements
javax.servlet.Servlet {
Properties props=null;
#Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
//super.doPost(req, resp);
final int maxRead=200;
String result="";
String args=req.getQueryString();
String baseURL=props.getProperty("searchFormBaseURL");
String urlStr=baseURL+"/suggest?"+args;
//System.out.println("Search suggestion URL: "+urlStr);
try {
int rCount=0;
int totalCount=0;
int baLen=maxRead;
byte[] ba=null;
byte[] bCopy;
URL url=new URL(urlStr);
HttpURLConnection conn=(HttpURLConnection)url.openConnection();
conn.setRequestMethod("POST");
// Setting these properties may be unnecessary - just did it
// because the GSA javascript does it.
conn.setRequestProperty("Content-Type","application/x-www-form-urlencoded");
conn.setRequestProperty("Content-Length","0");
InputStream is=conn.getInputStream();
ba=new byte[baLen];
while (rCount>=0) {
try {
rCount=is.read(ba,totalCount,baLen-totalCount);
if (rCount>0) {
totalCount+=rCount;
if (totalCount>=baLen) {
baLen+=maxRead;
bCopy=new byte[baLen];
System.arraycopy(ba,0,bCopy,0,totalCount);
ba=bCopy;
bCopy=null;
}
}
} catch(IOException e) {
// IOException while reading - allow the method to return
// anything we've read so far.
}
}
is.close();
conn.disconnect();
result=(totalCount==0?"":new String(ba,0,totalCount));
//System.out.println("Search suggestion result length: "
//+Integer.toString(result.length()));
} catch(MalformedURLException e) {
e.printStackTrace();
} catch(IOException e) {
e.printStackTrace();
}
PrintWriter pw=resp.getWriter();
pw.print(result);
}
#Override
public void init() throws ServletException {
super.init();
InputStream stream=this.getClass().getResourceAsStream("/WEB-INF/lib/endeavour.properties");
props=new Properties();
try {
props.load(stream);
stream.close();
} catch (Exception e) {
// TODO: handle exception
}
}
}
Start with a unit test. Servlets are pretty straightforward to unit test and HttpUnit has worked for us.
Debugging Servlet code in a browser and with println calls will cost more time in the long run and it's difficult for someone on SO to digest all of that information to help you.
Also, consider using a JavaScript framework such as JQuery for your AJAX calls. In my opinion there's little reason to touch an xmlHttp object directly now that frameworks will hide that for you.

Categories

Resources