From f3d1617ddeba4765135568485e8ceb30b56bca27 Mon Sep 17 00:00:00 2001 From: John Blum Date: Sat, 22 Aug 2015 13:38:35 -0700 Subject: [PATCH] SGF-408 - Provide support in the SDG XML namespace to load a pre-defined data set using GemFire Snapshot Service for development and testing purposes. Introduced a SnapshotApplicationEvent extending Spring ApplicationEvent to allow SDG-based GemFire applications to trigger GemFire Cache Region data snapshots (export ops). Refactored SnapshotServiceFactoryBean to be a ApplicationListener on SnapshotApplicationEvents. Introduced ExportSnapshotException and ImportSnapshotException to more succinctly desribe the snapshot import/export error. Refactored CacheSnapshotServiceAdapter.doImport(..) method to invoke load(:SnapshotFormat, :SnapshotOptions, :File...), i.e. file-based in order to apply the SnapshotFilter on load, which is not applicable to the CacheSnapshotService.load(:File, :SnapshotFormat) method that assumes the 'File' is a directory. Therefore doImport(..) expands the directory if the File given is indeed a valid file system directory. Added additional unit tests. (cherry picked from commit 96f6a70595543bc375b2f346bd1c0932b174e2b6) Signed-off-by: John Blum --- .../data/gemfire/ExportSnapshotException.java | 45 ++ .../data/gemfire/ImportSnapshotException.java | 45 ++ .../gemfire/SnapshotApplicationEvent.java | 139 ++++ .../gemfire/SnapshotServiceFactoryBean.java | 213 ++++-- .../gemfire/SnapshotApplicationEventTest.java | 173 +++++ .../SnapshotServiceFactoryBeanTest.java | 717 ++++++++++++++++-- .../gemfire/test/support/FileSystemUtils.java | 27 +- 7 files changed, 1235 insertions(+), 124 deletions(-) create mode 100644 src/main/java/org/springframework/data/gemfire/ExportSnapshotException.java create mode 100644 src/main/java/org/springframework/data/gemfire/ImportSnapshotException.java create mode 100644 src/main/java/org/springframework/data/gemfire/SnapshotApplicationEvent.java create mode 100644 src/test/java/org/springframework/data/gemfire/SnapshotApplicationEventTest.java diff --git a/src/main/java/org/springframework/data/gemfire/ExportSnapshotException.java b/src/main/java/org/springframework/data/gemfire/ExportSnapshotException.java new file mode 100644 index 00000000..6be20a53 --- /dev/null +++ b/src/main/java/org/springframework/data/gemfire/ExportSnapshotException.java @@ -0,0 +1,45 @@ +/* + * Copyright 2010-2013 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.data.gemfire; + +/** + * The ExportSnapshotException class is a RuntimeException indicating an error occurred while saving a snapshhot + * of GemFire's Cache Regions. + * + * @author John Blum + * @see java.lang.RuntimeException + * @since 1.7.0 + */ +@SuppressWarnings("unused") +public class ExportSnapshotException extends RuntimeException { + + public ExportSnapshotException() { + } + + public ExportSnapshotException(String message) { + super(message); + } + + public ExportSnapshotException(Throwable cause) { + super(cause); + } + + public ExportSnapshotException(String message, Throwable cause) { + super(message, cause); + } + +} diff --git a/src/main/java/org/springframework/data/gemfire/ImportSnapshotException.java b/src/main/java/org/springframework/data/gemfire/ImportSnapshotException.java new file mode 100644 index 00000000..d74cacc7 --- /dev/null +++ b/src/main/java/org/springframework/data/gemfire/ImportSnapshotException.java @@ -0,0 +1,45 @@ +/* + * Copyright 2010-2013 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.data.gemfire; + +/** + * The ImportSnapshotException class is a RuntimeException indicating an error occurred while loading GemFire Snapshots + * into the GemFire Cache Regions. + * + * @author John Blum + * @see java.lang.RuntimeException + * @since 1.7.0 + */ +@SuppressWarnings("unused") +public class ImportSnapshotException extends RuntimeException { + + public ImportSnapshotException() { + } + + public ImportSnapshotException(String message) { + super(message); + } + + public ImportSnapshotException(Throwable cause) { + super(cause); + } + + public ImportSnapshotException(String message, Throwable cause) { + super(message, cause); + } + +} diff --git a/src/main/java/org/springframework/data/gemfire/SnapshotApplicationEvent.java b/src/main/java/org/springframework/data/gemfire/SnapshotApplicationEvent.java new file mode 100644 index 00000000..c7daa251 --- /dev/null +++ b/src/main/java/org/springframework/data/gemfire/SnapshotApplicationEvent.java @@ -0,0 +1,139 @@ +/* + * Copyright 2010-2013 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.data.gemfire; + +import static org.springframework.data.gemfire.SnapshotServiceFactoryBean.SnapshotMetadata; + +import org.springframework.context.ApplicationEvent; + +import com.gemstone.gemfire.cache.Region; +import com.gemstone.gemfire.management.internal.cli.util.spring.StringUtils; + +/** + * The SnapshotApplicationEvent class is a Spring ApplicationEvent signaling a GemFire Cache Region(s) snapshot event + * triggering a snapshot to occur. + * + * @author John Blum + * @see org.springframework.context.ApplicationEvent + * @see com.gemstone.gemfire.cache.Region + * @since 1.7.0 + */ +@SuppressWarnings("unused") +public class SnapshotApplicationEvent extends ApplicationEvent { + + private final SnapshotMetadata[] snapshotMetadata; + + private final String regionPath; + + /** + * Constructs an instance of SnapshotApplicationEvent initialized with an event source along with optional meta-data + * describing the data snapshots to be taken. + * + * @param eventSource the source of the ApplicationEvent. + * @param snapshotMetadata an array of SnapshotMetadata containing details of each export. + * @see org.springframework.data.gemfire.SnapshotServiceFactoryBean.SnapshotMetadata + * @see #SnapshotApplicationEvent(Object, String, SnapshotMetadata[]) + */ + public SnapshotApplicationEvent(Object eventSource, SnapshotMetadata... snapshotMetadata) { + this(eventSource, null, snapshotMetadata); + } + + /** + * Constructs an instance of SnapshotApplicationEvent initialized with an event source and pathname of the Region + * on which the data snapshot(s) will be taken with details provided by the snapshot meta-data. + * + * @param eventSource the source of the ApplicationEvent. + * @param regionPath the absolute pathname of the Region. + * @param snapshotMetadata an array of SnapshotMetadata containing details of each export. + * @see org.springframework.data.gemfire.SnapshotServiceFactoryBean.SnapshotMetadata + */ + public SnapshotApplicationEvent(Object eventSource, String regionPath, SnapshotMetadata... snapshotMetadata) { + super(eventSource); + this.snapshotMetadata = snapshotMetadata; + this.regionPath = regionPath; + } + + /** + * Gets the absolute pathname of the Region in GemFire for which the snapshot will be taken. + * + * @return a String indicating the absolute pathname of the Region. + * @see com.gemstone.gemfire.cache.Region#getFullPath() + */ + public String getRegionPath() { + return regionPath; + } + + /** + * Gets the meta-data used to perform the GemFire Cache Region data snapshots. + * + * @return an array of SnapshotMetadata containing information necessary to perform the data export. + * @see org.springframework.data.gemfire.SnapshotServiceFactoryBean.SnapshotMetadata + */ + public SnapshotMetadata[] getSnapshotMetadata() { + return snapshotMetadata; + } + + /** + * Determines whether this event indicates a Cache-wide snapshot. + * + * @return a boolean value indicating whether a Cache-wide snapshot has been triggered. + * @see #isRegionSnapshotEvent() + */ + public boolean isCacheSnapshotEvent() { + return !isRegionSnapshotEvent(); + } + + /** + * Determines whether this event indicates a Region-specific snapshot. + * + * @return a boolean value indicating whether a Region-specific snapshot has been triggered. + * @see #isCacheSnapshotEvent() + */ + public boolean isRegionSnapshotEvent() { + return StringUtils.hasText(getRegionPath()); + } + + /** + * Determines whether this event has been targeted for the specified Region. + * + * @param region the Region being evaluated as the subject of this event. + * @return a boolean value indicating whether this event has been targeted for the specified Region + * @see com.gemstone.gemfire.cache.Region#getFullPath() + * @see #getRegionPath() + * @see #matches(String) + */ + public boolean matches(Region region) { + return (region != null && matches(region.getFullPath())); + } + + /** + * Determines whether this event has been targeted for a Region with the given absolute pathname. + * + * @param regionPath the absolute Region pathname being evaluated as the subject of this event. + * @return a boolean value indicating whether this event has been targeted for the absolute Region pathname. + * @see #getRegionPath() + */ + public boolean matches(String regionPath) { + return toString(regionPath).equals(toString(getRegionPath())); + } + + /* (non-Javadoc) */ + private String toString(String value) { + return String.valueOf(value).trim(); + } + +} diff --git a/src/main/java/org/springframework/data/gemfire/SnapshotServiceFactoryBean.java b/src/main/java/org/springframework/data/gemfire/SnapshotServiceFactoryBean.java index 4ee4dbf3..26011904 100644 --- a/src/main/java/org/springframework/data/gemfire/SnapshotServiceFactoryBean.java +++ b/src/main/java/org/springframework/data/gemfire/SnapshotServiceFactoryBean.java @@ -19,11 +19,13 @@ package org.springframework.data.gemfire; import static com.gemstone.gemfire.cache.snapshot.SnapshotOptions.SnapshotFormat; import java.io.File; +import java.io.FileFilter; import java.util.Arrays; import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.InitializingBean; +import org.springframework.context.ApplicationListener; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; @@ -43,13 +45,14 @@ import com.gemstone.gemfire.cache.snapshot.SnapshotOptions; * @see org.springframework.beans.factory.DisposableBean * @see org.springframework.beans.factory.FactoryBean * @see org.springframework.beans.factory.InitializingBean + * @see org.springframework.context.ApplicationListener * @see com.gemstone.gemfire.cache.snapshot.CacheSnapshotService * @see com.gemstone.gemfire.cache.snapshot.RegionSnapshotService * @since 1.7.0 */ @SuppressWarnings("unused") public class SnapshotServiceFactoryBean implements FactoryBean>, - InitializingBean, DisposableBean { + InitializingBean, DisposableBean, ApplicationListener> { protected static final SnapshotMetadata[] EMPTY_ARRAY = new SnapshotMetadata[0]; @@ -62,6 +65,22 @@ public class SnapshotServiceFactoryBean implements FactoryBean[] exports; private SnapshotMetadata[] imports; + /* (non-Javadoc) */ + @SuppressWarnings("unchecked") + static SnapshotMetadata[] nullSafeArray(SnapshotMetadata[] configurations) { + return (configurations != null ? configurations : EMPTY_ARRAY); + } + + /* (non-Javadoc) */ + static boolean nullSafeIsDirectory(File file) { + return (file != null && file.isDirectory()); + } + + /* (non-Javadoc) */ + static boolean nullSafeIsFile(File file) { + return (file != null && file.isFile()); + } + /** * Sets a reference to the GemFire Cache for which the snapshot will be taken. * @@ -88,18 +107,44 @@ public class SnapshotServiceFactoryBean implements FactoryBean[] exports) { this.exports = exports; } + /** + * Sets the meta-data (location, filter and format) used to create a snapshot from the Cache or Region data. + * + * @return an array of snapshot meta-data used for each export. + * @see org.springframework.data.gemfire.SnapshotServiceFactoryBean.SnapshotMetadata + */ protected SnapshotMetadata[] getExports() { return nullSafeArray(exports); } + /** + * Sets the meta-data (location, filter and format) used to read a data snapshot into an entire Cache + * or individual Region. + * + * @param imports an array of snapshot meta-data used for each import. + * @see org.springframework.data.gemfire.SnapshotServiceFactoryBean.SnapshotMetadata + */ public void setImports(SnapshotMetadata[] imports) { this.imports = imports; } + /** + * Gets the meta-data (location, filter and format) used to read a data snapshot into an entire Cache + * or individual Region. + * + * @return an array of snapshot meta-data used for each import. + * @see org.springframework.data.gemfire.SnapshotServiceFactoryBean.SnapshotMetadata + */ protected SnapshotMetadata[] getImports() { return nullSafeArray(imports); } @@ -173,6 +218,8 @@ public class SnapshotServiceFactoryBean implements FactoryBean implements FactoryBean implements FactoryBean implements FactoryBean wrap(RegionSnapshotService regionSnapshotService) { - return new RegionSnapshotServiceAdapter(regionSnapshotService); - } - - /* (non-Javadoc) */ - @SuppressWarnings("unchecked") - protected SnapshotMetadata[] nullSafeArray(SnapshotMetadata[] configurations) { - return (configurations != null ? configurations : EMPTY_ARRAY); + return new RegionSnapshotServiceAdapter(regionSnapshotService); } /** @@ -239,6 +282,60 @@ public class SnapshotServiceFactoryBean implements FactoryBean event) { + try { + if (isMatch(event)) { + getObject().doExport(resolveSnapshotMetadata(event)); + } + } + catch (Exception ignore) { + } + } + + /** + * Determines whether the details of the given SnapshotApplicationEvent match the criteria of this factory + * to trigger a GemFire Cache or Region data export. + * + * @param event the SnapshotApplicationEvent containing details of the application requested data export. + * @return a boolean value indicating whether the application requested snapshot event details match + * the criteria required by this factory to trigger a GemFire Cache or Region data export. + * @see org.springframework.data.gemfire.SnapshotApplicationEvent + */ + protected boolean isMatch(SnapshotApplicationEvent event) { + Region region = getRegion(); + + return ((event.isRegionSnapshotEvent() && event.matches(region)) + || (event.isCacheSnapshotEvent() && region == null)); + } + + /** + * Resolves the SnapshotMetadata used to perform the GemFire Cache or Region data snapshot (export). If the event + * contains specific SnapshotMetadata, then this is preferred over the factory's own "export" SnapshotMetadata. + * + * @param event the SnapshotApplicationEvent from which to resolve the SnapshotMetadata. + * @return the resolved SnapshotMetadata, either from the event or this factory's configured exports. + * @see org.springframework.data.gemfire.SnapshotApplicationEvent#getSnapshotMetadata() + * @see #getExports() + */ + protected SnapshotMetadata[] resolveSnapshotMetadata(SnapshotApplicationEvent event) { + SnapshotMetadata[] eventSnapshotMetadata = event.getSnapshotMetadata(); + + return (!ObjectUtils.isEmpty(eventSnapshotMetadata) ? eventSnapshotMetadata : getExports()); + } + public interface SnapshotServiceAdapter { SnapshotOptions createOptions(); @@ -257,7 +354,7 @@ public class SnapshotServiceFactoryBean implements FactoryBean { + protected static class CacheSnapshotServiceAdapter implements SnapshotServiceAdapter { private final CacheSnapshotService snapshotService; @@ -281,26 +378,22 @@ public class SnapshotServiceFactoryBean implements FactoryBean[] configurations) { - if (!ObjectUtils.isEmpty(configurations)) { - for (SnapshotMetadata configuration : configurations) { - save(configuration.getLocation(), configuration.getFormat(), - createOptions(configuration.getFilter())); - } + for (SnapshotMetadata configuration : nullSafeArray(configurations)) { + save(configuration.getLocation(), configuration.getFormat(), createOptions(configuration.getFilter())); } } @Override public void doImport(SnapshotMetadata[] configurations) { - if (!ObjectUtils.isEmpty(configurations)) { - for (SnapshotMetadata configuration : configurations) { - if (configuration.isDirectory()) { - load(configuration.getLocation(), configuration.getFormat()); - } - else { - load(configuration.getFormat(), createOptions(configuration.getFilter()), - configuration.getLocation()); - } - } + for (SnapshotMetadata configuration : nullSafeArray(configurations)) { + File[] snapshots = (configuration.isFile() ? new File[] { configuration.getLocation() } + : configuration.getLocation().listFiles(new FileFilter() { + @Override public boolean accept(File pathname) { + return nullSafeIsFile(pathname); + } + })); + + load(configuration.getFormat(), createOptions(configuration.getFilter()), snapshots); } } @@ -310,20 +403,20 @@ public class SnapshotServiceFactoryBean implements FactoryBean options, - File... snapshots) { + public void load(SnapshotFormat format, SnapshotOptions options, File... snapshots) { try { getSnapshotService().load(snapshots, format, options); } catch (Throwable t) { - throw new RuntimeException(String.format( - "Failed to load snapshots (%1$s) in format (%2$s) with options (%3$s)", + throw new ImportSnapshotException(String.format( + "Failed to load snapshots (%1$s) in format (%2$s) using options (%3$s)", Arrays.toString(snapshots), format, options), t); } } @@ -334,26 +427,26 @@ public class SnapshotServiceFactoryBean implements FactoryBean options) { + public void save(File directory, SnapshotFormat format, SnapshotOptions options) { try { getSnapshotService().save(directory, format, options); } catch (Throwable t) { - throw new RuntimeException(String.format( - "Failed to save snapshots to directory (%1$s) using format (%2$s) with options (%3$s)", + throw new ExportSnapshotException(String.format( + "Failed to save snapshots to directory (%1$s) in format (%2$s) using options (%3$s)", directory, format, options), t); } } } - protected class RegionSnapshotServiceAdapter implements SnapshotServiceAdapter { + protected static class RegionSnapshotServiceAdapter implements SnapshotServiceAdapter { private final RegionSnapshotService snapshotService; @@ -376,24 +469,16 @@ public class SnapshotServiceFactoryBean implements FactoryBean[] configurations) { - if (!ObjectUtils.isEmpty(configurations)) { - for (SnapshotMetadata configuration : configurations) { - Assert.isTrue(configuration.isFile(), String.format("The export location (%1$s) must be a file", - configuration.getLocation())); - save(configuration.getLocation(), configuration.getFormat(), createOptions(configuration.getFilter())); - } + public void doExport(SnapshotMetadata[] configurations) { + for (SnapshotMetadata configuration : nullSafeArray(configurations)) { + save(configuration.getLocation(), configuration.getFormat(), createOptions(configuration.getFilter())); } } @Override - public void doImport(final SnapshotMetadata[] configurations) { - if (!ObjectUtils.isEmpty(configurations)) { - for (SnapshotMetadata configuration : configurations) { - Assert.isTrue(configuration.isFile(), String.format("The import location (%1$s) must be a file", - configuration.getLocation())); - load(configuration.getFormat(), createOptions(configuration.getFilter()), configuration.getLocation()); - } + public void doImport(SnapshotMetadata[] configurations) { + for (SnapshotMetadata configuration : nullSafeArray(configurations)) { + load(configuration.getFormat(), createOptions(configuration.getFilter()), configuration.getLocation()); } } @@ -403,8 +488,9 @@ public class SnapshotServiceFactoryBean implements FactoryBean implements FactoryBean implements FactoryBean implements FactoryBean implements FactoryBean mockRegion; + + protected SnapshotMetadata newSnapshotMetadata() { + return new SnapshotMetadata(FileSystemUtils.WORKING_DIRECTORY, null, + SnapshotOptions.SnapshotFormat.GEMFIRE); + } + + @Test + public void constructSnapshotApplicationEventWithoutRegionOrSnapshotMetadata() { + SnapshotApplicationEvent event = new SnapshotApplicationEvent(this); + + assertThat((SnapshotApplicationEventTest) event.getSource(), is(equalTo(this))); + assertThat(event.getRegionPath(), is(nullValue())); + assertThat(event.getSnapshotMetadata(), is(notNullValue())); + assertThat(event.getSnapshotMetadata().length, is(equalTo(0))); + assertThat(event.isCacheSnapshotEvent(), is(true)); + assertThat(event.isRegionSnapshotEvent(), is(false)); + } + + @Test + public void constructSnapshotApplicationEventWithRegionAndSnapshotMetadata() { + SnapshotMetadata eventSnapshotMetadata = newSnapshotMetadata(); + + SnapshotApplicationEvent event = new SnapshotApplicationEvent(this, "/Example", eventSnapshotMetadata); + + assertThat((SnapshotApplicationEventTest) event.getSource(), is(equalTo(this))); + assertThat(event.getRegionPath(), is(equalTo("/Example"))); + assertThat(event.getSnapshotMetadata()[0], is(sameInstance(eventSnapshotMetadata))); + assertThat(event.isCacheSnapshotEvent(), is(false)); + assertThat(event.isRegionSnapshotEvent(), is(true)); + } + + @Test + public void constructSnapshotApplicationEventWithRegionButNoSnapshotMetadata() { + SnapshotApplicationEvent event = new SnapshotApplicationEvent(this, "/Example"); + + assertThat((SnapshotApplicationEventTest) event.getSource(), is(equalTo(this))); + assertThat(event.getRegionPath(), is(equalTo("/Example"))); + assertThat(event.getSnapshotMetadata(), is(notNullValue())); + assertThat(event.getSnapshotMetadata().length, is(equalTo(0))); + assertThat(event.isCacheSnapshotEvent(), is(false)); + assertThat(event.isRegionSnapshotEvent(), is(true)); + } + + @Test + public void constructSnapshotApplicationEventWithSnapshotMetadataButNoRegion() { + SnapshotMetadata eventSnapshotMetadataOne = newSnapshotMetadata(); + SnapshotMetadata eventSnapshotMetadataTwo = newSnapshotMetadata(); + + SnapshotApplicationEvent event = new SnapshotApplicationEvent(this, eventSnapshotMetadataOne, + eventSnapshotMetadataTwo); + + assertThat((SnapshotApplicationEventTest) event.getSource(), is(equalTo(this))); + assertThat(event.getRegionPath(), is(nullValue())); + assertThat(event.getSnapshotMetadata()[0], is(equalTo(eventSnapshotMetadataOne))); + assertThat(event.getSnapshotMetadata()[1], is(equalTo(eventSnapshotMetadataTwo))); + assertThat(event.isCacheSnapshotEvent(), is(true)); + assertThat(event.isRegionSnapshotEvent(), is(false)); + } + + @Test + public void matchesNullRegionIsFalse() { + assertThat(new SnapshotApplicationEvent(this).matches((Region) null), is(false)); + } + + @Test + public void matchesNonMatchingRegionIsFalse() { + Region mockRegion = mock(Region.class, "MockRegion"); + + when(mockRegion.getFullPath()).thenReturn("/Example"); + + assertThat(new SnapshotApplicationEvent(this, "/Prototype").matches(mockRegion), is(false)); + + verify(mockRegion, times(1)).getFullPath(); + } + + @Test + public void matchesMatchingRegionIsTrue() { + Region mockRegion = mock(Region.class, "MockRegion"); + + when(mockRegion.getFullPath()).thenReturn("/Example"); + + assertThat(new SnapshotApplicationEvent(this, "/Example").matches(mockRegion), is(true)); + + verify(mockRegion, times(1)).getFullPath(); + } + + @Test + public void matchesNonMatchingRegionPathsIsFalse() { + SnapshotApplicationEvent event = new SnapshotApplicationEvent(this, "/Example"); + + assertThat(event.getRegionPath(), is(equalTo("/Example"))); + assertThat(event.matches("/Prototype"), is(false)); + assertThat(event.matches("/Sample"), is(false)); + assertThat(event.matches("/Xmpl"), is(false)); + assertThat(event.matches("/Ex"), is(false)); + assertThat(event.matches("/E.g."), is(false)); + assertThat(event.matches("Example"), is(false)); + assertThat(event.matches("/example"), is(false)); + assertThat(event.matches("/Exam"), is(false)); + assertThat(event.matches("/"), is(false)); + assertThat(event.matches(" "), is(false)); + assertThat(event.matches(""), is(false)); + assertThat(event.matches((String) null), is(false)); + } + + @Test + public void matchesMatchingRegionPathsIsTrue() { + assertThat(new SnapshotApplicationEvent(this, "/Example").matches("/Example"), is(true)); + assertThat(new SnapshotApplicationEvent(this, "/").matches("/"), is(true)); + assertThat(new SnapshotApplicationEvent(this, " ").matches(" "), is(true)); + assertThat(new SnapshotApplicationEvent(this, "").matches(""), is(true)); + assertThat(new SnapshotApplicationEvent(this, (String) null).matches((String) null), is(true)); + } + +} diff --git a/src/test/java/org/springframework/data/gemfire/SnapshotServiceFactoryBeanTest.java b/src/test/java/org/springframework/data/gemfire/SnapshotServiceFactoryBeanTest.java index 134eaf2d..8a463ccf 100644 --- a/src/test/java/org/springframework/data/gemfire/SnapshotServiceFactoryBeanTest.java +++ b/src/test/java/org/springframework/data/gemfire/SnapshotServiceFactoryBeanTest.java @@ -16,6 +16,7 @@ package org.springframework.data.gemfire; +import static com.gemstone.gemfire.cache.snapshot.SnapshotOptions.SnapshotFormat; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.CoreMatchers.is; @@ -26,18 +27,29 @@ import static org.hamcrest.CoreMatchers.sameInstance; import static org.junit.Assert.assertThat; import static org.mockito.Matchers.any; import static org.mockito.Matchers.eq; +import static org.mockito.Matchers.isNull; +import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import static org.springframework.data.gemfire.SnapshotServiceFactoryBean.CacheSnapshotServiceAdapter; +import static org.springframework.data.gemfire.SnapshotServiceFactoryBean.RegionSnapshotServiceAdapter; +import static org.springframework.data.gemfire.SnapshotServiceFactoryBean.SnapshotMetadata; +import static org.springframework.data.gemfire.SnapshotServiceFactoryBean.SnapshotServiceAdapter; import java.io.File; +import java.io.IOException; +import java.util.Arrays; import org.junit.After; +import org.junit.BeforeClass; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; +import org.mockito.Matchers; +import org.springframework.data.gemfire.test.support.FileSystemUtils; import com.gemstone.gemfire.cache.Cache; import com.gemstone.gemfire.cache.Region; @@ -55,39 +67,52 @@ import com.gemstone.gemfire.cache.snapshot.SnapshotOptions; * @see org.junit.Test * @see org.mockito.Mockito * @see org.springframework.data.gemfire.SnapshotServiceFactoryBean + * @see com.gemstone.gemfire.cache.snapshot.CacheSnapshotService + * @see com.gemstone.gemfire.cache.snapshot.RegionSnapshotService * @since 1.7.0 */ @SuppressWarnings({ "rawtypes", "unchecked" }) public class SnapshotServiceFactoryBeanTest { + private static File snapshotDat; + @Rule public ExpectedException expectedException = ExpectedException.none(); - private SnapshotServiceFactoryBean factoryBean = new SnapshotServiceFactoryBean(); + private SnapshotServiceFactoryBean factoryBean = new SnapshotServiceFactoryBean(); - protected SnapshotServiceFactoryBean.SnapshotMetadata newSnapshotMetadata() { - return newSnapshotMetadata(new File(System.getProperty("user.dir"))); + protected SnapshotMetadata newSnapshotMetadata() { + return newSnapshotMetadata(FileSystemUtils.WORKING_DIRECTORY); } - protected SnapshotServiceFactoryBean.SnapshotMetadata newSnapshotMetadata(File location) { + protected SnapshotMetadata newSnapshotMetadata(File location) { return newSnapshotMetadata(location, null); } - protected SnapshotServiceFactoryBean.SnapshotMetadata newSnapshotMetadata(File location, - SnapshotFilter filter) { - return newSnapshotMetadata(location, filter, SnapshotOptions.SnapshotFormat.GEMFIRE); + protected SnapshotMetadata newSnapshotMetadata(SnapshotFilter filter) { + return newSnapshotMetadata(FileSystemUtils.WORKING_DIRECTORY, filter); } - protected SnapshotServiceFactoryBean.SnapshotMetadata newSnapshotMetadata(File location, - SnapshotFilter filter, SnapshotOptions.SnapshotFormat format) { - return new SnapshotServiceFactoryBean.SnapshotMetadata(location, filter, format); + protected SnapshotMetadata newSnapshotMetadata(File location, SnapshotFilter filter) { + return newSnapshotMetadata(location, filter, SnapshotFormat.GEMFIRE); } - protected SnapshotServiceFactoryBean.SnapshotMetadata[] toArray( - SnapshotServiceFactoryBean.SnapshotMetadata... metadata) { + protected SnapshotMetadata newSnapshotMetadata(File location, SnapshotFilter filter, + SnapshotFormat format) { + return new SnapshotMetadata(location, filter, format); + } + + protected SnapshotMetadata[] toArray(SnapshotMetadata... metadata) { return metadata; } + @BeforeClass + public static void setupBeforeClass() throws Exception { + snapshotDat = File.createTempFile("snapshot", "dat"); + snapshotDat.deleteOnExit(); + assertThat(snapshotDat.isFile(), is(true)); + } + @After public void tearDown() { factoryBean.setExports(null); @@ -95,6 +120,42 @@ public class SnapshotServiceFactoryBeanTest { factoryBean.setRegion(null); } + @Test + public void nullSafeArrayWithNonNullArray() { + SnapshotMetadata[] expectedConfigurations = new SnapshotMetadata[0]; + + assertThat(SnapshotServiceFactoryBean.nullSafeArray(expectedConfigurations), + is(sameInstance(expectedConfigurations))); + } + + @Test + public void nullSafeArrayWithNullArray() { + assertThat(SnapshotServiceFactoryBean.nullSafeArray(null), is(equalTo(SnapshotServiceFactoryBean.EMPTY_ARRAY))); + } + + @Test + public void nullSafeIsDirectoryWithDirectory() { + assertThat(SnapshotServiceFactoryBean.nullSafeIsDirectory(new File(System.getProperty("user.dir"))), is(true)); + } + + @Test + public void nullSafeIsDirectoryWithNonDirectories() { + assertThat(SnapshotServiceFactoryBean.nullSafeIsDirectory(new File("path/to/non-existing/directory")), is(false)); + assertThat(SnapshotServiceFactoryBean.nullSafeIsDirectory(FileSystemUtils.JAVA_EXE), is(false)); + } + + @Test + public void nullSafeIsFileWithFile() { + assertThat(SnapshotServiceFactoryBean.nullSafeIsFile(FileSystemUtils.JAVA_EXE), + is(FileSystemUtils.JAVA_EXE.isFile())); + } + + @Test + public void nullSafeIsFileWithNonFiles() { + assertThat(SnapshotServiceFactoryBean.nullSafeIsFile(new File("/path/to/non-existing/file.ext")), is(false)); + assertThat(SnapshotServiceFactoryBean.nullSafeIsFile(new File(System.getProperty("user.dir"))), is(false)); + } + @Test public void setCacheToNull() { expectedException.expect(IllegalArgumentException.class); @@ -123,12 +184,12 @@ public class SnapshotServiceFactoryBeanTest { @Test public void setAndGetExports() { - SnapshotServiceFactoryBean.SnapshotMetadata[] actualExports = factoryBean.getExports(); + SnapshotMetadata[] actualExports = factoryBean.getExports(); assertThat(actualExports, is(notNullValue())); assertThat(actualExports.length, is(equalTo(0))); - SnapshotServiceFactoryBean.SnapshotMetadata[] expectedExports = toArray(newSnapshotMetadata()); + SnapshotMetadata[] expectedExports = toArray(newSnapshotMetadata()); factoryBean.setExports(expectedExports); actualExports = factoryBean.getExports(); @@ -145,12 +206,12 @@ public class SnapshotServiceFactoryBeanTest { @Test public void setAndGetImports() { - SnapshotServiceFactoryBean.SnapshotMetadata[] actualImports = factoryBean.getImports(); + SnapshotMetadata[] actualImports = factoryBean.getImports(); assertThat(actualImports, is(notNullValue())); assertThat(actualImports.length, is(equalTo(0))); - SnapshotServiceFactoryBean.SnapshotMetadata[] expectedImports = toArray(newSnapshotMetadata()); + SnapshotMetadata[] expectedImports = toArray(newSnapshotMetadata()); factoryBean.setImports(expectedImports); actualImports = factoryBean.getImports(); @@ -169,7 +230,7 @@ public class SnapshotServiceFactoryBeanTest { public void setAndGetRegionSuccessfully() { assertThat(factoryBean.getRegion(), is(nullValue())); - Region mockRegion = mock(Region.class, "MockRegion"); + Region mockRegion = mock(Region.class, "MockRegion"); factoryBean.setRegion(mockRegion); @@ -181,16 +242,87 @@ public class SnapshotServiceFactoryBeanTest { } @Test - public void nullSafeArrayWithNonNullArray() { - SnapshotServiceFactoryBean.SnapshotMetadata[] expectedConfigurations = - new SnapshotServiceFactoryBean.SnapshotMetadata[0]; - - assertThat(factoryBean.nullSafeArray(expectedConfigurations), is(sameInstance(expectedConfigurations))); + public void isSingletonIsTrue() { + assertThat(factoryBean.isSingleton(), is(true)); } @Test - public void nullSafeArrayWithNullArray() { - assertThat(factoryBean.nullSafeArray(null), is(equalTo(SnapshotServiceFactoryBean.EMPTY_ARRAY))); + public void createCacheSnapshotService() { + Cache mockCache = mock(Cache.class, "MockCache"); + CacheSnapshotService mockCacheSnapshotService = mock(CacheSnapshotService.class, "MockCacheSnapshotService"); + + when(mockCache.getSnapshotService()).thenReturn(mockCacheSnapshotService); + + SnapshotServiceFactoryBean factoryBean = new SnapshotServiceFactoryBean(); + + factoryBean.setCache(mockCache); + + SnapshotServiceAdapter adapter = factoryBean.create(); + + assertThat(adapter, is(instanceOf(CacheSnapshotServiceAdapter.class))); + + verify(mockCache, times(1)).getSnapshotService(); + } + + @Test + public void createRegionSnapshotService() { + Region mockRegion = mock(Region.class, "MockRegion"); + RegionSnapshotService mockRegionSnapshotService = mock(RegionSnapshotService.class, "MockRegionSnapshotService"); + + when(mockRegion.getSnapshotService()).thenReturn(mockRegionSnapshotService); + + SnapshotServiceFactoryBean factoryBean = new SnapshotServiceFactoryBean(); + + factoryBean.setRegion(mockRegion); + + SnapshotServiceAdapter adapter = factoryBean.create(); + + assertThat(adapter, is(instanceOf(RegionSnapshotServiceAdapter.class))); + + verify(mockRegion, times(1)).getSnapshotService(); + } + + @Test + public void createSnapshotMetadataWithNullLocation() { + expectedException.expect(IllegalArgumentException.class); + expectedException.expectCause(is(nullValue(Throwable.class))); + expectedException.expectMessage("The File location (null) must exist"); + new SnapshotMetadata(null, mock(SnapshotFilter.class), SnapshotFormat.GEMFIRE); + } + + @Test + public void createSnapshotMetadataWithNonExistingLocation() { + expectedException.expect(IllegalArgumentException.class); + expectedException.expectCause(is(nullValue(Throwable.class))); + expectedException.expectMessage("The File location (/path/to/non-existing/location) must exist"); + new SnapshotMetadata(new File("/path/to/non-existing/location"), mock(SnapshotFilter.class), + SnapshotFormat.GEMFIRE); + } + + @Test + public void createSnapshotMetadataWithDirectoryFilterAndUnspecifiedFormat() { + SnapshotFilter mockSnapshotFilter = mock(SnapshotFilter.class, "MockSnapshotFilter"); + + SnapshotMetadata metadata = new SnapshotMetadata(FileSystemUtils.WORKING_DIRECTORY, mockSnapshotFilter, null); + + assertThat(metadata.getLocation(), is(equalTo(FileSystemUtils.WORKING_DIRECTORY))); + assertThat(metadata.isDirectory(), is(true)); + assertThat(metadata.isFile(), is(false)); + assertThat(metadata.getFilter(), is(equalTo(mockSnapshotFilter))); + assertThat(metadata.isFilterPresent(), is(true)); + assertThat(metadata.getFormat(), is(equalTo(SnapshotFormat.GEMFIRE))); + } + + @Test + public void createSnapshotMetadataWithFileNullFilterAndGemFireFormat() throws Exception { + SnapshotMetadata metadata = new SnapshotMetadata(snapshotDat, null, SnapshotFormat.GEMFIRE); + + assertThat(metadata.getLocation(), is(equalTo(snapshotDat))); + assertThat(metadata.isDirectory(), is(false)); + assertThat(metadata.isFile(), is(true)); + assertThat(metadata.getFilter(), is(nullValue())); + assertThat(metadata.isFilterPresent(), is(false)); + assertThat(metadata.getFormat(), is(equalTo(SnapshotFormat.GEMFIRE))); } @Test @@ -210,86 +342,555 @@ public class SnapshotServiceFactoryBeanTest { } @Test - public void isSingletonIsTrue() { - assertThat(factoryBean.isSingleton(), is(true)); + public void onApplicationEventWhenMatchUsingEventSnapshotMetadataPerformsExport() throws Exception { + Region mockRegion = mock(Region.class, "MockRegion"); + + SnapshotOptions mockSnapshotOptions = mock(SnapshotOptions.class, "MockSnapshotOptions"); + + final RegionSnapshotService mockSnapshotService = mock(RegionSnapshotService.class, "MockRegionSnapshotService"); + + when(mockRegion.getFullPath()).thenReturn("/Example"); + when(mockSnapshotService.createOptions()).thenReturn(mockSnapshotOptions); + when(mockSnapshotOptions.setFilter(any(SnapshotFilter.class))).thenReturn(mockSnapshotOptions); + + SnapshotServiceFactoryBean factoryBean = new SnapshotServiceFactoryBean() { + @Override public SnapshotServiceAdapter getObject() throws Exception { + return new RegionSnapshotServiceAdapter(mockSnapshotService); + } + }; + + factoryBean.setExports(toArray(newSnapshotMetadata())); + factoryBean.setRegion(mockRegion); + + SnapshotMetadata eventSnapshotMetadata = newSnapshotMetadata(snapshotDat); + + SnapshotApplicationEvent event = new SnapshotApplicationEvent(this, "/Example", eventSnapshotMetadata); + + assertThat(event.isRegionSnapshotEvent(), is(true)); + assertThat(event.getSnapshotMetadata()[0], is(sameInstance(eventSnapshotMetadata))); + assertThat(factoryBean.getExports()[0], is(not(sameInstance(eventSnapshotMetadata)))); + assertThat(factoryBean.getRegion(), is(sameInstance(mockRegion))); + + factoryBean.onApplicationEvent(event); + + verify(mockRegion, times(1)).getFullPath(); + verify(mockSnapshotOptions, times(1)).setFilter(isNull(SnapshotFilter.class)); + verify(mockSnapshotService, times(1)).createOptions(); + verify(mockSnapshotService, times(1)).save(eq(eventSnapshotMetadata.getLocation()), + eq(eventSnapshotMetadata.getFormat()), eq(mockSnapshotOptions)); } @Test - public void createCacheSnapshotServiceAndImportOnInitialization() throws Exception { + public void onApplicationEventWhenNoMatchDoesNotPerformExport() throws Exception { + final CacheSnapshotService mockSnapshotService = mock(CacheSnapshotService.class, "MockCacheSnapshotService"); + + SnapshotServiceFactoryBean factoryBean = new SnapshotServiceFactoryBean() { + @Override public SnapshotServiceAdapter getObject() throws Exception { + return new CacheSnapshotServiceAdapter(mockSnapshotService); + } + }; + + SnapshotApplicationEvent event = new SnapshotApplicationEvent(this, "/Example"); + + assertThat(event.isRegionSnapshotEvent(), is(true)); + assertThat(factoryBean.getRegion(), is(nullValue())); + + factoryBean.onApplicationEvent(event); + + verify(mockSnapshotService, never()).createOptions(); + verify(mockSnapshotService, never()).save(any(File.class), any(SnapshotFormat.class)); + verify(mockSnapshotService, never()).save(any(File.class), any(SnapshotFormat.class), any(SnapshotOptions.class)); + } + + @Test + public void resolveSnapshotMetadataFromEvent() { + SnapshotMetadata eventSnapshotMetadata = newSnapshotMetadata(snapshotDat); + SnapshotMetadata exportSnapshotMetadata = newSnapshotMetadata(); + + factoryBean.setExports(toArray(exportSnapshotMetadata)); + + assertThat(factoryBean.getExports()[0], is(equalTo(exportSnapshotMetadata))); + assertThat(factoryBean.resolveSnapshotMetadata(new SnapshotApplicationEvent( + this, eventSnapshotMetadata))[0], is(equalTo(eventSnapshotMetadata))); + } + + @Test + public void resolveSnapshotMetadataFromFactory() { + SnapshotMetadata exportSnapshotMetadata = newSnapshotMetadata(); + + factoryBean.setExports(toArray(exportSnapshotMetadata)); + + assertThat(factoryBean.getExports()[0], is(equalTo(exportSnapshotMetadata))); + assertThat(factoryBean.resolveSnapshotMetadata(new SnapshotApplicationEvent(this))[0], + is(equalTo(exportSnapshotMetadata))); + } + + @Test + public void withCacheBasedSnapshotServiceOnCacheSnapshotEventIsMatch() { + SnapshotApplicationEvent event = new SnapshotApplicationEvent(this); + + assertThat(event.isCacheSnapshotEvent(), is(true)); + assertThat(event.isRegionSnapshotEvent(), is(false)); + assertThat(factoryBean.isMatch(event), is(true)); + } + + @Test + public void withCacheBasedSnapshotServiceOnRegionSnapshotEventIsNotAMatch() { + SnapshotApplicationEvent event = new SnapshotApplicationEvent(this, "/Example"); + + assertThat(event.isCacheSnapshotEvent(), is(false)); + assertThat(event.isRegionSnapshotEvent(), is(true)); + assertThat(factoryBean.isMatch(event), is(false)); + } + + @Test + public void withRegionBasedSnapshotServiceOnRegionSnapshotEventIsMatch() { + SnapshotApplicationEvent event = new SnapshotApplicationEvent(this, "/Example"); + + assertThat(event.isCacheSnapshotEvent(), is(false)); + assertThat(event.isRegionSnapshotEvent(), is(true)); + + Region mockRegion = mock(Region.class, "MockRegion"); + + when(mockRegion.getFullPath()).thenReturn(event.getRegionPath()); + + factoryBean.setRegion(mockRegion); + + assertThat(factoryBean.getRegion(), is(sameInstance(mockRegion))); + assertThat(factoryBean.isMatch(event), is(true)); + + verify(mockRegion, times(1)).getFullPath(); + } + + @Test + public void withRegionBasedSnapshotServiceOnCacheSnapshotEventIsNotAMatch() { + SnapshotApplicationEvent event = new SnapshotApplicationEvent(this); + + assertThat(event.isCacheSnapshotEvent(), is(true)); + assertThat(event.isRegionSnapshotEvent(), is(false)); + + factoryBean.setRegion(mock(Region.class, "MockRegion")); + + assertThat(factoryBean.getRegion(), is(notNullValue())); + assertThat(factoryBean.isMatch(event), is(false)); + } + + @Test + public void importCacheSnapshotOnInitialization() throws Exception { Cache mockCache = mock(Cache.class, "MockCache"); + CacheSnapshotService mockCacheSnapshotService = mock(CacheSnapshotService.class, "MockCacheSnapshotService"); + SnapshotFilter mockSnapshotFilterOne = mock(SnapshotFilter.class, "MockSnapshotFilterOne"); + SnapshotFilter mockSnapshotFilterTwo = mock(SnapshotFilter.class, "MockSnapshotFilterTwo"); + + SnapshotOptions mockSnapshotOptionsOne = mock(SnapshotOptions.class, "MockSnapshotOptionsOne"); + SnapshotOptions mockSnapshotOptionsTwo = mock(SnapshotOptions.class, "MockSnapshotOptionsTwo"); + when(mockCache.getSnapshotService()).thenReturn(mockCacheSnapshotService); + when(mockCacheSnapshotService.createOptions()).thenReturn(mockSnapshotOptionsOne) + .thenReturn(mockSnapshotOptionsTwo); + when(mockSnapshotOptionsOne.setFilter(eq(mockSnapshotFilterOne))).thenReturn(mockSnapshotOptionsOne); + when(mockSnapshotOptionsTwo.setFilter(eq(mockSnapshotFilterTwo))).thenReturn(mockSnapshotOptionsTwo); - File userHomeDirectory = new File(System.getProperty("user.home")); + SnapshotMetadata[] expectedImports = toArray( + newSnapshotMetadata(FileSystemUtils.USER_HOME, mockSnapshotFilterOne), + newSnapshotMetadata(mockSnapshotFilterTwo)); - SnapshotServiceFactoryBean.SnapshotMetadata[] expectedImports = toArray( - newSnapshotMetadata(userHomeDirectory)); - - SnapshotServiceFactoryBean factoryBean = new SnapshotServiceFactoryBean(); + SnapshotServiceFactoryBean factoryBean = new SnapshotServiceFactoryBean(); factoryBean.setCache(mockCache); factoryBean.setImports(expectedImports); factoryBean.setRegion(null); assertThat(factoryBean.getObject(), is(nullValue())); - assertThat((Class) factoryBean.getObjectType(), - is(equalTo(SnapshotServiceFactoryBean.SnapshotServiceAdapter.class))); + assertThat((Class) factoryBean.getObjectType(), + is(equalTo(SnapshotServiceAdapter.class))); factoryBean.afterPropertiesSet(); - assertThat(factoryBean.getObject(), is(instanceOf(SnapshotServiceFactoryBean.CacheSnapshotServiceAdapter.class))); - assertThat((Class) factoryBean.getObjectType(), - is(equalTo(SnapshotServiceFactoryBean.CacheSnapshotServiceAdapter.class))); + assertThat(factoryBean.getObject(), is(instanceOf(CacheSnapshotServiceAdapter.class))); + assertThat((Class) factoryBean.getObjectType(), + is(equalTo(CacheSnapshotServiceAdapter.class))); verify(mockCache, times(1)).getSnapshotService(); - verify(mockCacheSnapshotService, times(1)).load(eq(userHomeDirectory), eq(SnapshotOptions.SnapshotFormat.GEMFIRE)); + verify(mockCacheSnapshotService, times(2)).createOptions(); + verify(mockCacheSnapshotService, times(1)).load(eq(FileSystemUtils.safeListFiles(FileSystemUtils.USER_HOME, FileSystemUtils.FileOnlyFilter.INSTANCE)), + eq(SnapshotFormat.GEMFIRE), eq(mockSnapshotOptionsOne)); + verify(mockCacheSnapshotService, times(1)).load(eq(FileSystemUtils.safeListFiles(FileSystemUtils.WORKING_DIRECTORY, FileSystemUtils.FileOnlyFilter.INSTANCE)), + eq(SnapshotFormat.GEMFIRE), eq(mockSnapshotOptionsTwo)); + verify(mockSnapshotOptionsOne, times(1)).setFilter(eq(mockSnapshotFilterOne)); + verify(mockSnapshotOptionsTwo, times(1)).setFilter(eq(mockSnapshotFilterTwo)); } @Test @SuppressWarnings("unchecked") - public void createRegionSnapshotServiceAndImportOnInitialization() throws Exception { + public void importRegionSnapshotOnInitialization() throws Exception { Cache mockCache = mock(Cache.class, "MockCache"); + Region mockRegion = mock(Region.class, "MockRegion"); + RegionSnapshotService mockRegionSnapshotService = mock(RegionSnapshotService.class, "MockRegionSnapshotService"); - SnapshotFilter mockFilter = mock(SnapshotFilter.class, "MockSnapshotFilter"); - SnapshotOptions mockSnapshotOptions = mock(SnapshotOptions.class, "MockSnapshotOptions"); + + SnapshotFilter mockSnapshotFilterOne = mock(SnapshotFilter.class, "MockSnapshotFilterOne"); + SnapshotFilter mockSnapshotFilterTwo = mock(SnapshotFilter.class, "MockSnapshotFilterTwo"); + + SnapshotOptions mockSnapshotOptionsOne = mock(SnapshotOptions.class, "MockSnapshotOptionsOne"); + SnapshotOptions mockSnapshotOptionsTwo = mock(SnapshotOptions.class, "MockSnapshotOptionsTwo"); when(mockCache.getSnapshotService()).thenThrow(new UnsupportedOperationException("operation not supported")); when(mockRegion.getSnapshotService()).thenReturn(mockRegionSnapshotService); - when(mockRegionSnapshotService.createOptions()).thenReturn(mockSnapshotOptions); - when(mockSnapshotOptions.setFilter(any(SnapshotFilter.class))).thenReturn(mockSnapshotOptions); + when(mockRegionSnapshotService.createOptions()).thenReturn(mockSnapshotOptionsOne) + .thenReturn(mockSnapshotOptionsTwo); + when(mockSnapshotOptionsOne.setFilter(eq(mockSnapshotFilterOne))).thenReturn(mockSnapshotOptionsOne); + when(mockSnapshotOptionsTwo.setFilter(eq(mockSnapshotFilterTwo))).thenReturn(mockSnapshotOptionsTwo); - File snapshot = File.createTempFile("snapshot", "dat"); + File snapshotDatTwo = File.createTempFile("snapshot-2", "dat"); - snapshot.deleteOnExit(); + snapshotDatTwo.deleteOnExit(); - SnapshotServiceFactoryBean.SnapshotMetadata[] expectedImports = toArray( - newSnapshotMetadata(snapshot, mockFilter)); + SnapshotMetadata[] expectedImports = toArray(newSnapshotMetadata(snapshotDat, mockSnapshotFilterOne), + newSnapshotMetadata(snapshotDatTwo, mockSnapshotFilterTwo)); - SnapshotServiceFactoryBean factoryBean = new SnapshotServiceFactoryBean(); + SnapshotServiceFactoryBean factoryBean = new SnapshotServiceFactoryBean(); factoryBean.setCache(mockCache); factoryBean.setImports(expectedImports); factoryBean.setRegion(mockRegion); assertThat(factoryBean.getObject(), is(nullValue())); - assertThat((Class) factoryBean.getObjectType(), - is(equalTo(SnapshotServiceFactoryBean.SnapshotServiceAdapter.class))); + assertThat((Class) factoryBean.getObjectType(), + is(equalTo(SnapshotServiceAdapter.class))); factoryBean.afterPropertiesSet(); - assertThat(factoryBean.getObject(), - is(instanceOf(SnapshotServiceFactoryBean.RegionSnapshotServiceAdapter.class))); - assertThat((Class) factoryBean.getObjectType(), - is(equalTo(SnapshotServiceFactoryBean.RegionSnapshotServiceAdapter.class))); + assertThat(factoryBean.getObject(), is(instanceOf(RegionSnapshotServiceAdapter.class))); + assertThat((Class) factoryBean.getObjectType(), + is(equalTo(RegionSnapshotServiceAdapter.class))); verify(mockCache, never()).getSnapshotService(); verify(mockRegion, times(1)).getSnapshotService(); - verify(mockRegionSnapshotService, times(1)).createOptions(); - verify(mockRegionSnapshotService, times(1)).load(eq(snapshot), - eq(SnapshotOptions.SnapshotFormat.GEMFIRE), eq(mockSnapshotOptions)); - verify(mockSnapshotOptions, times(1)).setFilter(eq(mockFilter)); + verify(mockRegionSnapshotService, times(2)).createOptions(); + verify(mockRegionSnapshotService, times(1)).load(eq(snapshotDat), eq(SnapshotFormat.GEMFIRE), + eq(mockSnapshotOptionsOne)); + verify(mockRegionSnapshotService, times(1)).load(eq(snapshotDatTwo), eq(SnapshotFormat.GEMFIRE), + eq(mockSnapshotOptionsTwo)); + verify(mockSnapshotOptionsOne, times(1)).setFilter(eq(mockSnapshotFilterOne)); + verify(mockSnapshotOptionsTwo, times(1)).setFilter(eq(mockSnapshotFilterTwo)); + } + + @Test + public void exportCacheSnapshotOnDestroy() throws Exception { + Cache mockCache = mock(Cache.class, "MockCache"); + + CacheSnapshotService mockCacheSnapshotService = mock(CacheSnapshotService.class, "MockCacheSnapshotService"); + + SnapshotFilter mockSnapshotFilterOne = mock(SnapshotFilter.class, "MockSnapshotFilterOne"); + SnapshotFilter mockSnapshotFilterTwo = mock(SnapshotFilter.class, "MockSnapshotFilterTwo"); + + SnapshotOptions mockSnapshotOptionsOne = mock(SnapshotOptions.class, "MockSnapshotOptionsOne"); + SnapshotOptions mockSnapshotOptionsTwo = mock(SnapshotOptions.class, "MockSnapshotOptionsTwo"); + + when(mockCache.getSnapshotService()).thenReturn(mockCacheSnapshotService); + when(mockCacheSnapshotService.createOptions()).thenReturn(mockSnapshotOptionsOne) + .thenReturn(mockSnapshotOptionsTwo); + when(mockSnapshotOptionsOne.setFilter(eq(mockSnapshotFilterOne))).thenReturn(mockSnapshotOptionsOne); + when(mockSnapshotOptionsTwo.setFilter(eq(mockSnapshotFilterTwo))).thenReturn(mockSnapshotOptionsTwo); + + SnapshotMetadata[] expectedExports = toArray(newSnapshotMetadata(mockSnapshotFilterOne), + newSnapshotMetadata(mockSnapshotFilterTwo)); + + SnapshotServiceFactoryBean factoryBean = new SnapshotServiceFactoryBean(); + + factoryBean.setCache(mockCache); + factoryBean.setExports(expectedExports); + factoryBean.setImports(null); + factoryBean.setRegion(null); + factoryBean.afterPropertiesSet(); + factoryBean.destroy(); + + assertThat(factoryBean.getObject(), is(instanceOf(CacheSnapshotServiceAdapter.class))); + + verify(mockCache, times(1)).getSnapshotService(); + verify(mockCacheSnapshotService, times(2)).createOptions(); + verify(mockCacheSnapshotService, times(1)).save(eq(expectedExports[0].getLocation()), + eq(expectedExports[0].getFormat()), eq(mockSnapshotOptionsOne)); + verify(mockCacheSnapshotService, times(1)).save(eq(expectedExports[1].getLocation()), + eq(expectedExports[1].getFormat()), eq(mockSnapshotOptionsTwo)); + verify(mockSnapshotOptionsOne, times(1)).setFilter(eq(mockSnapshotFilterOne)); + verify(mockSnapshotOptionsTwo, times(1)).setFilter(eq(mockSnapshotFilterTwo)); + } + + @Test + public void exportRegionSnapshotOnDestroy() throws Exception { + Cache mockCache = mock(Cache.class, "MockCache"); + + Region mockRegion = mock(Region.class, "MockRegion"); + + RegionSnapshotService mockRegionSnapshotService = mock(RegionSnapshotService.class, "MockRegionSnapshotService"); + + SnapshotFilter mockSnapshotFilterOne = mock(SnapshotFilter.class, "MockSnapshotFilterOne"); + SnapshotFilter mockSnapshotFilterTwo = mock(SnapshotFilter.class, "MockSnapshotFilterTwo"); + + SnapshotOptions mockSnapshotOptionsOne = mock(SnapshotOptions.class, "MockSnapshotOptionsOne"); + SnapshotOptions mockSnapshotOptionsTwo = mock(SnapshotOptions.class, "MockSnapshotOptionsTwo"); + + when(mockCache.getSnapshotService()).thenThrow(new UnsupportedOperationException("operation not supported")); + when(mockRegion.getSnapshotService()).thenReturn(mockRegionSnapshotService); + when(mockRegionSnapshotService.createOptions()).thenReturn(mockSnapshotOptionsOne) + .thenReturn(mockSnapshotOptionsTwo); + when(mockSnapshotOptionsOne.setFilter(eq(mockSnapshotFilterOne))).thenReturn(mockSnapshotOptionsOne); + when(mockSnapshotOptionsTwo.setFilter(eq(mockSnapshotFilterTwo))).thenReturn(mockSnapshotOptionsTwo); + + SnapshotMetadata[] expectedExports = toArray(newSnapshotMetadata(mockSnapshotFilterOne), + newSnapshotMetadata(mockSnapshotFilterTwo)); + + SnapshotServiceFactoryBean factoryBean = new SnapshotServiceFactoryBean(); + + factoryBean.setCache(mockCache); + factoryBean.setExports(expectedExports); + factoryBean.setImports(null); + factoryBean.setRegion(mockRegion); + factoryBean.afterPropertiesSet(); + factoryBean.destroy(); + + assertThat(factoryBean.getObject(), is(instanceOf(RegionSnapshotServiceAdapter.class))); + + verify(mockCache, never()).getSnapshotService(); + verify(mockRegion, times(1)).getSnapshotService(); + verify(mockRegionSnapshotService, times(2)).createOptions(); + verify(mockRegionSnapshotService, times(1)).save(eq(expectedExports[0].getLocation()), + eq(expectedExports[0].getFormat()), eq(mockSnapshotOptionsOne)); + verify(mockRegionSnapshotService, times(1)).save(eq(expectedExports[1].getLocation()), + eq(expectedExports[1].getFormat()), eq(mockSnapshotOptionsTwo)); + verify(mockSnapshotOptionsOne, times(1)).setFilter(eq(mockSnapshotFilterOne)); + verify(mockSnapshotOptionsTwo, times(1)).setFilter(eq(mockSnapshotFilterTwo)); + } + + @Test(expected = ImportSnapshotException.class) + public void loadCacheSnapshotWithDirectoryAndFormatHandlesExceptionAppropriately() throws Exception { + CacheSnapshotService mockCacheSnapshotService = mock(CacheSnapshotService.class, "MockCacheSnapshotService"); + + doThrow(new IOException("TEST")).when(mockCacheSnapshotService).load(any(File.class), any(SnapshotFormat.class)); + + CacheSnapshotServiceAdapter adapter = new CacheSnapshotServiceAdapter(mockCacheSnapshotService); + + assertThat(adapter.getSnapshotService(), is(equalTo(mockCacheSnapshotService))); + + try { + adapter.load(FileSystemUtils.WORKING_DIRECTORY, SnapshotFormat.GEMFIRE); + } + catch (ImportSnapshotException expected) { + assertThat(expected.getMessage(), is(equalTo(String.format( + "Failed to load snapshots from directory (%1$s) in format (GEMFIRE)", + FileSystemUtils.WORKING_DIRECTORY)))); + assertThat(expected.getCause(), is(instanceOf(IOException.class))); + assertThat(expected.getCause().getMessage(), is(equalTo("TEST"))); + throw expected; + } + finally { + verify(mockCacheSnapshotService, times(1)).load(eq(FileSystemUtils.WORKING_DIRECTORY), + eq(SnapshotFormat.GEMFIRE)); + } + } + + @Test(expected = ImportSnapshotException.class) + public void loadCacheSnapshotWithFormatOptionsAndSnapshotFilesHandlesExceptionAppropriately() throws Exception { + SnapshotOptions mockSnapshotOptions = mock(SnapshotOptions.class, "MockSnapshotOptions"); + + CacheSnapshotService mockCacheSnapshotService = mock(CacheSnapshotService.class, "MockCacheSnapshotService"); + + doThrow(new ClassCastException("TEST")).when(mockCacheSnapshotService).load(any(File[].class), + any(SnapshotFormat.class), any(SnapshotOptions.class)); + + CacheSnapshotServiceAdapter adapter = new CacheSnapshotServiceAdapter(mockCacheSnapshotService); + + assertThat(adapter.getSnapshotService(), is(equalTo(mockCacheSnapshotService))); + + try { + adapter.load(SnapshotFormat.GEMFIRE, mockSnapshotOptions, snapshotDat); + } + catch (ImportSnapshotException expected) { + assertThat(expected.getMessage(), is(equalTo(String.format( + "Failed to load snapshots (%1$s) in format (GEMFIRE) using options (%2$s)", + Arrays.toString(new File[] { snapshotDat }), mockSnapshotOptions)))); + assertThat(expected.getCause(), is(instanceOf(ClassCastException.class))); + assertThat(expected.getCause().getMessage(), is(equalTo("TEST"))); + throw expected; + } + finally { + verify(mockCacheSnapshotService, times(1)).load(eq(new File[] { snapshotDat }), + eq(SnapshotFormat.GEMFIRE), Matchers.isA(SnapshotOptions.class)); + } + } + + @Test(expected = ExportSnapshotException.class) + public void saveCacheSnapshotWithDirectoryAndFormatHandlesExceptionAppropriately() throws Exception { + CacheSnapshotService mockCacheSnapshotService = mock(CacheSnapshotService.class, "MockCacheSnapshotService"); + + doThrow(new IOException("TEST")).when(mockCacheSnapshotService).save(any(File.class), any(SnapshotFormat.class)); + + CacheSnapshotServiceAdapter adapter = new CacheSnapshotServiceAdapter(mockCacheSnapshotService); + + assertThat(adapter.getSnapshotService(), is(equalTo(mockCacheSnapshotService))); + + try { + adapter.save(FileSystemUtils.WORKING_DIRECTORY, SnapshotFormat.GEMFIRE); + } + catch (ExportSnapshotException expected) { + assertThat(expected.getMessage(), is(equalTo(String.format( + "Failed to save snapshots to directory (%1$s) in format (GEMFIRE)", + FileSystemUtils.WORKING_DIRECTORY)))); + assertThat(expected.getCause(), is(instanceOf(IOException.class))); + assertThat(expected.getCause().getMessage(), is(equalTo("TEST"))); + throw expected; + } + finally { + verify(mockCacheSnapshotService, times(1)).save(eq(FileSystemUtils.WORKING_DIRECTORY), + eq(SnapshotFormat.GEMFIRE)); + } + } + + @Test(expected = ExportSnapshotException.class) + public void saveCacheSnapshotWithDirectoryFormatAndOptionsHandlesExceptionAppropriately() throws Exception { + SnapshotOptions mockSnapshotOptions = mock(SnapshotOptions.class, "MockSnapshotOptions"); + + CacheSnapshotService mockCacheSnapshotService = mock(CacheSnapshotService.class, "MockCacheSnapshotService"); + + doThrow(new ClassCastException("TEST")).when(mockCacheSnapshotService).save(any(File.class), + any(SnapshotFormat.class), any(SnapshotOptions.class)); + + CacheSnapshotServiceAdapter adapter = new CacheSnapshotServiceAdapter(mockCacheSnapshotService); + + assertThat(adapter.getSnapshotService(), is(equalTo(mockCacheSnapshotService))); + + try { + adapter.save(FileSystemUtils.USER_HOME, SnapshotFormat.GEMFIRE, mockSnapshotOptions); + } + catch (ExportSnapshotException expected) { + assertThat(expected.getMessage(), is(equalTo(String.format( + "Failed to save snapshots to directory (%1$s) in format (GEMFIRE) using options (%2$s)", + FileSystemUtils.USER_HOME, mockSnapshotOptions)))); + assertThat(expected.getCause(), is(instanceOf(ClassCastException.class))); + assertThat(expected.getCause().getMessage(), is(equalTo("TEST"))); + throw expected; + } + finally { + verify(mockCacheSnapshotService, times(1)).save(eq(FileSystemUtils.USER_HOME), eq(SnapshotFormat.GEMFIRE), + Matchers.isA(SnapshotOptions.class)); + } + } + + @Test(expected = ImportSnapshotException.class) + public void loadRegionSnapshotWithSnapshotFileAndFormatHandlesExceptionAppropriately() throws Exception { + RegionSnapshotService mockRegionSnapshotService = mock(RegionSnapshotService.class, "MockRegionSnapshotService"); + + doThrow(new IOException("TEST")).when(mockRegionSnapshotService).load(any(File.class), + any(SnapshotFormat.class)); + + RegionSnapshotServiceAdapter adapter = new RegionSnapshotServiceAdapter(mockRegionSnapshotService); + + assertThat(adapter.getSnapshotService(), is(equalTo(mockRegionSnapshotService))); + + try { + adapter.load(snapshotDat, SnapshotFormat.GEMFIRE); + } + catch (ImportSnapshotException expected) { + assertThat(expected.getMessage(), is(equalTo(String.format( + "Failed to load snapshot from file (%1$s) in format (GEMFIRE)", snapshotDat)))); + assertThat(expected.getCause(), is(instanceOf(IOException.class))); + assertThat(expected.getCause().getMessage(), is(equalTo("TEST"))); + throw expected; + } + finally { + verify(mockRegionSnapshotService, times(1)).load(eq(snapshotDat), eq(SnapshotFormat.GEMFIRE)); + } + } + + @Test(expected = ImportSnapshotException.class) + public void loadRegionSnapshotWithFormatOptionsAndSnapshotFilesHandlesExceptionAppropriately() throws Exception { + SnapshotOptions mockSnapshotOptions = mock(SnapshotOptions.class, "MockSnapshotOptions"); + + RegionSnapshotService mockRegionSnapshotService = mock(RegionSnapshotService.class, "MockRegionSnapshotService"); + + doThrow(new ClassCastException("TEST")).when(mockRegionSnapshotService).load( + any(File.class), any(SnapshotFormat.class), any(SnapshotOptions.class)); + + RegionSnapshotServiceAdapter adapter = new RegionSnapshotServiceAdapter(mockRegionSnapshotService); + + assertThat(adapter.getSnapshotService(), is(equalTo(mockRegionSnapshotService))); + + try { + adapter.load(SnapshotFormat.GEMFIRE, mockSnapshotOptions, snapshotDat); + } + catch (ImportSnapshotException expected) { + assertThat(expected.getMessage(), is(equalTo(String.format( + "Failed to load snapshots (%1$s) in format (GEMFIRE) using options (%2$s)", + Arrays.toString(new File[] { snapshotDat }), mockSnapshotOptions)))); + assertThat(expected.getCause(), is(instanceOf(ClassCastException.class))); + assertThat(expected.getCause().getMessage(), is(equalTo("TEST"))); + throw expected; + } + finally { + verify(mockRegionSnapshotService, times(1)).load(eq(snapshotDat), eq(SnapshotFormat.GEMFIRE), + eq(mockSnapshotOptions)); + } + } + + @Test(expected = ExportSnapshotException.class) + public void saveRegionSnapshotWithSnapshotFileAndFormatHandlesExceptionAppropriately() throws Exception { + RegionSnapshotService mockRegionSnapshotService = mock(RegionSnapshotService.class, "MockRegionSnapshotService"); + + doThrow(new IOException("TEST")).when(mockRegionSnapshotService).save(any(File.class), + any(SnapshotFormat.class)); + + RegionSnapshotServiceAdapter adapter = new RegionSnapshotServiceAdapter(mockRegionSnapshotService); + + assertThat(adapter.getSnapshotService(), is(equalTo(mockRegionSnapshotService))); + + try { + adapter.save(snapshotDat, SnapshotFormat.GEMFIRE); + } + catch (ExportSnapshotException expected) { + assertThat(expected.getMessage(), is(equalTo(String.format( + "Failed to save snapshot to file (%1$s) in format (GEMFIRE)", snapshotDat)))); + assertThat(expected.getCause(), is(instanceOf(IOException.class))); + assertThat(expected.getCause().getMessage(), is(equalTo("TEST"))); + throw expected; + } + finally { + verify(mockRegionSnapshotService, times(1)).save(eq(snapshotDat), eq(SnapshotFormat.GEMFIRE)); + } + } + + @Test(expected = ExportSnapshotException.class) + public void saveRegionSnapshotWithSnapshotFileFormatAndOptionsHandlesExceptionAppropriately() throws Exception { + SnapshotOptions mockSnapshotOptions = mock(SnapshotOptions.class, "MockSnapahotOptions"); + + RegionSnapshotService mockRegionSnapshotService = mock(RegionSnapshotService.class, "MockRegionSnapshotService"); + + doThrow(new ClassCastException("TEST")).when(mockRegionSnapshotService).save(any(File.class), + any(SnapshotFormat.class), any(SnapshotOptions.class)); + + RegionSnapshotServiceAdapter adapter = new RegionSnapshotServiceAdapter(mockRegionSnapshotService); + + assertThat(adapter.getSnapshotService(), is(equalTo(mockRegionSnapshotService))); + + try { + adapter.save(snapshotDat, SnapshotFormat.GEMFIRE, mockSnapshotOptions); + } + catch (ExportSnapshotException expected) { + assertThat(expected.getMessage(), is(equalTo(String.format( + "Failed to save snapshot to file (%1$s) in format (GEMFIRE) using options (%2$s)", + snapshotDat, mockSnapshotOptions)))); + assertThat(expected.getCause(), is(instanceOf(ClassCastException.class))); + assertThat(expected.getCause().getMessage(), is(equalTo("TEST"))); + throw expected; + } + finally { + verify(mockRegionSnapshotService, times(1)).save(eq(snapshotDat), eq(SnapshotFormat.GEMFIRE), + eq(mockSnapshotOptions)); + } } } diff --git a/src/test/java/org/springframework/data/gemfire/test/support/FileSystemUtils.java b/src/test/java/org/springframework/data/gemfire/test/support/FileSystemUtils.java index 523b30a6..20eac2d2 100644 --- a/src/test/java/org/springframework/data/gemfire/test/support/FileSystemUtils.java +++ b/src/test/java/org/springframework/data/gemfire/test/support/FileSystemUtils.java @@ -39,6 +39,7 @@ import com.gemstone.gemfire.management.internal.cli.util.spring.Assert; public abstract class FileSystemUtils extends FileUtils { public static final File JAVA_HOME = new File(System.getProperty("java.home")); + public static final File JAVA_EXE = new File(new File(JAVA_HOME, "bin"), "java"); public static final File USER_HOME = new File(System.getProperty("user.home")); public static final File WORKING_DIRECTORY = new File(System.getProperty("user.dir")); @@ -70,7 +71,7 @@ public abstract class FileSystemUtils extends FileUtils { public static File[] listFiles(final File directory, final FileFilter fileFilter) { Assert.isTrue(directory != null && directory.isDirectory(), String.format( - "The File (%1$s) does not refer to a valid directory!", directory)); + "File (%1$s) does not refer to a valid directory", directory)); List results = new ArrayList(); @@ -86,11 +87,11 @@ public abstract class FileSystemUtils extends FileUtils { return results.toArray(new File[results.size()]); } - private static File[] safeListFiles(final File directory) { + public static File[] safeListFiles(final File directory) { return safeListFiles(directory, AllFiles.INSTANCE); } - private static File[] safeListFiles(final File directory, final FileFilter fileFilter) { + public static File[] safeListFiles(final File directory, final FileFilter fileFilter) { File[] files = (directory != null ? directory.listFiles(fileFilter) : new File[0]); return (files != null ? files : new File[0]); } @@ -105,4 +106,24 @@ public abstract class FileSystemUtils extends FileUtils { } } + public static final class FileOnlyFilter implements FileFilter { + + public static final FileOnlyFilter INSTANCE = new FileOnlyFilter(); + + @Override + public boolean accept(final File pathname) { + return (pathname != null && pathname.isFile()); + } + } + + public static final class DirectoryOnlyFilter implements FileFilter { + + public static final DirectoryOnlyFilter INSTANCE = new DirectoryOnlyFilter(); + + @Override + public boolean accept(final File pathname) { + return (pathname != null && pathname.isDirectory()); + } + } + }