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