Reputation: 2831
I am using a separate thread to handle the processing of a file. The main thread holds the table to display to the user using a listView, and uses an AjaxSelfUpdatingTimer to refresh the list every second.
The problem is, after processing about 100 lines in my CSV file, I keep getting a no requestCyle exception:
Exception in thread "Thread-12" org.apache.wicket.WicketRuntimeException: No RequestCycle is currently set!
at org.apache.wicket.Component.getRequest(Component.java:1804)
at org.apache.wicket.markup.html.WebPage.dirty(WebPage.java:318)
at org.apache.wicket.Page.dirty(Page.java:249)
at org.apache.wicket.Page.componentStateChanging(Page.java:926)
at org.apache.wicket.Component.addStateChange(Component.java:3528)
at org.apache.wicket.Component.error(Component.java:1225)
at com.wicket.BulkLoadPage$BatchLoaderProcessingThread.processLine(BulkLoadPage.java:806)
at com.wicket.BulkLoadPage$BatchLoaderProcessingThread.run(BulkLoadPage.java:674)
at java.lang.Thread.run(Thread.java:662)
these are my runnable classes the thread calls:
class BatchLoaderProcessingThread implements Runnable
{
@Override
public void run()
{
processLine();
loaderFinished();
}
public void cancelThread()
{
cancelLoaderThread = true;
}
}
class BatchDeleteProcessingThread implements Runnable
{
@Override
public void run()
{
processLine();
deleterFinished();
}
public void cancelThread()
{
cancelDeleterThread = true;
}
}
I don't understand why the requestCycle would just go null.. How can I prevent this from happening?
Edit:
Commenting out feedback message do troubleshoot requestRecycle error, I receive this error:
java.io.IOException: Read error
at java.io.FileInputStream.readBytes(Native Method)
at java.io.FileInputStream.read(FileInputStream.java:220)
at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:264)
at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:306)
at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:158)
at java.io.InputStreamReader.read(InputStreamReader.java:167)
at java.io.BufferedReader.read1(BufferedReader.java:185)
at java.io.BufferedReader.read(BufferedReader.java:261)
at java.io.BufferedReader.fill(BufferedReader.java:136)
Dec 30 13:14:31 ERROR BulkLoadPage-java.io.IOException: Read error
at java.io.BufferedReader.readLine(BufferedReader.java:299)
at java.io.BufferedReader.readLine(BufferedReader.java:362)
at au.com.bytecode.opencsv.CSVReader.getNextLine(CSVReader.java:266)
at au.com.bytecode.opencsv.CSVReader.readNext(CSVReader.java:233)
at com..wicket.BulkLoadPage.processLine(BulkLoadPage.java:547)
at com..wicket.BulkLoadPage.access$0(BulkLoadPage.java:532)
at
com..wicket.BulkLoadPage$BatchLoaderProcessingThread.run(BulkLoadPage.java:1294)
at java.lang.Thread.run(Thread.java:662)
this error only occurs with the larger file as well. All of the lines inside the csv are duplicate only to mimic a large file.. so all lines are the same, and there shouldn't be an error caused directly from the file. Is there something else that I should be looking for that would cause this error from using another thread?
Upvotes: 0
Views: 2040
Reputation: 4347
RequestCycle is a thread local singleton. If you're running a process in another thread that really means the RequestCycle singleton doesn't exist in your new thread.
The following example is placed on https://repo.twinstone.org/projects/WISTF/repos/wicket-examples-1.4/browse
The idea is about holding the reference to the current RequestCycle, you cannot call RequestCycle.get() due to the thread local singleton doesn't exist in any other thread. This CustomRequestCycle implementation is ever waiting until the new thread notify its finishing. The counter of loops is just a protection to do not freeze the primary thread if the WaitingRunnable stops working/freezes.
RESULTS oF TESTS:
Check your logs for case 1, the separate thread is finishing very soon, RequestCycle doesn't wait for detaching
http://localhost:8080/wicket-examples14/wait?millis=10
DEBUG - CustomRequestCycle - Waiting until notify: 0
INFO - WaitingRunnableNotifier - Separate thread waiting finished cz.wicketstuff.enteam.wicket.examples14.request.ThreadWaitingPage
INFO - CustomRequestCycle - Notifier returned: Successfully finished
Check your logs for case 2, the separate thread is finishing in time, RequestCycle has to wait for detaching
http://localhost:8080/wicket-examples14/wait?millis=3000
DEBUG - CustomRequestCycle - Waiting until notify: 0
DEBUG - CustomRequestCycle - Waiting until notify: 1
DEBUG - CustomRequestCycle - Waiting until notify: 2
INFO - WaitingRunnableNotifier - Separate thread waiting finished cz.wicketstuff.enteam.wicket.examples14.request.ThreadWaitingPage
INFO - CustomRequestCycle - Notifier returned: Successfully finished
Check your logs for case 3, the separate thread is finishing on time, RequestCycle is already detached
http://localhost:8080/wicket-examples14/wait?millis=10000
DEBUG - CustomRequestCycle - Waiting until notify: 0
DEBUG - CustomRequestCycle - Waiting until notify: 1
DEBUG - CustomRequestCycle - Waiting until notify: 2
DEBUG - CustomRequestCycle - Waiting until notify: 3
DEBUG - CustomRequestCycle - Waiting until notify: 4
DEBUG - CustomRequestCycle - Waiting until notify: 5
DEBUG - CustomRequestCycle - Waiting until notify: 6
DEBUG - CustomRequestCycle - Waiting until notify: 7
INFO - CustomRequestCycle - Notifier returned: null
INFO - WaitingRunnableNotifier - Separate thread waiting finished cz.wicketstuff.enteam.wicket.examples14.request.ThreadWaitingPage
SOURCES:
WicketApplication
@Override
public RequestCycle newRequestCycle(Request request, Response response) {
return new CustomRequestCycle(this, (WebRequest)request, (WebResponse)response);
}
CustomRequestCycle
public class CustomRequestCycle extends WebRequestCycle implements INotifier<String> {
private static final Logger log = LoggerFactory.getLogger(CustomRequestCycle.class);
private long sleepTime = 1000L;
private long maxLoops = 8;
private boolean canDetach = true;
private String notifierResult;
public CustomRequestCycle(WicketApplication application, WebRequest request,
Response response) {
super(application, request, response);
}
public void notifyAny(String payload) {
notifierResult = payload;
canDetach = true;
}
@Override
public void detach() {
long counter = 0;
while(!canDetach && maxLoops > counter) {
log.debug("Waiting until notify: " + counter);
try {
Thread.sleep(sleepTime);
} catch (InterruptedException e) {
// do nothing
}
counter++;
}
log.info("Notifier returned: " + notifierResult);
super.detach();
}
public static CustomRequestCycle get() {
return (CustomRequestCycle)RequestCycle.get();
}
/**
* @return the canDetach
*/
public boolean isCanDetach() {
return canDetach;
}
/**
* @param canDetach the canDetach to set
*/
public void setCanDetach(boolean canDetach) {
this.canDetach = canDetach;
}
}
WaitingRunnableNotifier
public class WaitingRunnableNotifier implements Runnable {
private static final Logger log = LoggerFactory.getLogger(WaitingRunnableNotifier.class);
private final long waitTime;
private RequestCycle requestCycle;
private INotifier<String> notifier;
public WaitingRunnableNotifier(RequestCycle requestCycle, long waitTime, INotifier<String> notifier) {
super();
this.notifier = notifier;
this.requestCycle = requestCycle;
this.waitTime = waitTime;
}
public void run() {
String message = null;
try {
try {
Thread.sleep(waitTime);
} catch (InterruptedException e) {
}
log.info("Separate thread waiting finished " + requestCycle.getResponsePageClass().getCanonicalName());
message = "Successfully finished";
} catch (Exception e) {
log.error("Exception during WaitingRunnableNotifier.run()", e);
message = "Exception: " + e.getMessage();
} finally {
notifier.notifyAny(message);
clean();
}
}
/**
* Clean object references
*/
private void clean() {
requestCycle = null;
notifier = null;
}
}
ThreadWaitingPage is a page with parameter 'millis'. There you can invoke the another thread and wait unitl it is finished.
public class ThreadWaitingPage extends WebPage {
private static final long serialVersionUID = 1L;
private final long millis;
public ThreadWaitingPage(final PageParameters parameters) {
super(parameters);
millis = parameters.getLong("millis");
add(new Label("millis", String.valueOf(millis)));
}
@Override
protected void onInitialize() {
super.onInitialize();
CustomRequestCycle requestCycle = CustomRequestCycle.get();
requestCycle.setCanDetach(false);
new Thread(new WaitingRunnableNotifier(requestCycle, millis, requestCycle)).start();
}
}
Upvotes: 1
Reputation: 2831
I have temporarily fixed this issue by first loading the lines into a string ArrayList in the main thread and just read from that list inside the thread..
I don't really see any latency issues, so maybe this can be considered a good alternative? If it isn't please let me know, otherwise hopefully this helps someone else some day who struggles with this.
I have also posted my issue here: http://apache-wicket.1842946.n4.nabble.com/MultiThreading-issues-with-Wicket-td4663325.html#a4663389
and was given some good resource links to review:
Multithreading in wicket:
http://javathoughts.capesugarbird.com/2008/04/spawning-thread-for-lengthy-operation.html
RequestCycle: http://wicket.apache.org/guide/guide/chapter8.html
Upvotes: 1
Reputation: 736
Ideally, you shouldn't be creating your own threads, the container should be left to do all that for you. Of course, this means you can't leave bulk processes running in the background between requests.
I've done something similar before (breaking the rule I just mentioned!) and the easiest way is to keep Wicket out of your own threads. If your threads populate a List for example, and your ajax timer callback extracts the records from the list and adds them to the list view.
The problem with list views though is you have to add the parent component to the ajax request target, meaning that the whole list view will be sent back in the ajax response, not just the new entries you've added but all the old ones as well, which would defeat the point of loading them incrementally.
As its only a CSV file, it might be better for you to use a data table, and write a data provider to open the file and read only the records for that request which should be much simpler.
Upvotes: 1