Wicket setPageExpiredErrorPage only for specific page? - java

Is there any way to do that?
I heard something about implemented RequestCycle, how to acomplish that?
Tried How can I get the responsePage from a RequestCycle in Wicket 1.5? this, but doesnt work.

The reason why you get a PageExpiredException in Wicket is because Wicket is unable to find the page. There is no way of determining the type of the page that is no longer available, because, well, the page actually is no longer there. It ceased to exist, met its maker, bereft of life, rests in peace, its lifecycle are now 'istory, kicked the bucket. It is an ex-page.
So Wicket's only recourse is to serve the PageExpiredException, and there is no way (in Wicket itself) to retrieve the page that was attempted to resurrect.
Now what you can try to do is to store the class of the last rendered page in the session, and use that in your RequestCycleListener implementation of onException() and return the appropriate request handler. In code:
#Override
public void onRequestHandlerExecuted(RequestCycle cycle, IRequestHandler handler) {
Class<? extends Page> p = null;
if(handler instanceof IPageRequestHandler)
p = ((IPageRequestHandler)handler).getPageClass();
else if(handler instanceof IComponentRequestHandler)
p = ((IComponentRequestHandler)handler).getComponent().getPage().getClass();
MySession.get().setLastPageClass(p);
}
#Override
public IRequestHandler onException(RequestCycle cycle, Exception ex) {
Class<? extends Page> pageClass MySession.get().getLastPageClass();
... return some handler based on your logic
}
You might want to check for more IRequestHandler implementations in onRequestHandlerExecuted.

If I understand correctly you want to redirect user only if pageExpired happened from specific page? You can try something like this in you implementation of Application:
getRequestCycleListeners().add(new AbstractRequestCycleListener() {
#Override
public IRequestHandler onException(RequestCycle cycle, Exception e) {
if(e.getClass().equals(PageExpiredException.class)) {
//check if url in request responds to correct mounted page type
if(isPageUrl(cycle.getRequest().getUrl()))) {
return new RenderPageRequestHandler(new PageProvider(MyPage.class));
} else {
return super.onException(cycle, e);
}
} else {
return super.onException(cycle, e);
}
}
}
This assumes few things - that the page at which you got the exception has been mounted, and that you will be able to parse request url to be sure it is it.
I haven't tested it but we are doing something similar.

Related

ExternalContext redirect leads to IllegalStateException

I am facing a problem that is coming from one of our users. Somehow, the user manages to make multiple redirects with just one click. And by doing this, the redirect method is called multiple times and this leads to the IllegalStateException. I tried to first check if the response is committed and only call the redirect method if the response is not committed. And it works. Just one redirect request instead of all is being sent to the browser. But I was wondering if it's possible to send multiple redirects statements. Is it possible to create a new request with a new response after the old response was committed ?
Here is the working check for the committed redirect:
public static final boolean redirect(String targetPath) {
try {
if(!isCommitted()) {
exContext().redirect(checkAppendContextPath(targetPath));
return true;
}else{
return false;
}
} catch (IOException e) {
if (LOG.isErrorEnabled()) {
LOG.error(String.format(ERROR_REDIRECT, context().getViewRoot().getViewId(), targetPath));
}
return false;
}
}
private static final boolean isCommitted(){
if(exContext().getResponse() instanceof HttpServletResponse){
if(((HttpServletResponse) exContext().getResponse()).isCommitted()){
return true;
}else{
return false;
}
}else{
return false;
}
}
No, requests cannot be made on the server side. They must originate from the client.
I find it hard to believe that multiple requests are triggered from a single click, unless there is JavaScript involved. If JavaScript is involved, check it for bugs. Else, you could try to prevent multiple clicking at the client side. If you are using PrimeFaces and the redirect is triggered from a p:commandButton, you could try using the PrimeFaces Extensions CommandButtonSingleClickRenderer.

JavaEE Globally Catch Runtime exception

I want to "globally" catch a Runtime exception, now this sounds silly right but let me explain.
I have created a interceptor that I use on all my ejbs which require authorization to use, this interceptor is called every time a method is called.
See here the code:
#Secure
#Interceptor
public class SecurityInterceptor {
#EJB
private SessionManager sessionManager;
#AroundInvoke
private Object securityCheck(InvocationContext ctx) throws Exception {
System.out.println("hello");
List<UserGroup> allowedRoles = (List<UserGroup>) ctx.getContextData().get("rolesAllowed");
sessionManager.isAuthorized(allowedRoles);
return ctx.proceed();
}
}
Now I don't want in my Named Bean to surround the body of every method that uses one of these secured beans with a try and catch block, like this:
public List<Contracts> getContracts() {
List<Contracts> contracts = new ArrayList<>();
try {
contracts = contractEntityManager.getAll();
} catch (EJBTransactionRolledbackException e) {
Throwable throwable = ExceptionUtils.getRootCause(e);
if (throwable instanceof NotAuthenticatedException) {
System.out.println("Not Authenticated");
}
else if (throwable instanceof UnAuthorizedException) {
System.out.println("Not Authorized");
}
}
return contracts;
}
So is there some way to globally catch a runtime exception and the redirect the user to the login page if he is unauthenticated and to a error page when he is unauthorized.
Maybe my design is just generally bad and I would need to complety rethink this idea.
Please let me know.
Thank you
So, in the end, it is the in the web tier that you want to catch the exceptions. This is easy:
If you are only on servlets, specify the error-page element in web.xml for each exception you wish to handle, e.g. as follows:
<error-page>
<exception-type>fully.quallified.NotAuthenticatedException</exception-type>
<location>/where/to/redirect/eg/login</location>
</error-page>
You declare the exception you want to handle globally and the URL you want to handle it, it may be a servlet, a JSP or any other resource.
JAX-RS offers a similar mechanism with the javax.ws.rs.ext.ExceptionMapper:
#Provider
public class NotAuthenticatedExceptionMapper implements ExceptionMapper<NotAuthenticatedException> {
...
}

PageExpiredException, migration to wicket 1.5 not working

I am trying to server a particular error page when session timeouts to my users.
For this i configured the error page on my Application's init method.
But this thing is not working.
I set up the session tiemout in 1 minute, after that nothing happen, I went through the logs, but wicket didn't throw any PageExpiredException.
When session timeouts wicket simply logs it as:
Session unbound: C061F4F21C41EDF13C66795DAC9EDD02
Removing data for pages in session with id 'C061F4F21C41EDF13C66795DAC9EDD02'
this is my init method in my customApplication
protected void init() {
super.init();
this.getApplicationSettings().setPageExpiredErrorPage(SessionExpiredPage.class);
...
...
}
my SessionExpiredPage.class
public class SessionExpiredPage extends TecnoAccionPage {
public SessionExpiredPage() {
this.setOutputMarkupId(true);
this.add(new Label("title", "SesiĆ³n Expirada"));
CSSLoader.get().appendCssUntil(this, SessionExpiredPage.class);
}
}
And i have a custom implementation of AbstractRequestCycleListener i override the OnException method But, when my session expire, I never pass in the "onException".
Thank You, best regards.
For some reason there is no PageExpiredException thrown by wicket, while it can reconstruct requested page, even if the session was expired.
So, there is another way to deal with this problem.
You have to override onRequestHandlerResolved method in your AbstractRequestCycleListener, to catch all incoming requests, and check there if incoming session id is outdated.
To check this, you must have list of the expired sessions in your app and catch unbound event to manage them.
This is going to be something like that:
public class YourApp extends WebApplication {
//synchronized list with ids
private List<String> unboundSessions = new CopyOnWriteArrayList<String>();
#Override
protected void init() {
super.init();
this.getApplicationSettings().setPageExpiredErrorPage(SessionExpiredPage.class);
//add request listener
getRequestCycleListeners().add(new AbstractRequestCycleListener() {
public void onRequestHandlerResolved(RequestCycle cycle, IRequestHandler handler) {
if (handler instanceof IPageRequestHandler) {
HttpServletRequest request = (HttpServletRequest) cycle.getRequest().getContainerRequest();
String sessionId = request.getRequestedSessionId();
//check whether the requested session has expired
boolean expired = sessionId != null && !request.isRequestedSessionIdValid();
//if session is not valid and it was really expired
if (expired && unboundSessions.contains(sessionId)) {
//then remove it from unbound list
unboundSessions.remove(sessionId);
//and throw exception
throw new PageExpiredException("Expired");
}
}
super.onRequestHandlerResolved(cycle, handler);
}
});
...
}
//this method called when any session is invalidated, so check your manual invalidating calls (if you ever do them)
#Override
public void sessionUnbound(String sessionId) {
super.sessionUnbound(sessionId);
if (!unboundSessions.contains(sessionId)) {
unboundSessions.add(sessionId);
}
}
}
Unbound sessions list needs for us to know, that user's session is really expired, since the expired variable in our listener could be also true when user just openes our site after redeploy, for example. His session is taken from his cookies and it could be already expired, but that would be weird to redirect him to SessionExpiredPage immediately.
It looks like a workaround, but it should work.

Access Wicket page object outside of wicket component hierarchy

I have pojo classes that handle my backend connections. I want to encapsulate my (backend) error handling in these classes by catching backend exceptions inside.
Is there any way to access the current wicket page (or any component for that matter) to enable me to give feedback to the user from outside the wicket component hierarchy?
class MyService {
...
public void doBackEndThing(){
try {
backEndService.doRemoteCall();
} catch (BackendException e) {
//we're not inside the component hierarchy! so no getPage() available
WebPage page = getCurrentPage();
page.error("Backend is currently not available");
}
}
I've tried the PageManager, but I have no idea how to retrieve the correct version and so I do not know if would work at all:
int version = ?;
WebPage page = (WebPage )Session.get().getPageManager().getPage(version);
There isn't a nice way and it doesn't seem to be a good idea to do this. Your frontend should call your backend not the other way. So the easiest way to do this would be to store the errors inside your service and have your page get these.
class MyService {
private String error;
public void doBackEndThing(){
try {
backEndService.doRemoteCall();
} catch (BackendException e) {
error ="Backend is currently not available";
}
}
}
and
class MyPage extends WebPage {
private MySerivce service;
public void doSomethingFrontendy() {
error = service.getError();
}
}
or you could return an error from your backend method or throw an Exception and handle this in your WebPage or use IRequestCycleListener#onException() like #svenmeier pointed out.
IRequestCycleListener#onException() is a better place for this - you can get access to the current page via RequestCycle#getActiveRequestHandler().

Does this program introduce a parallel execution?

Here is a simple server application using Bonjour and written in Java. The main part of the code is given here:
public class ServiceAnnouncer implements IServiceAnnouncer, RegisterListener {
private DNSSDRegistration serviceRecord;
private boolean registered;
public boolean isRegistered(){
return registered;
}
public void registerService() {
try {
serviceRecord = DNSSD.register(0,0,null,"_killerapp._tcp", null,null,1234,null,this);
} catch (DNSSDException e) {
// error handling here
}
}
public void unregisterService(){
serviceRecord.stop();
registered = false;
}
public void serviceRegistered(DNSSDRegistration registration, int flags,String serviceName, String regType, String domain){
registered = true;
}
public void operationFailed(DNSSDService registration, int error){
// do error handling here if you want to.
}
}
I understand it in the following way. We can try to register a service calling "registerService" method which, in its turn, calls "DNSSD.register" method. "DNSSD.register" try to register the service and, in general case, it can end up with two results: service was "successfully registered" and "registration failed". In both cases "DNSSD.register" calls a corresponding method (either "serviceRegistered" or "operationFailed") of the object which was given to the DNSSD.register as the last argument. And programmer decides what to put into "serviceRegistered" and "operationFailed". It is clear.
But should I try to register a service from the "operationFailed"? I am afraid that in this way my application will try to register the service too frequently. Should I put some "sleep" or "pause" into "operationFailed"? But in any case, it seems to me, that when the application is unable to register a service it will be also unable to do something else (for example to take care of GUI). Or may be DNSSD.register introduce some kind of parallelism? I mean it starts a new thread but that if I try to register service from "operation Failed", I could generate a huge number of the threads. Can it happen? If it is the case, should it be a problem? And if it is the case, how can I resolve this problem?
Yes, callbacks from the DNSSD APIs can come asynchronously from another thread. This exerpt from the O'Reilly book on ZeroConf networking gives some useful information.
I'm not sure retrying the registration from your operationFailed callback is a good idea. At least without some understanding of why the registration failed, is simply retrying it with the same parameters going to make sense?

Categories

Resources