How do you access posted form data in a servlet? - java

If I have a servlet running JVM1.4.2, and it is receiving a POST request with form data fields. I use req.getParameterNames() to get, what I would expect, all the query string and form data. However, all I ever get are the querystring parameters.
Literature I am reading from various sources says that getParameterNames() and getParameterValues(String) should be the way to get all query string and posted form data sent by the browser for JDK 1.4. Here is the method I use to extract all the parameters, which I expect would include posted form data :
public Map getParameterMap(HttpServletRequest req) {
Map params= new HashMap();
String name = null;
System.out.println("<< Getting Parameter Map.>>");
Enumeration enumParams = req.getParameterNames();
for (; enumParams.hasMoreElements(); ) {
// Get the name of the request parameter
name = (String)enumParams.nextElement();
// Get the value of the request parameters
// If the request parameter can appear more than once
// in the query string, get all values
String[] values = req.getParameterValues(name);
params.put(name, values);
String sValues = "";
for(int i=0;i<values.length;i++){
if(0<i) {
sValues+=",";
}
sValues +=values[i];
}
System.out.println("Param " + name + ": " + sValues);
}
System.out.println("<< END >>");
return params;
}
This question also agrees with my expectations, but the servlet is not picking up the form data. Obviously I am missing something....
Update: The post data is very straight forward and is not a Multipart form or rich media. Just plain'ol text submitted via an AJAX POST that looks like this in post body
c1=Value%20A&c2=Value%20B&c3=Value%20C

I managed to identify the problem. Because there is so much chatter from JDK 1.5+ and talk of getParameterMaps() method for 1.5, info on how 1.4 handles form post data was scarce and ambiguous. (Please post a comment if you find something that is specific for 1.4).
Pre-1.5 you have to manually get the form data via getInputStream, and then parse it out. I found this method, (posted below), from the java sun site that does a nice job using a Hashtable. I had to make a minor mod for deprecated methods. But seems to work quite robustly, "out of the box", so you should able to just cut-n-paste. I know it's "old tech" but I thought it worthwhile for those who may be in the same situation as me who are stuck on solving (what seems to be) straight forward problems.
public Hashtable parsePostData(int length, ServletInputStream instream) {
String valArray[] = null;
int inputLen, offset;
byte[] postedBytes = null;
boolean dataRemaining=true;
String postedBody;
Hashtable ht = new Hashtable();
//Vector paramOrder = new Vector(10);
StringBuffer sb = new StringBuffer();
if (length <=0) {
return null;
}
postedBytes = new byte[length];
try {
offset = 0;
while(dataRemaining) {
inputLen = instream.read (postedBytes, offset, length - offset);
if (inputLen <= 0) {
throw new IOException ("read error");
}
offset += inputLen;
if((length-offset) ==0) {
dataRemaining=false;
}
}
} catch (IOException e) {
System.out.println("Exception ="+e);
return null;
}
postedBody = new String (postedBytes);
StringTokenizer st = new StringTokenizer(postedBody, "&");
String key=null;
String val=null;
while (st.hasMoreTokens()) {
String pair = (String)st.nextToken();
int pos = pair.indexOf('=');
if (pos == -1) {
throw new IllegalArgumentException();
}
try {
key = URLDecoder.decode(pair.substring(0, pos),"UTF8");
val = java.net.URLDecoder.decode(pair.substring(pos+1,pair.length()),"UTF8");
} catch (Exception e) {
throw new IllegalArgumentException();
}
if (ht.containsKey(key)) {
String oldVals[] = (String []) ht.get(key);
valArray = new String[oldVals.length + 1];
for (int i = 0; i < oldVals.length; i++) {
valArray[i] = oldVals[i];
}
valArray[oldVals.length] = val;
} else {
valArray = new String[1];
valArray[0] = val;
}
ht.put(key, valArray);
String sValues = "";
for(int i=0;i<valArray.length;i++) {
if (0<i) {
sValues+=",";
}
sValues = valArray[i];
}
System.out.println("Form data field " + key + ":" +sValues);
//paramOrder.addElement(key);
}
return ht;
}

That's true. The getParameterNames(), getParameterValues(), and getParameter() methods are the way to access form data unless it's a multipart form, in which case you'll have to use something like Commons Fileupload to parse the multipart request before all the parameters are accessible to you.
Edit: You're probably not encoding the POST data properly in your AJAX call. POST data must carry a Content-Type of application/x-www-form-urlencoded or else multipart/form-data. If you're sending it as something else, it doesn't qualify as a request parameter, and I expect you'd see the behavior you're describing. The solution you've engineered essentially consists of setting up custom parsing of custom content.

Related

Strange JSON-String distortion in Tomcat

Please explain to me, what is the cause of distortion of JSON-String sent from JavaFX application to Tomcat server. Part of symbols is replaced by strange square symbols:
Conversation to JSON pass correctly – I checked it by printing JSON-String to console in JavaFX app after serializing. But after transfer to Tomcat-server by DoPost method JSON-String is distorted. And I tried to transfer XML too – it distorts by the same way.
After small investigation, I got some result: String sent by DoPost stays correct if it's length is 7782 symbols or less. Plus one symbol – and squares appears:
Both the JavaFX app and Tomcat server starts on local machine under Intellij IDEA, so it's not a network problem.
Very thanks to user Nick on ru.stackoverflow.com:
https://ru.stackoverflow.com/users/216863/nick
He gave exact cause – length of inputStream. GZIP gives not correct value of inputStream length to Servlet.
And when I replaced this code:
`public String getInputString(HttpServletRequest req) {
String receivedString = "";
int len = req.getContentLength();
byte[] input = new byte[len];
try {
ServletInputStream sin = req.getInputStream();
int c = 0;
int count = 0;
while ((c = sin.read(input, count, (input.length - count))) > 0) {
count += 1;
}
sin.close();
} catch (IOException e) {
}
receivedString = new String(input);
return receivedString;
}`[PasteBin-1][1]
by this:
`public String getInputString(HttpServletRequest req) {
String receivedString = "";
try (BufferedReader reader = new BufferedReader(new InputStreamReader(req.getInputStream()))) {
StringBuilder sb = new StringBuilder("");
while (reader.ready()) {
sb.append(reader.readLine());
}
if (sb.length() > 0) {
receivedString = sb.toString();
}
} catch (IOException e) {
}
return receivedString;
}`[PasteBin-2][2]
everything started to work correctly.
This is same question thread on russian:
answer on ru.stackoverflow.com

Where to declare the ArrayList, if I want dynamically to fill it

I have a Java Server, I want to store all IP, which send requests to Server, because I want to check, if user was here already, and if he was I want to send him another message.
There method sendStaticResource at the bottom should do it.
For this purpose I want to use ArrayList ips, which I will check every time, when request comes. My problem is that the values(IPs) won't be store - each time request is made, the old IP disappears ( so the length is always 1).
So the question is - where and how could I initialise the ArrayList, so each time request is made, ip goes in the Arraylist and the old won't disappear
public class Response {
Request request;
OutputStream output;
ArrayList<String> ips = new ArrayList<String>();
public Response(OutputStream output) {
this.output = output;
}
public void setRequest(Request request) {
this.request = request;
}
public String getIp() {
InetAddress thisIp = null;
try {
thisIp = InetAddress.getLocalHost();
} catch (Exception e) {
System.out.println(e.toString() );
}
return thisIp.getHostAddress();
}
public void content(String header, int contentLength, String msg ) throws IOException {
System.out.println(msg);
String message = "HTTP/1.1" + header + "\r\n" +
"Content-Type: text/html\r\n" +
"Content-Length: "+ contentLength + "\r\n" +
"\r\n" +
"<h1>" + msg + "</h1>";
try {
output.write(message.getBytes());
output.flush();
} catch (Exception e) {
System.out.println(e.toString());
}
}
public void sendStaticResource() throws IOException {
if(request.getUri().equals("/")) {
content("200 OK", 20, "Hello world");
ips.add (getIp());
System.out.println(ips.size());
}
}
}
I suppose that each a request comes in you create a new Response object. Since the ips list is an instance member of the Response object, each time you create a new instance of it you also create a new list of ips.
If you want to have a list of strings storing all the ips of the requests you have received, I suggest you modify your code in such manner that the ip list is a static field of the Response object.
This way you'll end up with something that is shared between all the objects of the Response class and not something unique to each instance. For more information I would suggest to check on the differences between instance and static members of a class. For more you can check here: https://docs.oracle.com/javase/tutorial/java/javaOO/classvars.html
Finally a small hint concerning code style and best practices revolves around this line:
ArrayList<String> ips = new ArrayList<String>();
I would strongly suggest to user something like this:
List<String> ips = new ArrayList<String>();
In my honest opinion it's much better to code to and interface rather than the concrete implementation.

Error properly reading JSON file in Android

I am having an odd issue reading in the following JSON formatted API - Trivia API (this URL is passed in through the Async). I will explain in more detail below, but basically my code appears to be not properly reading in a JSON file, which results in me being unable to loop through it and set my question objects accordingly.
Logs revealed that the reading of the input stream at IOUtils seems to not finish reading the entire JSON file, often stopping by the 11th question object within the file.
I think this has to do with a Log limit, as when I add more to the Log (such as "This is the json String -") it prints less, but this does not explain why it is not properly looping through the question objects and setting them accordingly. Other logs revealed that it does not even loop through the questions, it only goes through one time and only sets the title.
I am clueless as to what is happening as I have parsed other JSON files literally the exact same way as this.
#Override
protected ArrayList<Question> doInBackground(String... strings) {
HttpURLConnection connection = null;
ArrayList<Question> result = new ArrayList<>();
try {
URL url = new URL(strings[0]);
connection = (HttpURLConnection) url.openConnection();
connection.connect();
if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) {
String json = IOUtils.toString(connection.getInputStream(), "UTF-8");
Log.d("demo", "This is the json String - " + json);
JSONObject root = new JSONObject(json);
JSONArray questions = root.getJSONArray("questions");
for (int i = 0; i < questions.length(); i++) {
JSONObject questionsJSONObject = questions.getJSONObject(i);
Question question = new Question();
question.setText(questionsJSONObject.getString("text"));
//Choice array
JSONObject choicesObject = questionsJSONObject.getJSONObject("choices");
String[] temp = new String[choicesObject.getJSONArray("choice").length()];
for (int j = 0; j < choicesObject.getJSONArray("choice").length(); j++) {
temp[j] = choicesObject.getJSONArray("choice").getJSONObject(j).toString();
}
question.setChoices(temp);
//End of Choice array
question.setAnswer(questionsJSONObject.getInt("answer");
question.setUrlToImage(questionsJSONObject.getString("image"));
result.add(question);
}
}
} catch (Exception e) {
//Handle Exceptions
} finally {
//Close the connections
}
Log.d("demo", "toString - " + result.toString());
return result;
}

How to send multiple arrays from servlet to jsp using sendRedirect

I need to forward two arrays, processed by a servlet to a jsp page to display them. I have been successful in forwarding a single array using the code below:
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {
String n = request.getParameter("name");
int k = Integer.parseInt(n);
int array[] = new int[3];
PrintWriter out = response.getWriter();
List<RecommendedItem> recommendations = new ArrayList<RecommendedItem>();
try {
recommendations = App.getRecommend(k);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
int i = 0;
// out.println("we recommend");
for (RecommendedItem recommendation : recommendations) {
// out.println(recommendation.getItemID()+" "
// +recommendation.getValue());
array[i] = (int) recommendation.getItemID();
i++;
}
String param = Arrays.toString(array);
param = param.substring(1, param.length() - 1);// removing enclosing []
String encArray = URLEncoder.encode(param, "utf-8");
// Send encArray as parameter.
response.sendRedirect(("output.jsp?fib=" + encArray));
}
But now I want to forward a second array composed of (int)recommendation.getValue() to the output.jsp. Can it be done using response.sendRedirect()?
It's possible to send any String you want via parameters. In your case, just add a second parameter to your redirect with your second encoded array, like in:
response.sendRedirect(("output.jsp?fib=" + encArray + "&fib2=" + encArray2));
And get it in the same way you did with fib parameter.
However, a better and preferable way to handle the transportation of values in a Servlet would be via RequestDispatcher. Code for that:
req.setAttribute("fib", encArray);
req.setAttribute("fib2", encArray2);
req.getRequestDispatcher("output.jsp").forward(req, response);
And recover the arrays in the JSP using:
<%
String encArray = (String) request.getAttribute("fib");
// ...
String encArray2 = (String) request.getAttribute("fib2");
%>
Note: With this second option you can even set and recover the Java array object (String[]) without needing to encode it.

Multithreading (Stateless Classes)

Apologies for the long code post but am wondering if someone can help with a multithreading question (I am quite new to multi-threading). I am trying to design a facade class to a RESTFUL web services API that can be shared with multiple threads. I am using HttpURLConnection to do the connection and Google GSON to convert to and from JSON data.
The below class is what I have so far. In this example it has one public method to make an API call (authenticateCustomer()) and the private methods are used to facilitate the API call (i.e to build the POST data string, make a POST request etc).
I make one instance of this class and share it with 1000 threads. The threads call the authenticateCustomer() method. Most of the threads work but there is some threads that get a null pointer exception which is because I haven't implemented any synchronization. If I make the authenticateCustomer() method 'synchronized' it works. The problem is this results in poor concurrency (say, for example, the POST request suddenly takes a long time to complete, this will then hold up all the other threads).
Now to my question. Is the below class not stateless and therefore thread-safe? The very few fields that are in the class are declared final and assigned in the constructor. All of the methods use local variables. The Gson object is stateless (according to their web site) and created as a local variable in the API method anyway.
public final class QuizSyncAPIFacade
{
// API Connection Details
private final String m_apiDomain;
private final String m_apiContentType;
private final int m_bufferSize;
// Constructors
public QuizSyncAPIFacade()
{
m_apiDomain = "http://*****************************";
m_apiContentType = ".json";
m_bufferSize = 8192; // 8k
}
private String readInputStream(InputStream stream) throws IOException
{
// Create a buffer for the input stream
byte[] buffer = new byte[m_bufferSize];
int readCount;
StringBuilder builder = new StringBuilder();
while ((readCount = stream.read(buffer)) > -1) {
builder.append(new String(buffer, 0, readCount));
}
return builder.toString();
}
private String buildPostData(HashMap<String,String> postData) throws UnsupportedEncodingException
{
String data = "";
for (Map.Entry<String,String> entry : postData.entrySet())
{
data += (URLEncoder.encode(entry.getKey(), "UTF-8") + "=" + URLEncoder.encode(entry.getValue(), "UTF-8") + "&");
}
// Trim the last character (a trailing ampersand)
int length = data.length();
if (length > 0) {
data = data.substring(0, (length - 1));
}
return data;
}
private String buildJSONError(String message, String name, String at)
{
String error = "{\"errors\":[{\"message\":\"" + message + "\",\"name\":\"" + name + "\",\"at\":\"" + at + "\"}]}";
return error;
}
private String callPost(String url, HashMap<String,String> postData) throws IOException
{
// Set up the URL for the API call
URL apiUrl = new URL(url);
// Build the post data
String data = buildPostData(postData);
// Call the API action
HttpURLConnection conn;
try {
conn = (HttpURLConnection)apiUrl.openConnection();
} catch (IOException e) {
throw new IOException(buildJSONError("Failed to open a connection.", "CONNECTION_FAILURE", ""));
}
// Set connection parameters for posting data
conn.setRequestMethod("POST");
conn.setUseCaches(false);
conn.setDoInput(true);
conn.setDoOutput(true);
// Write post data
try {
DataOutputStream wr = new DataOutputStream(conn.getOutputStream());
wr.writeBytes(data);
wr.flush();
wr.close();
} catch (IOException e) {
throw new IOException(buildJSONError("Failed to post data in output stream (Connection OK?).", "POST_DATA_FAILURE", ""));
}
// Read the response from the server
InputStream is;
try {
is = conn.getInputStream();
} catch (IOException e) {
InputStream errStr = conn.getErrorStream();
if (errStr != null)
{
String errResponse = readInputStream(errStr);
throw new IOException(errResponse);
}
else
{
throw new IOException(buildJSONError("Failed to read error stream (Connection OK?).", "ERROR_STREAM_FAILURE", ""));
}
}
// Read and return response from the server
return readInputStream(is);
}
/* -------------------------------------
*
* Synchronous API calls
*
------------------------------------- */
public APIResponse<CustomerAuthentication> authenticateCustomer(HashMap<String,String> postData)
{
// Set the URL for this API call
String apiURL = m_apiDomain + "/customer/authenticate" + m_apiContentType;
Gson jsonConv = new Gson();
String apiResponse = "";
try
{
// Call the API action
apiResponse = callPost(apiURL, postData);
// Convert JSON response to the required object type
CustomerAuthentication customerAuth = jsonConv.fromJson(apiResponse, CustomerAuthentication.class);
// Build and return the API response object
APIResponse<CustomerAuthentication> result = new APIResponse<CustomerAuthentication>(true, customerAuth, null);
return result;
}
catch (IOException e)
{
// Build and return the API response object for a failure with error list
APIErrorList errorList = jsonConv.fromJson(e.getMessage(), APIErrorList.class);
APIResponse<CustomerAuthentication> result = new APIResponse<CustomerAuthentication>(false, null, errorList);
return result;
}
}
}
If you are getting an error it could be because you are overloading the authentication service (something which doesn't happen if you do this one at a time) Perhaps it returning a error like 500, 503 or 504 which you could be ignoring and getting nothing you expect back, you return null http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
I would use less threads assuming you don't have 1000 cpus, its possible having this many threads will be slower rather than more efficeint.
I would also check that your service is returning correctly every time and investigate why you get a null value.
If your service can only handle say 20 requests at once, you can try using a Semaphore as a last resort. This can be using to limit the numebr of concurrent requests.
Any stateless class is inherently threadsafe, provided that the objects it accesses are either private to the thread, or threadsafe themselves.

Categories

Resources