Author: manaRH
Date: 2013-01-17 07:36:10 -0500 (Thu, 17 Jan 2013)
New Revision: 15397
Added:
branches/enterprise/WFK-2_1/jboss-seam/src/main/java/org/jboss/seam/util/FacesUrlTransformer.java
Modified:
branches/enterprise/WFK-2_1/jboss-seam/src/main/java/org/jboss/seam/jsf/SeamViewHandler.java
branches/enterprise/WFK-2_1/seam-integration-tests/src/test/java/org/jboss/seam/test/integration/faces/BoundComponentConversationTest.java
branches/enterprise/WFK-2_1/seam-integration-tests/src/test/java/org/jboss/seam/test/integration/faces/RestoreViewComponentAccessTest.java
Log:
JBSEAM-5020, JBSEAM-4976 added new JSF2 methods like getActionURL, getRedirectURL and
getBookmarkableURL into SeamViewHandler
Modified:
branches/enterprise/WFK-2_1/jboss-seam/src/main/java/org/jboss/seam/jsf/SeamViewHandler.java
===================================================================
---
branches/enterprise/WFK-2_1/jboss-seam/src/main/java/org/jboss/seam/jsf/SeamViewHandler.java 2013-01-17
12:35:52 UTC (rev 15396)
+++
branches/enterprise/WFK-2_1/jboss-seam/src/main/java/org/jboss/seam/jsf/SeamViewHandler.java 2013-01-17
12:36:10 UTC (rev 15397)
@@ -1,7 +1,9 @@
package org.jboss.seam.jsf;
import java.io.IOException;
+import java.util.List;
import java.util.Locale;
+import java.util.Map;
import javax.faces.FacesException;
import javax.faces.application.ViewHandler;
@@ -10,27 +12,35 @@
import javax.faces.context.FacesContext;
import org.jboss.seam.contexts.Contexts;
+import org.jboss.seam.core.Conversation;
+import org.jboss.seam.core.Manager;
import org.jboss.seam.international.LocaleSelector;
+import org.jboss.seam.util.FacesUrlTransformer;
/**
* Allows the JSF view locale to be integrated with
* the locale coming from Seam internationalization.
- *
+ *
* @see org.jboss.seam.international.LocaleSelector
- *
+ *
* @author Gavin King
*
*/
public class SeamViewHandler extends ViewHandlerWrapper
{
-
+ private static enum Source
+ {
+ ACTION, BOOKMARKABLE, REDIRECT, RESOURCE
+ }
+
private ViewHandler viewHandler;
-
+ private static final ThreadLocal<Source> source = new
ThreadLocal<Source>();
+
public SeamViewHandler(ViewHandler viewHandler)
{
this.viewHandler = viewHandler;
}
-
+
@Override
public String calculateCharacterEncoding(FacesContext context)
{
@@ -42,9 +52,9 @@
{
viewHandler.initView(context);
}
-
+
@Override
- public Locale calculateLocale(FacesContext facesContext)
+ public Locale calculateLocale(FacesContext facesContext)
{
Locale jsfLocale = viewHandler.calculateLocale(facesContext);
if ( !Contexts.isSessionContextActive() )
@@ -58,38 +68,124 @@
}
@Override
- public String calculateRenderKitId(FacesContext ctx)
+ public String calculateRenderKitId(FacesContext ctx)
{
return viewHandler.calculateRenderKitId(ctx);
}
@Override
- public UIViewRoot createView(FacesContext ctx, String viewId)
+ public UIViewRoot createView(FacesContext ctx, String viewId)
{
return viewHandler.createView(ctx, viewId);
}
+ /**
+ * Allow the delegate to produce the action URL. If the conversation is
+ * long-running, append the conversation id request parameter to the query
+ * string part of the URL, but only if the request parameter is not already
+ * present.
+ * <p/>
+ * This covers form actions Ajax calls, and redirect URLs (which we want) and
+ * link hrefs (which we don't)
+ *
+ * @see {@link ViewHandler#getActionURL(FacesContext, String)}
+ */
@Override
- public String getActionURL(FacesContext ctx, String viewId)
+ public String getActionURL(FacesContext facesContext, String viewId) {
+ String actionUrl = super.getActionURL(facesContext, viewId);
+ Conversation conversation = Conversation.instance();
+ Manager manager = Manager.instance();
+ String conversationIdParameter = manager.getConversationIdParameter();
+
+ if (!getSource().equals(Source.BOOKMARKABLE) )
+ {
+ if ( !conversation.isNested() || conversation.isLongRunning() )
+ {
+ return new FacesUrlTransformer(actionUrl, facesContext)
+ .appendConversationIdIfNecessary(conversationIdParameter,
conversation.getId())
+ .getUrl();
+ }
+ else
+ {
+ return new FacesUrlTransformer(actionUrl, facesContext)
+ .appendConversationIdIfNecessary(conversationIdParameter,
conversation.getParentId())
+ .getUrl();
+ }
+
+ } else {
+ return actionUrl;
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see
javax.faces.application.ViewHandlerWrapper#getRedirectURL(javax.faces.context.FacesContext,
java.lang.String, java.util.Map, boolean)
+ */
+ @Override
+ public String getRedirectURL(FacesContext context, String viewId, Map<String,
List<String>> parameters, boolean includeViewParams)
{
- return viewHandler.getActionURL(ctx, viewId);
+ try
+ {
+ source.set(Source.REDIRECT);
+ return super.getRedirectURL(context, viewId, parameters, includeViewParams);
+ }
+ finally
+ {
+ source.remove();
+ }
}
+ /* (non-Javadoc)
+ * @see
javax.faces.application.ViewHandlerWrapper#getBookmarkableURL(javax.faces.context.FacesContext,
java.lang.String, java.util.Map, boolean)
+ */
@Override
- public String getResourceURL(FacesContext ctx, String path)
+ public String getBookmarkableURL(FacesContext context, String viewId, Map<String,
List<String>> parameters, boolean includeViewParams)
{
- return viewHandler.getResourceURL(ctx, path);
+ try
+ {
+ source.set(Source.BOOKMARKABLE);
+ return super.getBookmarkableURL(context, viewId, parameters,
includeViewParams);
+ }
+ finally
+ {
+ source.remove();
+ }
}
+ private Source getSource()
+ {
+ if (source.get() == null)
+ {
+ return Source.ACTION;
+ }
+ else
+ {
+ return source.get();
+ }
+ }
+
@Override
+ public String getResourceURL(FacesContext ctx, String path)
+ {
+ try
+ {
+ source.set(Source.RESOURCE);
+ return super.getResourceURL(ctx, path);
+ }
+ finally
+ {
+ source.remove();
+ }
+ }
+
+ @Override
public void renderView(FacesContext ctx, UIViewRoot viewRoot)
- throws IOException, FacesException
+ throws IOException, FacesException
{
viewHandler.renderView(ctx, viewRoot);
}
@Override
- public UIViewRoot restoreView(FacesContext ctx, String viewId)
+ public UIViewRoot restoreView(FacesContext ctx, String viewId)
{
UIViewRoot viewRoot =viewHandler.restoreView(ctx, viewId);
if (viewRoot != null)
@@ -100,7 +196,7 @@
}
@Override
- public void writeState(FacesContext ctx) throws IOException
+ public void writeState(FacesContext ctx) throws IOException
{
viewHandler.writeState(ctx);
}
Added:
branches/enterprise/WFK-2_1/jboss-seam/src/main/java/org/jboss/seam/util/FacesUrlTransformer.java
===================================================================
---
branches/enterprise/WFK-2_1/jboss-seam/src/main/java/org/jboss/seam/util/FacesUrlTransformer.java
(rev 0)
+++
branches/enterprise/WFK-2_1/jboss-seam/src/main/java/org/jboss/seam/util/FacesUrlTransformer.java 2013-01-17
12:36:10 UTC (rev 15397)
@@ -0,0 +1,117 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2008, Red Hat, Inc., and individual contributors
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * 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.
+ */
+package org.jboss.seam.util;
+
+import javax.faces.context.FacesContext;
+
+/**
+ * Helper class for preparing JSF URLs which include the conversation id.
+ * <p/>
+ * TODO This class has the potential to be better designed to make it fit more use
cases.
+ *
+ * @author Nicklas Karlsson
+ * @author Dan Allen
+ * @author Marko Luksa
+ */
+public class FacesUrlTransformer {
+ private static final String HTTP_PROTOCOL_URL_PREFIX = "http://";
+ private static final String HTTPS_PROTOCOL_URL_PREFIX = "https://";
+ private static final String QUERY_STRING_DELIMITER = "?";
+ private static final String PARAMETER_PAIR_DELIMITER = "&";
+ private static final String PARAMETER_ASSIGNMENT_OPERATOR = "=";
+
+ private String url;
+ private final FacesContext context;
+
+ public FacesUrlTransformer(String url, FacesContext facesContext) {
+ this.url = url;
+ this.context = facesContext;
+ }
+
+ public FacesUrlTransformer appendConversationIdIfNecessary(String cidParameterName,
String cid) {
+ this.url = appendParameterIfNeeded(url, cidParameterName, cid);
+ return this;
+ }
+
+ private static String appendParameterIfNeeded(String url, String parameterName,
String parameterValue) {
+ int queryStringIndex = url.indexOf(QUERY_STRING_DELIMITER);
+ // if there is no query string or there is a query string but the param is
+ // absent, then append it
+ if (queryStringIndex < 0 || url.indexOf(parameterName +
PARAMETER_ASSIGNMENT_OPERATOR, queryStringIndex) < 0) {
+ StringBuilder builder = new StringBuilder(url);
+ if (queryStringIndex < 0) {
+ builder.append(QUERY_STRING_DELIMITER);
+ } else {
+ builder.append(PARAMETER_PAIR_DELIMITER);
+ }
+ builder.append(parameterName).append(PARAMETER_ASSIGNMENT_OPERATOR);
+ if (parameterValue != null) {
+ builder.append(parameterValue);
+ }
+ return builder.toString();
+ } else {
+ return url;
+ }
+ }
+
+ public String getUrl() {
+ return url;
+ }
+
+ public FacesUrlTransformer toRedirectViewId() {
+ String requestPath = context.getExternalContext().getRequestContextPath();
+ if (isUrlAbsolute()) {
+ url = url.substring(url.indexOf(requestPath) + requestPath.length());
+ } else if (url.startsWith(requestPath)) {
+ url = url.substring(requestPath.length());
+ }
+ return this;
+ }
+
+ public FacesUrlTransformer toActionUrl() {
+ String actionUrl =
context.getApplication().getViewHandler().getActionURL(context, url);
+
+ int queryStringIndex = url.indexOf(QUERY_STRING_DELIMITER);
+ if (queryStringIndex < 0) {
+ url = actionUrl;
+ } else {
+ String queryParameters = url.substring(queryStringIndex + 1);
+
+ int actionQueryStringIndex = actionUrl.indexOf(QUERY_STRING_DELIMITER);
+ if (actionQueryStringIndex < 0) {
+ url = actionUrl + QUERY_STRING_DELIMITER + queryParameters;
+ } else {
+ String actionQueryParameters = actionUrl.substring(actionQueryStringIndex
+ 1);
+ if (queryParameters.startsWith(actionQueryParameters)) {
+ url = actionUrl.substring(0, actionQueryStringIndex) +
QUERY_STRING_DELIMITER + queryParameters;
+ } else {
+ url = actionUrl + PARAMETER_PAIR_DELIMITER + queryParameters;
+ }
+ }
+ }
+ return this;
+ }
+
+ public String encode() {
+ return context.getExternalContext().encodeActionURL(url);
+ }
+
+ private boolean isUrlAbsolute() {
+ // TODO: any API call to do this?
+ return url.startsWith(HTTP_PROTOCOL_URL_PREFIX) ||
url.startsWith(HTTPS_PROTOCOL_URL_PREFIX);
+ }
+}
Modified:
branches/enterprise/WFK-2_1/seam-integration-tests/src/test/java/org/jboss/seam/test/integration/faces/BoundComponentConversationTest.java
===================================================================
---
branches/enterprise/WFK-2_1/seam-integration-tests/src/test/java/org/jboss/seam/test/integration/faces/BoundComponentConversationTest.java 2013-01-17
12:35:52 UTC (rev 15396)
+++
branches/enterprise/WFK-2_1/seam-integration-tests/src/test/java/org/jboss/seam/test/integration/faces/BoundComponentConversationTest.java 2013-01-17
12:36:10 UTC (rev 15397)
@@ -64,7 +64,6 @@
"</html>"), "test.xhtml");
}
- @Ignore // JBSEAM-5020
@Test
public void testConversationRestoration() throws Exception
{
Modified:
branches/enterprise/WFK-2_1/seam-integration-tests/src/test/java/org/jboss/seam/test/integration/faces/RestoreViewComponentAccessTest.java
===================================================================
---
branches/enterprise/WFK-2_1/seam-integration-tests/src/test/java/org/jboss/seam/test/integration/faces/RestoreViewComponentAccessTest.java 2013-01-17
12:35:52 UTC (rev 15396)
+++
branches/enterprise/WFK-2_1/seam-integration-tests/src/test/java/org/jboss/seam/test/integration/faces/RestoreViewComponentAccessTest.java 2013-01-17
12:36:10 UTC (rev 15397)
@@ -1,9 +1,8 @@
package org.jboss.seam.test.integration.faces;
-import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
-
import java.io.Serializable;
import java.net.URL;
import java.util.Deque;
@@ -21,7 +20,6 @@
import org.jboss.seam.test.integration.Deployments;
import org.jboss.shrinkwrap.api.asset.StringAsset;
import org.jboss.shrinkwrap.api.spec.WebArchive;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -38,21 +36,21 @@
public class RestoreViewComponentAccessTest
{
private final WebClient client = new WebClient();
-
+
@ArquillianResource
URL contextPath;
-
+
@Deployment(name="RestoreViewComponentAccessTest")
- @OverProtocol("Servlet 3.0")
+ @OverProtocol("Servlet 3.0")
public static WebArchive createDeployment()
{
// This is a client test, use a real (non-mocked) Seam deployment
WebArchive war = Deployments.realSeamDeployment()
.addClasses(SequenceAction.class);
-
+
war.delete("WEB-INF/pages.xml");
war.delete("WEB-INF/components.xml");
-
+
war.addAsWebResource(new StringAsset(
"<html
xmlns=\"http://www.w3.org/1999/xhtml\"" +
"
xmlns:h=\"http://java.sun.com/jsf/html\"" +
@@ -69,7 +67,7 @@
"</h:inputText>" +
"<h:commandButton id='append' value='Append'
action='#{sequence.append}'/>" +
"</h:form>" +
- "</h:body>" +
+ "</h:body>" +
"</html>"), "test.xhtml");
war.addAsWebInfResource(new StringAsset(
@@ -79,56 +77,56 @@
"<begin-conversation join='true'/>" +
"<navigation><redirect
view-id='/test.xhtml'/></navigation>" +
"</page></pages>"), "pages.xml");
-
+
return war;
}
-
- @Ignore // JBSEAM-4976
+
+ //@Ignore // JBSEAM-4976
@Test
public void testConversationWithValidator() throws Exception {
HtmlPage page = client.getPage(contextPath + "test.seam");
assertTrue(page.getBody().getTextContent().contains("Sequence: "));
-
+
((HtmlTextInput)page.getElementById("form:input")).setText("1");
page = page.getElementById("form:append").click();
-
+
assertTrue(page.getBody().getTextContent().contains("Sequence: 1"));
-
+
((HtmlTextInput)page.getElementById("form:input")).setText("2");
page = page.getElementById("form:append").click();
-
+
assertTrue(page.getBody().getTextContent().contains("Sequence: 1, 2"));
-
+
((HtmlTextInput)page.getElementById("form:input")).setText("1");
page = page.getElementById("form:append").click();
-
+
assertFalse(page.getBody().getTextContent().contains("Sequence: 1, 2,
1"));
assertTrue(page.getBody().getTextContent().contains("value must be greater
than or equal to 2"));
}
-
+
@Name("sequence")
@Scope(ScopeType.CONVERSATION)
public static class SequenceAction implements Serializable {
private static final long serialVersionUID = 1L;
-
+
private Deque<Long> sequence;
private Long input;
-
+
@Create
public void create() {
sequence = new LinkedList<Long>();
}
-
+
public String getOutput() {
StringBuilder sb = new StringBuilder();
for (Long n : sequence) {
sb.append(n);
sb.append(", ");
}
-
+
return sb.toString();
}
-
+
public void append() {
sequence.add(input);
}
@@ -142,12 +140,12 @@
{
this.input = input;
}
-
+
public Long getMinimum() {
if (sequence.isEmpty()) {
return 0L;
}
-
+
return sequence.getLast();
}
}