Index: api/src/main/java/org/hippoecm/frontend/RepositoryRuntimeException.java =================================================================== --- api/src/main/java/org/hippoecm/frontend/RepositoryRuntimeException.java (revision 0) +++ api/src/main/java/org/hippoecm/frontend/RepositoryRuntimeException.java (revision 0) @@ -0,0 +1,41 @@ +/* + * Copyright 2014-2014 Hippo B.V. (http://www.onehippo.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.hippoecm.frontend; + +/** + * JCR Repository runtime exception in frontend application + */ +public class RepositoryRuntimeException extends RuntimeException { + + private static final long serialVersionUID = 1L; + + public RepositoryRuntimeException() { + super(); + } + + public RepositoryRuntimeException(String message) { + super(message); + } + + public RepositoryRuntimeException(String message, Throwable cause) { + super(message, cause); + } + + public RepositoryRuntimeException(Throwable cause) { + super(cause); + } + +} Index: api/src/main/java/org/hippoecm/frontend/RepositoryUnavailableException.java =================================================================== --- api/src/main/java/org/hippoecm/frontend/RepositoryUnavailableException.java (revision 0) +++ api/src/main/java/org/hippoecm/frontend/RepositoryUnavailableException.java (revision 0) @@ -0,0 +1,41 @@ +/* + * Copyright 2014-2014 Hippo B.V. (http://www.onehippo.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.hippoecm.frontend; + +/** + * Runtime exception representing the status that there's no available JCR repository. + */ +public class RepositoryUnavailableException extends RepositoryRuntimeException { + + private static final long serialVersionUID = 1L; + + public RepositoryUnavailableException() { + super(); + } + + public RepositoryUnavailableException(String message) { + super(message); + } + + public RepositoryUnavailableException(String message, Throwable cause) { + super(message, cause); + } + + public RepositoryUnavailableException(Throwable cause) { + super(cause); + } + +} Index: api/src/main/java/org/hippoecm/frontend/model/map/JcrMap.java =================================================================== --- api/src/main/java/org/hippoecm/frontend/model/map/JcrMap.java (revision 45755) +++ api/src/main/java/org/hippoecm/frontend/model/map/JcrMap.java (working copy) @@ -35,10 +35,9 @@ import javax.jcr.nodetype.NodeDefinition; import javax.jcr.nodetype.NodeType; -import org.apache.wicket.RestartResponseException; import org.apache.wicket.model.IDetachable; -import org.apache.wicket.protocol.http.WebApplication; import org.hippoecm.frontend.model.JcrNodeModel; +import org.hippoecm.frontend.session.InvalidSessionException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -443,10 +442,14 @@ try { if (!getNode().getSession().isLive()) { log.error("Found session in an invalid unallowed state: not live. Return log in screen"); - throw new RestartResponseException(WebApplication.get().getHomePage()); + throw new InvalidSessionException("Invalid (non-live) session found."); } } catch (RepositoryException e) { - // log the original ex above + if (log.isDebugEnabled()) { + log.warn("Failed to check the liveness of the session.", e); + } else { + log.warn("Failed to check the liveness of the session. {}", e); + } } if (log.isDebugEnabled()) { Index: api/src/main/java/org/hippoecm/frontend/session/InvalidSessionException.java =================================================================== --- api/src/main/java/org/hippoecm/frontend/session/InvalidSessionException.java (revision 0) +++ api/src/main/java/org/hippoecm/frontend/session/InvalidSessionException.java (revision 0) @@ -0,0 +1,43 @@ +/* + * Copyright 2014-2014 Hippo B.V. (http://www.onehippo.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.hippoecm.frontend.session; + +import org.hippoecm.frontend.RepositoryRuntimeException; + +/** + * Runtime exception representing the JCR session is invalid. e.g, session is no more live because it's closed. + */ +public class InvalidSessionException extends RepositoryRuntimeException { + + private static final long serialVersionUID = 1L; + + public InvalidSessionException() { + super(); + } + + public InvalidSessionException(String message) { + super(message); + } + + public InvalidSessionException(String message, Throwable cause) { + super(message, cause); + } + + public InvalidSessionException(Throwable cause) { + super(cause); + } + +} Index: engine/src/main/java/org/hippoecm/frontend/Main.java =================================================================== --- engine/src/main/java/org/hippoecm/frontend/Main.java (revision 45755) +++ engine/src/main/java/org/hippoecm/frontend/Main.java (working copy) @@ -30,6 +30,7 @@ import javax.jcr.observation.EventListener; import javax.jcr.observation.EventListenerIterator; +import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.math.NumberUtils; import org.apache.wicket.Component; import org.apache.wicket.DefaultPageManagerProvider; @@ -58,7 +59,9 @@ import org.apache.wicket.request.Response; import org.apache.wicket.request.Url; import org.apache.wicket.request.component.IRequestablePage; +import org.apache.wicket.request.cycle.IRequestCycleListener; import org.apache.wicket.request.cycle.RequestCycle; +import org.apache.wicket.request.cycle.RequestCycleListenerCollection; import org.apache.wicket.request.handler.render.PageRenderer; import org.apache.wicket.request.handler.render.WebPageRenderer; import org.apache.wicket.request.http.WebRequest; @@ -115,6 +118,12 @@ public final static String PLUGIN_APPLICATION_NAME_PARAMETER = "config"; /** + * Custom Wicket {@link IRequestCycleListener} class names parameter + * which can be comma or whitespace-separated string to set multiple {@link IRequestCycleListener}s. + */ + public final static String REQUEST_CYCLE_LISTENERS_PARAM = "wicket.request.cycle.listeners"; + + /** * Wicket RequestCycleSettings timeout configuration parameter name in development mode. */ public final static String DEVELOPMENT_REQUEST_TIMEOUT_PARAM = "wicket.development.request.timeout"; @@ -146,6 +155,8 @@ protected void init() { super.init(); + addRequestCycleListeners(); + getPageSettings().setVersionPagesByDefault(false); // getPageSettings().setAutomaticMultiWindowSupport(false); @@ -573,5 +584,30 @@ } } + /** + * Adds the default built-in {@link IRequestCycleListener} or configured custom {@link IRequestCycleListener}s. + *

+ * If no custom {@link IRequestCycleListener}s are configured, then this simply registers the default built-in + * {@link RepositoryRuntimeExceptionHandlingRequestCycleListener}. + * Otherwise, this registers only the custom configured {@link IRequestCycleListener}s. + *

+ */ + private void addRequestCycleListeners() { + String [] listenerClassNames = StringUtils.split(getConfigurationParameter(REQUEST_CYCLE_LISTENERS_PARAM, null), " ,\t\r\n"); + RequestCycleListenerCollection requestCycleListenerCollection = getRequestCycleListeners(); + if (listenerClassNames == null || listenerClassNames.length == 0) { + requestCycleListenerCollection.add(new RepositoryRuntimeExceptionHandlingRequestCycleListener()); + } else { + for (String listenerClassName : listenerClassNames) { + try { + Class clazz = Class.forName(listenerClassName); + IRequestCycleListener listener = (IRequestCycleListener) clazz.newInstance(); + requestCycleListenerCollection.add(listener); + } catch (Throwable th) { + log.error("Failed to register RequestCycleListener, " + listenerClassName, th); + } + } + } + } } Index: engine/src/main/java/org/hippoecm/frontend/RepositoryRuntimeExceptionHandlingRequestCycleListener.java =================================================================== --- engine/src/main/java/org/hippoecm/frontend/RepositoryRuntimeExceptionHandlingRequestCycleListener.java (revision 0) +++ engine/src/main/java/org/hippoecm/frontend/RepositoryRuntimeExceptionHandlingRequestCycleListener.java (revision 0) @@ -0,0 +1,87 @@ +/* + * Copyright 2014-2014 Hippo B.V. (http://www.onehippo.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.hippoecm.frontend; + +import static org.hippoecm.frontend.util.WebApplicationHelper.HIPPO_AUTO_LOGIN_COOKIE_BASE_NAME; + +import org.apache.wicket.core.request.handler.PageProvider; +import org.apache.wicket.core.request.handler.RenderPageRequestHandler; +import org.apache.wicket.core.request.handler.RenderPageRequestHandler.RedirectPolicy; +import org.apache.wicket.protocol.http.WebApplication; +import org.apache.wicket.request.IRequestHandler; +import org.apache.wicket.request.cycle.AbstractRequestCycleListener; +import org.apache.wicket.request.cycle.IRequestCycleListener; +import org.apache.wicket.request.cycle.RequestCycle; +import org.apache.wicket.util.lang.Exceptions; +import org.hippoecm.frontend.session.InvalidSessionException; +import org.hippoecm.frontend.session.UserSession; +import org.hippoecm.frontend.util.WebApplicationHelper; + +/** + * The default {@link IRequestCycleListener} implementation to handle {@link RepositoryRuntimeException}s. + * If a {@link RepositoryRuntimeException} occurs, then this listener redirects to either home page + * or {@link NoRepositoryAvailablePage} depending on the exception type. + */ +public class RepositoryRuntimeExceptionHandlingRequestCycleListener extends AbstractRequestCycleListener { + + @Override + public IRequestHandler onException(RequestCycle cycle, Exception ex) { + IRequestHandler handler = null; + RepositoryRuntimeException rrEx = Exceptions.findCause(ex, RepositoryRuntimeException.class); + + if (rrEx != null) { + handler = createRequestHandler(rrEx); + + if (handler != null) { + clearStates(); + } + } + + return handler; + } + + /** + * Creates IRequestHandler based on the given RepositoryRuntimeException. + * @param rrEx RepositoryRuntimeException + * @return + */ + protected IRequestHandler createRequestHandler(final RepositoryRuntimeException rrEx) { + IRequestHandler handler = null; + + if (rrEx instanceof InvalidSessionException) { + handler = new RenderPageRequestHandler(new PageProvider(WebApplication.get().getHomePage()), RedirectPolicy.AUTO_REDIRECT); + } else if (rrEx instanceof RepositoryUnavailableException) { + handler = new RenderPageRequestHandler(new PageProvider(NoRepositoryAvailablePage.class), RedirectPolicy.AUTO_REDIRECT); + } + + return handler; + } + + /** + * Clear any user states other than user session. + */ + protected void clearStates() { + // Remove the Hippo Auto Login cookie + WebApplicationHelper.clearCookie(WebApplicationHelper.getFullyQualifiedCookieName(HIPPO_AUTO_LOGIN_COOKIE_BASE_NAME)); + + UserSession userSession = UserSession.get(); + + if (userSession != null) { + // Clears HTTP session states + userSession.invalidate(); + } + } +} Index: engine/src/main/java/org/hippoecm/frontend/session/PluginUserSession.java =================================================================== --- engine/src/main/java/org/hippoecm/frontend/session/PluginUserSession.java (revision 45755) +++ engine/src/main/java/org/hippoecm/frontend/session/PluginUserSession.java (working copy) @@ -31,7 +31,6 @@ import org.apache.cxf.common.util.StringUtils; import org.apache.wicket.Application; import org.apache.wicket.Component; -import org.apache.wicket.RestartResponseException; import org.apache.wicket.ThreadContext; import org.apache.wicket.model.IModel; import org.apache.wicket.model.LoadableDetachableModel; @@ -39,8 +38,8 @@ import org.apache.wicket.request.cycle.RequestCycle; import org.hippoecm.frontend.Home; import org.hippoecm.frontend.Main; -import org.hippoecm.frontend.NoRepositoryAvailablePage; import org.hippoecm.frontend.PluginApplication; +import org.hippoecm.frontend.RepositoryUnavailableException; import org.hippoecm.frontend.model.JcrSessionModel; import org.hippoecm.frontend.model.UserCredentials; import org.hippoecm.frontend.observation.FacetRootsObserver; @@ -199,7 +198,7 @@ session = fallbackSession; if (session == null) { main.resetConnection(); - throw new RestartResponseException(NoRepositoryAvailablePage.class); + throw new RepositoryUnavailableException("Repository is not available."); } } else if (fallbackSession != null) { fallbackSession.logout(); @@ -217,6 +216,7 @@ if (!result.isLive()) { log.error("Found session in an invalid unallowed state: not live. Logout PluginUserSession"); logout(); + throw new InvalidSessionException("Invalid (non-live) session found."); } return result; }