Sorry for the long code post, but I'm wondering if anyone can help with the multithreading issue (I'm completely new to multithreading). I am trying to create a facade class for the RESTFUL web services API that can be used by multiple threads. I use HttpURLConnection to connect and Google GSON to convert JSON data to and from.
The next class is what I still have. In this example, it has one public method for calling the API (authenticateCustomer ()), and private methods are used to facilitate calling the API (for example, to build a POST data string, create a POST request, etc.).
I am making one instance of this class and sharing it with 1000 threads. Streams use the authenticateCustomer () method. Most threads work, but there are some threads that get a null pointer exception because I did not perform any synchronization. If I make the authenticateCustomer () method "synchronized", it will work. The problem is that this leads to bad concurrency (say, for example, a POST request suddenly takes a lot of time, it delays all other threads).
Now to my question. Is the class below not stateless and therefore thread safe? Very few fields that are in a class are declared final and assigned in the constructor. All methods use local variables. The Gson object is stateless (according to their website) and is in any case created as a local variable in the API method.
public final class QuizSyncAPIFacade
{
private final String m_apiDomain;
private final String m_apiContentType;
private final int m_bufferSize;
public QuizSyncAPIFacade()
{
m_apiDomain = "http://*****************************";
m_apiContentType = ".json";
m_bufferSize = 8192;
}
private String readInputStream(InputStream stream) throws IOException
{
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") + "&");
}
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
{
URL apiUrl = new URL(url);
String data = buildPostData(postData);
HttpURLConnection conn;
try {
conn = (HttpURLConnection)apiUrl.openConnection();
} catch (IOException e) {
throw new IOException(buildJSONError("Failed to open a connection.", "CONNECTION_FAILURE", ""));
}
conn.setRequestMethod("POST");
conn.setUseCaches(false);
conn.setDoInput(true);
conn.setDoOutput(true);
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", ""));
}
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", ""));
}
}
return readInputStream(is);
}
public APIResponse<CustomerAuthentication> authenticateCustomer(HashMap<String,String> postData)
{
String apiURL = m_apiDomain + "/customer/authenticate" + m_apiContentType;
Gson jsonConv = new Gson();
String apiResponse = "";
try
{
apiResponse = callPost(apiURL, postData);
CustomerAuthentication customerAuth = jsonConv.fromJson(apiResponse, CustomerAuthentication.class);
APIResponse<CustomerAuthentication> result = new APIResponse<CustomerAuthentication>(true, customerAuth, null);
return result;
}
catch (IOException e)
{
APIErrorList errorList = jsonConv.fromJson(e.getMessage(), APIErrorList.class);
APIResponse<CustomerAuthentication> result = new APIResponse<CustomerAuthentication>(false, null, errorList);
return result;
}
}
}