You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@deltaspike.apache.org by gp...@apache.org on 2013/01/02 14:00:46 UTC

git commit: DELTASPIKE-288 type-safe navigation via view-configs

Updated Branches:
  refs/heads/master 9d4e719ae -> b04774ecf


DELTASPIKE-288 type-safe navigation via view-configs


Project: http://git-wip-us.apache.org/repos/asf/incubator-deltaspike/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-deltaspike/commit/b04774ec
Tree: http://git-wip-us.apache.org/repos/asf/incubator-deltaspike/tree/b04774ec
Diff: http://git-wip-us.apache.org/repos/asf/incubator-deltaspike/diff/b04774ec

Branch: refs/heads/master
Commit: b04774ecf350d2234a725c75cb3fd65068723b38
Parents: 9d4e719
Author: gpetracek <gp...@apache.org>
Authored: Wed Jan 2 13:46:18 2013 +0100
Committer: gpetracek <gp...@apache.org>
Committed: Wed Jan 2 13:57:52 2013 +0100

----------------------------------------------------------------------
 .../navigation/DefaultViewNavigationHandler.java   |   44 +++
 .../navigation/DeltaSpikeNavigationHandler.java    |  174 ++++++++++
 .../impl/navigation/NavigationCaseMapWrapper.java  |  220 +++++++++++++
 .../ViewConfigAwareNavigationHandler.java          |  254 +++++++++++++++
 .../apache/deltaspike/jsf/impl/util/JsfUtils.java  |  109 ++++++
 .../deltaspike/jsf/impl/util/RequestParameter.java |   66 ++++
 .../src/main/resources/META-INF/faces-config.xml   |    4 +
 7 files changed, 871 insertions(+), 0 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-deltaspike/blob/b04774ec/deltaspike/modules/jsf/impl/src/main/java/org/apache/deltaspike/jsf/impl/navigation/DefaultViewNavigationHandler.java
----------------------------------------------------------------------
diff --git a/deltaspike/modules/jsf/impl/src/main/java/org/apache/deltaspike/jsf/impl/navigation/DefaultViewNavigationHandler.java b/deltaspike/modules/jsf/impl/src/main/java/org/apache/deltaspike/jsf/impl/navigation/DefaultViewNavigationHandler.java
new file mode 100644
index 0000000..ee0228a
--- /dev/null
+++ b/deltaspike/modules/jsf/impl/src/main/java/org/apache/deltaspike/jsf/impl/navigation/DefaultViewNavigationHandler.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.deltaspike.jsf.impl.navigation;
+
+import org.apache.deltaspike.core.api.config.view.ViewConfig;
+import org.apache.deltaspike.jsf.api.navigation.ViewNavigationHandler;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.faces.context.FacesContext;
+
+/**
+ * Navigation handler for view-configs
+ */
+@ApplicationScoped
+public class DefaultViewNavigationHandler implements ViewNavigationHandler
+{
+    protected DefaultViewNavigationHandler()
+    {
+    }
+
+    @Override
+    public void navigateTo(Class<? extends ViewConfig> targetView)
+    {
+        FacesContext facesContext = FacesContext.getCurrentInstance();
+        facesContext.getApplication().getNavigationHandler().handleNavigation(facesContext, null, targetView.getName());
+        facesContext.renderResponse();
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-deltaspike/blob/b04774ec/deltaspike/modules/jsf/impl/src/main/java/org/apache/deltaspike/jsf/impl/navigation/DeltaSpikeNavigationHandler.java
----------------------------------------------------------------------
diff --git a/deltaspike/modules/jsf/impl/src/main/java/org/apache/deltaspike/jsf/impl/navigation/DeltaSpikeNavigationHandler.java b/deltaspike/modules/jsf/impl/src/main/java/org/apache/deltaspike/jsf/impl/navigation/DeltaSpikeNavigationHandler.java
new file mode 100644
index 0000000..30693ea
--- /dev/null
+++ b/deltaspike/modules/jsf/impl/src/main/java/org/apache/deltaspike/jsf/impl/navigation/DeltaSpikeNavigationHandler.java
@@ -0,0 +1,174 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.deltaspike.jsf.impl.navigation;
+
+import org.apache.deltaspike.core.api.config.view.DefaultErrorView;
+import org.apache.deltaspike.core.api.config.view.ViewConfig;
+import org.apache.deltaspike.core.api.config.view.metadata.ViewConfigDescriptor;
+import org.apache.deltaspike.core.api.config.view.metadata.ViewConfigResolver;
+import org.apache.deltaspike.core.spi.activation.Deactivatable;
+import org.apache.deltaspike.core.util.ClassDeactivationUtils;
+import org.apache.deltaspike.core.util.ClassUtils;
+import org.apache.deltaspike.jsf.api.config.view.Page;
+import org.apache.deltaspike.jsf.impl.util.JsfUtils;
+
+import javax.faces.application.ConfigurableNavigationHandler;
+import javax.faces.application.NavigationCase;
+import javax.faces.application.NavigationHandler;
+import javax.faces.context.FacesContext;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CopyOnWriteArraySet;
+
+public class DeltaSpikeNavigationHandler extends ConfigurableNavigationHandler implements Deactivatable
+{
+    private Set<String> otherOutcomes = new CopyOnWriteArraySet<String>();
+
+    private Map<String, NavigationCase> viewConfigBasedNavigationCaseCache
+        = new ConcurrentHashMap<String, NavigationCase>();
+
+    private final NavigationHandler wrapped;
+    private final boolean activated;
+
+    /**
+     * Constructor for wrapping the given {@link NavigationHandler}
+     *
+     * @param navigationHandler navigation-handler which should be wrapped
+     */
+    public DeltaSpikeNavigationHandler(NavigationHandler navigationHandler)
+    {
+        this.wrapped = navigationHandler;
+        this.activated = ClassDeactivationUtils.isActivated(getClass());
+    }
+
+    @Override
+    public void handleNavigation(FacesContext context, String fromAction, String outcome)
+    {
+        if (!this.activated || isUnhandledExceptionQueued(context))
+        {
+            this.wrapped.handleNavigation(context, fromAction, outcome);
+        }
+
+        //don't refactor it - currently we need the lazy wrapping due to special jsf2 constellations
+        getWrappedNavigationHandler().handleNavigation(context, fromAction, outcome);
+    }
+
+    private boolean isUnhandledExceptionQueued(FacesContext context)
+    {
+        return context.getExceptionHandler().getUnhandledExceptionQueuedEvents() != null &&
+                context.getExceptionHandler().getUnhandledExceptionQueuedEvents().iterator().hasNext();
+    }
+
+    private NavigationHandler getWrappedNavigationHandler()
+    {
+        ViewConfigAwareNavigationHandler viewConfigAwareNavigationHandler =
+                new ViewConfigAwareNavigationHandler(this.wrapped);
+
+        //TODO add AccessScopeAwareNavigationHandler
+        return viewConfigAwareNavigationHandler;
+    }
+
+    @Override
+    public NavigationCase getNavigationCase(FacesContext context, String action, String outcome)
+    {
+        if (this.wrapped instanceof ConfigurableNavigationHandler)
+        {
+            if (action == null && outcome != null && outcome.contains(".") && outcome.startsWith("class ") &&
+                    !otherOutcomes.contains(outcome))
+            {
+                String originalOutcome = outcome;
+
+                NavigationCase navigationCase = this.viewConfigBasedNavigationCaseCache.get(originalOutcome);
+
+                if (navigationCase != null)
+                {
+                    return navigationCase;
+                }
+
+                outcome = outcome.substring(6);
+
+                ViewConfigDescriptor entry = null;
+
+                if (DefaultErrorView.class.getName().equals(originalOutcome))
+                {
+                    ViewConfigResolver viewConfigResolver = JsfUtils.getViewConfigResolver();
+                    entry = viewConfigResolver.getDefaultErrorViewConfigDescriptor();
+                }
+
+                if (entry == null)
+                {
+                    Object loadedClass = ClassUtils.tryToLoadClassForName(outcome);
+
+                    if (loadedClass == null)
+                    {
+                        this.otherOutcomes.add(originalOutcome);
+                    }
+                    else if (ViewConfig.class.isAssignableFrom((Class) loadedClass))
+                    {
+                        entry = JsfUtils.getViewConfigResolver()
+                                .getViewConfigDescriptor((Class<? extends ViewConfig>) loadedClass);
+                    }
+                }
+
+                if (entry != null)
+                {
+                    Page.NavigationMode navigationMode = entry.getMetaData(Page.class).iterator().next().navigation();
+
+                    navigationCase = new NavigationCase("*",
+                            null,
+                            null,
+                            null,
+                            entry.getViewId(),
+                            null,
+                            Page.NavigationMode.REDIRECT.equals(navigationMode),
+                            false);
+                    this.viewConfigBasedNavigationCaseCache.put(originalOutcome, navigationCase);
+                    return navigationCase;
+                }
+            }
+            return ((ConfigurableNavigationHandler) this.wrapped).getNavigationCase(context, action, outcome);
+        }
+        return null;
+    }
+
+    @Override
+    public Map<String, Set<NavigationCase>> getNavigationCases()
+    {
+        Map<String, Set<NavigationCase>> result = null;
+
+        if (this.wrapped instanceof ConfigurableNavigationHandler)
+        {
+            result = ((ConfigurableNavigationHandler) this.wrapped).getNavigationCases();
+        }
+
+        if (result == null)
+        {
+            result = new HashMap<String, Set<NavigationCase>>();
+        }
+
+        if (!this.activated)
+        {
+            return result;
+        }
+
+        return new NavigationCaseMapWrapper(result);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-deltaspike/blob/b04774ec/deltaspike/modules/jsf/impl/src/main/java/org/apache/deltaspike/jsf/impl/navigation/NavigationCaseMapWrapper.java
----------------------------------------------------------------------
diff --git a/deltaspike/modules/jsf/impl/src/main/java/org/apache/deltaspike/jsf/impl/navigation/NavigationCaseMapWrapper.java b/deltaspike/modules/jsf/impl/src/main/java/org/apache/deltaspike/jsf/impl/navigation/NavigationCaseMapWrapper.java
new file mode 100644
index 0000000..44ded77
--- /dev/null
+++ b/deltaspike/modules/jsf/impl/src/main/java/org/apache/deltaspike/jsf/impl/navigation/NavigationCaseMapWrapper.java
@@ -0,0 +1,220 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.deltaspike.jsf.impl.navigation;
+
+import org.apache.deltaspike.core.api.config.view.metadata.ViewConfigDescriptor;
+import org.apache.deltaspike.core.api.config.view.metadata.ViewConfigResolver;
+import org.apache.deltaspike.core.api.provider.BeanProvider;
+import org.apache.deltaspike.jsf.api.config.view.Page;
+import org.apache.deltaspike.jsf.impl.util.JsfUtils;
+import org.apache.deltaspike.jsf.impl.util.RequestParameter;
+
+import javax.faces.application.NavigationCase;
+import java.util.List;
+import java.util.Set;
+import java.util.Map;
+import java.util.HashSet;
+import java.util.HashMap;
+import java.util.Collection;
+
+/**
+ * Destructive operations aren't supported (compared to the SubKeyMap used in MyFaces).
+ * Reason: It isn't allowed to remove navigation cases
+ * (which are based on {@link org.apache.deltaspike.core.api.config.view.ViewConfig})
+ */
+class NavigationCaseMapWrapper implements Map<String, Set<NavigationCase>>
+{
+    private Map<String, Set<NavigationCase>> wrappedNavigationCaseMap;
+    private final Map<String, Set<NavigationCase>> viewConfigBasedNavigationCaseCache;
+
+    /**
+     * Constructor for wrapping the given navigation-cases
+     *
+     * @param navigationCases current navigation-cases
+     */
+    public NavigationCaseMapWrapper(Map<String, Set<NavigationCase>> navigationCases)
+    {
+        this.wrappedNavigationCaseMap = navigationCases;
+        this.viewConfigBasedNavigationCaseCache = createViewConfigBasedNavigationCases(false);
+    }
+
+    private Map<String, Set<NavigationCase>> createViewConfigBasedNavigationCases(boolean allowParameters)
+    {
+        Map<String, Set<NavigationCase>> result = new HashMap<String, Set<NavigationCase>>();
+
+        Collection<ViewConfigDescriptor> viewConfigDescriptors =
+                BeanProvider.getContextualReference(ViewConfigResolver.class).getViewConfigDescriptors();
+
+        if (!viewConfigDescriptors.isEmpty())
+        {
+            Set<NavigationCase> navigationCase = new HashSet<NavigationCase>();
+
+            Map<String, List<String>> parameters = null;
+
+            if (allowParameters)
+            {
+                parameters = resolveParameters();
+            }
+
+            boolean includeParameters;
+
+            for (ViewConfigDescriptor entry : viewConfigDescriptors)
+            {
+                Page pageMetaData = entry.getMetaData(Page.class).iterator().next();
+                includeParameters = Page.ViewParameterMode.INCLUDE
+                        .equals(pageMetaData.viewParams());
+
+                navigationCase.add(new NavigationCase("*",
+                        null,
+                        null,
+                        null,
+                        entry.getViewId(),
+                        includeParameters ? parameters : null,
+                        Page.NavigationMode.REDIRECT.equals(pageMetaData.navigation()),
+                        includeParameters));
+
+                result.put(entry.getViewId(), navigationCase);
+            }
+        }
+        return result;
+    }
+
+    private Map<String, List<String>> resolveParameters()
+    {
+        Map<String, List<String>> parameters = new HashMap<String, List<String>>();
+
+        for (RequestParameter parameter : JsfUtils.getViewConfigPageParameters())
+        {
+            parameters.put(parameter.getKey(), parameter.getValueList());
+        }
+
+        return parameters;
+    }
+
+    /**
+     * @return the final size (might be a combination of the configured navigation cases (via XML) and the
+     *         {@link org.apache.deltaspike.core.api.config.view.ViewConfig}s
+     */
+    @Override
+    public int size()
+    {
+        return this.wrappedNavigationCaseMap.size() + this.viewConfigBasedNavigationCaseCache.size();
+    }
+
+    @Override
+    public boolean isEmpty()
+    {
+        return this.wrappedNavigationCaseMap.isEmpty() &&
+                this.viewConfigBasedNavigationCaseCache.isEmpty();
+    }
+
+    @Override
+    public boolean containsKey(Object key)
+    {
+        return this.wrappedNavigationCaseMap.containsKey(key) ||
+                this.viewConfigBasedNavigationCaseCache.containsKey(key);
+    }
+
+    @Override
+    public boolean containsValue(Object value)
+    {
+        return this.wrappedNavigationCaseMap.containsValue(value) ||
+                this.viewConfigBasedNavigationCaseCache.containsValue(value);
+    }
+
+    /**
+     * XML configuration overrules {@link org.apache.deltaspike.core.api.config.view.ViewConfig}s
+     */
+    @Override
+    public Set<NavigationCase> get(Object key)
+    {
+        Set<NavigationCase> result = this.wrappedNavigationCaseMap.get(key);
+
+        if (result == null)
+        {
+            return createViewConfigBasedNavigationCases(true).get(key);
+        }
+        return result;
+    }
+
+    @Override
+    public Set<NavigationCase> put(String key, Set<NavigationCase> value)
+    {
+        return this.wrappedNavigationCaseMap.put(key, value);
+    }
+
+    @Override
+    public Set<NavigationCase> remove(Object key)
+    {
+        return this.wrappedNavigationCaseMap.remove(key);
+    }
+
+    @Override
+    public void putAll(Map<? extends String, ? extends Set<NavigationCase>> m)
+    {
+        this.wrappedNavigationCaseMap.putAll(m);
+    }
+
+    @Override
+    public void clear()
+    {
+        this.wrappedNavigationCaseMap.clear();
+    }
+
+    /**
+     * @return a combination of navigation-cases configured via XML and
+     *         {@link org.apache.deltaspike.core.api.config.view.ViewConfig}s
+     */
+    @Override
+    public Set<String> keySet()
+    {
+        Set<String> result = new HashSet<String>();
+        result.addAll(this.wrappedNavigationCaseMap.keySet());
+        result.addAll(this.viewConfigBasedNavigationCaseCache.keySet());
+        return result;
+    }
+
+    /**
+     * @return a combination of navigation-cases configured via XML and
+     *         {@link org.apache.deltaspike.core.api.config.view.ViewConfig}s
+     */
+    @Override
+    public Collection<Set<NavigationCase>> values()
+    {
+        Collection<Set<NavigationCase>> result = new HashSet<Set<NavigationCase>>();
+
+        result.addAll(this.wrappedNavigationCaseMap.values());
+        result.addAll(createViewConfigBasedNavigationCases(true).values());
+        return result;
+    }
+
+    /**
+     * @return a combination of navigation-cases configured via XML and
+     *         {@link org.apache.deltaspike.core.api.config.view.ViewConfig}s
+     */
+    @Override
+    public Set<Entry<String, Set<NavigationCase>>> entrySet()
+    {
+        Set<Entry<String, Set<NavigationCase>>> result = new HashSet<Entry<String, Set<NavigationCase>>>();
+
+        result.addAll(this.wrappedNavigationCaseMap.entrySet());
+        result.addAll(createViewConfigBasedNavigationCases(true).entrySet());
+        return result;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-deltaspike/blob/b04774ec/deltaspike/modules/jsf/impl/src/main/java/org/apache/deltaspike/jsf/impl/navigation/ViewConfigAwareNavigationHandler.java
----------------------------------------------------------------------
diff --git a/deltaspike/modules/jsf/impl/src/main/java/org/apache/deltaspike/jsf/impl/navigation/ViewConfigAwareNavigationHandler.java b/deltaspike/modules/jsf/impl/src/main/java/org/apache/deltaspike/jsf/impl/navigation/ViewConfigAwareNavigationHandler.java
new file mode 100644
index 0000000..2a4d89c
--- /dev/null
+++ b/deltaspike/modules/jsf/impl/src/main/java/org/apache/deltaspike/jsf/impl/navigation/ViewConfigAwareNavigationHandler.java
@@ -0,0 +1,254 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.deltaspike.jsf.impl.navigation;
+
+import org.apache.deltaspike.core.api.config.view.DefaultErrorView;
+import org.apache.deltaspike.core.api.config.view.ViewConfig;
+import org.apache.deltaspike.core.api.config.view.metadata.ViewConfigDescriptor;
+import org.apache.deltaspike.core.api.config.view.metadata.ViewConfigResolver;
+import org.apache.deltaspike.core.api.provider.BeanManagerProvider;
+import org.apache.deltaspike.core.api.provider.BeanProvider;
+import org.apache.deltaspike.core.util.ClassUtils;
+import org.apache.deltaspike.jsf.api.config.view.Page;
+import org.apache.deltaspike.jsf.api.config.view.PageParameter;
+import org.apache.deltaspike.jsf.api.config.view.event.PreViewConfigNavigateEvent;
+import org.apache.deltaspike.jsf.api.navigation.PageParameterContext;
+import org.apache.deltaspike.jsf.impl.util.JsfUtils;
+
+import javax.enterprise.inject.spi.BeanManager;
+import javax.faces.application.NavigationHandler;
+import javax.faces.context.ExternalContext;
+import javax.faces.context.FacesContext;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CopyOnWriteArraySet;
+
+class ViewConfigAwareNavigationHandler extends NavigationHandler
+{
+    private Set<String> otherOutcomes = new CopyOnWriteArraySet<String>();
+    private Map<String, ViewConfigDescriptor> viewConfigs = new ConcurrentHashMap<String, ViewConfigDescriptor>();
+
+    private final NavigationHandler navigationHandler;
+
+    private BeanManager beanManager;
+
+    private PageParameterContext pageParameterContext;
+
+    /**
+     * Constructor which allows to use the given {@link NavigationHandler}
+     *
+     * @param navigationHandler navigation-handler of jsf
+     */
+    public ViewConfigAwareNavigationHandler(NavigationHandler navigationHandler)
+    {
+        this.navigationHandler = navigationHandler;
+    }
+
+    //Security checks will be performed by the view-handler provided by ds
+    @Override
+    public void handleNavigation(FacesContext facesContext, String fromAction, String outcome)
+    {
+        initBeanManager();
+        if (outcome != null && outcome.contains("."))
+        {
+            String originalOutcome = outcome;
+
+            if (!this.otherOutcomes.contains(outcome))
+            {
+                //it isn't possible to support interfaces due to cdi restrictions
+                if (outcome.startsWith("class "))
+                {
+                    outcome = outcome.substring(6);
+                }
+                ViewConfigDescriptor entry = this.viewConfigs.get(outcome);
+
+                if (entry == null)
+                {
+                    if (DefaultErrorView.class.getName().equals(originalOutcome))
+                    {
+                        entry = JsfUtils.getViewConfigResolver().getDefaultErrorViewConfigDescriptor();
+                    }
+                }
+
+                boolean allowCaching = true;
+                if (entry == null)
+                {
+                    Class<?> loadedClass = ClassUtils.tryToLoadClassForName(outcome);
+
+                    if (loadedClass == null)
+                    {
+                        this.otherOutcomes.add(originalOutcome);
+                    }
+                    else if (ViewConfig.class.isAssignableFrom(loadedClass))
+                    {
+                        //a sub-classed page-config for annotating it with different view params
+                        if (loadedClass.getAnnotation(Page.class) == null &&
+                                loadedClass.getSuperclass().getAnnotation(Page.class) != null)
+                        {
+                            allowCaching = false;
+                            addConfiguredViewParameters(loadedClass);
+
+                            loadedClass = loadedClass.getSuperclass();
+                        }
+                        entry = JsfUtils.getViewConfigResolver()
+                                .getViewConfigDescriptor((Class<? extends ViewConfig>) loadedClass);
+                    }
+                }
+
+                if (entry != null)
+                {
+                    if (allowCaching)
+                    {
+                        this.viewConfigs.put(outcome, entry);
+                        addConfiguredViewParameters(entry.getViewConfig()); //in case of false it has been added already
+                    }
+
+                    String oldViewId = null;
+
+                    if (facesContext.getViewRoot() != null)
+                    {
+                        oldViewId = facesContext.getViewRoot().getViewId();
+                    }
+
+                    PreViewConfigNavigateEvent navigateEvent = firePreViewConfigNavigateEvent(oldViewId, entry);
+
+                    entry = tryToUpdateEntry(entry, navigateEvent);
+
+                    if (entry != null)
+                    {
+                        outcome = convertEntryToOutcome(facesContext.getExternalContext(), entry);
+                    }
+                }
+            }
+        }
+
+        this.navigationHandler.handleNavigation(facesContext, fromAction, outcome);
+    }
+
+    private void addConfiguredViewParameters(Class<?> viewConfigClass)
+    {
+        if (this.pageParameterContext != null)
+        {
+            PageParameter pageParameter = viewConfigClass.getAnnotation(PageParameter.class);
+
+            if (pageParameter != null)
+            {
+                addConfiguredPageParameter(pageParameter);
+            }
+            else
+            {
+                PageParameter.List pageParameterList = viewConfigClass.getAnnotation(PageParameter.List.class);
+
+                if (pageParameterList != null)
+                {
+                    for (PageParameter currentPageParameter : pageParameterList.value())
+                    {
+                        addConfiguredPageParameter(currentPageParameter);
+                    }
+                }
+            }
+        }
+    }
+
+    private void addConfiguredPageParameter(PageParameter viewParameter)
+    {
+        this.pageParameterContext.addPageParameter(viewParameter.key(), viewParameter.value());
+    }
+
+    private String convertEntryToOutcome(ExternalContext externalContext, ViewConfigDescriptor entry)
+    {
+        Page pageMetaData = entry.getMetaData(Page.class).iterator().next();
+
+        boolean performRedirect = Page.NavigationMode.REDIRECT.equals(pageMetaData.navigation());
+        boolean includeViewParameters = Page.ViewParameterMode.INCLUDE.equals(pageMetaData.viewParams());
+
+        StringBuilder result = new StringBuilder(entry.getViewId());
+
+        if (performRedirect)
+        {
+            result.append("?faces-redirect=true");
+        }
+        if (includeViewParameters)
+        {
+            if (performRedirect)
+            {
+                result.append("&");
+            }
+            else
+            {
+                result.append("?");
+            }
+            result.append("includeViewParams=true");
+
+            return JsfUtils.addPageParameters(externalContext, result.toString(), false);
+        }
+
+        return result.toString();
+    }
+
+    private ViewConfigDescriptor tryToUpdateEntry(ViewConfigDescriptor viewConfigDescriptor,
+                                                  PreViewConfigNavigateEvent navigateEvent)
+    {
+        if (navigateEvent == null)
+        {
+            return viewConfigDescriptor;
+        }
+
+        if (navigateEvent.getToView() == null)
+        {
+            return null;
+        }
+
+        if (navigateEvent.getToView().equals(viewConfigDescriptor.getViewConfig()))
+        {
+            return viewConfigDescriptor;
+        }
+
+        return JsfUtils.getViewConfigResolver().getViewConfigDescriptor(navigateEvent.getToView());
+    }
+
+    private PreViewConfigNavigateEvent firePreViewConfigNavigateEvent(String oldViewId,
+                                                                      ViewConfigDescriptor newViewConfigDescriptor)
+    {
+        ViewConfigResolver viewConfigResolver = JsfUtils.getViewConfigResolver();
+
+        ViewConfigDescriptor oldViewConfigDescriptor = viewConfigResolver.getViewConfigDescriptor(oldViewId);
+
+        if (oldViewConfigDescriptor != null)
+        {
+            PreViewConfigNavigateEvent navigateEvent = new PreViewConfigNavigateEvent(
+                    oldViewConfigDescriptor.getViewConfig(), newViewConfigDescriptor.getViewConfig());
+
+            this.beanManager.fireEvent(navigateEvent);
+            return navigateEvent;
+        }
+        return null;
+    }
+
+    private void initBeanManager()
+    {
+        if (this.beanManager == null)
+        {
+            this.beanManager = BeanManagerProvider.getInstance().getBeanManager();
+            this.pageParameterContext =
+                    BeanProvider.getContextualReference(PageParameterContext.class);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-deltaspike/blob/b04774ec/deltaspike/modules/jsf/impl/src/main/java/org/apache/deltaspike/jsf/impl/util/JsfUtils.java
----------------------------------------------------------------------
diff --git a/deltaspike/modules/jsf/impl/src/main/java/org/apache/deltaspike/jsf/impl/util/JsfUtils.java b/deltaspike/modules/jsf/impl/src/main/java/org/apache/deltaspike/jsf/impl/util/JsfUtils.java
index 2a73c94..05a2abb 100644
--- a/deltaspike/modules/jsf/impl/src/main/java/org/apache/deltaspike/jsf/impl/util/JsfUtils.java
+++ b/deltaspike/modules/jsf/impl/src/main/java/org/apache/deltaspike/jsf/impl/util/JsfUtils.java
@@ -18,7 +18,17 @@
  */
 package org.apache.deltaspike.jsf.impl.util;
 
+import org.apache.deltaspike.core.api.config.view.metadata.ViewConfigResolver;
+import org.apache.deltaspike.core.api.provider.BeanProvider;
+import org.apache.deltaspike.jsf.api.navigation.PageParameterContext;
+
+import javax.faces.context.ExternalContext;
 import javax.faces.context.FacesContext;
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
 
 public abstract class JsfUtils
 {
@@ -34,4 +44,103 @@ public abstract class JsfUtils
 
         return result != null ? result.toString() : "null";
     }
+
+    public static Set<RequestParameter> getViewConfigPageParameters()
+    {
+        ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();
+
+        Set<RequestParameter> result = new HashSet<RequestParameter>();
+
+        if (externalContext == null || //detection of early config for different mojarra versions
+                externalContext.getRequestParameterValuesMap() == null || externalContext.getRequest() == null)
+        {
+            return result;
+        }
+
+        PageParameterContext pageParameterContext = BeanProvider.getContextualReference(PageParameterContext.class);
+
+        for (Map.Entry<String, String> entry : pageParameterContext.getPageParameters().entrySet())
+        {
+            //TODO add multi-value support
+            result.add(new RequestParameter(entry.getKey(), new String[]{entry.getValue()}));
+        }
+
+        return result;
+    }
+
+    /**
+     * Adds the current request-parameters to the given url
+     *
+     * @param externalContext current external-context
+     * @param url             current url
+     * @param encodeValues    flag which indicates if parameter values should be encoded or not
+     * @return url with request-parameters
+     */
+    public static String addPageParameters(ExternalContext externalContext, String url, boolean encodeValues)
+    {
+        StringBuilder finalUrl = new StringBuilder(url);
+        boolean existingParameters = url.contains("?");
+
+        for (RequestParameter requestParam : getViewConfigPageParameters())
+        {
+            String key = requestParam.getKey();
+
+            for (String parameterValue : requestParam.getValues())
+            {
+                if (!url.contains(key + "=" + parameterValue) &&
+                        !url.contains(key + "=" + encodeURLParameterValue(parameterValue, externalContext)))
+                {
+                    if (!existingParameters)
+                    {
+                        finalUrl.append("?");
+                        existingParameters = true;
+                    }
+                    else
+                    {
+                        finalUrl.append("&");
+                    }
+                    finalUrl.append(key);
+                    finalUrl.append("=");
+
+                    if (encodeValues)
+                    {
+                        finalUrl.append(JsfUtils.encodeURLParameterValue(parameterValue, externalContext));
+                    }
+                    else
+                    {
+                        finalUrl.append(parameterValue);
+                    }
+                }
+            }
+        }
+        return finalUrl.toString();
+    }
+
+    /**
+     * Encodes the given value using URLEncoder.encode() with the charset returned
+     * from ExternalContext.getResponseCharacterEncoding().
+     * This is exactly how the ExternalContext impl encodes URL parameter values.
+     *
+     * @param value           value which should be encoded
+     * @param externalContext current external-context
+     * @return encoded value
+     */
+    public static String encodeURLParameterValue(String value, ExternalContext externalContext)
+    {
+        // copied from MyFaces ServletExternalContextImpl.encodeURL()
+        try
+        {
+            return URLEncoder.encode(value, externalContext.getResponseCharacterEncoding());
+        }
+        catch (UnsupportedEncodingException e)
+        {
+            throw new UnsupportedOperationException("Encoding type="
+                    + externalContext.getResponseCharacterEncoding() + " not supported", e);
+        }
+    }
+
+    public static ViewConfigResolver getViewConfigResolver()
+    {
+        return BeanProvider.getContextualReference(ViewConfigResolver.class);
+    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-deltaspike/blob/b04774ec/deltaspike/modules/jsf/impl/src/main/java/org/apache/deltaspike/jsf/impl/util/RequestParameter.java
----------------------------------------------------------------------
diff --git a/deltaspike/modules/jsf/impl/src/main/java/org/apache/deltaspike/jsf/impl/util/RequestParameter.java b/deltaspike/modules/jsf/impl/src/main/java/org/apache/deltaspike/jsf/impl/util/RequestParameter.java
new file mode 100644
index 0000000..e460411
--- /dev/null
+++ b/deltaspike/modules/jsf/impl/src/main/java/org/apache/deltaspike/jsf/impl/util/RequestParameter.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.deltaspike.jsf.impl.util;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class RequestParameter
+{
+    private final String key;
+    private final String[] values;
+
+    /**
+     * Constructor for creating a parameter for the given key and values
+     * @param key current key
+     * @param values current values
+     */
+    RequestParameter(String key, String[] values)
+    {
+        this.key = key;
+        this.values = values;
+    }
+
+    /**
+     * Key of the parameter
+     * @return current key
+     */
+    public String getKey()
+    {
+        return key;
+    }
+
+    /**
+     * Exposes the values of the parameter as list
+     * @return values of the parameter
+     */
+    public List<String> getValueList()
+    {
+        return Arrays.asList(this.values);
+    }
+
+    /**
+     * Exposes the values of the parameter as array
+     * @return values of the parameter
+     */
+    public String[] getValues()
+    {
+        return values;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-deltaspike/blob/b04774ec/deltaspike/modules/jsf/impl/src/main/resources/META-INF/faces-config.xml
----------------------------------------------------------------------
diff --git a/deltaspike/modules/jsf/impl/src/main/resources/META-INF/faces-config.xml b/deltaspike/modules/jsf/impl/src/main/resources/META-INF/faces-config.xml
index 5475a6a..f0d190a 100644
--- a/deltaspike/modules/jsf/impl/src/main/resources/META-INF/faces-config.xml
+++ b/deltaspike/modules/jsf/impl/src/main/resources/META-INF/faces-config.xml
@@ -28,6 +28,10 @@
         <phase-listener>org.apache.deltaspike.jsf.impl.listener.phase.JsfRequestLifecyclePhaseListener</phase-listener>
     </lifecycle>
 
+    <application>
+        <navigation-handler>org.apache.deltaspike.jsf.impl.navigation.DeltaSpikeNavigationHandler</navigation-handler>
+    </application>
+
     <factory>
         <lifecycle-factory>org.apache.deltaspike.jsf.impl.listener.request.DeltaSpikeLifecycleFactoryWrapper</lifecycle-factory>
         <faces-context-factory>org.apache.deltaspike.jsf.impl.listener.request.DeltaSpikeFacesContextFactory</faces-context-factory>