From 31be8ba9e61a994bcfd979ba18df8bfde19323d0 Mon Sep 17 00:00:00 2001 From: Erwin Vervaet Date: Sat, 6 Sep 2008 14:13:11 +0000 Subject: [PATCH] Added serializable variants of commonly used JSF data model classes (SWF-748) --- spring-webflow/changelog.txt | 1 + .../executor/jsf/model/ArrayDataModel.java | 64 ++++++ .../executor/jsf/model/ListDataModel.java | 65 ++++++ .../executor/jsf/model/ScalarDataModel.java | 64 ++++++ .../webflow/executor/jsf/model/package.html | 5 + .../jsf/model/SerializableDataModelTests.java | 202 ++++++++++++++++++ 6 files changed, 401 insertions(+) create mode 100644 spring-webflow/src/main/java/org/springframework/webflow/executor/jsf/model/ArrayDataModel.java create mode 100644 spring-webflow/src/main/java/org/springframework/webflow/executor/jsf/model/ListDataModel.java create mode 100644 spring-webflow/src/main/java/org/springframework/webflow/executor/jsf/model/ScalarDataModel.java create mode 100644 spring-webflow/src/main/java/org/springframework/webflow/executor/jsf/model/package.html create mode 100644 spring-webflow/src/test/java/org/springframework/webflow/executor/jsf/model/SerializableDataModelTests.java diff --git a/spring-webflow/changelog.txt b/spring-webflow/changelog.txt index 54d5aea4..83351ede 100644 --- a/spring-webflow/changelog.txt +++ b/spring-webflow/changelog.txt @@ -21,6 +21,7 @@ Package org.springframework.webflow.executor the flow execution (SWF-745). * FlowPhaseListener will now save the JSF component state before a flow execution redirect is issued (SWF-747). * Fixed FlowPhaseListener to correctly deal with scenarios where we fallback to standard JSF navigation. +* Added serializable variants of commonly used JSF data model classes (SWF-748). * Fixed use of wrong flow execution key in externalRedirect view when using PortletFlowController (SWF-435). Changes in version 1.0.5 (03.10.2007) diff --git a/spring-webflow/src/main/java/org/springframework/webflow/executor/jsf/model/ArrayDataModel.java b/spring-webflow/src/main/java/org/springframework/webflow/executor/jsf/model/ArrayDataModel.java new file mode 100644 index 00000000..4208f684 --- /dev/null +++ b/spring-webflow/src/main/java/org/springframework/webflow/executor/jsf/model/ArrayDataModel.java @@ -0,0 +1,64 @@ +/* + * Copyright 2004-2007 the original author or authors. + * + * 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.springframework.webflow.executor.jsf.model; + +import java.io.Externalizable; +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; + +import javax.faces.model.DataModelListener; + +/** + * Serializable variant of JSF's {@link javax.faces.model.ArrayDataModel}. + * + * @author Erwin Vervaet + */ +public class ArrayDataModel extends javax.faces.model.ArrayDataModel implements Externalizable { + + /** + * Construct a new {@link ArrayDataModel} with no specified wrapped data. + * + * @see javax.faces.model.ArrayDataModel#ArrayDataModel() + */ + public ArrayDataModel() { + } + + /** + * Construct a new {@link ArrayDataModel} wrapping the specified array. + * @param array the array to be wrapped, if any + * + * @see javax.faces.model.ArrayDataModel#ArrayDataModel(Object[]) + */ + public ArrayDataModel(Object[] array) { + super(array); + } + + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + setWrappedData(in.readObject()); + setRowIndex(in.readInt()); + DataModelListener[] listeners = (DataModelListener[]) in.readObject(); + for (int i = 0; i < listeners.length; i++) { + addDataModelListener(listeners[i]); + } + } + + public void writeExternal(ObjectOutput out) throws IOException { + out.writeObject(getWrappedData()); + out.writeInt(getRowIndex()); + out.writeObject(getDataModelListeners()); + } +} diff --git a/spring-webflow/src/main/java/org/springframework/webflow/executor/jsf/model/ListDataModel.java b/spring-webflow/src/main/java/org/springframework/webflow/executor/jsf/model/ListDataModel.java new file mode 100644 index 00000000..d847d51f --- /dev/null +++ b/spring-webflow/src/main/java/org/springframework/webflow/executor/jsf/model/ListDataModel.java @@ -0,0 +1,65 @@ +/* + * Copyright 2004-2007 the original author or authors. + * + * 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.springframework.webflow.executor.jsf.model; + +import java.io.Externalizable; +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; +import java.util.List; + +import javax.faces.model.DataModelListener; + +/** + * Serializable variant of JSF's {@link javax.faces.model.ListDataModel}. + * + * @author Erwin Vervaet + */ +public class ListDataModel extends javax.faces.model.ListDataModel implements Externalizable { + + /** + * Construct a new {@link ListDataModel} with no specified wrapped data. + * + * @see javax.faces.model.ListDataModel#ListDataModel() + */ + public ListDataModel() { + } + + /** + * Construct a new {@link ListDataModel} wrapping the specified list. + * @param list the list to be wrapped, if any + * + * @see javax.faces.model.ListDataModel#ListDataModel(List) + */ + public ListDataModel(List list) { + super(list); + } + + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + setWrappedData(in.readObject()); + setRowIndex(in.readInt()); + DataModelListener[] listeners = (DataModelListener[]) in.readObject(); + for (int i = 0; i < listeners.length; i++) { + addDataModelListener(listeners[i]); + } + } + + public void writeExternal(ObjectOutput out) throws IOException { + out.writeObject(getWrappedData()); + out.writeInt(getRowIndex()); + out.writeObject(getDataModelListeners()); + } +} diff --git a/spring-webflow/src/main/java/org/springframework/webflow/executor/jsf/model/ScalarDataModel.java b/spring-webflow/src/main/java/org/springframework/webflow/executor/jsf/model/ScalarDataModel.java new file mode 100644 index 00000000..c0c15b2c --- /dev/null +++ b/spring-webflow/src/main/java/org/springframework/webflow/executor/jsf/model/ScalarDataModel.java @@ -0,0 +1,64 @@ +/* + * Copyright 2004-2007 the original author or authors. + * + * 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.springframework.webflow.executor.jsf.model; + +import java.io.Externalizable; +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; + +import javax.faces.model.DataModelListener; + +/** + * Serializable variant of JSF's {@link javax.faces.model.ScalarDataModel}. + * + * @author Erwin Vervaet + */ +public class ScalarDataModel extends javax.faces.model.ScalarDataModel implements Externalizable { + + /** + * Construct a new {@link ScalarDataModel} with no specified wrapped data. + * + * @see javax.faces.model.ScalarDataModel#ScalarDataModel() + */ + public ScalarDataModel() { + } + + /** + * Construct a new {@link ScalarDataModel} wrapping the specified object. + * @param scalar the object to wrap, if any + * + * @see javax.faces.model.ScalarDataModel#setWrappedData(Object) + */ + public ScalarDataModel(Object scalar) { + super(scalar); + } + + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + setWrappedData(in.readObject()); + setRowIndex(in.readInt()); + DataModelListener[] listeners = (DataModelListener[]) in.readObject(); + for (int i = 0; i < listeners.length; i++) { + addDataModelListener(listeners[i]); + } + } + + public void writeExternal(ObjectOutput out) throws IOException { + out.writeObject(getWrappedData()); + out.writeInt(getRowIndex()); + out.writeObject(getDataModelListeners()); + } +} diff --git a/spring-webflow/src/main/java/org/springframework/webflow/executor/jsf/model/package.html b/spring-webflow/src/main/java/org/springframework/webflow/executor/jsf/model/package.html new file mode 100644 index 00000000..6aa16af0 --- /dev/null +++ b/spring-webflow/src/main/java/org/springframework/webflow/executor/jsf/model/package.html @@ -0,0 +1,5 @@ + + +

Provides serializable variants of commonly used JSF data model classes for use with Spring Web Flow.

+ + \ No newline at end of file diff --git a/spring-webflow/src/test/java/org/springframework/webflow/executor/jsf/model/SerializableDataModelTests.java b/spring-webflow/src/test/java/org/springframework/webflow/executor/jsf/model/SerializableDataModelTests.java new file mode 100644 index 00000000..afebb8b1 --- /dev/null +++ b/spring-webflow/src/test/java/org/springframework/webflow/executor/jsf/model/SerializableDataModelTests.java @@ -0,0 +1,202 @@ +/* + * Copyright 2004-2007 the original author or authors. + * + * 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.springframework.webflow.executor.jsf.model; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.NotSerializableException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.EventListener; +import java.util.List; + +import javax.faces.model.DataModelEvent; +import javax.faces.model.DataModelListener; + +import junit.framework.TestCase; + +/** + * Tests for the serializable JSF data model variants provided by Spring Web Flow. + * + * @author Erwin Vervaet + */ +public class SerializableDataModelTests extends TestCase { + + public void testListDataModelSerializability() throws Exception { + // from scratch + ListDataModel ldm = (ListDataModel) writeAndReadBack(new ListDataModel()); + assertNull(ldm.getWrappedData()); + assertEquals(-1, ldm.getRowIndex()); + assertEquals(0, ldm.getDataModelListeners().length); + + // with input list + ldm = (ListDataModel) writeAndReadBack(new ListDataModel(sampleList())); + assertEquals(sampleList(), ldm.getWrappedData()); + assertEquals(0, ldm.getRowIndex()); + assertEquals(0, ldm.getDataModelListeners().length); + + // manipulated + ldm = new ListDataModel(sampleList()); + ldm.setRowIndex(1); + ldm = (ListDataModel) writeAndReadBack(ldm); + assertEquals(sampleList(), ldm.getWrappedData()); + assertEquals(1, ldm.getRowIndex()); + assertEquals(0, ldm.getDataModelListeners().length); + + // with listeners + ldm = new ListDataModel(sampleList()); + ldm.addDataModelListener(new TestDataModelListener("swf")); + ldm.setRowIndex(1); + ldm = (ListDataModel) writeAndReadBack(ldm); + assertEquals(sampleList(), ldm.getWrappedData()); + assertEquals(1, ldm.getRowIndex()); + assertEquals(1, ldm.getDataModelListeners().length); + assertEquals("swf", ldm.getDataModelListeners()[0].toString()); + + // serialization fails with non serializable objects + try { + ldm = new ListDataModel(Collections.singletonList(new EventListener() { + })); + writeAndReadBack(ldm); + fail(); + } catch (NotSerializableException e) { + // expected + } + } + + public void testArrayDataModelSerializability() throws Exception { + // from scratch + ArrayDataModel adm = (ArrayDataModel) writeAndReadBack(new ArrayDataModel()); + assertNull(adm.getWrappedData()); + assertEquals(-1, adm.getRowIndex()); + assertEquals(0, adm.getDataModelListeners().length); + + // with input array + adm = (ArrayDataModel) writeAndReadBack(new ArrayDataModel(sampleList().toArray())); + assertEquals(sampleList(), Arrays.asList((Object[]) adm.getWrappedData())); + assertEquals(0, adm.getRowIndex()); + assertEquals(0, adm.getDataModelListeners().length); + + // manipulated + adm = new ArrayDataModel(sampleList().toArray()); + adm.setRowIndex(1); + adm = (ArrayDataModel) writeAndReadBack(adm); + assertEquals(sampleList(), Arrays.asList((Object[]) adm.getWrappedData())); + assertEquals(1, adm.getRowIndex()); + assertEquals(0, adm.getDataModelListeners().length); + + // with listeners + adm = new ArrayDataModel(sampleList().toArray()); + adm.addDataModelListener(new TestDataModelListener("swf")); + adm.setRowIndex(1); + adm = (ArrayDataModel) writeAndReadBack(adm); + assertEquals(sampleList(), Arrays.asList((Object[]) adm.getWrappedData())); + assertEquals(1, adm.getRowIndex()); + assertEquals(1, adm.getDataModelListeners().length); + assertEquals("swf", adm.getDataModelListeners()[0].toString()); + + // serialization fails with non serializable objects + try { + adm = new ArrayDataModel(new Object[] { new EventListener() { + } }); + writeAndReadBack(adm); + fail(); + } catch (NotSerializableException e) { + // expected + } + } + + public void testScalarDataModelSerializability() throws Exception { + // from scratch + ScalarDataModel sdm = (ScalarDataModel) writeAndReadBack(new ScalarDataModel()); + assertNull(sdm.getWrappedData()); + assertEquals(-1, sdm.getRowIndex()); + assertEquals(0, sdm.getDataModelListeners().length); + + // with input object + sdm = (ScalarDataModel) writeAndReadBack(new ScalarDataModel("foobar")); + assertEquals("foobar", sdm.getWrappedData()); + assertEquals(0, sdm.getRowIndex()); + assertEquals(0, sdm.getDataModelListeners().length); + + // manipulated + sdm = new ScalarDataModel("foobar"); + sdm.setRowIndex(1); + sdm = (ScalarDataModel) writeAndReadBack(sdm); + assertEquals("foobar", sdm.getWrappedData()); + assertEquals(1, sdm.getRowIndex()); + assertEquals(0, sdm.getDataModelListeners().length); + + // with listeners + sdm = new ScalarDataModel("foobar"); + sdm.addDataModelListener(new TestDataModelListener("swf")); + sdm.setRowIndex(1); + sdm = (ScalarDataModel) writeAndReadBack(sdm); + assertEquals("foobar", sdm.getWrappedData()); + assertEquals(1, sdm.getRowIndex()); + assertEquals(1, sdm.getDataModelListeners().length); + assertEquals("swf", sdm.getDataModelListeners()[0].toString()); + + // serialization fails with non serializable objects + try { + sdm = new ScalarDataModel(new EventListener() { + }); + writeAndReadBack(sdm); + fail(); + } catch (NotSerializableException e) { + // expected + } + } + + // internal helpers + + private Object writeAndReadBack(Object obj) throws Exception { + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + ObjectOutputStream oout = new ObjectOutputStream(bout); + oout.writeObject(obj); + oout.flush(); + ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray()); + ObjectInputStream oin = new ObjectInputStream(bin); + return oin.readObject(); + } + + private List sampleList() { + List list = new ArrayList(2); + list.add("foo"); + list.add("bar"); + return list; + } + + private static class TestDataModelListener implements DataModelListener, Serializable { + + private String name; + + public TestDataModelListener(String name) { + this.name = name; + } + + public void rowSelected(DataModelEvent event) { + } + + public String toString() { + return name; + } + } +}