/*
* (C) Copyright 2006-2007 Nuxeo SA (http://nuxeo.com/) and others.
*
* 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.
*
* Contributors:
* Nuxeo - initial API and implementation
*
* $Id: StaticNavigationHandler.java 21462 2007-06-26 21:16:36Z sfermigier $
*/
package org.nuxeo.ecm.platform.ui.web.rest;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
import javax.faces.FactoryFinder;
import javax.faces.application.NavigationCase;
import javax.faces.context.FacesContext;
import javax.faces.context.FacesContextFactory;
import javax.faces.lifecycle.Lifecycle;
import javax.faces.lifecycle.LifecycleFactory;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.jboss.seam.util.DTDEntityResolver;
import org.nuxeo.runtime.api.Framework;
import com.sun.faces.application.ApplicationAssociate;
/**
* View id helper that matches view ids and outcomes thanks to navigation cases defined in a faces-config.xml file.
* <p>
* Also handle some hot reload cases, by parsing the main faces-config.xml file.
*/
public class StaticNavigationHandler {
private static final Log log = LogFactory.getLog(StaticNavigationHandler.class);
private final HashMap<String, String> outcomeToViewId = new HashMap<String, String>();
private final HashMap<String, String> viewIdToOutcome = new HashMap<String, String>();
public StaticNavigationHandler(ServletContext context, HttpServletRequest request, HttpServletResponse response) {
boolean created = false;
FacesContext faces = FacesContext.getCurrentInstance();
if (faces == null) {
// Acquire the FacesContext instance for this request
FacesContextFactory facesContextFactory = (FacesContextFactory) FactoryFinder.getFactory(FactoryFinder.FACES_CONTEXT_FACTORY);
LifecycleFactory lifecycleFactory = (LifecycleFactory) FactoryFinder.getFactory(FactoryFinder.LIFECYCLE_FACTORY);
// force using default lifecycle instead of performing lookup on
// conf
Lifecycle lifecycle = lifecycleFactory.getLifecycle(LifecycleFactory.DEFAULT_LIFECYCLE);
faces = facesContextFactory.getFacesContext(context, request, response, lifecycle);
created = true;
}
try {
ApplicationAssociate associate = ApplicationAssociate.getCurrentInstance();
for (Set<NavigationCase> cases : associate.getNavigationCaseListMappings().values()) {
for (NavigationCase cnc : cases) {
String toViewId = cnc.getToViewId(faces);
String fromOutcome = cnc.getFromOutcome();
outcomeToViewId.put(fromOutcome, toViewId);
viewIdToOutcome.put(toViewId, fromOutcome);
}
}
if (Framework.isDevModeSet()) {
handleHotReloadResources(context);
}
} finally {
if (created) {
faces.release();
}
}
}
public String getOutcomeFromViewId(String viewId) {
if (viewId == null) {
return null;
}
viewId = viewId.replace(".faces", ".xhtml");
if (viewIdToOutcome.containsKey(viewId)) {
return viewIdToOutcome.get(viewId);
}
return viewId;
}
public String getViewIdFromOutcome(String outcome) {
if (outcome == null) {
return null;
}
if (outcomeToViewId.containsKey(outcome)) {
return outcomeToViewId.get(outcome).replace(".xhtml", ".faces");
}
// try to guess the view name
String viewId = "/" + outcome + ".faces";
log.warn("Guessing view id for outcome '" + outcome + "': use '" + viewId + "'");
return viewId;
}
/**
* XXX hack: add manual parsing of the main faces-config.xml file navigation cases, to handle hot reload and work
* around the JSF application cache.
* <p>
* TODO: try to reset and rebuild the app navigation cases by reflection, if it works...
*
* @since 5.6
*/
protected void handleHotReloadResources(ServletContext context) {
InputStream stream = null;
if (context != null) {
stream = context.getResourceAsStream("/WEB-INF/faces-config.xml");
}
if (stream != null) {
parse(stream);
}
}
/**
* @since 5.6
*/
@SuppressWarnings("unchecked")
protected void parse(InputStream stream) {
Element root = getDocumentRoot(stream);
List<Element> elements = root.elements("navigation-rule");
for (Element rule : elements) {
List<Element> nav_cases = rule.elements("navigation-case");
for (Element nav_case : nav_cases) {
Element from_el = nav_case.element("from-outcome");
Element to_el = nav_case.element("to-view-id");
if ((from_el != null) && (to_el != null)) {
String from = from_el.getTextTrim();
String to = to_el.getTextTrim();
outcomeToViewId.put(from, to);
viewIdToOutcome.put(to, from);
}
}
}
}
/**
* Gets the root element of the document.
*
* @since 5.6
*/
protected static Element getDocumentRoot(InputStream stream) {
try {
SAXReader saxReader = new SAXReader();
saxReader.setEntityResolver(new DTDEntityResolver());
saxReader.setMergeAdjacentText(true);
return saxReader.read(stream).getRootElement();
} catch (DocumentException de) {
throw new RuntimeException(de);
}
}
}