Implement DataSource.dispose()
DataSources need to be destroyable to free up memory on the client,
especially if the nature of the data prevents it from being re-used.
diff --git a/bundles/org.eclipse.rap.addons.autosuggest/src/org/eclipse/rap/addons/autosuggest/AutoSuggest.java b/bundles/org.eclipse.rap.addons.autosuggest/src/org/eclipse/rap/addons/autosuggest/AutoSuggest.java
index 5bc1f6a..db9dcf6 100644
--- a/bundles/org.eclipse.rap.addons.autosuggest/src/org/eclipse/rap/addons/autosuggest/AutoSuggest.java
+++ b/bundles/org.eclipse.rap.addons.autosuggest/src/org/eclipse/rap/addons/autosuggest/AutoSuggest.java
@@ -125,6 +125,7 @@
* @param dataSource the DataSource (can be null)
*
* @exception IllegalStateException when the receiver is disposed
+ * @exception IllegalArgumentException when the argument is disposed
*
* <p>
* NOTE: The dataSource may be changed at any time
@@ -132,6 +133,9 @@
*/
public void setDataSource( DataSource dataSource ) {
checkDisposed();
+ if( dataSource != null && dataSource.isDisposed() ) {
+ throw new IllegalArgumentException( "DataSource is disposed" );
+ }
remoteObject.set( "dataSourceId", dataSource != null ? dataSource.getId() : null );
if( dataSource != null ) {
ColumnTemplate template = dataSource.getTemplate();
diff --git a/bundles/org.eclipse.rap.addons.autosuggest/src/org/eclipse/rap/addons/autosuggest/DataSource.java b/bundles/org.eclipse.rap.addons.autosuggest/src/org/eclipse/rap/addons/autosuggest/DataSource.java
index 79300ae..b182ac1 100644
--- a/bundles/org.eclipse.rap.addons.autosuggest/src/org/eclipse/rap/addons/autosuggest/DataSource.java
+++ b/bundles/org.eclipse.rap.addons.autosuggest/src/org/eclipse/rap/addons/autosuggest/DataSource.java
@@ -21,8 +21,8 @@
*
* <p>
* A single instance can be used by multiple <code>AutoSuggest</code> instances simultaneously.
- * Each new DataSource is linked to the lifecycle of the UISession,
- * therefore no duplicates should be created.
+ * DataSources should be disposed when no longer needed. Disposing the <code>AutoSuggest</code>
+ * has no effect on the <code>DataSource</code>.
* </p>
*
* <p>
@@ -43,6 +43,7 @@
private final RemoteObject remoteObject;
private DataProvider<Object> dataProvider;
private ColumnTemplate template;
+ private boolean isDisposed;
/**
* Constructs a new instance of this class. A {@link DataProvider} has to be set before it can be
@@ -67,12 +68,14 @@
* @param dataProvider the DataProvider instance (may not be null)
*
* @exception NullPointerException when dataProvider is null
+ * @exception IllegalStateException when the receiver is disposed
*
* @see DataSource#setTemplate(ColumnTemplate)
* @see DataSource#setFilterScript(String)
*/
@SuppressWarnings( "unchecked" )
public void setDataProvider( DataProvider<?> dataProvider ) {
+ checkDisposed();
if( dataProvider == null ) {
throw new NullPointerException( "Parameter must not be null: dataProvider" );
}
@@ -95,9 +98,12 @@
*
* @param script the filterScript, or <code>null</code> to use default script
*
+ * @exception IllegalStateException when the receiver is disposed
+ *
* @see DataSource#setDataProvider(DataProvider)
*/
public void setFilterScript( String script ) {
+ checkDisposed();
remoteObject.set( "filterScript", script );
}
@@ -114,12 +120,35 @@
*
* @param template the template (may be null)
*
+ * @exception IllegalStateException when the receiver is disposed
+ *
* @see DataSource#setDataProvider(DataProvider)
*/
public void setTemplate( ColumnTemplate template ) {
+ checkDisposed();
this.template = template;
}
+ /**
+ * Disposes the receiver with all resources it created, but not the <code>DataProvider</code>
+ * that may be attached to it. If the instance is already disposed, nothing happens.
+ */
+ public void dispose() {
+ if( !isDisposed ) {
+ isDisposed = true;
+ remoteObject.destroy();
+ }
+ }
+
+ /**
+ * Indicates whether the receiver has been disposed.
+ *
+ * @return true if the receiver is disposed
+ */
+ public boolean isDisposed() {
+ return isDisposed;
+ }
+
String getId() {
return remoteObject.getId();
}
@@ -128,6 +157,12 @@
return template;
}
+ private void checkDisposed() {
+ if( isDisposed ) {
+ throw new IllegalStateException( "AutoSuggest is disposed" );
+ }
+ }
+
private void setInitialData() {
boolean hasColumns = dataProvider instanceof ColumnDataProvider;
remoteObject.set( "data", hasColumns ? getColumnData() : getStringData() );
diff --git a/tests/org.eclipse.rap.addons.autosuggest.test/src/org/eclipse/rap/addons/autosuggest/AutoSuggest_Test.java b/tests/org.eclipse.rap.addons.autosuggest.test/src/org/eclipse/rap/addons/autosuggest/AutoSuggest_Test.java
index e6103c0..6fdb971 100644
--- a/tests/org.eclipse.rap.addons.autosuggest.test/src/org/eclipse/rap/addons/autosuggest/AutoSuggest_Test.java
+++ b/tests/org.eclipse.rap.addons.autosuggest.test/src/org/eclipse/rap/addons/autosuggest/AutoSuggest_Test.java
@@ -284,6 +284,15 @@
autoSuggest.setDataSource( mock( DataSource.class ) );
}
+ @Test( expected = IllegalArgumentException.class )
+ public void testSetDataSource_failsIfDataSourceIsDisposed() {
+ AutoSuggest autoSuggest = new AutoSuggest( text );
+ DataSource dataSource = mock( DataSource.class );
+ when( new Boolean( dataSource.isDisposed() ) ).thenReturn( Boolean.TRUE );
+
+ autoSuggest.setDataSource( dataSource );
+ }
+
@Test
public void testSetDataSource_setsDataSourceOnRemoteObject() {
AutoSuggest autoSuggest = new AutoSuggest( text );
diff --git a/tests/org.eclipse.rap.addons.autosuggest.test/src/org/eclipse/rap/addons/autosuggest/DataSource_Test.java b/tests/org.eclipse.rap.addons.autosuggest.test/src/org/eclipse/rap/addons/autosuggest/DataSource_Test.java
index 25b917a..b00a098 100644
--- a/tests/org.eclipse.rap.addons.autosuggest.test/src/org/eclipse/rap/addons/autosuggest/DataSource_Test.java
+++ b/tests/org.eclipse.rap.addons.autosuggest.test/src/org/eclipse/rap/addons/autosuggest/DataSource_Test.java
@@ -11,10 +11,13 @@
package org.eclipse.rap.addons.autosuggest;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -32,10 +35,9 @@
public class DataSource_Test {
private static final String REMOTE_TYPE = "rwt.remote.Model";
-
private Connection connection;
-
private RemoteObject remoteObject;
+ private DataSource dataSource;
@Before
public void setUp() {
@@ -45,6 +47,7 @@
when( connection.createRemoteObject( anyString() ) ).thenReturn( remoteObject );
when( remoteObject.getId() ).thenReturn( "idFoo" );
Fixture.fakeConnection( connection );
+ dataSource = new DataSource();
}
@After
@@ -54,38 +57,36 @@
@Test
public void testConstructor_createsRemoteObject() {
- new DataSource();
-
verify( connection ).createRemoteObject( eq( REMOTE_TYPE ) );
}
@Test
public void testGetId() {
- DataSource dataSource = new DataSource();
assertEquals( "idFoo", dataSource.getId() );
}
@Test ( expected = NullPointerException.class )
public void testSetDataProvider_failsWithNullArgument() {
- DataSource dataSource = new DataSource();
-
dataSource.setDataProvider( null );
}
@Test
public void testSetDataProvider_setsDataOnRemoteObject() {
- DataSource dataSource = new DataSource();
-
dataSource.setDataProvider( new ArrayDataProvider( "foo", "bar" ) );
JsonArray array = new JsonArray().add( "foo" ).add( "bar" );
verify( remoteObject ).set( eq( "data" ), eq( array ) );
}
+ @Test( expected = IllegalStateException.class )
+ public void testSetDataProvider_failsIfDisposed() {
+ dataSource.dispose();
+
+ dataSource.setDataProvider( new ArrayDataProvider( "foo", "bar" ) );
+ }
+
@Test
public void testSetDataProvider_setsDataOnRemoteObject_forColumnDataProvider() {
- DataSource dataSource = new DataSource();
-
dataSource.setDataProvider( new ColumnDataProvider() {
public Iterable<?> getSuggestions() {
return Arrays.asList( "foo", "bar" );
@@ -106,16 +107,20 @@
@Test
public void testSetFilterScript_setsFilterScriptOnRemoteObject() {
- DataSource dataSource = new DataSource();
-
dataSource.setFilterScript( "foobar" );
verify( remoteObject ).set( eq( "filterScript" ), eq( "foobar" ) );
}
+ @Test( expected = IllegalStateException.class )
+ public void testSetFilterScript_failsIfDisposed() {
+ dataSource.dispose();
+
+ dataSource.setFilterScript( "foobar" );
+ }
+
@Test
public void testSetTemplate() {
- DataSource dataSource = new DataSource();
ColumnTemplate template = mock( ColumnTemplate.class );
dataSource.setTemplate( template );
@@ -123,4 +128,37 @@
assertSame( template, dataSource.getTemplate() );
}
+ @Test( expected = IllegalStateException.class )
+ public void testSetTemplate_failsIfDisposed() {
+ dataSource.dispose();
+
+ dataSource.setTemplate( mock( ColumnTemplate.class ) );
+ }
+
+ @Test
+ public void testIsDisposed_returnsFalse() {
+ assertFalse( dataSource.isDisposed() );
+ }
+
+ @Test
+ public void testIsDisposed_returnsTrueAfterDispose() {
+ dataSource.dispose();
+
+ assertTrue( dataSource.isDisposed() );
+ }
+
+ @Test
+ public void testDispose_destroyRemoteObject() {
+ dataSource.dispose();
+
+ verify( remoteObject ).destroy();
+ }
+
+ @Test
+ public void testDispose_callingTwicedestroysRemoteObjectOnce() {
+ dataSource.dispose();
+ dataSource.dispose();
+
+ verify( remoteObject, times( 1 ) ).destroy();
+ }
}