Author: nbelaevski
Date: 2010-05-07 17:31:29 -0400 (Fri, 07 May 2010)
New Revision: 16937
Modified:
root/framework/trunk/api/src/main/java/org/ajax4jsf/Messages.java
root/framework/trunk/api/src/main/java/org/ajax4jsf/javascript/ScriptUtils.java
root/framework/trunk/api/src/test/java/org/ajax4jsf/javascript/ResponseWriterWrapperTest.java
root/framework/trunk/api/src/test/java/org/ajax4jsf/javascript/ScriptUtilsTest.java
root/framework/trunk/impl/src/main/resources/org/ajax4jsf/messages.properties
Log:
https://jira.jboss.org/jira/browse/RF-4903
Modified: root/framework/trunk/api/src/main/java/org/ajax4jsf/Messages.java
===================================================================
--- root/framework/trunk/api/src/main/java/org/ajax4jsf/Messages.java 2010-05-07 21:29:53
UTC (rev 16936)
+++ root/framework/trunk/api/src/main/java/org/ajax4jsf/Messages.java 2010-05-07 21:31:29
UTC (rev 16937)
@@ -295,7 +295,8 @@
public static final String VAR_MUST_BE_LITERAL = "VAR_MUST_BE_LITERAL";
public static final String VIEW_HANDLER_INSTANTIATION_ERROR =
"VIEW_HANDLER_INSTANTIATION_ERROR";
public static final String WRAPPED_RULES_NULL_ERROR =
"WRAPPED_RULES_NULL_ERROR";
-
+ public static final String JAVASCRIPT_CIRCULAR_REFERENCE =
"JAVASCRIPT_CIRCULAR_REFERENCE";
+
private static final String BUNDLE_NAME = "org.ajax4jsf.messages";
private static Map<ClassLoader, ResourceBundle> bundles = new
HashMap<ClassLoader, ResourceBundle>();
Modified: root/framework/trunk/api/src/main/java/org/ajax4jsf/javascript/ScriptUtils.java
===================================================================
---
root/framework/trunk/api/src/main/java/org/ajax4jsf/javascript/ScriptUtils.java 2010-05-07
21:29:53 UTC (rev 16936)
+++
root/framework/trunk/api/src/main/java/org/ajax4jsf/javascript/ScriptUtils.java 2010-05-07
21:31:29 UTC (rev 16937)
@@ -24,34 +24,56 @@
package org.ajax4jsf.javascript;
import java.beans.PropertyDescriptor;
-
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
-
import java.lang.reflect.Array;
-
+import java.text.MessageFormat;
import java.util.Collection;
+import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.Map;
+import java.util.MissingResourceException;
import javax.faces.FacesException;
import javax.faces.context.ResponseWriter;
+import org.ajax4jsf.Messages;
+import org.richfaces.log.RichfacesLogger;
+import org.slf4j.Logger;
+
/**
* @author shura (latest modification by $Author: alexsmirnov $)
* @version $Revision: 1.1.2.3 $ $Date: 2007/01/24 13:22:31 $
*/
public final class ScriptUtils {
+ private static final Logger LOG = RichfacesLogger.UTIL.getLogger();
+
/**
* This is utility class, don't instantiate.
*/
private ScriptUtils() {}
- private static void writeScriptToStream(Writer writer, Object obj) throws IOException
{
- if (null == obj) {
+ private static void writeScriptToStream(Writer writer, Object obj, Map<Object,
Boolean> cycleBusterMap) throws IOException {
+ Boolean cycleBusterValue = cycleBusterMap.put(obj, Boolean.TRUE);
+
+ if (cycleBusterValue != null) {
+ if (LOG.isDebugEnabled()) {
+ String formattedMessage;
+ try {
+ formattedMessage =
Messages.getMessage(Messages.JAVASCRIPT_CIRCULAR_REFERENCE, obj);
+ } catch (MissingResourceException e) {
+ //ignore exception: workaround for unit tests
+ formattedMessage = MessageFormat.format("Circular reference
serializing object to JS: {0}", obj);
+ }
+
+ LOG.debug(formattedMessage);
+ }
writer.write("null");
+ } else if (null == obj) {
+ //TODO nick - skip non-rendered values like Integer.MIN_VALUE
+ writer.write("null");
} else if (obj instanceof ScriptString) {
writer.write(((ScriptString) obj).toScript());
} else if (obj.getClass().isArray()) {
@@ -66,7 +88,7 @@
writer.write(',');
}
- writeScriptToStream(writer, element);
+ writeScriptToStream(writer, element, cycleBusterMap);
first = false;
}
@@ -87,7 +109,7 @@
writer.write(',');
}
- writeScriptToStream(writer, element);
+ writeScriptToStream(writer, element, cycleBusterMap);
first = false;
}
@@ -108,7 +130,7 @@
writeEncodedString(writer, entry.getKey());
writer.write(":");
- writeScriptToStream(writer, entry.getValue());
+ writeScriptToStream(writer, entry.getValue(), cycleBusterMap);
first = false;
}
@@ -166,12 +188,16 @@
writeEncodedString(writer, key);
writer.write(":");
- writeScriptToStream(writer, propertyValue);
+ writeScriptToStream(writer, propertyValue, cycleBusterMap);
first = false;
}
writer.write("} ");
}
+
+ if (cycleBusterValue == null) {
+ cycleBusterMap.remove(obj);
+ }
}
/**
@@ -183,7 +209,7 @@
* @throws IOException
*/
public static void writeToStream(final ResponseWriter responseWriter, Object obj)
throws IOException {
- writeScriptToStream(new ResponseWriterWrapper(responseWriter), obj);
+ writeScriptToStream(new ResponseWriterWrapper(responseWriter), obj, new
IdentityHashMap<Object, Boolean>());
}
/**
@@ -196,7 +222,7 @@
StringBuilder sb = new StringBuilder();
try {
- writeScriptToStream(new StringBuilderWriter(sb), obj);
+ writeScriptToStream(new StringBuilderWriter(sb), obj, new
IdentityHashMap<Object, Boolean>());
} catch (IOException e) {
// ignore
Modified:
root/framework/trunk/api/src/test/java/org/ajax4jsf/javascript/ResponseWriterWrapperTest.java
===================================================================
---
root/framework/trunk/api/src/test/java/org/ajax4jsf/javascript/ResponseWriterWrapperTest.java 2010-05-07
21:29:53 UTC (rev 16936)
+++
root/framework/trunk/api/src/test/java/org/ajax4jsf/javascript/ResponseWriterWrapperTest.java 2010-05-07
21:31:29 UTC (rev 16937)
@@ -27,14 +27,9 @@
import static org.easymock.EasyMock.eq;
import static org.easymock.EasyMock.isNull;
import static org.easymock.EasyMock.reportMatcher;
-import static org.easymock.classextension.EasyMock.createStrictMock;
-import static org.easymock.classextension.EasyMock.replay;
-import static org.easymock.classextension.EasyMock.verify;
import java.io.Writer;
-
import java.lang.reflect.Array;
-
import java.util.Random;
import javax.faces.context.ResponseWriter;
@@ -42,25 +37,36 @@
import junit.framework.TestCase;
import org.easymock.IArgumentMatcher;
+import org.jboss.test.faces.mock.MockFacesEnvironment;
/**
* @author Nick Belaevski
* @since 3.3.2
*/
public class ResponseWriterWrapperTest extends TestCase {
+
+ private MockFacesEnvironment facesEnvironment;
+
private ResponseWriter mockWriter;
private Writer writer;
@Override
protected void setUp() throws Exception {
super.setUp();
- mockWriter = createStrictMock(ResponseWriter.class);
+
+ facesEnvironment = MockFacesEnvironment.createEnvironment();
+ mockWriter = facesEnvironment.createMock(ResponseWriter.class);
writer = new ResponseWriterWrapper(mockWriter);
}
@Override
protected void tearDown() throws Exception {
super.tearDown();
+
+ facesEnvironment.verify();
+ facesEnvironment.release();
+ facesEnvironment = null;
+
this.writer = null;
this.mockWriter = null;
}
@@ -69,9 +75,10 @@
char[] cs = new char[] {'a', 'b'};
mockWriter.writeText(cs, 0, 2);
- replay(mockWriter);
+
+ facesEnvironment.replay();
+
writer.write(cs);
- verify(mockWriter);
}
private static char[] expectSingleChar(final char c) {
@@ -141,37 +148,41 @@
public void testWrite2() throws Exception {
mockWriter.writeText(expectSingleChar((char) 0x5678), eq(0), eq(1));
mockWriter.writeText(expectSingleChar((char) 0xBA98), eq(0), eq(1));
- replay(mockWriter);
+
+ facesEnvironment.replay();
+
writer.write(0x12345678);
writer.write(0xFECDBA98);
- verify(mockWriter);
}
public void testWrite3() throws Exception {
mockWriter.writeText(eq("test"), (String) isNull());
- replay(mockWriter);
+
+ facesEnvironment.replay();
+
writer.write("test");
- verify(mockWriter);
}
public void testWrite4() throws Exception {
mockWriter.writeText(aryEq("abcd".toCharArray()), eq(1), eq(2));
mockWriter.writeText(aryEq("efgh".toCharArray()), eq(0), eq(3));
mockWriter.writeText(aryEq("ijklm".toCharArray()), eq(2), eq(3));
- replay(mockWriter);
+
+ facesEnvironment.replay();
+
writer.write("abcd".toCharArray(), 1, 2);
writer.write("efgh".toCharArray(), 0, 3);
writer.write("ijklm".toCharArray(), 2, 3);
- verify(mockWriter);
}
public void testWrite5() throws Exception {
mockWriter.writeText(expectFirstChars("string to".toCharArray()),
eq(0), eq(9));
mockWriter.writeText(expectFirstChars("one".toCharArray()), eq(0),
eq(3));
- replay(mockWriter);
+
+ facesEnvironment.replay();
+
writer.write("string to test", 0, 9);
writer.write("short one", 6, 3);
- verify(mockWriter);
}
public void testWrite6() throws Exception {
@@ -183,22 +194,25 @@
}
mockWriter.writeText(expectFirstChars(cs, length), eq(0), eq(length));
- replay(mockWriter);
+
+ facesEnvironment.replay();
+
writer.write(String.valueOf(cs), 0, length);
- verify(mockWriter);
}
public void testFlush() throws Exception {
mockWriter.flush();
- replay(mockWriter);
+
+ facesEnvironment.replay();
+
writer.flush();
- verify(mockWriter);
}
public void testClose() throws Exception {
mockWriter.close();
- replay(mockWriter);
+
+ facesEnvironment.replay();
+
writer.close();
- verify(mockWriter);
}
}
Modified:
root/framework/trunk/api/src/test/java/org/ajax4jsf/javascript/ScriptUtilsTest.java
===================================================================
---
root/framework/trunk/api/src/test/java/org/ajax4jsf/javascript/ScriptUtilsTest.java 2010-05-07
21:29:53 UTC (rev 16936)
+++
root/framework/trunk/api/src/test/java/org/ajax4jsf/javascript/ScriptUtilsTest.java 2010-05-07
21:31:29 UTC (rev 16937)
@@ -24,13 +24,15 @@
package org.ajax4jsf.javascript;
import static org.easymock.EasyMock.capture;
+import static org.easymock.EasyMock.eq;
+import static org.easymock.EasyMock.expectLastCall;
import static org.easymock.EasyMock.isNull;
-import static org.easymock.classextension.EasyMock.replay;
-import static org.easymock.classextension.EasyMock.*;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -43,6 +45,7 @@
import org.easymock.Capture;
import org.easymock.CaptureType;
+import org.jboss.test.faces.mock.MockFacesEnvironment;
/**
* @author shura
@@ -65,6 +68,56 @@
}
}
+ public static class ReferencedBean {
+
+ private String name;
+
+ private ReferenceHolderBean parent;
+
+ public ReferencedBean(String name, ReferenceHolderBean parent) {
+ super();
+ this.name = name;
+ this.parent = parent;
+ }
+
+ public ReferenceHolderBean getParent() {
+ return parent;
+ }
+
+ public String getName() {
+ return name;
+ }
+ }
+
+ public static class ReferenceHolderBean {
+
+ private String name;
+
+ private Object reference;
+
+ public ReferenceHolderBean(String name) {
+ super();
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public Object getReference() {
+ return reference;
+ }
+
+ public void setReference(Object reference) {
+ this.reference = reference;
+ }
+
+ }
+
+ private static String dehydrate(String s) {
+ return s != null ? s.replaceAll("\\s", "") : s;
+ }
+
/**
* Test method for {@link
org.ajax4jsf.javascript.ScriptUtils#toScript(java.lang.Object)}.
*/
@@ -247,7 +300,9 @@
* Test method for {@link
ScriptUtils#writeToStream(javax.faces.context.ResponseWriter, Object)}
*/
public void testWriteToStream() throws Exception {
- ResponseWriter mockWriter = createNiceMock(ResponseWriter.class);
+ MockFacesEnvironment environment = MockFacesEnvironment.createEnvironment();
+
+ ResponseWriter mockWriter = environment.createMock(ResponseWriter.class);
Capture<? extends Object> capture = new
Capture<Object>(CaptureType.ALL) {
/**
@@ -270,10 +325,11 @@
expectLastCall().anyTimes();
mockWriter.writeText((char[]) capture(capture), eq(0), eq(1));
expectLastCall().anyTimes();
- replay(mockWriter);
+ environment.replay();
ScriptUtils.writeToStream(mockWriter, Collections.singletonMap("delay",
Integer.valueOf(1500)));
- verify(mockWriter);
+ environment.verify();
assertCaptureEquals(capture, "{'delay':1500}");
+ environment.release();
}
/**
@@ -339,4 +395,57 @@
this.foo = foo;
}
}
+
+ public void testCircularReferenceBeans() throws Exception {
+ ReferenceHolderBean parent = new ReferenceHolderBean("parent");
+ ReferencedBean child = new ReferencedBean("child", parent);
+
+ assertEquals(dehydrate("{'name': 'child', 'parent':
{'name': 'parent', 'reference': null}}"),
+ dehydrate(ScriptUtils.toScript(child)));
+ }
+
+ public void testCircularReferenceViaProperty() throws Exception {
+ ReferenceHolderBean parent = new ReferenceHolderBean("parent");
+ ReferencedBean child = new ReferencedBean("child", parent);
+
+ parent.setReference(child);
+
+ assertEquals(dehydrate("{'name': 'parent',
'reference': {'name': 'child', 'parent': null}}"),
+ dehydrate(ScriptUtils.toScript(parent)));
+ }
+
+ public void testCircularReferenceViaArray() throws Exception {
+ ReferenceHolderBean parent = new ReferenceHolderBean("parent");
+ ReferencedBean child = new ReferencedBean("child", parent);
+
+ parent.setReference(new Object[] {child});
+
+ assertEquals(dehydrate("{'name': 'parent',
'reference': [{'name': 'child', 'parent': null}]}"),
+ dehydrate(ScriptUtils.toScript(parent)));
+ }
+
+ public void testCircularReferenceViaCollection() throws Exception {
+ ReferenceHolderBean parent = new ReferenceHolderBean("parent");
+ ReferencedBean child = new ReferencedBean("child", parent);
+
+ Collection<Object> set = new ArrayList<Object>();
+ set.add(child);
+ parent.setReference(set);
+
+ assertEquals(dehydrate("{'name': 'parent',
'reference': [{'name': 'child', 'parent': null}]}"),
+ dehydrate(ScriptUtils.toScript(parent)));
+ }
+
+ public void testCircularReferenceViaMap() throws Exception {
+ ReferenceHolderBean parent = new ReferenceHolderBean("parent");
+ ReferencedBean child = new ReferencedBean("child", parent);
+
+ Map<String, Object> map = new HashMap<String, Object>();
+ map.put("key", child);
+ parent.setReference(map);
+
+ assertEquals(dehydrate("{'name': 'parent',
'reference': {'key': {'name': 'child', 'parent':
null}}}"),
+ dehydrate(ScriptUtils.toScript(parent)));
+ }
+
}
Modified: root/framework/trunk/impl/src/main/resources/org/ajax4jsf/messages.properties
===================================================================
---
root/framework/trunk/impl/src/main/resources/org/ajax4jsf/messages.properties 2010-05-07
21:29:53 UTC (rev 16936)
+++
root/framework/trunk/impl/src/main/resources/org/ajax4jsf/messages.properties 2010-05-07
21:31:29 UTC (rev 16937)
@@ -259,4 +259,5 @@
INVALID_VALUE="Component {0} has invalid value expression {1}"
DATASCROLLER_PAGE_MISSING=Datascroller {0}: The requested page #{1} isn''t found
in the model containing {2} pages. Paging is reset to page #{3}
DATASCROLLER_PAGES_DIFFERENT=Datascroller components attached to component\: {0} have
different values of ''page'' attribute \: {1}
-COMPONENT_CONVERSION_ERROR=Component {0}\: conversion error occurred for value {1}
\ No newline at end of file
+COMPONENT_CONVERSION_ERROR=Component {0}\: conversion error occurred for value {1}
+JAVASCRIPT_CIRCULAR_REFERENCE=Circular reference occurred during serialization of {0}
object to JavaScript. Such references are not currently supported and will be nullified.
\ No newline at end of file