Reputation: 73
I'm continuing on with a logging behavior using the WebSocketBehavior. It currently logs the correct data to a console, but also throws a terrible serialization error. It is because I am providing the WicketApplication itself as a constructor argument for the behavior. I've tried passing it my session object and using that to get the WebApplication, but it consistently returns null. The broadcaster object requires the application in order to function properly. My question is how can I provide the WebApplication to the behavior while avoiding the nasty serialization error?? Here is my behavior class:
public class LogWebSocketBehavior extends WebSocketBehavior implements Serializable {
private static final long serialVersionUID = 1L;
private Console console;
private Handler logHandler;
private Model<LogRecord> model = new Model<>();
private WebApplication application;
public LogWebSocketBehavior(Console console, WebApplication application) {
super();
configureLogger();
this.console = console;
this.application = application;
}
private void configureLogger() {
Enumeration<String> list = LogManager.getLogManager().getLoggerNames();
list.hasMoreElements();
Logger l = Logger.getLogger(AppUtils.loggerName);
l.addHandler(getLoggerHandler());
}
@Override
protected synchronized void onPush(WebSocketRequestHandler handler, IWebSocketPushMessage message) {
LogRecord r = model.getObject();
sendRecordToConsole(handler, r);
}
private Handler getLoggerHandler() {
return new LogHandler() {
private static final long serialVersionUID = 1L;
@Override
public void publish(LogRecord record) {
model.setObject(record);
sendToAllConnectedClients("data");
}
};
}
private synchronized void sendToAllConnectedClients(String message) {
IWebSocketConnectionRegistry registry = new SimpleWebSocketConnectionRegistry();
WebSocketPushBroadcaster b = new WebSocketPushBroadcaster(registry);
b.broadcastAll(application, new Message());
}
private void sendRecordToConsole(WebSocketRequestHandler handler, LogRecord r) {
Level level = r.getLevel();
if (level.equals(Level.INFO)) {
console.info(handler, new SimpleFormatter().formatMessage(r));
} else {
console.error(handler, new SimpleFormatter().formatMessage(r));
}
}
class Message implements IWebSocketPushMessage {
public Message() {
}
}
}
Here is the panel that is being used to display the messages:
public class FooterPanel extends Panel {
private static final long serialVersionUID = 1L;
private Form form;
private Console console;
public FooterPanel(String id) {
super(id);
}
@Override
public void onInitialize() {
super.onInitialize();
form = new Form("form");
form.add(console = getConsole("feedback_console"));
console.setOutputMarkupId(true);
form.setOutputMarkupId(true);
add(form);
add(getLoggingBehavior());
}
private Console getConsole(String id) {
return new Console(id) {
private static final long serialVersionUID = 1L;
};
}
private WebSocketBehavior getLoggingBehavior() {
return new LogWebSocketBehavior(console, this.getWebApplication());
}
}
I updated my behavior as follows:
public class LogWebSocketBehavior extends WebSocketBehavior implements Serializable {
private static final long serialVersionUID = 1L;
private Console console;
private Handler logHandler;
private Model<LogRecord> model = new Model<>();
public LogWebSocketBehavior(Console console) {
super();
configureLogger();
this.console = console;
}
private void configureLogger() {
Enumeration<String> list = LogManager.getLogManager().getLoggerNames();
list.hasMoreElements();
Logger l = Logger.getLogger(AppUtils.loggerName);
l.addHandler(getLoggerHandler());
}
@Override
protected synchronized void onPush(WebSocketRequestHandler handler, IWebSocketPushMessage message) {
LogRecord r = model.getObject();
sendRecordToConsole(handler, r);
}
private Handler getLoggerHandler() {
return new LogHandler() {
private static final long serialVersionUID = 1L;
@Override
public void publish(LogRecord record) {
model.setObject(record);
sendToAllConnectedClients("data");
}
};
}
private synchronized void sendToAllConnectedClients(String message) {
WebApplication application = WebApplication.get();
IWebSocketConnectionRegistry registry = new SimpleWebSocketConnectionRegistry();
WebSocketPushBroadcaster b = new WebSocketPushBroadcaster(registry);
b.broadcastAll(application, new Message());
}
private void sendRecordToConsole(WebSocketRequestHandler handler, LogRecord r) {
Level level = r.getLevel();
String message = AppUtils.consoleDateTimeFormat.format(LocalDateTime.now()) + " - " + AppUtils.LogFormatter.formatMessage(r);
if (level.equals(Level.INFO)) {
console.info(handler, message);
} else {
console.error(handler, message);
}
}
class Message implements IWebSocketPushMessage {
public Message() {
}
}
}
And I'm back to the original issues I started with, which is the following error:
ERROR - ErrorLogger - Job (report.DB5E002E046235586592E7E984338DEE3 : 653 threw an exception. org.quartz.SchedulerException:
Job threw an unhandled exception. [See nested exception: org.apache.wicket.WicketRuntimeException: There is no application attached to current thread DefaultQuartzScheduler_Worker-1] at org.quartz.core.JobRunShell.run(JobRunShell.java:213)
at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:573)
Caused by: org.apache.wicket.WicketRuntimeException: There is no application attached to current thread DefaultQuartzScheduler_Worker-1 at org.apache.wicket.Application.get(Application.java:236)
at org.apache.wicket.protocol.http.WebApplication.get(WebApplication.java:160)
at eb.wicket.behaviors.LogWebSocketBehavior.sendToAllConnectedClients(LogWebSocketBehavior.java:77)
at eb.wicket.behaviors.LogWebSocketBehavior.access$100(LogWebSocketBehavior.java:29)
at eb.wicket.behaviors.LogWebSocketBehavior$1.publish(LogWebSocketBehavior.java:70)
Finally working as desired.. Here's the behavior class:
public class LogWebSocketBehavior extends WebSocketBehavior implements Serializable {
private static final long serialVersionUID = 1L;
private Console console;
private Model<LogRecord> model = new Model<>();
public LogWebSocketBehavior(Console console) {
super();
configureLogger();
this.console = console;
}
private void configureLogger() {
Enumeration<String> list = LogManager.getLogManager().getLoggerNames();
list.hasMoreElements();
Logger l = Logger.getLogger(AppUtils.loggerName);
l.addHandler(getLoggerHandler());
}
@Override
protected synchronized void onPush(WebSocketRequestHandler handler, IWebSocketPushMessage message) {
LogRecord r = model.getObject();
sendRecordToConsole(handler, r);
}
private Handler getLoggerHandler() {
return new LogHandler() {
private static final long serialVersionUID = 1L;
@Override
public void publish(LogRecord record) {
model.setObject(record);
sendToAllConnectedClients("data");
}
};
}
private synchronized void sendToAllConnectedClients(String message) {
IWebSocketConnectionRegistry registry = new SimpleWebSocketConnectionRegistry();
WebSocketPushBroadcaster b = new WebSocketPushBroadcaster(registry);
b.broadcastAll(Application.get("eb.wicket.MyWicketFilter"), new Message());
}
private void sendRecordToConsole(WebSocketRequestHandler handler, LogRecord r) {
Level level = r.getLevel();
String message = AppUtils.consoleDateTimeFormat.format(LocalDateTime.now()) + " - " + AppUtils.LogFormatter.formatMessage(r);
if (level.equals(Level.INFO)) {
console.info(handler, message);
} else {
console.error(handler, message);
}
}
class Message implements IWebSocketPushMessage {
public Message() {
}
}
}
Upvotes: 0
Views: 361
Reputation: 17533
Instead of keeping a reference to the Application just look it up when needed: Application.get()
.
After updating your question we can see:
Caused by: org.apache.wicket.WicketRuntimeException:
There is no application attached to current thread DefaultQuartzScheduler_Worker-1
This explains it - this is a thread started by Quartz, it is not a http thread.
The only way to overcome this is to use Application.get(String)
. The value should be the application name (Application#getName()) that is specified as a value for <filter-name>
in your web.xml.
This way you can get the Application instance, but there is no way to do the same for Session and/or RequestCycle in case you need them too.
Upvotes: 2