i have a Wicket 6.10.0 Web Application. I want to implement a timer display the remaining time in the current user session. Something link this:
http://jsfiddle.net/justinobney/uZMGD/
I have a Wicket Label in the footer of my Page. This label uses JavaScript to countdown the time from for example 5 minutes down to 0 min 0 sek. This countdown (label) should be refreshed on every request.
Now the question is how to do this?
In the init() method i added and AbstractRequestCycleListener which notifies me of every end request like this:
getRequestCycleListeners().add(new AbstractRequestCycleListener() {
#Override
public void onEndRequest(RequestCycle cycle) {
super.onEndRequest(cycle);
System.out.println("End Request - here i would like to refresh somehow the label");
}
});
but i dont know how to send this message to the Label to refresh itself. Any ideas?
* E D I T : *
I have a SPA:
<body>
lots of AJAX components here
<div wicket:id="footer">Footer always stays here. <br />
Session timeout in: <span wicket:id="sessionTimeoutLabel">countdown here</span>
</div>
</body>
The countdown works a little bit like this: http://jsfiddle.net/justinobney/uZMGD/
Now on every AJAX Request i need the sessionTimeoutLabel label to rerender to start counting from lets say 600 seconds down to 0.
Don't focus too much on the "end of request". This is about 10µs after to the render phase. Also note that the session timeout is reset after each request, so there is no need to update the label with a new value, other than to reset it to the session timeout parameter.
You should just have a parameter somewhere that dictates the session timeout you configured, and use that to show the label, if it is application specific you can create a getter in your Application class:
public MyApplication extends WebApplication {
public static final int SESSION_TIMEOUT = 30; // minutes
public int getSessionTimeout() {
return SESSION_TIMEOUT;
}
}
And in your footer you can add a label:
add(new Label("timeout", PropertyModel.of(this, "application.sessionTimeout")));
Or if it is a property of your session, you can add the getter to your custom session:
public MySession extends WebSession {
public static final int SESSION_TIMEOUT = 30; // minutes
public int getSessionTimeout() {
return SESSION_TIMEOUT;
}
}
And retrieve it in your label:
add(new Label("timeout", PropertyModel.of(this, "session.sessionTimeout")));
The expression for the property models make use of the fact that Component has a getter for the Application and a getter for the Session.
Related
I'm actually writing a java code in the setupRender() method. Depending of a value provided by the server side, i would like to display an Alert dialog box to the user. By clicking on ok, the application should be closed.
I have not already found how to display an Alert dialog box with tapestry. Do somebody know how to procedd?
Thanks
It's not quite clear to me what you are trying to achieve, but perhaps the following two suggestions are useful.
Suggestion 1 - Display a message using AlertManager
In the page class, inject AlertManager and add the message to it.
public class YourPage {
#Inject
AlertManager alertManager;
Object setupRender() {
// ...
alertManager.alert(Duration.UNTIL_DISMISSED, Severity.INFO, "Love Tapestry");
}
}
Then use the <t:alerts/> component in the page template file to have the message displayed.
Note: The user may dismiss the message, that is, make it disappear. But it doesn't 'close the application' (whatever it is that you mean by that).
Suggestion 2 - Redirect to another page
The setupRender method can return different things. For example, it could return another page class, causing a redirect to that page. On that page, you could have the messages displayed and the session subsequently invalidated (if that's what you meant by 'application should be closed'.
public class YourPage {
Object setupRender() {
// ...
return AnotherPage.class;
}
}
public class AnotherPage {
#Inject
Request request;
void afterRender() {
Session session = request.getSession(false);
session.invalidate();
}
}
See the Tapestry docs for details about what setupRender() can return.
Suggestion 3 - Use JavaScript to display Alert and trigger Component Event
This approach uses JavaScript to display an Alert and subsequently trigger a component event via ajax. The event handler takes care of invalidating the session.
Note: Closing the current browser windows/tab with JavaScript isn't as easy as it used to be. See this Stackoverflow question for details.
YourPage.java
public class YourPage {
boolean someCondition;
void setupRender() {
someCondition = true;
}
#Inject
private JavaScriptSupport javaScriptSupport;
#Inject
ComponentResources resources;
public static final String EVENT = "logout";
void afterRender() {
if (someCondition) {
Link link = resources.createEventLink(EVENT);
JSONObject config = new JSONObject(
"msg", "See ya.",
"link", link.toAbsoluteURI()
);
javaScriptSupport.require("LogoutAndCloseWindow").with(config);
}
}
#Inject Request request;
#OnEvent(value = EVENT)
void logout() {
Session session = request.getSession(false);
if (session != null) session.invalidate();
}
}
YourPage.tml
<!DOCTYPE html>
<html
xmlns:t="http://tapestry.apache.org/schema/tapestry_5_4.xsd"
xmlns:p="tapestry:parameter">
<h1>Hit the Road Jack</h1>
</html>
LogoutAndCloseWindow.js
define(["jquery"], function($) {
return function(config) {
alert(config.msg);
$.ajax({
type: "GET",
url: config.link
});
window.close(); // Legacy. Doesn't work in current browsers.
// See https://stackoverflow.com/questions/2076299/how-to-close-current-tab-in-a-browser-window
}
})
I am implementing a notification service that notifies all users (with active sessions) about new notifications. Since this web application uses Struts MVC (not a single page application), the use of the Web Socket didn't seem appropriate as every page load would require a new WS connection. Instead, my approach was to run a Spring #Service every minute to update all the active users session with a "Unread_Notification_Count" so each page load could simply check the user's session for this value and change the display accordingly (eliminating a database query for every page load).
I have 2 questions: 1) Is this the best approach to solve this problem as I've read in the Spring docs that you should avoid changing users sessions directly.
2) If this approach is "ok", then how do I access each users session to insert/change the "Unread_Notification_Count" value?
#Service("notifySessionsService")
public class NotifySessionsServiceImpl implements NotifySessionsService {
#Autowired
private transient SessionRegistry sessionRegistry;
#Override
public void updateUnReadNotificationCount() {
for (Object principal : sessionRegistry.getAllPrincipals()) {
List<SessionInformation> sessionInformationList = sessionRegistry.getAllSessions(principal, false);
for (SessionInformation sessionInformation : sessionInformationList) {
System.out.println("Updating session for user: " + ((User)principal).getUsername() + ", sessionId: " + sessionInformation.getSessionId());
// [Get users notification count here]
// [Put in session, e.g. session.put("Unread_Notification_Count", count)]
}
}
}
public void setSessionRegistry(SessionRegistry sessionRegistry) {
this.sessionRegistry = sessionRegistry;
}
}
Andrew thanks for the idea. I ended up choosing the javascript/client pull version. Just for closure my code ended up like the code below. The client checks for notifications every 5 minutes (based off the clock - not a timer)
var queryNotificationsEvery = 5; // in minutes
function startNotificationService(appUserId) {
// Check each minute to see if we need to query for notifications
setInterval(queryNotificationCount, 1000 * 60, appUserId)
}
function queryNotificationCount(appUserId) {
var currentMinute = new Date().getMinutes();
if (currentMinute % queryNotificationsEvery === 0) {
// Query server via AJAX
var notificationCount = queryServer(appUserId);
// Change UI
changeUINotifications(notificationCount);
}
}
I am attempting to load a custom component(descending from UIInput). I then encode an html input back to the client. My component loader is thus:
#FacesComponent("TomsWidgetComponent")
#SessionScoped
public class TomsWidgetComponent {
public TomsInput getNewInput(UIComponent parent)
{
ExpressionFactory factory = getFacesContext().getApplication().getExpressionFactory();
TomsInput newComponent = (TomsInput) getFacesContext().getApplication().createComponent(getFacesContext(), "org.tom.example.toms.TomsInput", "org.tom.example.toms.TomsInput");
String newId = FacesContext.getCurrentInstance().getViewRoot().createUniqueId();
newComponent.setId(newId);
elements.put(newId, newComponent);
newComponent.setInputData(new InputData());
ValueExpression valueExpression = factory.createValueExpression(getFacesContext().getELContext(),"#{tomsInput.string}",String.class);
newComponent.setValueExpression("value", valueExpression);
getChildren().add(newComponent);
pushComponentToEL(getFacesContext(), newComponent);
return newComponent;
}
html:
<"input type="tel" id="j_id2" oninput="mojarra.ab(this,event,0,'execute',0)" /input >"
The input shows up just fine, but ajax event never seems show up in my component. I've tried several permutations, with the key on setValueExpression, and adding behavior listeners.
...
Ive gotten passed the exceptions but the Ajax now coming back from the form is causing my component to reload. It's content is:
tomswidgetform=tomswidgetform&j_id2=fffdsdfgbg&javax.faces.ViewState=-6270730402975544133%3A7227399941332846704&javax.faces.source=j_id2&javax.faces.partial.event=input&javax.faces.partial.execute=j_id2%20j_id2&javax.faces.behavior.event=change&AJAX%3AEVENTS_COUNT=1&javax.faces.partial.ajax=true
Any idea what might be wrong? Thanks.
As the title explains by itself, I have an issue with managing the currently active tab in the tab menu. I'm using JSF 2.1 w/ PF 3.4. Here is the code fragment with the tab menu:
<h:form>
<p:tabMenu activeIndex="#{navigationMB.activeIndex}" >
<p:menuitem value="Početna" action="#{navigationMB.navigateStudent('home')}" icon="ui-icon-home" />
<p:menuitem value="Konsultacije" action="#{navigationMB.navigateStudent('konsultacije')}" icon="ui-icon-search" />
<p:menuitem value="Zakazivanje" action="#{navigationMB.navigateStudent('zakazivanje')}" icon="ui-icon-document"/>
<p:menuitem value="Profesori" action="#{navigationMB.navigateStudent('profesori')}"/>
<p:menuitem value="Moj profil" action="#{navigationMB.navigateStudent('profil')}" icon="ui-icon-person" />
</p:tabMenu>
</h:form>
Here is the code of the backing bean which serves for the sole purpose of navigating that tab menu:
#Named(value = "navigationMB")
#RequestScoped
public class NavigationMB {
private int activeIndex = 0;
public NavigationMB() {
}
public String navigateStudent(String page) {
System.out.println("go to page " + page);
if ("home".equals(page)) {
activeIndex = 0;
return "/student/home?faces-redirect=true";
}
if ("konsultacije".equals(page)) {
activeIndex = 1;
return "/student/allConsults?faces-redirect=true";
}
if ("zakazivanje".equals(page)) {
activeIndex = 2;
return "/student/newConsult?faces-redirect=true";
}
if ("profesori".equals(page)) {
activeIndex = 3;
return "/student/allProfessors?faces-redirect=true";
}
if ("profil".equals(page)) {
activeIndex = 4;
return "/student/profile?faces-redirect=true";
}
return "";
}
This runs fine when just browsing, but when I logout (invalidate the session) and later return with same or different user, the activeIndex is remembered. Am I not understanding something here? I suppose that the request scoped bean would be created every time there's a navigation action, and even if the user doesn't navigate anywhere, the integer I set to 0 would always point to "home" but it doesn't.
Any help would be awesome.
edit:
It seems that even without logging out, when two users (two tabs in browser) navigate around, if user 1 clicks on, for instance, tab menu item 2, and user 2 refreshes his page, user 2 will see tab menu item 2 selected as well, and vice versa.
edit2: I made a mistake with the previous edit, please forget about this above (I didn't notice that refresh on user 2 side actually loads user 1 with his view).
As discussed among the comments of the question, the bean is not recognized as being request scoped. It is created during application startup and lives as long as the application is running.
As Spring is used, using Spring annotations will resolve this issue:
#Scope("request")
public class NavigationMB {
}
For a request scoped bean, or:
#Scope("session")
public class NavigationMB {
}
to make it session scoped.
I have 2 inputs. When I press a button(AjaxFallbackButton), those inputs are saved into database.
If one of the input is greater than 10, when I press the button, I want to show a modal panel, for asking the user if is the sure about his option. But the modal component is not appearing. Any thoughts?
#Override
public void onSubmit(AjaxRequestTarget target) {
if (input < 10) { //save to database
} else {
AskingDialogPanel panel = new AskingDialogPanel("content",
new ResourceModel("asking.title"),
new ResourceModel("asking.message")) {
#Override
public void onOkClick(AjaxRequestTarget target) {
super.onOkClick(target);
//save to database
modalWindow.close(target);
}
#Override
public void onCancelClick(AjaxRequestTarget target) {
super.onCancelClick(target);
modalWindow.close(target);
}
};
panel.setOutputMarkupId(true);
target.addComponent(panel);
modalWindow.setContent(panel);
modalWindow.show(target);
}
Have a look at the documentation for the AjaxRequestTarget.
A component whose markup needs to be
updated should be added to this target
via
AjaxRequestTarget#addComponent(Component)
method. Its body will be rendered and
added to the envelope when the target
is processed, and refreshed on the
client side when the ajax response is
received.
I'm not sure if I remember this correctly (I've had trouble implementing the correct refresh behavior previously), but I believe you could only addComponent components that were previously added to the page, but not rendered / invisible. These will than be updated and/or their visibility re-evaluated.
I could be wrong however.. Does the above work if you substitute a normal Label for the AskingDialogPanel? (Just to verify I'm talking out the wrong end ;))