Seems I've run into a wall with the SessionListener implementation in XPages. The listener prints output to the log when a session is created, so I know its registered properly. However, it doesn't call sessionDestroyed upon logout. Is there any special URL redirect I need to perform to have the Domino / XPage session destroyed immediately upon logout? As you can see I've tried clearing scopes, and clearing cookies trying to get the sessionDestroyed method to fire. Note that sessionDestroyed does get called when I restart the http server task, so it seems the sessions might be lingering until the inactivity timeout.
Dev Server is: 9.0.1 (64 bit, running locally on Win7)
Running Session Based Authentication single server (note: I tried basic auth, same problem)
logout utility method (called by SSJS):
public static void logout(){
String url = XSPUtils.externalContext().getRequestContextPath() + "?logout&redirectto=" + externalContext().getRequestContextPath();
XSPUtils.getRequest().getSession(false).invalidate();
//wipe out the cookies
for(Cookie cookie : getCookies()){
cookie.setValue("");
cookie.setPath("/");
cookie.setMaxAge(0);
XSPUtils.getResponse().addCookie(cookie);
}
try {
XSPUtils.externalContext().redirect(url);
} catch (IOException e) {
logger.log(Level.SEVERE,null,e);
}
}
simple session listener:
public class MySessionListener implements SessionListener {
public void sessionCreated(ApplicationEx arg0, HttpSessionEvent event) {
System.out.println("***sessionCreated***");
}
public void sessionDestroyed(ApplicationEx arg0, HttpSessionEvent event) {
System.out.println("***sessionDestroyed***");
}
}
we're looking at coupling the traditional http stack "?logout" behavior with the XPages runtime session management layer. Currently, sessions are discarded based on the session timeout expiry and/or http stack restart. If you want to force session removal and have your SessionListener.sessionDestroyed invoked refer to the following XSP fragment - this is equally applicable for porting into Java:
<xp:button value="Logout" id="button2">
<xp:eventHandler event="onclick" submit="true"
refreshMode="complete">
<xp:this.action>
<![CDATA[#{javascript:
// things we need...
var externalContext = facesContext.getExternalContext();
var request = externalContext.getRequest();
var response = externalContext.getResponse();
var currentContext = com.ibm.domino.xsp.module.nsf.NotesContext.getCurrent();
var session = request.getSession(false);
var sessionId = session.getId();
// flush the cookies and invalidate the HTTP session...
for(var cookie in request.getCookies()){
cookie.setValue("");
cookie.setPath("/");
cookie.setMaxAge(0);
response.addCookie(cookie);
}
session.invalidate();
// now nuke the XSP session from RAM, then jump to logout...
currentContext.getModule().removeSession(sessionId);
externalContext.redirect("http://foo/bar.nsf?logout");
}]]>
</xp:this.action>
</xp:eventHandler>
</xp:button>
Related
I need a technique in which I can call java method for retrieving information from DB and displaying message in dialog box and by pressing "Next" button, if have any next message it will appear. That java method calls automatically after a specific time, says first time when user logged-in and then after ever 3 min. This I want in JSF & PrimeFaces.
I used ScheduleExecutarService for scheduling and my run method as below
public void run() {
try {
System.out.println("beep");
notificationList = retrieveNotificationsFromDB(userId, groupCode, functionCode, chooseNotificationType());
if(notificationList != null && !notificationList.isEmpty()){
showPopup();
}
} catch(Exception e) {
LOGGER.error(e.getMessage());
}
}
public void showPopup() {
if (notificationList != null && !notificationList.isEmpty()) {
setNotificatiomMessage(notificationList.get(0).getMessage());
notificationList.remove(0);
RequestContext.getCurrentInstance().execute("PF('dialogVar').show()");
}
}
In showPopup() I'm not able to get the object of RequestContext
Other Way I tried <p:poll> tag, I read that about the tag is there is a problem for stopping it.
Please guide me what I should use.
In showPopup() I'm not able to get the object of RequestContext
Because there is no active HTTP request (from the client to the server) which is handled at this point.
Solution:
Let the Scheduler fire a CDI event and catch this event with a CDI observer in your client bean.
Now you have to options: Either let the JSF view poll your client bean to ask for new messages, or you create a socket, over which the client bean may inform the JSF view about new messages. You are able to handle the incoming message on your JSF view via JavaScript and do with it whatever you want.
For a very basic example for sockets, see here:
http://www.primefaces.org/showcase/push/counter.xhtml
See also:
https://docs.oracle.com/javaee/6/tutorial/doc/gkhic.html
Finally I got the simple solution with these 2 line of code
<p:poll id="initNotificationAlert" interval="1" oncomplete="PF('initNotificationVar').stop()" listener="#{notificationView.retrieveAlertNotification()}" widgetVar="initNotificationVar"></p:poll>
<p:poll id="notificationAlert" autoStart="false" interval="180" listener="#{notificationView.retrieveAlertNotification()}" widgetVar="notificationVar"></p:poll>
first <p:pol> for initial message and second for <p:pol> for rest.
if any better solution of improvement need in this solution improve it.
Thanks
You can open dialog manually like this:
Scheduler
There are many schdulers like Quartz or for WebApplication
Dialog
public String showDialog(String dialogName){
Map<String, Object> options = new HashMap<String, Object>();
options.put("modal", true);
options.put("closable", true);
options.put("draggable", true);
options.put("resizable", true);
options.put("contentHeight", 386);
options.put("contentWidth", 1266);
RequestContext.getCurrentInstance().openDialog(dialogName, options, null);
return "";
}
While dialogName is that .xhtml page you are trying to show. You just need a schedular to call this method and dialog will appears.
A week ago, I have studied about ViewExpiredException, and I've read several things about it.
viewExpiredException JSF
How to control web page caching, across all browsers?
Session timeout and ViewExpiredException handling on JSF/PrimeFaces ajax request
My problem, which is some cases, I would like to ignore the ViewExpiredException. These are situations that do not need a "session", and my Beans are #RequestScoped. As an example, the pages login.xhtml, register.xhtml and passwordRecovery.xhtml.
In these cases, it is very strange display an error to the user saying that your session has expired. So if you open the login page and stand still for a while, when he inform your data and click Login, it would be forwarded to an error page. I would just ignore it and let transparent to the user.
So, my solution so far is create a ExceptionHandler to ignore these exceptions:
#Override
public void handle() throws FacesException {
for (Iterator<ExceptionQueuedEvent> i = getUnhandledExceptionQueuedEvents().iterator(); i.hasNext();) {
ExceptionQueuedEvent event = i.next();
ExceptionQueuedEventContext context = (ExceptionQueuedEventContext) event.getSource();
Throwable t = context.getException();
// just remove the exception from queue
if (t instanceof ViewExpiredException) {
i.remove();
}
}
getWrapped().handle();
}
Then, I created a filter to check whether the user is logged in, if not then redirected to login page (This filter applies only pages that require authentication):
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
if (!loginManagedBean.isLogged()) {
String pathLogin = request.getContextPath() + "/" + LOGIN_VIEW;
if (isAJAXRequest(request)) {
response.setContentType("text/xml");
response.getWriter()
.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>")
.printf("<partial-response><redirect url=\"%s\"></redirect></partial-response>", pathLogin);
return;
}
pathLogin += "?source=" + request.getServletPath();
response.sendRedirect(pathLogin);
return;
}
chain.doFilter(request, response);
}
So when the session expires, does not affect the user experience in the login and registration pages. And on pages I wish session, are handled by the filter.
That would be a good solution? Are there any security risk to ignore ViewExpiredException in a ExceptionHandler?
Ignoring them is not technically bad in this specific case, but it indicates a bad design. It's as if you're using the wrong tool for the job. I.e. those views should actually never expire.
Just make specifically those views stateless.
<f:view transient="true">
...
</f:view>
This can be placed anywhere in the page, even duplicated, but most self-documenting is making it the top level tag of the page, composition or definition.
See also:
What is the usefulness of statelessness in JSF?
I would like to use the standard XPages xp:fileDownload control and bind it to a Java Bean rather than a document source.
I have an RTF field in my form - 'resourceAttachments' - along with several other fields in which in which I will be storing several attachments and nothing else.
Can anyone provide me with an example or point me to some documentation. I have a similar requirement for the xp:uploadControl, I can find samples which create a new document, but I am struggling to implement adding and saving to existing documents, I guess I should post another question for that though, but as the two go together I thought I would at least mention it here.
Many thanks.
Mark
public class TrainingModule implements Serializable {
private static final long serialVersionUID = -6998234266563204541L;
private String description;
private ???? resourceAttachments; --something here ??
private String unid;
public TrainingModule() {
String documentId = ExtLibUtil.readParameter(FacesContext.getCurrentInstance(), "key");
if (StringUtil.isNotEmpty(documentId)) {
load(documentId);
}
}
public String getUnid() {return unid;}
public void setUnid(final String unid) {this.unid = unid;}
public String getDescription() {return description;}
public void setDescription(final String description) {this.description = description;}
? Some attachment Getter & Setter here??
public void load(final String unid) {setUnid(unid);{
Document doc = null;
try {
doc = ExtLibUtil.getCurrentDatabase().getDocumentByUNID(getUnid());
setDescription(doc.getItemValueString("Description"));
??Some load here here??
} catch (Throwable t) { t.printStackTrace();
} finally {
try {
doc.recycle();
?some other recycle here?
} catch (Exception ) {
// Fail Silent
}
}
In my Custom Control amongst other things I have...
<xp:this.beforePageLoad><![CDATA[#{javascript:if(param.containsKey("key"))
{viewScope.put("docAttach",(param.get("key")));}}]]></xp:this.beforePageLoad>
.......
<xp:fileDownload rows="30" id="fileDownload2" displayLastModified="false"
value="#{TrainingModule.ResourceAttachments}" hideWhen="true" allowDelete="true">
</xp:fileDownload>
I have dealt a similar problem last year. I haven't looked for an appropriate binding for fileUpload. Instead, I used a different approach.
When you submit an XPage with a file upload, it will upload the file to the disk (regardless of any binding) and create an com.ibm.xsp.http.UploadedFile object in the requestMap. So you can grab it from a bean method and do the magic.
This is the IBM Connect 2014 demo and I have used this technique to upload file into the BaseCamp.
XSP code is simple (here is the Github repo)
<xp:fileUpload id="fileUpload1"></xp:fileUpload>
<xp:button id="button1"
value="Upload Local File">
<xp:eventHandler event="onclick"
submit="true" refreshMode="complete">
<xp:this.action>
<xp:executeScript
script="#{bcs.uploadLocalFile}">
</xp:executeScript>
</xp:this.action>
</xp:eventHandler>
</xp:button>
bcs is the managed bean and here is the code snippet for upload (from Github repo)
public void uploadLocalFile() {
FacesContext facesContext = FacesContext.getCurrentInstance();
ExternalContext externalContext = facesContext.getExternalContext();
HttpServletRequest request = (HttpServletRequest) externalContext.getRequest();
String fileUploadID = ExtLibUtil.getClientId(facesContext, facesContext.getViewRoot(), "fileUpload1", false);
UploadedFile uploadedFile = ((UploadedFile) request.getParameterMap().get(fileUploadID));
if (uploadedFile == null) {
facesContext.addMessage("messages1", new FacesMessage(FacesMessage.SEVERITY_ERROR, "No file uploaded. Use the file upload button to upload a file.", ""));
return;
}
java.io.File file = uploadedFile.getServerFile();
String contentType=uploadedFile.getContentType();
String fileName = uploadedFile.getClientFileName();
// removed unrelated code...
}
So, basically, you get the requestMap. The UploadedFile object refers to a file object that has been uploaded to a temporary area on the server and it's mapped with the clientId of the fileUpload component.
After this point, you might create a stream and attach the file into a field. When you bind it to a field, document wrapper does the same thing, I guess.
I'm using Iceface Icepush in my JSF application to send some notifications to client. Because of this session timeout never happen on my application.
I've specified session timeout 15 mins. My requirement is, server should invalidate session if there is no actual client interaction for 15 mins.
I did some search in iceface forum and added this context param in web.xml
<context-param>
<param-name>org.icefaces.strictSessionTimeout</param-name>
<param-value>true</param-value>
</context-param>
also someone has specified to use sessionTimeoutMonitor in faces-config.xml
<application>
<resource-handler>org.icefaces.impl.application.SessionTimeoutMonitor</resource-handler>
</application>
But nothing works.
You can simply add to your main template, which is inherited by others secured *.xhtml files (perhaps works ONLY with PrimeFaces):
<p:idleMonitor timeout="600000">
<p:ajax event="idle" listener="#{loggedBean.logOut()}" update="messagesIdle" />
Or <p:ajax event="active" listener="#{messagesBean.showMessageInfo('Czy ta kawka nie była zbyt gorąca? :)')}" update="messagesIdle" />
</p:idleMonitor>
One hint - you can use only one idleMonitor per page!
I wrote my own timeout monitor by extending the org.icefaces.impl.application.SessionTimeoutMonitor class and this works fine.
public class RWSessionTimeoutMonitor extends SessionTimeoutMonitor{
private static Logger log = LoggerFactory.getLogger(RWSessionTimeoutMonitor.class);
public RWSessionTimeoutMonitor(ResourceHandler handler) {
super(handler);
}
#Override
public boolean isResourceRequest(FacesContext context) {
final ExternalContext externalContext = context.getExternalContext();
//create session if non-ajax request
final Object session = externalContext.getSession(!context.getPartialViewContext().isAjaxRequest());
//if session invalid or expired block other resource handlers from running
if (session == null) {
//return false to force JSF to run and throw ViewExpiredException which eventually will be captured
//and re-cast in a SessionExpiredException
return false;
}
if (!EnvUtils.isStrictSessionTimeout(context)) {
return getWrapped().isResourceRequest(context);
}
Map sessionMap = externalContext.getSessionMap();
Long lastAccessTime = (Long) sessionMap.get(SessionTimeoutMonitor.class.getName());
boolean isPushRelatedRequest = EnvUtils.isPushRequest(context);
if (lastAccessTime == null || !isPushRelatedRequest) {
lastAccessTime = System.currentTimeMillis();
sessionMap.put(SessionTimeoutMonitor.class.getName(), System.currentTimeMillis());
}
int maxInactiveInterval;
maxInactiveInterval = ((javax.servlet.http.HttpSession) session).getMaxInactiveInterval();
if (System.currentTimeMillis() - lastAccessTime > maxInactiveInterval * 1000) {
sessionMap.remove(SessionTimeoutMonitor.class.getName());
externalContext.invalidateSession();
log.info("No user request b/w max interval [{}], session is invalidated." , maxInactiveInterval );
}
return super.isResourceRequest(context);
}
}
I am using ExternalContext.redirect(String); method to redirect user to another page:
FacesContext.getCurrentInstance().addMessage(new FacesMessage("Bla bla bla..."));
FacesContext.getCurrentInstance().getExternalContext().getFlash().setKeepMessages(true);
ExternalContext ec = FacesContext.getCurrentInstance().getExternalContext();
ec.redirect(ec.getRequestContextPath() + "/scenario.xhtml");
As Matt Handy mentioned in his answer, I used Flash.setKeepMessages(true); but it does not seem to work with ExternalContext.redirect. (Although it works when I redirect by returning a page name from bean's action method.)
Now how can I add FacesMessage so that it is visible in the redirected (scenario.xhtml) page?
This seems to be a timing problem. This listener method is invoked during the preRenderView event. According to the source code of ELFlash (Mojarra's Flash implementation as returned by ExternalContext#getFlash()) it turns out that it won't set the flash cookie when you're currently sitting in the render response phase and the flash cookie hasn't been set yet for the current request:
Here are the relevant lines from ELFlash:
if (currentPhase.getOrdinal() < PhaseId.RENDER_RESPONSE.getOrdinal()) {
flashInfo = flashManager.getPreviousRequestFlashInfo();
} else {
flashInfo = flashManager.getNextRequestFlashInfo(this, true);
maybeWriteCookie(context, flashManager);
}
The maybeWriteCookie would only set the cookie when the flash cookie needs to be passed through for the second time (i.e. when the redirected page in turn redirects to another page).
This is an unfortunate corner case. This ELFlash logic makes sense, but this isn't what you actually want. Basically you need to add the message during INVOKE_APPLICATION phase instead. There is however no such event as postInvokeAction. With the new JSF 2.2 <f:viewAction> tag it should be possible as it really runs during invoke application phase.
<f:viewAction action="#{bean.onload}" />
As long as you're not on JSF 2.2 yet, you'd need to look for alternate ways. The easiest way would be to create a custom ComponentSystemEvent.
#NamedEvent(shortName="postInvokeAction")
public class PostInvokeActionEvent extends ComponentSystemEvent {
public PostInvokeActionEvent(UIComponent component) {
super(component);
}
}
Now you need somewhere a hook to publish this event. The most sensible place is a PhaseListener listening on after phase of INVOKE_APPLICATION.
public class PostInvokeActionListener implements PhaseListener {
#Override
public PhaseId getPhaseId() {
return PhaseId.INVOKE_APPLICATION;
}
#Override
public void beforePhase(PhaseEvent event) {
// NOOP.
}
#Override
public void afterPhase(PhaseEvent event) {
FacesContext context = FacesContext.getCurrentInstance();
context.getApplication().publishEvent(context, PostInvokeActionEvent.class, context.getViewRoot());
}
}
If you register it as follows in faces-config.xml
<lifecycle>
<phase-listener>com.example.PostInvokeActionListener</phase-listener>
</lifecycle>
then you'll be able to use the new event as follows
<f:event type="postInvokeAction" listener="#{bean.onload}" />
Update this is also available in the JSF utility library OmniFaces, so you don't need to homebrew the one and other. See also the InvokeActionEventListener showcase example.
Use the flash to keep messages over a redirect.
Add these two lines to your code before redirecting:
FacesContext context = FacesContext.getCurrentInstance();
context.getExternalContext().getFlash().setKeepMessages(true);
Note that the there are some issues with Mojarra's flash scope implementation. Keep this in mind if you use it.
Using Matt Handy's example as a reference, I created the method below that worked very well for me.
public static void Message(String message) {
FacesMessage fm = new FacesMessage(FacesMessage.SEVERITY_INFO, mensagem, null);
FacesContext context = FacesContext.getCurrentInstance();
context.getExternalContext().getFlash().setKeepMessages(true);
context.addMessage(null, fm);
}