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>