001/*
002 * (C) Copyright 2006-2007 Nuxeo SAS (http://nuxeo.com/) and contributors.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the GNU Lesser General Public License
006 * (LGPL) version 2.1 which accompanies this distribution, and is available at
007 * http://www.gnu.org/licenses/lgpl.html
008 *
009 * This library is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012 * Lesser General Public License for more details.
013 *
014 * Contributors:
015 *     <a href="mailto:at@nuxeo.com">Anahide Tchertchian</a>
016 *
017 * $Id: FancyNavigationHandler.java 28924 2008-01-10 14:04:05Z sfermigier $
018 */
019
020package org.nuxeo.ecm.platform.ui.web.rest;
021
022import java.io.IOException;
023import java.util.Map;
024import java.util.Set;
025
026import javax.faces.FacesException;
027import javax.faces.application.ConfigurableNavigationHandler;
028import javax.faces.application.NavigationCase;
029import javax.faces.application.NavigationHandler;
030import javax.faces.application.ViewHandler;
031import javax.faces.component.UIViewRoot;
032import javax.faces.context.ExternalContext;
033import javax.faces.context.FacesContext;
034import javax.servlet.http.HttpServletRequest;
035
036import org.apache.commons.logging.Log;
037import org.apache.commons.logging.LogFactory;
038import org.jboss.seam.contexts.Contexts;
039import org.nuxeo.ecm.platform.ui.web.rest.api.URLPolicyService;
040import org.nuxeo.ecm.platform.ui.web.util.BaseURL;
041import org.nuxeo.runtime.api.Framework;
042
043import com.sun.faces.util.Util;
044
045/**
046 * Navigation handler that keeps outcome information available so that it can be used for a document view when
047 * redirecting to this context.
048 *
049 * @author <a href="mailto:at@nuxeo.com">Anahide Tchertchian</a>
050 */
051public class FancyNavigationHandler extends ConfigurableNavigationHandler {
052
053    private static final Log log = LogFactory.getLog(FancyNavigationHandler.class);
054
055    private final NavigationHandler parent;
056
057    public static final String DISABLE_REDIRECT_FOR_URL_REWRITE = FancyNavigationHandler.class.getCanonicalName()
058            + ".DISABLE_REDIRECT_FOR_URL_REWRITE";
059
060    public FancyNavigationHandler(NavigationHandler navigationHandler) {
061        parent = navigationHandler;
062    }
063
064    @Override
065    public void handleNavigation(FacesContext context, String fromAction, String outcome) {
066        ExternalContext eContext = context.getExternalContext();
067        HttpServletRequest httpRequest = (HttpServletRequest) eContext.getRequest();
068        // put outcome in request params
069        httpRequest.setAttribute(URLPolicyService.POST_OUTCOME_REQUEST_KEY, outcome);
070        URLPolicyService pservice = Framework.getService(URLPolicyService.class);
071        pservice.appendParametersToRequest(context);
072        // get old root to check if it's changed
073        UIViewRoot oldRoot = context.getViewRoot();
074        parent.handleNavigation(context, fromAction, outcome);
075        UIViewRoot newRoot = context.getViewRoot();
076        boolean rootChanged = !oldRoot.equals(newRoot);
077        if (outcome != null && !context.getResponseComplete() && !rootChanged && pservice != null
078                && Framework.isDevModeSet()) {
079            // navigation was not done => maybe a hot reload issue: perform
080            // navigation again using local code because it uses
081            // information from the StaticNavigationHandler that is
082            // hot-reloaded correctly
083            // TODO: check if still relevant in JSF2
084            handleHotReloadNavigation(pservice, context, fromAction, outcome);
085        }
086        Object disable = httpRequest.getAttribute(DISABLE_REDIRECT_FOR_URL_REWRITE);
087        if (Boolean.TRUE.equals(disable)) {
088            // avoid redirect
089            return;
090        }
091        // force redirect if outcome is null so that url can be
092        // re-written except in an ajax request
093        boolean ajaxRequest = context.getPartialViewContext().isAjaxRequest();
094        if (outcome == null && !ajaxRequest && !context.getResponseComplete()) {
095            String url = httpRequest.getRequestURL().toString();
096            String localUrl = BaseURL.getServerURL(httpRequest, true);
097            String baseUrl = BaseURL.getServerURL(httpRequest, false);
098            if (localUrl != null && !localUrl.equals(baseUrl)) {
099                url = url.replaceFirst(localUrl, baseUrl);
100            }
101            if (Contexts.isEventContextActive()) {
102                // strip any jsession id before redirect
103                int jsessionidIndex = url.indexOf(";jsessionid");
104                if (jsessionidIndex != -1) {
105                    url = url.substring(0, jsessionidIndex);
106                }
107                // add conversation id before redirect
108                url = RestHelper.addMainConversationParameters(url);
109            }
110            try {
111                eContext.redirect(url);
112            } catch (IOException e) {
113                // do nothing...
114                log.error(e, e);
115            }
116        }
117    }
118
119    protected void handleHotReloadNavigation(URLPolicyService pservice, FacesContext context, String fromAction,
120            String outcome) {
121        String viewId = pservice.getViewIdFromOutcome(outcome, null);
122        ExternalContext extContext = context.getExternalContext();
123        if (viewId != null) {
124            ViewHandler viewHandler = Util.getViewHandler(context);
125            // always redirect
126            String newPath = viewHandler.getActionURL(context, viewId);
127            try {
128                extContext.redirect(extContext.encodeActionURL(newPath));
129            } catch (java.io.IOException ioe) {
130                throw new FacesException(ioe.getMessage(), ioe);
131            }
132            context.responseComplete();
133        }
134    }
135
136    @Override
137    public NavigationCase getNavigationCase(FacesContext context, String fromAction, String outcome) {
138        if (parent instanceof ConfigurableNavigationHandler) {
139            return ((ConfigurableNavigationHandler) parent).getNavigationCase(context, fromAction, outcome);
140        }
141        return null;
142    }
143
144    @Override
145    public Map<String, Set<NavigationCase>> getNavigationCases() {
146        if (parent instanceof ConfigurableNavigationHandler) {
147            return ((ConfigurableNavigationHandler) parent).getNavigationCases();
148        }
149        return null;
150    }
151}