I have an SQLiteOpenHelper class that implements the singleton pattern and has three methods: addLogs(), getLogs(), and deleteLogs(). Seven threads (triggered by a BroadcastReceiver at certain schedules) access the database--six of them only use addLogs(), and the other one uses getLogs() and calls deleteLogs() before it finishes.
My problem is, because the last thread makes two different calls to the SQLite database, the other threads can call on addLogs() even before deleteLogs() is called, which corrupts my output. How can I make that one thread treat the two calls to the SQLiteOpenHelper as a single, atomic transaction?
What I've tried: Create a static ReentrantLock member in the SQLiteOpenHelper, and call lock() and unlock() from every one of the seven threads before and after their transactions. The other six threads still get through even though the last one still has the lock, which it calls before getLogs() and releases after deleteLogs().
UPDATE: This is the seventh thread that accesses the two methods.
public class Uploader extends Thread {
private Context context;
public Uploader(Context context) {
this.context = context;
}
#Override
public void run() {
Logger.d("STARTED: Uploader");
DB db = DB.getInstance(context);
List<JsonLog> logs = db.getLogs(); // FIRST CALL
String json = new Gson().toJson(logs); // convert to JSON
// connect to the server and upload
HttpClient client = new DefaultHttpClient();
HttpPost post = new HttpPost(This.URL);
post.setHeader("content-type", "application/json");
try {
Logger.d("to upload: " + json);
StringEntity se = new StringEntity(json);
post.setEntity(se);
StatusLine status = client.execute(post).getStatusLine();
// if upload is successful
if (status.getStatusCode() == HttpStatus.SC_OK) {
db.deleteLogs(); // SECOND CALL
} else {
Logger.d("Uploader failed. " + status.getStatusCode() + ": " + status.getReasonPhrase());
}
} catch (Exception e) {
e.printStackTrace();
}
Logger.d("FINISHED: Uploader");
}
}
SQLite has transactions as in db.beginTransaction(), db.setTransactionSuccessful() to mark the Tx to be committed and db.endTransaction() to finally mark the Tx as to be done.
So you could in your getLogs() do the beginTransaction and in the deleteLogs to mark it as done (and successful).
[Update]
Now with your code snippet you probably would want to call beginTransaction before your 1st call and in a finally block, that adds to the try-catch block call into endTransaction. At the end of the try part if the status is ok, you would call setTransactionSuccessful (after the delete call.
Related
I want to create an AWS Lambda function in java that writes to a database in Firestore. The short story is that, while the code does what it should when I execute it
on my own computer, using NetBeans (the truth is that it works most of the time, but not always, maybe due to problems with my internet connection), nothing at all
happens when I deploy it as a Lambda function and invoke this. I suspect that this has less to do with Firestore itself, but rather with how AWS Lambda handles asynchronous
operations.
Now to the details!
As a simple example, the method that writes to the Firestore object db reads
public static void writeFirestore(Firestore db){
try{
DateTime now = DateTime.now();
String time = now.toString();
Map<String, String> data = new HashMap<>();
data.put("time", time);
String collTitle = "Notebook";
String docTitle = "Document: "+time;
db.collection(collTitle).document(docTitle).set(data);
System.out.println("wrote to Firestore");
}
catch(Exception e){
System.out.println("Could not write to db: "+e.toString());
}
}
Now, as it takes some time to connect to Firestore and initialize db, I want to make sure that db is not passed as an argument into writeFirestore() before it
has been properly retrieved. So, I define a version of db in the form of a Future object, using ExecutorService, and then retrieve
the object db with the get()-method. For this, I define the class TaskRunner:
public class TaskRunner {
ExecutorService executor;
public TaskRunner(){
executor = Executors.newSingleThreadExecutor();
}
public static interface Callback<T>{
public void onCallback(T result);
}
public <T> void executeAsync(Callable<T> callable, Callback<T> callback) throws Exception{
try{
Future future = executor.submit(callable);
Object result = future.get();
if(result != null){
System.out.println("result is not null; applying callback...");
callback.onCallback((T) result);
}
else{
System.out.println("result is null");
}
}
catch(Exception e){
System.out.println("Problem running executeAsync: "+e.toString());
}
}
}
Writing the example document to my fixed database db now goes as follows:
I define the class FirestoreCreator that implements Callable with the purpose of retrieving the Firestore object db:
public static class FirestoreCreator implements Callable<Firestore>{
#Override
public Firestore call() throws Exception {
String projectId = "myProjectId";
GoogleCredentials credentials =
GoogleCredentials.fromStream(new FileInputStream("myCredentialsFile.json"));
FirestoreOptions firestoreOptions = FirestoreOptions.getDefaultInstance()
.toBuilder()
.setProjectId(projectId)
.setCredentials(credentials)
.build();
Firestore db = firestoreOptions.getService();
return db;
}
}
I implement the TaskRunner.Callback interface using writeFirestore().
I create a TaskRunner object, taskRunner, and call its executeAsync() method with the above two objects as parameters.
These three steps are collected in the final method testUpdateFirestore() that does the job:
public static void testUpdateFirestoreInterface(){
FirestoreCreator fsCreator = new FirestoreCreator();
TaskRunner.Callback<Firestore> updateCallback = new TaskRunner.Callback<Firestore>() {
#Override
public void onCallback(Firestore result) {
writeFirestore(result);
}
};
TaskRunner taskRunner = new TaskRunner();
try {
taskRunner.executeAsync(fsCreator, updateCallback);
} catch (Exception ex) {
System.out.println("Failed to run executeAsync");
}
}
As I already mentioned in the introduction, the code works (most times) when I run it on my computer, but not at all in AWS Lambda. No exception is thrown, and yet no document has been written in Firestore.
The discussion about threads in AWS Lambda (https://dzone.com/articles/multi-threaded-programming-with-aws-lambda) made me suspect that reason is that the use of some thread that runs when ExecutorService is used is not being handled properly.
Does anyone know what goes wrong and what a solution could look like?
Apache HttpClient 4.2.1
I have a Java process that downloads multiple files from one CouchDB A and uploads the same to another CouchDB B. The downloads and uploads are done using 40 worker threads using an ExecutorService.
When the file count is high (~25k) The process throws a SocketException of "too many open files" due to the Linux open file descriptor limit. I figured this might be due to a resource leak and proceeded to analyze the code which is as follows:
Initiate worker threads
private boolean copyDocumentsFromAtoB(Array ids) {
ExecutorService downloader = Executors.newFixedThreadPool(50);
ExecutorService uploader = Executors.newFixedThreadPool(50);
for (String id: ids) {
downloader.execute(new DownloaderThread(id, downloadedDocs));
}
for (JsonElement doc: downloadedDocs) {
uploader.execute(new UploaderThread(doc));
}
}
Runnable Downloader class (UploaderThread class has a similar implementation)
private static final class DocumentDownloader implements Runnable {
private final String documentId;
private final JsonArray downloadedDocs;
DocumentDownloader(String documentId, JsonArray downloadedDocs) {
this.documentId = documentId;
this.downloadedDocs = downloadedDocs;
}
#Override
public void run() {
InputStream docStream = null;
String url = buildUrl(documentId);
HttpGet doc = new HttpGet(url);
try {
//doc.setHeaders()
HttpClient httpClient = new DefaultHttpClient();
HttpResponse docResponse = httpClient.execute(doc);
docStream = docResponse.getEntity().getContent();
//document parsing and adding to downloadedDocs. Not important
} catch (Exception e) {
//handle exceptions
} finally {
if (docStream != null) {
try {
docStream.close();
} catch (IOException e) {
LOGGER.debug("Cannot close input stream", e);
}
}
}
}
}
Findings:
HttpClient is local to each thread. This is sub-optimal according to many resources and posts I found online.
If I did, httpClient.getConnectionManager().shutdown(); at the end of the Runnable class, the open file count decreased.
Using a global HttpClient (instantiated only once in the copyDocumentsFromAtoB method and passed down to the Runnable classes through the contructors) with a PoolingClientConnectionManager instance and a local context object, decreased the open file count further.
Questions:
Why did the original implementation have a increased number of open file descriptors during the copying process? How does multiple HttpClient instances contribute towards this?
Doesn't the docStream.close(); in the finally block close whatever Http connection that is created between the process and the database?
Does the HttpClient instance get destroyed (releasing any open resources in the process) when the thread that it belongs to terminates? (This would explain why the open file count decreases after the copying process terminates prematurely)
Are there any other optimizations I can do (in terms of resource leaks) other than using a single global HttpClient?
Are there tools that I could use to obtain quantifiable statistics of this scenario for different implementations?
What steps can I follow to find the optimum number of worker
threads, setMaxTotal and setDefaultMaxPerRoute (for the
PoolingClientConnectionManager)?
Here is the code of my servlet.
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// TODO Auto-generated method stub
Map m=request.getParameterMap();
Set s = m.entrySet();
Iterator it = s.iterator();
int index=0;
while (it.hasNext()) {
Map.Entry<String,String[]> entry = (Map.Entry<String,String[]>) it.next();
String key = entry.getKey();
String[] value = entry.getValue();
System.out.println("Value is " + value[0].toString());
switch(key) {
case "RegId":
RegId = value[0].toString();
break;
case "isTrackingRequested":
isTrackingRequested = Boolean.valueOf(value[0]);
break;
}
}
// Create a session object if it is already not created.
HttpSession session = request.getSession(true);
if (session.isNew()) {
session.setAttribute("id",isTrackingRequested);
}
ScheduledExecutorService ses = Executors.newSingleThreadScheduledExecutor();
ses.scheduleAtFixedRate(new Runnable() {
#Override
public void run() {
boolean isTrackingRequestednew = (boolean) session.getAttribute("id");
// code to run
if (isTrackingRequestednew) {
try {
System.out.println("===========================================================================");
System.out.println("new track status is " + isTrackingRequestednew);
System.out.println("===========================================================================");
} catch (Exception e) {
}
} else {
ses.shutdown();
}
}
}, 0, 1, TimeUnit.SECONDS);
}
I am trying to track a vehicle by using a ScheduledExcecutorService. I am using a flag isTrackingRequested to check if user has requested the tracking. So I am saving the values in the session, but whenever I request for the tracking to stop, the previously set session attribute shows null value.
In short, I am trying to access a previously set session variable, but I end up getting null. No solution I have tried seems to work.
whenever I request for the tracking to stop, the previously set session attribute shows null value.
A reasonably likely explanation is that the requests are not in the same session. Maintaining a session requires cooperation from the client, which is not guaranteed to be given. The most common mechanisms for associating requests with sessions are cookies and URL rewriting. If the client refuses cookies and is making its requests to a static URL then every request will likely be in its own session.
That's one of the lesser of your problems, however. You also have these:
On every POST request, you create a new ScheduledExecutorService and a new task for it to manage. Surely that's not what you intended.
Added: You do not update existing sessions with the tracking state carried by requests belonging to those sessions. Only if the session is newly created for the request being serviced do you set the session attribute.
Moreover, when last I studied the JavaEE specs (a version ago) JavaEE components such as servlets were not permitted to start their own threads, but yours does -- many of them -- within all the ScheduledExecutorServices. That doesn't mean starting a new thread (or creating a ScheduledExecutorService) will necessarily fail, but your violation of the specs does mean that you cannot rely on the JavaEE APIs to behave as documented.
Moreover, your code is not properly synchronized. You access shared state (the Session) without proper synchronization.
Furthermore, you appear to have no mechanism to shut down tracking when a session expires or is manually terminated.
To do this properly, the tracking should be performed in a separate service running outside the servlet container. Alternatively, you could hack it together with only the scheduler itself running outside the container, and all the tracked state living inside. The scheduler would then need only to serve as a clock by sending a periodic request to a different servlet in the same container.
You would do well to decouple your task from the session. Instead of having it get the tracking state from the session, give it a member variable for that, and store a reference to the task itself in the session. Modify the task's state directly in response to requests, instead of passing that information indirectly via the session. And make sure all accesses to that object's shared state are properly synchronized!
Added: Furthermore, I suggest that you make the task implement HttpSessionBindingListener, so that when it is unbound from the session -- either manually or as a result of the session reaching the end of its life -- it can cancel itself.
Added: Additionally, note that modern JavaEE requires the container to make a ScheduledExecutorService available to enterprise components. You should be able to obtain a reference to it via the JNDI name java:comp/DefaultManagedScheduledExecutorService (see section EE.5.21 in the Java EE 7 platform specification). It would be wise to use this container-provided service instead of attempting to set up your own.
There are a few bugs in the code you provided. Value of isTrackingRequested should be checked every time as requester may send a value false to stop tracking. Also null value of isTrackingRequested should be taken into account. If it is null then it probably means user wants to carry on as with the previous decision.
These have been corrected in the code below, this should be working now.
protected void doPost(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
String RegId = String.valueOf(request.getParameter("RegId"));
// Create a session object if it is already not created.
final HttpSession session = request.getSession(true);
String trackingRequestParam = request.getParameter("isTrackingRequested");
boolean isTrackingRequested = false;
if(trackingRequestParam != null) {
isTrackingRequested = Boolean.valueOf(trackingRequestParam);
session.setAttribute("id", isTrackingRequested);
}
if(trackingRequestParam != null && isTrackingRequested) {
final ScheduledExecutorService ses = Executors
.newSingleThreadScheduledExecutor();
session.setAttribute("isRunning", true);
ses.scheduleAtFixedRate(new Runnable() {
#Override
public void run() {
boolean isTrackingRequestednew = Boolean.valueOf(String.valueOf(session.getAttribute("id") ));
System.out.println("inside repeater : " + session.getAttribute("id") + " : " + isTrackingRequestednew);
// code to run
if (isTrackingRequestednew) {
try {
System.out.println("===========================================================================");
System.out.println("new track status is " + isTrackingRequestednew);
System.out.println("===========================================================================");
} catch (Exception e) {
}
} else {
ses.shutdown();
}
}
}, 0, 1, TimeUnit.SECONDS);
}
}
Edits *****
I'm adding entire TestServlet code that I used here. I have converted POST method to GET method for ease of testing.
TestServlet
package test;
import java.io.IOException;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
#WebServlet("/TestServlet")
public class TestServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
Boolean isLogout = Boolean.valueOf(String.valueOf(request.getParameter("logout")));
String RegId = String.valueOf(request.getParameter("RegId"));
// Create a session object if it is already not created.
final HttpSession session = request.getSession(true);
String trackingRequestParam = request.getParameter("isTrackingRequested");
boolean isTrackingRequested = false;
if(trackingRequestParam != null) {
isTrackingRequested = Boolean.valueOf(trackingRequestParam);
session.setAttribute("id", isTrackingRequested);
}
if(isLogout) {
session.invalidate();
}
if(trackingRequestParam != null && isTrackingRequested) {
final ScheduledExecutorService ses = Executors
.newSingleThreadScheduledExecutor();
session.setAttribute("isRunning", true);
ses.scheduleAtFixedRate(new Runnable() {
#Override
public void run() {
try {
boolean isTrackingRequestednew = Boolean.valueOf(String.valueOf(session.getAttribute("id") ));
System.out.println("inside repeater : " + session.getAttribute("id") + " : " + isTrackingRequestednew);
// code to run
if (isTrackingRequestednew) {
try {
System.out.println("===========================================================================");
System.out.println("new track status is " + isTrackingRequestednew);
System.out.println("===========================================================================");
} catch (Exception e) {
}
} else {
ses.shutdown();
}
} catch (Exception ex) {
ex.printStackTrace();
ses.shutdown();
}
}
}, 0, 1, TimeUnit.SECONDS);
}
}
protected void doPost(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
}
}
Entire TestServlet
Testing
Hit below URLs (TestWeb was context name in my case, replace it with yours)
http://localhost:8080/TestWeb/TestServlet?isTrackingRequested=true<br>
//then after a few seconds<br>
http://localhost:8080/TestWeb/TestServlet?isTrackingRequested=false
Output (Test was done on Tomcat 7.0.59)
inside repeater : true : true
===========================================================================
new track status is true
===========================================================================
inside repeater : true : true
===========================================================================
new track status is true
===========================================================================
inside repeater : true : true
===========================================================================
new track status is true
===========================================================================
inside repeater : true : true
===========================================================================
new track status is true
===========================================================================
inside repeater : true : true
===========================================================================
new track status is true
===========================================================================
inside repeater : true : true
===========================================================================
new track status is true
===========================================================================
inside repeater : false : false
After tracking was set to false, it stops printing as executor was shutdown.
Note: Clear your cookies to start a new session.
Edit2 ***
Added code to support manual logout call URL below to logout.
http://localhost:8080/TestWeb/TestServlet?logout=true
I am taking some data from a database via a servlet and a db handler java class and hosting it at a url. Since the database is changing I'm taking care only to host the changes rather than the entire db data.
I'm getting the required functionality by a browser i.e after every (manual) reload, I'm getting the data as required by me,
1. at the first page load, entire data gets displayed.
2. at subsequent reloads, I get either null data if there is no change in the database, or the appended rows if the database extends. (the database can only extend).
But then in a java program, I'm not getting the same functionality. The java program using HttpUrlConnection.
This is the code for the java client for servlet...
public class HTTPClient implements Runnable {
private CallbackInterface callbackinterface;
private URL url;
private HttpURLConnection http;
private InputStream response;
private String previousMessage = "";
public HTTPClient() {
try {
url = new URL("http://localhost:8080/RESTful-Server/index.jsp");
http = (HttpURLConnection) url.openConnection();
http.connect();
} catch (IOException e) {
}
}
#Override
public void run() {
while (true) {
try {
String currentmessage = "";
response = http.getInputStream();
if (http.getResponseCode() == HttpURLConnection.HTTP_OK) {
BufferedReader buffread = new BufferedReader(new InputStreamReader(response));
String line;
for (; (line = buffread.readLine()) != null;) {
currentmessage += line;
}
if ((!currentmessage.equals(previousMessage)
|| !previousMessage.equals(""))
&& !currentmessage.equals("")) {
//this.callbackinterface.event(currentmessage);\
System.out.println(currentmessage + "\t" + previousMessage);
}
previousMessage = currentmessage;
Thread.sleep(2500);
} else {
throw new IOException();
}
} catch (IOException | InterruptedException e) {
System.err.println("Exception" + e);
}
}
}
The shown class is a thread which read the connections every 2.5 s. If it gets something significant in the getline(), it will issue a callback to a worker method, which takes care of remaining things.
I am thinking the issues is because of the class variable conn, and that reload as in the browser is not getting replicated..
Any idea how to do this?
You're basically connecting (requesting) only once and trying to read the response multiple times, while it can be read only once. You basically need to create a new connection (request) everytime. You need to move the creation of the connection by url.openConnection() to inside the loop. The line http.connect() is by the way superfluous. You can safely omit it. The http.getInputStream() will already implicitly do it.
See also:
Using java.net.URLConnection to fire and handle HTTP requests
I am doing HTTP POSTs very frequently (>= 1/sec) to an API endpoint and I want to make sure I'm doing it efficiently. My goal is to succeed or fail as soon as possible, especially since I have separate code to retry failed POSTs. There is a nice page of HttpClient performance tips, but I'm not sure if exhaustively implementing them all will have real benefits. Here is my code right now:
public class Poster {
private String url;
// re-use our request
private HttpClient client;
// re-use our method
private PostMethod method;
public Poster(String url) {
this.url = url;
// Set up the request for reuse.
HttpClientParams clientParams = new HttpClientParams();
clientParams.setSoTimeout(1000); // 1 second timeout.
this.client = new HttpClient(clientParams);
// don't check for stale connections, since we want to be as fast as possible?
// this.client.getParams().setParameter("http.connection.stalecheck", false);
this.method = new PostMethod(this.url);
// custom RetryHandler to prevent retry attempts
HttpMethodRetryHandler myretryhandler = new HttpMethodRetryHandler() {
public boolean retryMethod(final HttpMethod method, final IOException exception, int executionCount) {
// For now, never retry
return false;
}
};
this.method.getParams().setParameter(HttpMethodParams.RETRY_HANDLER, myretryhandler);
}
protected boolean sendData(SensorData data) {
NameValuePair[] payload = {
// ...
};
method.setRequestBody(payload);
// Execute it and get the results.
try {
// Execute the POST method.
client.executeMethod(method);
} catch (IOException e) {
// unable to POST, deal with consequences here
method.releaseConnection();
return false;
}
// don't release so that it can be reused?
method.releaseConnection();
return method.getStatusCode() == HttpStatus.SC_OK;
}
}
Would it make sense to disable the check for stale connections? Should I be looking at using the MultiThreadedConnectionManager? Of course, actual benchmarking would help but I wanted to check if my code is on the right track first.
Much of the performance hit of http connections is establishing the socket connection. You can avoid this by using 'keep-alive' http connections. To do this, it's best to use HTTP 1.1 and make sure that "Content-Length: xx" is always set in requests and responses, "Connecction: close" is correctly set when appropriate and is properly acted upon when received.