How can I display a message through multiple sessions? JSF 2.1 & Primefaces 3.1 - session

I have an application that shows some data in p:DataTable.... This table is accessible by many users. When one user modify a record in the table, (create, edit, delete), this action is notified to an #ApplicationScoped ManagedBean that notify all other session (opened by other users), that the items in the table are changed, then it must be reload from database.
As you can see this is the controller that notify to all session that the items are changed,
/**
* #author Simone Rondelli
*/
#ManagedBean(name="singleton")
#ApplicationScoped
public class SingletonBean {
private int count;
private HashMap<Class, List<AbstractController>> sessions;
public SingletonBean() {
sessions = new HashMap<Class, List<AbstractController>>();
}
public void addSession(AbstractController session, Class c) {
List<AbstractController> sessionList = sessions.get(c);
if(sessionList == null)
sessionList = new ArrayList<AbstractController>();
sessionList.add(session);
sessions.put(c, sessionList);
}
public void notifyItemsChanged(Class type) {
for(AbstractController a : sessions.get(type)) {
a.prepareList();
a.addWarningMessage("Attenzione i record sono stati modificati!!");
}
}
}
this is the code in AbstractController that "try" to show message
public void addWarningMessage(String msg) {
//JsfUtil.addWarningMessage(msg);
FacesMessage facesMsg = new FacesMessage(FacesMessage.SEVERITY_WARN, msg, msg);
FacesContext.getCurrentInstance().addMessage(null, facesMsg);
}
if you press f5, in any other page, the new records are showed... But i want that messages are sent to all other sessions... Now with my code the messages are showed in the page where the modify is done, many time as the number of opened sessions... So if i have 3 sessions with 3 users and one of these makes some change in the table, he will see 3 messages in his page, meanwhile the other users don't see anything.
How can i send Messages to all sessions??

I think that you should check Primefaces Push , take a look at the examples over there...
PrimePush
Or you can try the Ajax Poll AJAX Poll
Here a ref' for Push in Glassfish question

Related

Outlook-Redemption - RDOFolder.Items ItemAdd Event not triggered regular with Exchange in Online-Mode

System-Environment:
Windows 10 Pro - Version: 1909 - OS System Build: 18363.752
Microsoft Outlook 2019 MSO - Version 1808 - 32-Bit
Microsoft Exchange 2016 15.1 Build (Build 1979.3)
-- Microsoft Exchange is installed on Microsoft Server 2016
Outlook Redemption COM-Library - Version 5.22.0.5498
Issue Summary:
The application sends emails via Outlook using the Outlook-Redemption COM-Library. The class "RedemptionHandler" is our Singleton-Class which interacts with the Outlook-Redemption COM-Library. During the construction of the RedemptionHandler we create a RDOSession with a static class named RedemptionLoader and call Logon() on the RDOSession. The RDOSession is used afterwards in Initialize() to retrieve the Folders for Drafts and mails which are sent.
public static class RedemptionLoader
{
public static RDOSession new_RDOSession()
{
return (RDOSession)NewRedemptionObject(new Guid("29AB7A12-B531-450E-8F7A-EA94C2F3C05F"));
}
}
public class RedemptionHandler
{
private static RedemptionHandler instance = null;
private static readonly object padlock = new object();
private RDOSession _rdoSession;
private RDOFolder _rdoSentFolder;
private RDOFolder _rdoDraftsFolder;
private RDOItems _sentItems = null;
public EventHandler<MailGesendetEventArgs> MailSuccessfullySent;
private RedemptionHandler()
{
_rdoSession = RedemptionLoader.new_RDOSession();
_rdoSession.Logon(null, null, false, null, null, null);
Initialize();
}
public static RedemptionHandler Instance
{
get
{
lock (padlock)
{
if (instance == null)
{
instance = new RedemptionHandler();
}
return instance;
}
}
}
private void Initialize()
{
try
{
if (isInitialized) return;
_rdoSentFolder = _rdoSession.GetDefaultFolder(Redemption.rdoDefaultFolders.olFolderSentMail);
_sentItems = _rdoSentFolder.Items;
_sentItems.ItemAdd += MailSent;
_rdoDraftsFolder = _rdoSession.GetDefaultFolder(Redemption.rdoDefaultFolders.olFolderDrafts);
isInitialized = true;
}
catch
{
//TODO
isInitialized = false;
}
}
}
At this point, we have a working instance from our RedemptionHandler. The COM-Object RDOSession is created and referenced within just as the RDOFolder for Drafts and Sent. We have also registrered an event-listener for the Sent-Folder to recognize new Mails in this folder.
In the next steps we want to send an email and recognize this email if its stored in the sent-folder. We use the RDOMail.Fields - Property to store custom data within the RDOMail-Object.
public RDOMail CreateMail(string recipient, string subject, string body, Guid gdSender, string storagePath)
{
RDOMail newMail = _rdoDraftsFolder.Items.Add(Redemption.rdoItemType.olMailItem);
newMail.Recipients.Add(recipient);
newMail.Recipients.ResolveAll();
newMail.Subject = subject;
newMail.HTMLBody = body;
newMail.BodyFormat = (int)rdoBodyFormat.olFormatHTML;
// Here we want to store an identifier in the RDOMail.Fields
int id = newMail.GetIDsFromNames(PropertyGuid, PropertyGdItemId);
newMail.Fields[id] = Guid.NewGuid().ToString();
return newMail;
}
After the mail creation we want to display the mail to the user because we dont want to send data without letting the user know about it.
public void DisplayMail(RDOMail mail, bool modal = false)
{
mail.Display(modal, null);
}
The Outlook window now comes to front and the user checks the mail and clicks on send.
The Mail is now stored in the Sent-Folder.
The MailSent Event gets invoked by the RDOFolder.Items.Add Listener.
private void MailSent(RDOMail mail)
{
var test = mail.Fields[SenderId];
Console.WriteLine(test);
// test value is correct!
}
Difference between Exchange in Online-Mode and Cache-Mode:
If we use the Exchange with Cache-Mode, everything works fine. Everytime we send an email, the MailSent is triggered and we can read data from the RDOMail.Fields-Property. If we switch to Exchange without Cache, the MailSent Event is triggered only once, when the first mail is sent. All emails afterwars are sent but dont trigger the MailSent-Event. If we delete this line of code, everything works also fine without Cache-Mode.
var test = mail.Fields[SenderId];
This is because we think that reading data from the RDOMail.Fields - Property does something special if the cache-mode from exchange is deactivated.
We need to store custom data within the mails to check if new mails in the sent-folder are created by our application or not.
We highly appreciate help and hints.
I tried to fix this issue without success. I've set-up a new Project without any other code:
public partial class RedemptionTest : Form
{
static RDOSession _rdoSession;
static RDOFolder _rdoSentFolder;
static RDOFolder _rdoDraftsFolder;
static RDOItems _draftItems;
static RDOItems _sentItems;
public RedemptionTest()
{
InitializeComponent();
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
_rdoSession = RedemptionLoader.new_RDOSession();
_rdoSession.Logon();
_rdoSentFolder = _rdoSession.GetDefaultFolder(rdoDefaultFolders.olFolderSentMail);
_rdoDraftsFolder = _rdoSession.GetDefaultFolder(rdoDefaultFolders.olFolderDrafts);
_sentItems = _rdoSentFolder.Items;
_draftItems = _rdoDraftsFolder.Items;
_draftItems.ItemAdd += DraftAdd;
_sentItems.ItemAdd += MailSent;
}
private void DraftAdd(RDOMail Item)
{
Console.WriteLine(Item.Subject);
}
private void MailSent(RDOMail Item)
{
Console.WriteLine(Item.Subject);
}
}
The Drafts-Folder Event is fired all the time, the MailSent Event is only fired the first time. I have stored all RDO-Objects in static variables to avoid them from being garbage collected.
The object raising the events (RDOItems) must be alive be able to fire the events. Your code is using multiple dot notation, which means the compiler creates an implicit variable to hold the RDOItems collection. As soon as that variable is released by the Garbage Collector, no events will be fired.
The line
_rdoSentFolder.Items.ItemAdd += MailSent;
must be changed to
RDOItems _sentItems; //global/class variable
..
_sentItems = _rdoSentFolder.Items;
_sentItems .ItemAdd += MailSent;
Have the same issue in Outlook VSTO add-in using Redemption. Happens for both Sent and Inbox folder. The same code works correctly in cached mode but fires events only once in Online mode.
Native Outlook object model Items.ItemAdd works correctly in Online mode for the same folder.
Currently, we were able to do a workaround for this by unsubscribing and resubscribing to event right in the event handler. Like this:
private void SentItems_ItemAdd(RDOMail rdoMail)
{
_sentItems.ItemAdd -= SentItems_ItemAdd;
_sentItems.ItemAdd += SentItems_ItemAdd;
Log.Debug("SentItems.ItemAdd");
SentMailItemAdd?.Invoke(rdoMail);
}

Wicket page is refreshed if use ajax after form is submitted with target=_blank

I have a preview button. When user press preview, form is submitted on new tab to show a pdf file have data in form.
I use a custom SubmitLink to do that
SubmitResourceLink
public abstract class SubmitResourceLink extends SubmitLink implements IResourceListener {
private final IResource resource;
#Override
public final void onResourceRequested() {
Attributes a = new Attributes(RequestCycle.get().getRequest(), RequestCycle.get().getResponse(), null);
resource.respond(a);
}
Implement on form
new SubmitResourceLink("previewBtn", form, new JasperReportsResource() {
private static final long serialVersionUID = -2596569027102924489L;
#Override
public byte[] getData(Attributes attributes) {
return control.getExportPreviewByteStream(estimateModel.getObject());
}
}) {
private static final long serialVersionUID = 1L;
#Override
protected String getTriggerJavaScript() {
String js = super.getTriggerJavaScript();
js = "document.getElementById('" + form.getMarkupId() + "').target='_blank';" + js;
return js;
}
#Override
public void onSubmit() {
form.add(AttributeModifier.append("target", Model.of("_blank")));
processInputs(form);
onResourceRequested();
}
}.setDefaultFormProcessing(false);
When I press preview, a new tab is opend. But when I input in any ajax component (ex:AutoCompleteTextField), ajax reponse data xml: <ajax-response><redirect>....</redirect></ajax-response> and refresh page.
Now, I want after press preview, I still use current form normaly.
Thank.
This is caused by the "stale page protection" in Wicket.
The first click opens the same page instance in a new tab/window. This increments the page's renderCount counter, i.e. it says "this page has been rendered N times".
The links in Wicket look like ?2-1.ILinkListener-component~path. Here '2' is the page id and '1' is the page render count.
So the links in tab1 have renderCount 'N', and the links in tab2 - 'N+1'.
Clicking on a link in tab1 will fail with StalePageException that tells Wicket "the user is trying to use an obsolete version of the page. Please render the latest version of the page so the user can try again".
This protection is needed because the user may do many actions in tab3, e.g. replace a panel that replaces/hides the link the user wants to click in tab1. If there is no such protection Wicket will either fail with
ComponentNotFoundException while trying to click the Link or even worse can do the wrong action if the Link/Button is in a repeater and the repeater has changed its items in tab2.
To overcome your problem you should open a new page instance in tab2, i.e. it submits the form but in onSubmit() does something like setResponsePage(getPage().getClass()). This way it won't re-render the current page instance N+1 time.

Display message to user on expired session when using wicket-auth-roles

Hi I have been unable to solve the following problem in Wicket 6.*:
In our webapp we are using wicket-auth-roles to manage authentication/authorization. When session expires, user should be redirected to a page set by getApplicationSettings().setPageExpiredErrorPage(SomePage.class) on his next action. However, if the user tries to access a page which doesn't allow guests, he is redirected to a login page skipping the PageExpiredPage altogether.
My question is - how can I display "Session has expired." message to the user?
Among other things, I have tried session.info("message") during onInvalidate phase of session's lifecycle, however the feedback message is then rendered on the first page after login (not on the login page).
Thank you for your anwsers.
You could use a RequestCycleListener to record when a PageExpiredException is thrown.
public class ExceptionMapperListener extends AbstractRequestCycleListener {
#Override
public IRequestHandler onException(RequestCycle cycle, Exception ex) {
if (ex instanceof PageExpiredException) {
// Record in session or request cycle
// OR
// Create a RenderPageRequestHandler yourself and add a page parameter
// See DefaultExceptionMapper#internalMap(Exception)
}
return null;
}
}
// In Application#init():
getRequestCycleListeners().add(new ExceptionMapperListener());
ORINAL ANSWER
(kept because it could still help...)
I haven't tried it myself since I don't use wicket-auth-roles, but try overriding the method AuthenticatedWebApplication#restartResponseAtSignInPage() with something like this:
if (isSessionExpired()) {
PageParameters params = new PageParameters();
params.add("showSessionExpired", true);
throw new RestartResponseAtInterceptPageException(getSignInPageClass(), params);
} else {
throw new RestartResponseAtInterceptPageException(getSignInPageClass());
}
And then in the SignInPageClass, display the desired message if the showSessionExpired page parameter is present.
I'm not sure how you implement isSessionExpired(), but you seem to have that part already covered.
OR
Depending on how you implemented isSessionExpired(), maybe you could do the following in your SignInPageClass:
if (sessionExpired()) {
session.info("message")
}
After bernie put me on the right path, I eventually figured out a solution to the problem:
First it is required to override RequestCycleListener:
public class SessionExpiredListener extends AbstractRequestCycleListener {
public void onRequestHandlerResolved(RequestCycle cycle, IRequestHandler handler) {
if (handler instanceof IPageRequestHandler) {
IPageRequestHandler pageHandler = (IPageRequestHandler) handler;
HttpServletRequest request = (HttpServletRequest) cycle.getRequest().getContainerRequest();
//check whether the requested session has expired
boolean expired = request.getRequestedSessionId() != null && !request.isRequestedSessionIdValid();
//check whether the requested page can be instantiated with the current session
boolean authorized = Session.get().getAuthorizationStrategy().isInstantiationAuthorized(pageHandler.getPageClass());
if (expired && !authorized) {
throw new PageExpiredException("Session has expired!");
}
}
super.onRequestHandlerResolved(cycle, handler);
}
}
Check for authorized prevents the session-expired message from displaying on log-out or when accessing unprotected pages.
Finally, you must register your listener and PageRequestHandlerTracker in your WebApplication:
getRequestCycleListeners().add(new SessionExpiredListener());
getRequestCycleListeners().add(new PageRequestHandlerTracker());

struts jqgrid server validation error messages

I have a project using Struts2 on the server side and I am trying to make it work with jqGrid (using JSON format). I have several tables made with jqGrid and I am using the add/edit/delete buttons from navGrid.
The main problem I have is with server validation error messages. I have created custom validators and they work with jsp pages, using s:fielderror, but I don't know how to make them work for add/edit popups from jqGrid. I am aware that jqGrid provides the users with custom validation on client, but this has its limitations(think about testing whether the email of a user is unique, you definitely must use the database for that, or if some fields depend on each other and must be tested together, like if isManager is true, then the managerCode must be not empty and vice versa...).
When I use the client validation, there is a message in the add/edit window whenever an error occurs. Can I somehow display my server validation error messages in the window in the same way?
I managed to solve the issue. I will explain how using a simple custom validator for age field, which must be > 18 for an Employee. It is supposed next that the validator was already declared in validators.xml and mapped on the action and that the message in case of ValidationException is "An employee should be older than 18.".
Using Firebug, I figured out that the id of the error area in the form is FormError. It is possible to configure a callback function errorTextFormat in jqgrid, in order to get a response from the server and process it. In the jqgrid configuration, one could write
errorTextFormat : errorFormat,
with
var errorFormat = function(response) {
var text = response.responseText;
$('#FormError').text(text); //sets the text in the error area to the validation //message from the server
return text;
};
The problem is now that the server will send implicitly a response containing the whole exception stack trace. To deal with it, I decided to create a new result type.
public class MyResult implements Result {
/**
*
*/
private static final long serialVersionUID = -6814596446076941639L;
private int errorCode = 500;
public void execute(ActionInvocation invocation) throws Exception {
ActionContext actionContext = invocation.getInvocationContext();
HttpServletResponse response = (HttpServletResponse) actionContext
.get("com.opensymphony.xwork2.dispatcher.HttpServletResponse");
Exception exception = (Exception) actionContext
.getValueStack().findValue("exception");
response.setStatus(getErrorCode());
try {
PrintWriter out = response.getWriter();
out.print(exception.getMessage());
} catch (IOException e) {
throw e;
}
}
/**
* #return the errorCode
*/
public int getErrorCode() {
return errorCode;
}
/**
* #param errorCode the errorCode to set
*/
public void setErrorCode(int errorCode) {
this.errorCode = errorCode;
}
}
It must also be configured in struts.xml as follows:
<package name="default" abstract="true" extends="struts-default">
...
<result-types>
<result-type name="validationError"
class="exercises.ex5.result.MyResult">
</result-type>
</result-types>
...
<action name="myaction">
...
<result name="validationException" type="validationError"></result>
<exception-mapping result="validationException"
exception="java.lang.Exception"></exception-mapping>
</action>
...
</package>
These are the steps I followed to get a validation error message in the add/edit window and now it works.

Cannot get session ID for "remember me" users inside spring security login event listener

I'm writing a web app using grails and spring-security 3.0.3 which requires me to keep a databse record of all successful logins, including the username, time, ip, and sessionId.
I have created a login listener as so(groovy code):
class DpLoginListener implements ApplicationListener<InteractiveAuthenticationSuccessEvent> {
void onApplicationEvent(InteractiveAuthenticationSuccessEvent event) {
def source = event.getSource()
def principal = source.principal
def details = source.details
TrafficLogin.withTransaction {
new TrafficLogin(userId: principal.id, ipAddress: details.remoteAddress, sessionId: details.sessionId ?: 'remember me').save(failOnError: true)
}
}
}
This fires as expected when a user logs in, but when someone comes back to a site as a "remember me" user, the sessionId is null. Is this the expected behavior? Shouldn't a session be created by the time login has succeeded?
I tried to workaround this by adding a separate SessionCreationEvent listener, which would find the latest databse login record for the user and update that row with the correct sessionId as soon as the session exists, but it seems that this session creation event never fires, and I can't figure out why.
The session creation listener looks like this:
class DpSessionCreatedListener implements ApplicationListener<SessionCreationEvent> {
void onApplicationEvent(SessionCreationEvent event) {
def source = event.getSource()
def principal = source.principal
def details = source.details
TrafficLogin.withTransaction {
def rememberMe = TrafficLogin.find("from TrafficLogin as t where t.userId=? and t.sessionId='remember me' order by t.dateCreated desc", principal.id)
if (rememberMe) {
rememberMe.sessionId = details.sessionId
rememberMe.save(failOnError:true)
}
}
}
}
And a bean is defined for it in my resources.groovy file, in the same manner as the login listener, which fires fine.
Any ideas how to correctly set this sessionId?
Not related to grails, but this SO question may offer a workaround if grails supports it.

Resources