Initial spring-data-tests-4-gemfire project commit.
This commit is contained in:
@@ -0,0 +1,328 @@
|
||||
/*
|
||||
* Copyright 2016 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.tests.integration;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.springframework.data.gemfire.tests.process.ProcessExecutor.launch;
|
||||
import static org.springframework.data.gemfire.util.ArrayUtils.asArray;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import org.apache.geode.cache.server.CacheServer;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.data.gemfire.tests.process.ProcessWrapper;
|
||||
import org.springframework.data.gemfire.tests.util.FileSystemUtils;
|
||||
import org.springframework.data.gemfire.tests.util.FileUtils;
|
||||
import org.springframework.data.gemfire.tests.util.SocketUtils;
|
||||
import org.springframework.data.gemfire.tests.util.ThreadUtils;
|
||||
import org.springframework.data.gemfire.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* The {@link ClientServerIntegrationTestsSupport} class is a abstract base class encapsulating common functionality
|
||||
* to support the implementation of GemFire client/server tests.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see java.io.File
|
||||
* @see java.net.ServerSocket
|
||||
* @see java.net.Socket
|
||||
* @see java.time.LocalDateTime
|
||||
* @see org.apache.geode.cache.server.CacheServer
|
||||
* @see org.springframework.context.annotation.AnnotationConfigApplicationContext
|
||||
* @see org.springframework.data.gemfire.tests.process.ProcessExecutor
|
||||
* @see org.springframework.data.gemfire.tests.process.ProcessWrapper
|
||||
* @see SocketUtils
|
||||
* @see ThreadUtils
|
||||
* @since 0.0.1
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class ClientServerIntegrationTestsSupport {
|
||||
|
||||
protected static final long DEFAULT_WAIT_DURATION = TimeUnit.SECONDS.toMillis(30);
|
||||
protected static final long DEFAULT_WAIT_INTERVAL = 500L; // milliseconds
|
||||
|
||||
protected static final String DEBUG_ENDPOINT = "-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005";
|
||||
protected static final String DEBUGGING_ENABLED_PROPERTY = "spring.data.gemfire.debugging.enabled";
|
||||
protected static final String DEFAULT_HOSTNAME = "localhost";
|
||||
protected static final String DIRECTORY_DELETE_ON_EXIT_PROPERTY = "spring.data.gemfire.directory.delete-on-exit";
|
||||
protected static final String GEMFIRE_CACHE_SERVER_PORT_PROPERTY = "spring.data.gemfire.cache.server.port";
|
||||
protected static final String GEMFIRE_LOG_FILE = "gemfire-server.log";
|
||||
protected static final String GEMFIRE_LOG_FILE_PROPERTY = "spring.data.gemfire.log.file";
|
||||
protected static final String GEMFIRE_LOG_LEVEL = "warning";
|
||||
protected static final String GEMFIRE_LOG_LEVEL_PROPERTY = "spring.data.gemfire.log.level";
|
||||
protected static final String PROCESS_RUN_MANUAL_PROPERTY = "spring.data.gemfire.process.run-manual";
|
||||
protected static final String SYSTEM_PROPERTIES_LOG_FILE = "system-properties.log";
|
||||
protected static final String TEST_GEMFIRE_LOG_LEVEL = "warning";
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static String asApplicationName(Class<?> type) {
|
||||
return type.getSimpleName();
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static String asDirectoryName(Class<?> type) {
|
||||
return String.format("%1$s-%2$s", asApplicationName(type),
|
||||
LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd-hh-mm-ss")));
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static File createDirectory(String pathname) {
|
||||
return createDirectory(new File(pathname));
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static File createDirectory(File directory) {
|
||||
|
||||
assertThat(directory.isDirectory() || directory.mkdirs())
|
||||
.as(String.format("Failed to create directory [%s]", directory)).isTrue();
|
||||
|
||||
if (isDeleteDirectoryOnExit()) {
|
||||
directory.deleteOnExit();
|
||||
}
|
||||
|
||||
return directory;
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static int findAvailablePort() throws IOException {
|
||||
|
||||
ServerSocket serverSocket = null;
|
||||
|
||||
try {
|
||||
serverSocket = new ServerSocket(0);
|
||||
return serverSocket.getLocalPort();
|
||||
}
|
||||
finally {
|
||||
SocketUtils.close(serverSocket);
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static String getClassNameAsPath(Class type) {
|
||||
return type.getName().replaceAll("\\.", "/");
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static String getClassNameAsPath(Object obj) {
|
||||
return getClassNameAsPath(obj.getClass());
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static String getPackageNameAsPath(Class type) {
|
||||
return type.getPackage().getName().replaceAll("\\.", "/");
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static String getPackageNameAsPath(Object obj) {
|
||||
return getPackageNameAsPath(obj.getClass());
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static String getContextXmlFileLocation(Class type) {
|
||||
return getClassNameAsPath(type).concat("-context.xml");
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static String getServerContextXmlFileLocation(Class type) {
|
||||
return getClassNameAsPath(type).concat("-server-context.xml");
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static boolean isDeleteDirectoryOnExit() {
|
||||
return Boolean.valueOf(System.getProperty(DIRECTORY_DELETE_ON_EXIT_PROPERTY, Boolean.TRUE.toString()));
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static int intValue(Number number) {
|
||||
return (number != null ? number.intValue() : 0);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static String logFile() {
|
||||
return logFile(GEMFIRE_LOG_FILE);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static String logFile(String defaultLogFilePathname) {
|
||||
return System.getProperty(GEMFIRE_LOG_FILE_PROPERTY, defaultLogFilePathname);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static String logLevel() {
|
||||
return logLevel(GEMFIRE_LOG_LEVEL);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static String logLevel(String defaultLogLevel) {
|
||||
return System.getProperty(GEMFIRE_LOG_LEVEL_PROPERTY, defaultLogLevel);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static void logSystemProperties() throws IOException {
|
||||
FileUtils.write(new File(SYSTEM_PROPERTIES_LOG_FILE),
|
||||
String.format("%s", CollectionUtils.toString(System.getProperties())));
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static ProcessWrapper run(Class<?> type, String... arguments) throws IOException {
|
||||
return run(createDirectory(asDirectoryName(type)), type, arguments);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static ProcessWrapper run(File workingDirectory, Class<?> type, String... arguments) throws IOException {
|
||||
return (isProcessRunAuto() ? launch(createDirectory(workingDirectory), type, arguments) : null);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static ProcessWrapper run(String classpath, Class<?> type, String... arguments) throws IOException {
|
||||
return run(createDirectory(asDirectoryName(type)), classpath, type, arguments);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static ProcessWrapper run(File workingDirectory, String classpath, Class<?> type, String... arguments)
|
||||
throws IOException {
|
||||
|
||||
return (isProcessRunAuto() ? launch(createDirectory(workingDirectory), classpath, type, arguments) : null);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static boolean isProcessRunAuto() {
|
||||
return !isProcessRunManual();
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static boolean isProcessRunManual() {
|
||||
return Boolean.getBoolean(PROCESS_RUN_MANUAL_PROPERTY);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static AnnotationConfigApplicationContext runSpringApplication(Class<?> annotatedClass, String... args) {
|
||||
return runSpringApplication(asArray(annotatedClass), args);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static AnnotationConfigApplicationContext runSpringApplication(Class<?>[] annotatedClasses,
|
||||
String... args) {
|
||||
|
||||
AnnotationConfigApplicationContext applicationContext =
|
||||
new AnnotationConfigApplicationContext(annotatedClasses);
|
||||
|
||||
applicationContext.registerShutdownHook();
|
||||
|
||||
return applicationContext;
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static boolean stop(ProcessWrapper process) {
|
||||
return stop(process, DEFAULT_WAIT_DURATION);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static boolean stop(ProcessWrapper process, long duration) {
|
||||
|
||||
return Optional.ofNullable(process)
|
||||
.map(it -> {
|
||||
|
||||
it.stop(duration);
|
||||
|
||||
if (it.isNotRunning() && isDeleteDirectoryOnExit()) {
|
||||
FileSystemUtils.deleteRecursive(it.getWorkingDirectory());
|
||||
}
|
||||
|
||||
return it.isRunning();
|
||||
})
|
||||
.orElse(true);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static boolean waitForCacheServerToStart(CacheServer cacheServer) {
|
||||
return waitForServerToStart(cacheServer.getBindAddress(), cacheServer.getPort(), DEFAULT_WAIT_DURATION);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static boolean waitForCacheServerToStart(CacheServer cacheServer, long duration) {
|
||||
return waitForServerToStart(cacheServer.getBindAddress(), cacheServer.getPort(), duration);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static boolean waitForServerToStart(String host, int port) {
|
||||
return waitForServerToStart(host, port, DEFAULT_WAIT_DURATION);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static boolean waitForServerToStart(final String host, final int port, long duration) {
|
||||
|
||||
return ThreadUtils.timedWait(duration, DEFAULT_WAIT_INTERVAL, new ThreadUtils.WaitCondition() {
|
||||
|
||||
AtomicBoolean connected = new AtomicBoolean(false);
|
||||
|
||||
public boolean waiting() {
|
||||
|
||||
Socket socket = null;
|
||||
|
||||
try {
|
||||
if (!connected.get()) {
|
||||
socket = new Socket(host, port);
|
||||
connected.set(true);
|
||||
}
|
||||
}
|
||||
catch (IOException ignore) {
|
||||
}
|
||||
finally {
|
||||
SocketUtils.close(socket);
|
||||
}
|
||||
|
||||
return !connected.get();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected static boolean waitOn(Condition condition) {
|
||||
return waitOn(condition, DEFAULT_WAIT_DURATION);
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
protected static boolean waitOn(Condition condition, long duration) {
|
||||
|
||||
long timeout = (System.currentTimeMillis() + duration);
|
||||
|
||||
try {
|
||||
while (!condition.evaluate() && System.currentTimeMillis() < timeout) {
|
||||
synchronized (condition) {
|
||||
TimeUnit.MILLISECONDS.timedWait(condition, DEFAULT_WAIT_INTERVAL);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
|
||||
return condition.evaluate();
|
||||
}
|
||||
|
||||
protected interface Condition {
|
||||
boolean evaluate();
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,189 @@
|
||||
/*
|
||||
* Copyright 2017 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.tests.mock;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.mockito.invocation.InvocationOnMock;
|
||||
import org.mockito.stubbing.Answer;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* The {@link MockObjectsSupport} class is an abstract base class encapsulating common operations and utilities
|
||||
* used in mocking using Mockito.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see org.mockito.invocation.InvocationOnMock
|
||||
* @see org.mockito.stubbing.Answer
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public abstract class MockObjectsSupport {
|
||||
|
||||
private static final AtomicLong mockObjectIdentifier = new AtomicLong(0L);
|
||||
|
||||
private static final String DEFAULT_MOCK_OBJECT_NAME = "MockObject";
|
||||
|
||||
public static String mockObjectIdentifier() {
|
||||
return mockObjectIdentifier(DEFAULT_MOCK_OBJECT_NAME);
|
||||
}
|
||||
|
||||
public static String mockObjectIdentifier(String mockObjectName) {
|
||||
return String.format("%s%d", Optional.ofNullable(mockObjectName).filter(StringUtils::hasText)
|
||||
.orElse(DEFAULT_MOCK_OBJECT_NAME), mockObjectIdentifier.incrementAndGet());
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static Answer<Boolean> newGetter(AtomicBoolean returnValue) {
|
||||
return invocation -> returnValue.get();
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static Answer<Integer> newGetter(AtomicInteger returnValue) {
|
||||
return invocation -> returnValue.get();
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static Answer<Long> newGetter(AtomicLong returnValue) {
|
||||
return invocation -> returnValue.get();
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static <R> Answer<R> newGetter(AtomicReference<R> returnValue) {
|
||||
return invocation -> returnValue.get();
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static <R, S> Answer<S> newGetter(AtomicReference<R> returnValue, Function<R, S> converter) {
|
||||
return invocation -> converter.apply(returnValue.get());
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static <R> Answer<R> newGetter(Supplier<R> returnValue) {
|
||||
return invocation -> returnValue.get();
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static <R, S> Answer<S> newGetter(Supplier<R> returnValue, Function<R, S> converter) {
|
||||
return invocation -> converter.apply(returnValue.get());
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static <E, C extends Collection<E>, R> Answer<R> newAdder(C collection, R returnValue) {
|
||||
return invocation -> {
|
||||
collection.add(invocation.getArgument(0));
|
||||
return returnValue;
|
||||
};
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static <R> Answer<R> newSetter(AtomicBoolean argument, R returnValue) {
|
||||
return invocation -> {
|
||||
argument.set(invocation.getArgument(0));
|
||||
return returnValue;
|
||||
};
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static <R> Answer<R> newSetter(AtomicBoolean argument, Boolean value, R returnValue) {
|
||||
return invocation -> {
|
||||
argument.set(value);
|
||||
return returnValue;
|
||||
};
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static <R> Answer<R> newSetter(AtomicInteger argument, R returnValue) {
|
||||
return invocation -> {
|
||||
argument.set(invocation.getArgument(0));
|
||||
return returnValue;
|
||||
};
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static <R> Answer<R> newSetter(AtomicInteger argument, Integer value, R returnValue) {
|
||||
return invocation -> {
|
||||
argument.set(value);
|
||||
return returnValue;
|
||||
};
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static <R> Answer<R> newSetter(AtomicLong argument, R returnValue) {
|
||||
return invocation -> {
|
||||
argument.set(invocation.getArgument(0));
|
||||
return returnValue;
|
||||
};
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static <R> Answer<R> newSetter(AtomicLong argument, Long value, R returnValue) {
|
||||
return invocation -> {
|
||||
argument.set(value);
|
||||
return returnValue;
|
||||
};
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static <T, R> Answer<R> newSetter(AtomicReference<T> argument, R returnValue) {
|
||||
return invocation -> {
|
||||
argument.set(invocation.getArgument(0));
|
||||
return returnValue;
|
||||
};
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static <T, R> Answer<R> newSetter(AtomicReference<T> argument, T value, R returnValue) {
|
||||
return invocation -> {
|
||||
argument.set(value);
|
||||
return returnValue;
|
||||
};
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static <T, R> Answer<R> newSetter(AtomicReference<T> argument, Function<?, T> converter, R returnValue) {
|
||||
return invocation -> {
|
||||
argument.set(converter.apply(invocation.getArgument(0)));
|
||||
return returnValue;
|
||||
};
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static <K, V, R> Answer<R> newSetter(Map<K, V> argument, R returnValue) {
|
||||
return invocation -> {
|
||||
argument.put(invocation.getArgument(0), invocation.getArgument(1));
|
||||
return returnValue;
|
||||
};
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static <T> Answer<Void> newVoidAnswer(Consumer<InvocationOnMock> methodInvocation) {
|
||||
return invocation -> {
|
||||
methodInvocation.accept(invocation);
|
||||
return null;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright 2017 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.tests.mock.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.apache.geode.cache.GemFireCache;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
/**
|
||||
* The {@link EnableGemFireMockObjects} annotation enables mocking of GemFire Objects in Unit Tests.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see Documented
|
||||
* @see Inherited
|
||||
* @see Retention
|
||||
* @see Target
|
||||
* @see org.apache.geode.cache.GemFireCache
|
||||
* @see org.springframework.context.annotation.Import
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
@Documented
|
||||
@Import(GemFireMockObjectsConfiguration.class)
|
||||
@SuppressWarnings("unused")
|
||||
public @interface EnableGemFireMockObjects {
|
||||
|
||||
/**
|
||||
* Determines whether the mock {@link GemFireCache} created for Unit Tests is a Singleton.
|
||||
*
|
||||
* Defaults to {@literal false}.
|
||||
*
|
||||
* @return a boolean value indicating whether the mock {@link GemFireCache} created for Unit Tests is a Singleton.
|
||||
*/
|
||||
boolean useSingletonCache() default false;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright 2017 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.tests.mock.annotation;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.ImportAware;
|
||||
import org.springframework.context.event.ContextClosedEvent;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.core.annotation.AnnotationAttributes;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.data.gemfire.tests.mock.MockGemFireObjectsSupport;
|
||||
import org.springframework.data.gemfire.tests.mock.config.MockGemFireObjectsBeanPostProcessor;
|
||||
|
||||
/**
|
||||
* The {@link GemFireMockObjectsConfiguration} class is a Spring {@link Configuration @Configuration} class
|
||||
* containing bean definitions to configure GemFire Object mocking.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see Annotation
|
||||
* @see org.springframework.beans.factory.config.BeanPostProcessor
|
||||
* @see org.springframework.context.annotation.Bean
|
||||
* @see org.springframework.context.annotation.Configuration
|
||||
* @see org.springframework.context.annotation.ImportAware
|
||||
* @see org.springframework.core.annotation.AnnotationAttributes
|
||||
* @see org.springframework.core.type.AnnotationMetadata
|
||||
* @see org.springframework.data.gemfire.test.mock.config.MockGemFireObjectsBeanPostProcessor
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
@Configuration
|
||||
public class GemFireMockObjectsConfiguration implements ImportAware {
|
||||
|
||||
private boolean useSingletonCache = false;
|
||||
|
||||
@Override
|
||||
public void setImportMetadata(AnnotationMetadata importingClassMetadata) {
|
||||
|
||||
if (isAnnotationPresent(importingClassMetadata)) {
|
||||
|
||||
AnnotationAttributes enableGemFireMockingAttributes = getAnnotationAttributes(importingClassMetadata);
|
||||
|
||||
this.useSingletonCache = enableGemFireMockingAttributes.getBoolean("useSingletonCache");
|
||||
}
|
||||
}
|
||||
|
||||
private Class<? extends Annotation> getAnnotationType() {
|
||||
return EnableGemFireMockObjects.class;
|
||||
}
|
||||
|
||||
private boolean isAnnotationPresent(AnnotationMetadata importingClassMetadata) {
|
||||
return isAnnotationPresent(importingClassMetadata, getAnnotationType());
|
||||
}
|
||||
|
||||
private boolean isAnnotationPresent(AnnotationMetadata importingClassMetadata,
|
||||
Class<? extends Annotation> annotationType) {
|
||||
|
||||
return importingClassMetadata.hasAnnotation(annotationType.getName());
|
||||
}
|
||||
|
||||
private AnnotationAttributes getAnnotationAttributes(AnnotationMetadata importingClassMetadata) {
|
||||
return getAnnotationAttributes(importingClassMetadata, getAnnotationType());
|
||||
}
|
||||
|
||||
private AnnotationAttributes getAnnotationAttributes(AnnotationMetadata importingClassMetadata,
|
||||
Class<? extends Annotation> annotationType) {
|
||||
|
||||
return AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(annotationType.getName()));
|
||||
}
|
||||
|
||||
@Bean
|
||||
public BeanPostProcessor mockGemFireObjectsBeanPostProcessor() {
|
||||
return MockGemFireObjectsBeanPostProcessor.newInstance(this.useSingletonCache);
|
||||
}
|
||||
|
||||
@EventListener
|
||||
public void releaseMockResources(ContextClosedEvent event) {
|
||||
MockGemFireObjectsSupport.destroy();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright 2017 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.tests.mock.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.data.gemfire.tests.mock.context.MockGemFireObjectsApplicationContextInitializer;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
/**
|
||||
* The {@link GemFireUnitTest} annotation marks a test class as a GemFire Unit Test
|
||||
* with GemFire Object mocking enabled.
|
||||
*
|
||||
* Additionally, this annotation enables Spring's {@link SpringRunner} JUnit Runner implementation
|
||||
* using JUnit's {@link RunWith} annotation.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see Documented
|
||||
* @see Inherited
|
||||
* @see Retention
|
||||
* @see Target
|
||||
* @see org.junit.runner.RunWith
|
||||
* @see org.springframework.data.gemfire.test.mock.context.MockGemFireObjectsApplicationContextInitializer
|
||||
* @see org.springframework.test.context.ContextConfiguration
|
||||
* @see org.springframework.test.context.junit4.SpringRunner
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
@Documented
|
||||
@RunWith(SpringRunner.class)
|
||||
@ContextConfiguration(initializers = MockGemFireObjectsApplicationContextInitializer.class)
|
||||
@SuppressWarnings("unused")
|
||||
public @interface GemFireUnitTest {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
/*
|
||||
* Copyright 2017 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.tests.mock.config;
|
||||
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.apache.geode.cache.CacheFactory;
|
||||
import org.apache.geode.cache.GemFireCache;
|
||||
import org.apache.geode.cache.client.ClientCacheFactory;
|
||||
import org.apache.geode.cache.client.PoolFactory;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.data.gemfire.CacheFactoryBean;
|
||||
import org.springframework.data.gemfire.client.ClientCacheFactoryBean;
|
||||
import org.springframework.data.gemfire.client.PoolFactoryBean;
|
||||
import org.springframework.data.gemfire.tests.mock.MockGemFireObjectsSupport;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* The {@link MockGemFireObjectsBeanPostProcessor} class is a Spring {@link BeanPostProcessor} that applies
|
||||
* mocks and spies to Spring Data GemFire / Spring Data Geode and Pivotal GemFire / Apache Geode objects
|
||||
* and components.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see org.apache.geode.cache.CacheFactory
|
||||
* @see org.apache.geode.cache.client.ClientCacheFactory
|
||||
* @see org.apache.geode.cache.client.PoolFactory
|
||||
* @see org.springframework.beans.factory.config.BeanPostProcessor
|
||||
* @see org.springframework.data.gemfire.CacheFactoryBean
|
||||
* @see org.springframework.data.gemfire.client.ClientCacheFactoryBean
|
||||
* @see org.springframework.data.gemfire.client.PoolFactoryBean
|
||||
* @see org.springframework.data.gemfire.test.mock.MockGemFireObjectsSupport
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public class MockGemFireObjectsBeanPostProcessor implements BeanPostProcessor {
|
||||
|
||||
private static final boolean DEFAULT_USE_SINGLETON_CACHE = false;
|
||||
|
||||
private static final String GEMFIRE_PROPERTIES_BEAN_NAME = "gemfireProperties";
|
||||
|
||||
private volatile boolean useSingletonCache;
|
||||
|
||||
private final AtomicReference<Properties> gemfireProperties = new AtomicReference<>(new Properties());
|
||||
|
||||
public static MockGemFireObjectsBeanPostProcessor newInstance() {
|
||||
return newInstance(DEFAULT_USE_SINGLETON_CACHE);
|
||||
}
|
||||
|
||||
public static MockGemFireObjectsBeanPostProcessor newInstance(boolean useSingletonCache) {
|
||||
|
||||
MockGemFireObjectsBeanPostProcessor beanPostProcessor = new MockGemFireObjectsBeanPostProcessor();
|
||||
|
||||
beanPostProcessor.useSingletonCache = useSingletonCache;
|
||||
|
||||
return beanPostProcessor;
|
||||
}
|
||||
|
||||
@Nullable @Override
|
||||
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
|
||||
|
||||
return (isGemFireProperties(bean, beanName) ? set((Properties) bean)
|
||||
: (bean instanceof CacheFactoryBean ? spyOnCacheFactoryBean((CacheFactoryBean) bean, this.useSingletonCache)
|
||||
: (bean instanceof PoolFactoryBean ? mockThePoolFactoryBean((PoolFactoryBean) bean)
|
||||
: bean)));
|
||||
}
|
||||
|
||||
@Nullable @Override
|
||||
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
|
||||
|
||||
if (bean instanceof GemFireCache) {
|
||||
|
||||
GemFireCache gemfireCache = (GemFireCache) bean;
|
||||
|
||||
when(gemfireCache.getDistributedSystem().getProperties()).thenReturn(this.gemfireProperties.get());
|
||||
}
|
||||
|
||||
return bean;
|
||||
}
|
||||
|
||||
private boolean isGemFireProperties(Object bean, String beanName) {
|
||||
return (bean instanceof Properties && GEMFIRE_PROPERTIES_BEAN_NAME.equals(beanName));
|
||||
}
|
||||
|
||||
private Object set(Properties gemfireProperties) {
|
||||
this.gemfireProperties.set(gemfireProperties);
|
||||
return gemfireProperties;
|
||||
}
|
||||
|
||||
private Object spyOnCacheFactoryBean(CacheFactoryBean bean, boolean useSingletonCache) {
|
||||
|
||||
return (bean instanceof ClientCacheFactoryBean
|
||||
? SpyingClientCacheFactoryInitializer.spyOn((ClientCacheFactoryBean) bean, useSingletonCache)
|
||||
: SpyingCacheFactoryInitializer.spyOn(bean, useSingletonCache));
|
||||
}
|
||||
|
||||
private Object mockThePoolFactoryBean(PoolFactoryBean bean) {
|
||||
return MockingPoolFactoryInitializer.mock(bean);
|
||||
}
|
||||
|
||||
protected static class SpyingCacheFactoryInitializer
|
||||
implements CacheFactoryBean.CacheFactoryInitializer<CacheFactory> {
|
||||
|
||||
public static CacheFactoryBean spyOn(CacheFactoryBean cacheFactoryBean, boolean useSingletonCache) {
|
||||
cacheFactoryBean.setCacheFactoryInitializer(new SpyingCacheFactoryInitializer(useSingletonCache));
|
||||
return cacheFactoryBean;
|
||||
}
|
||||
|
||||
private final boolean useSingletonCache;
|
||||
|
||||
protected SpyingCacheFactoryInitializer(boolean useSingletonCache) {
|
||||
this.useSingletonCache = useSingletonCache;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CacheFactory initialize(CacheFactory cacheFactory) {
|
||||
return MockGemFireObjectsSupport.spyOn(cacheFactory, useSingletonCache);
|
||||
}
|
||||
}
|
||||
|
||||
protected static class SpyingClientCacheFactoryInitializer
|
||||
implements CacheFactoryBean.CacheFactoryInitializer<ClientCacheFactory> {
|
||||
|
||||
public static ClientCacheFactoryBean spyOn(ClientCacheFactoryBean clientCacheFactoryBean,
|
||||
boolean useSingletonCache) {
|
||||
|
||||
clientCacheFactoryBean.setCacheFactoryInitializer(
|
||||
new SpyingClientCacheFactoryInitializer(useSingletonCache));
|
||||
|
||||
return clientCacheFactoryBean;
|
||||
}
|
||||
|
||||
private final boolean useSingletonCache;
|
||||
|
||||
protected SpyingClientCacheFactoryInitializer(boolean useSingletonCache) {
|
||||
this.useSingletonCache = useSingletonCache;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientCacheFactory initialize(ClientCacheFactory clientCacheFactory) {
|
||||
return MockGemFireObjectsSupport.spyOn(clientCacheFactory, this.useSingletonCache);
|
||||
}
|
||||
}
|
||||
|
||||
protected static class MockingPoolFactoryInitializer implements PoolFactoryBean.PoolFactoryInitializer {
|
||||
|
||||
public static PoolFactoryBean mock(PoolFactoryBean poolFactoryBean) {
|
||||
poolFactoryBean.setPoolFactoryInitializer(new MockingPoolFactoryInitializer());
|
||||
return poolFactoryBean;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PoolFactory initialize(PoolFactory poolFactory) {
|
||||
return MockGemFireObjectsSupport.mockPoolFactory();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2017 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.tests.mock.context;
|
||||
|
||||
import org.springframework.context.ApplicationContextInitializer;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.data.gemfire.tests.mock.config.MockGemFireObjectsBeanPostProcessor;
|
||||
|
||||
/**
|
||||
* The {@link MockGemFireObjectsApplicationContextInitializer} class is a Spring {@link ApplicationContextInitializer}
|
||||
* used to initialize the Spring {@link ConfigurableApplicationContext} with GemFire Object mocking.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see org.springframework.context.ApplicationContextInitializer
|
||||
* @see org.springframework.context.ConfigurableApplicationContext
|
||||
* @see org.springframework.data.gemfire.test.mock.config.MockGemFireObjectsBeanPostProcessor
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public class MockGemFireObjectsApplicationContextInitializer
|
||||
implements ApplicationContextInitializer<ConfigurableApplicationContext> {
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("all")
|
||||
public void initialize(ConfigurableApplicationContext applicationContext) {
|
||||
applicationContext.getBeanFactory().addBeanPostProcessor(MockGemFireObjectsBeanPostProcessor.newInstance());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright 2017 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.tests.mock.support;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* The {@link MockObjectInvocationException} class is an extension of {@link MockObjectsException} to categorize
|
||||
* problems with {@link Method method} invocations on {@link Object Mock Objects}.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see org.springframework.data.gemfire.test.mock.support.MockObjectsException
|
||||
* @since 2.0.1
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class MockObjectInvocationException extends MockObjectsException {
|
||||
|
||||
/**
|
||||
* Constructs a new instance of the {@link MockObjectInvocationException} class with no message or underlying cause.
|
||||
*/
|
||||
public MockObjectInvocationException() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new instance of the {@link MockObjectInvocationException} class initialized with
|
||||
* the given {@link String message} describing the problem.
|
||||
*
|
||||
* @param message {@link String} describing the problem.
|
||||
* @see String
|
||||
*/
|
||||
public MockObjectInvocationException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new instance of the {@link MockObjectInvocationException} class initialized with
|
||||
* the given {@link Throwable cause} of the underlying problem.
|
||||
*
|
||||
* @param cause {@link Throwable} object containing the cause of this exception.
|
||||
* @see Throwable
|
||||
*/
|
||||
public MockObjectInvocationException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new instance of the {@link MockObjectInvocationException} class initialized with
|
||||
* the given {@link String message} describing the underlying problem as well as the {@link Throwable cause}
|
||||
* of the underlying problem.
|
||||
*
|
||||
* @param message {@link String} describing the problem.
|
||||
* @param cause {@link Throwable} object containing the cause of this exception.
|
||||
* @see Throwable
|
||||
* @see String
|
||||
*/
|
||||
public MockObjectInvocationException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright 2017 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.tests.mock.support;
|
||||
|
||||
/**
|
||||
* The {@link MockObjectsException} class is a {@link RuntimeException} indicating a general problem
|
||||
* with the Mock Objects infrastructure.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see RuntimeException
|
||||
* @since 2.0.1
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class MockObjectsException extends RuntimeException {
|
||||
|
||||
/**
|
||||
* Constructs a new instance of the {@link MockObjectsException} class with no message or underlying cause.
|
||||
*/
|
||||
public MockObjectsException() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new instance of the {@link MockObjectsException} class initialized with
|
||||
* the given {@link String message} describing the problem.
|
||||
*
|
||||
* @param message {@link String} describing the problem.
|
||||
* @see String
|
||||
*/
|
||||
public MockObjectsException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new instance of the {@link MockObjectsException} class initialized with
|
||||
* the given {@link Throwable cause} of the underlying problem.
|
||||
*
|
||||
* @param cause {@link Throwable} object containing the cause of this exception.
|
||||
* @see Throwable
|
||||
*/
|
||||
public MockObjectsException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new instance of the {@link MockObjectsException} class initialized with
|
||||
* the given {@link String message} describing the underlying problem as well as the {@link Throwable cause}
|
||||
* of the underlying problem.
|
||||
*
|
||||
* @param message {@link String} describing the problem.
|
||||
* @param cause {@link Throwable} object containing the cause of this exception.
|
||||
* @see Throwable
|
||||
* @see String
|
||||
*/
|
||||
public MockObjectsException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
@@ -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.tests.process;
|
||||
|
||||
/**
|
||||
* The PidUnavailableException class is a RuntimeException indicating that the process ID (PID) is unobtainable for
|
||||
* the current process.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see RuntimeException
|
||||
* @since 1.5.0
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class PidUnavailableException extends RuntimeException {
|
||||
|
||||
public PidUnavailableException() {
|
||||
}
|
||||
|
||||
public PidUnavailableException(final String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public PidUnavailableException(final Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public PidUnavailableException(final String message, final Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* 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.tests.process;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.data.gemfire.tests.util.FileSystemUtils;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* The {@link ProcessConfiguration} class is a container encapsulating configuration and context meta-data
|
||||
* for a running process.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see ProcessBuilder
|
||||
* @see org.springframework.data.gemfire.process.ProcessExecutor
|
||||
* @since 1.5.0
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class ProcessConfiguration {
|
||||
|
||||
private final boolean redirectingErrorStream;
|
||||
|
||||
private final File workingDirectory;
|
||||
|
||||
private final List<String> command;
|
||||
|
||||
private final Map<String, String> environment;
|
||||
|
||||
public static ProcessConfiguration create(ProcessBuilder processBuilder) {
|
||||
Assert.notNull(processBuilder, "The ProcessBuilder used to configure and start the Process must not be null");
|
||||
|
||||
return new ProcessConfiguration(processBuilder.command(), processBuilder.directory(),
|
||||
processBuilder.environment(), processBuilder.redirectErrorStream());
|
||||
}
|
||||
|
||||
public ProcessConfiguration(List<String> command, File workingDirectory, Map<String, String> environment,
|
||||
boolean redirectErrorStream) {
|
||||
|
||||
Assert.notEmpty(command, "Process command must be specified");
|
||||
|
||||
Assert.isTrue(FileSystemUtils.isDirectory(workingDirectory), String.format(
|
||||
"Process working directory [%s] is not valid", workingDirectory));
|
||||
|
||||
this.command = new ArrayList<String>(command);
|
||||
this.workingDirectory = workingDirectory;
|
||||
this.redirectingErrorStream = redirectErrorStream;
|
||||
|
||||
this.environment = (environment != null
|
||||
? Collections.unmodifiableMap(new HashMap<String, String>(environment))
|
||||
: Collections.<String, String>emptyMap());
|
||||
}
|
||||
|
||||
public List<String> getCommand() {
|
||||
return Collections.unmodifiableList(command);
|
||||
}
|
||||
|
||||
public String getCommandString() {
|
||||
return StringUtils.arrayToDelimitedString(getCommand().toArray(), " ");
|
||||
}
|
||||
|
||||
public Map<String, String> getEnvironment() {
|
||||
return environment;
|
||||
}
|
||||
|
||||
public boolean isRedirectingErrorStream() {
|
||||
return redirectingErrorStream;
|
||||
}
|
||||
|
||||
public File getWorkingDirectory() {
|
||||
return workingDirectory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "{ command = ".concat(getCommandString())
|
||||
.concat(", workingDirectory = ".concat(getWorkingDirectory().getAbsolutePath()))
|
||||
.concat(", environment = ".concat(String.valueOf(getEnvironment())))
|
||||
.concat(", redirectingErrorStream = ".concat(String.valueOf(isRedirectingErrorStream())))
|
||||
.concat(" }");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* 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.tests.process;
|
||||
|
||||
import static org.springframework.data.gemfire.util.ArrayUtils.nullSafeArray;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.data.gemfire.tests.util.FileSystemUtils;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* The {@link ProcessExecutor} class is a utility class for launching and running Java processes.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see java.io.File
|
||||
* @see java.lang.Process
|
||||
* @see java.lang.ProcessBuilder
|
||||
* @see java.lang.System
|
||||
* @see org.springframework.data.gemfire.tests.process.ProcessConfiguration
|
||||
* @see org.springframework.data.gemfire.tests.process.ProcessWrapper
|
||||
* @since 1.5.0
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public abstract class ProcessExecutor {
|
||||
|
||||
public static final File JAVA_EXE = new File(new File(FileSystemUtils.JAVA_HOME, "bin"), "java");
|
||||
|
||||
public static final String JAVA_CLASSPATH = System.getProperty("java.class.path");
|
||||
|
||||
protected static final String SPRING_GEMFIRE_SYSTEM_PROPERTY_PREFIX = "spring.gemfire.";
|
||||
protected static final String SPRING_DATA_GEMFIRE_SYSTEM_PROPERTY_PREFIX = "spring.data.gemfire.";
|
||||
|
||||
public static ProcessWrapper launch(Class<?> type, String... args) throws IOException {
|
||||
return launch(FileSystemUtils.WORKING_DIRECTORY, type, args);
|
||||
}
|
||||
|
||||
public static ProcessWrapper launch(File workingDirectory, Class<?> type, String... args) throws IOException {
|
||||
return launch(workingDirectory, JAVA_CLASSPATH, type, args);
|
||||
}
|
||||
|
||||
public static ProcessWrapper launch(File workingDirectory, String classpath, Class<?> type, String... args)
|
||||
throws IOException {
|
||||
|
||||
ProcessBuilder processBuilder = new ProcessBuilder()
|
||||
.command(buildCommand(classpath, type, args))
|
||||
.directory(validateDirectory(workingDirectory))
|
||||
.redirectErrorStream(true);
|
||||
|
||||
Process process = processBuilder.start();
|
||||
|
||||
ProcessWrapper processWrapper = new ProcessWrapper(process, ProcessConfiguration.create(processBuilder));
|
||||
|
||||
processWrapper.register((input) -> System.err.printf("[FORK] - %s%n", input));
|
||||
|
||||
return processWrapper;
|
||||
}
|
||||
|
||||
protected static String[] buildCommand(String classpath, Class<?> type, String... args) {
|
||||
Assert.notNull(type, "The main Java class to launch must not be null");
|
||||
|
||||
List<String> command = new ArrayList<>();
|
||||
List<String> programArguments = new ArrayList<>(args.length);
|
||||
|
||||
command.add(JAVA_EXE.getAbsolutePath());
|
||||
command.add("-server");
|
||||
command.add("-ea");
|
||||
command.add("-classpath");
|
||||
command.add(StringUtils.hasText(classpath) ? classpath : JAVA_CLASSPATH);
|
||||
command.addAll(getSpringGemFireSystemProperties());
|
||||
|
||||
for (String arg : nullSafeArray(args, String.class)) {
|
||||
if (isJvmOption(arg)) {
|
||||
command.add(arg);
|
||||
}
|
||||
else if (!StringUtils.isEmpty(arg)) {
|
||||
programArguments.add(arg);
|
||||
}
|
||||
}
|
||||
|
||||
command.add(type.getName());
|
||||
command.addAll(programArguments);
|
||||
|
||||
return command.toArray(new String[command.size()]);
|
||||
}
|
||||
|
||||
protected static Collection<? extends String> getSpringGemFireSystemProperties() {
|
||||
return System.getProperties().stringPropertyNames().stream()
|
||||
.filter(property -> property.startsWith(SPRING_DATA_GEMFIRE_SYSTEM_PROPERTY_PREFIX)
|
||||
|| property.startsWith(SPRING_GEMFIRE_SYSTEM_PROPERTY_PREFIX))
|
||||
.map(property -> String.format("-D%1$s=%2$s", property, System.getProperty(property)))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
protected static boolean isJvmOption(String option) {
|
||||
return (StringUtils.hasText(option) && (option.startsWith("-D") || option.startsWith("-X")));
|
||||
}
|
||||
|
||||
protected static File validateDirectory(File workingDirectory) {
|
||||
Assert.isTrue(workingDirectory != null && (workingDirectory.isDirectory() || workingDirectory.mkdirs()),
|
||||
String.format("Failed to create working directory [%s]", workingDirectory));
|
||||
|
||||
return workingDirectory;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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.tests.process;
|
||||
|
||||
import java.util.EventListener;
|
||||
|
||||
/**
|
||||
* The {@link ProcessInputStreamListener} is a callback interface that gets called when input arrives from either a
|
||||
* {@link Process process's} standard output steam or standard error stream.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see EventListener
|
||||
* @since 1.5.0
|
||||
*/
|
||||
public interface ProcessInputStreamListener extends EventListener {
|
||||
|
||||
/**
|
||||
* Callback method that gets called when the {@link Process} sends output from either its standard out
|
||||
* or standard error streams.
|
||||
*
|
||||
* @param input {@link String} containing output from the {@link Process} that this listener is listening to.
|
||||
* @see Process#getErrorStream()
|
||||
* @see Process#getInputStream()
|
||||
*/
|
||||
void onInput(String input);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,233 @@
|
||||
/*
|
||||
* 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.tests.process;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.lang.management.RuntimeMXBean;
|
||||
import java.util.Scanner;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.springframework.data.gemfire.tests.util.FileSystemUtils;
|
||||
import org.springframework.data.gemfire.tests.util.IOUtils;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* The {@link ProcessUtils} class is a utility class for working with Operating System (OS) {@link Process processes}.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see File
|
||||
* @see Process
|
||||
* @since 1.5.0
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public abstract class ProcessUtils {
|
||||
|
||||
protected static final Logger log = Logger.getLogger(ProcessUtils.class.getName());
|
||||
|
||||
protected static final String TERM_TOKEN = "<TERM/>";
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public static int currentPid() {
|
||||
RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean();
|
||||
String runtimeMXBeanName = runtimeMXBean.getName();
|
||||
|
||||
Exception cause = null;
|
||||
|
||||
if (StringUtils.hasText(runtimeMXBeanName)) {
|
||||
int atSignIndex = runtimeMXBeanName.indexOf('@');
|
||||
|
||||
if (atSignIndex > 0) {
|
||||
try {
|
||||
return Integer.parseInt(runtimeMXBeanName.substring(0, atSignIndex));
|
||||
}
|
||||
catch (NumberFormatException e) {
|
||||
cause = e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new PidUnavailableException(String.format("Process ID (PID) not available [%s]", runtimeMXBeanName),
|
||||
cause);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public static boolean isAlive(Process process) {
|
||||
return (process != null && process.isAlive());
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public static boolean isRunning(int processId) {
|
||||
/*
|
||||
for (VirtualMachineDescriptor vmDescriptor : VirtualMachine.list()) {
|
||||
if (String.valueOf(processId).equals(vmDescriptor.id())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
*/
|
||||
|
||||
throw new UnsupportedOperationException("operation not supported");
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public static boolean isRunning(Process process) {
|
||||
try {
|
||||
process.exitValue();
|
||||
return false;
|
||||
}
|
||||
catch (IllegalThreadStateException ignore) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public static void signalStop(Process process) throws IOException {
|
||||
if (isRunning(process)) {
|
||||
OutputStream processOutputStream = process.getOutputStream();
|
||||
processOutputStream.write(TERM_TOKEN.concat("\n").getBytes());
|
||||
processOutputStream.flush();
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
@SuppressWarnings("all")
|
||||
public static void waitForStopSignal() {
|
||||
Scanner in = new Scanner(System.in);
|
||||
while (!TERM_TOKEN.equals(in.next()));
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public static int findAndReadPid(File workingDirectory) {
|
||||
File pidFile = findPidFile(workingDirectory);
|
||||
|
||||
if (pidFile == null) {
|
||||
throw new PidUnavailableException(String.format(
|
||||
"No PID file was found in working directory [%s] or any of it's sub-directories",
|
||||
workingDirectory));
|
||||
}
|
||||
|
||||
return readPid(pidFile);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
@SuppressWarnings("all")
|
||||
protected static File findPidFile(File workingDirectory) {
|
||||
Assert.isTrue(FileSystemUtils.isDirectory(workingDirectory), String.format(
|
||||
"File [%s] is not a valid directory", workingDirectory));
|
||||
|
||||
for (File file : workingDirectory.listFiles(DirectoryPidFileFilter.INSTANCE)) {
|
||||
if (file.isDirectory()) {
|
||||
file = findPidFile(file);
|
||||
}
|
||||
|
||||
if (PidFileFilter.INSTANCE.accept(file)) {
|
||||
return file;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
@SuppressWarnings("all")
|
||||
public static int readPid(File pidFile) {
|
||||
Assert.isTrue(pidFile != null && pidFile.isFile(), String.format(
|
||||
"File [%s] is not a valid file", pidFile));
|
||||
|
||||
BufferedReader fileReader = null;
|
||||
String pidValue = null;
|
||||
|
||||
try {
|
||||
fileReader = new BufferedReader(new FileReader(pidFile));
|
||||
pidValue = String.valueOf(fileReader.readLine()).trim();
|
||||
|
||||
return Integer.parseInt(pidValue);
|
||||
}
|
||||
catch (FileNotFoundException e) {
|
||||
throw new PidUnavailableException(String.format("PID file [%s] not found", pidFile), e);
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new PidUnavailableException(String.format("failed to read PID from file [%s]", pidFile), e);
|
||||
}
|
||||
catch (NumberFormatException e) {
|
||||
throw new PidUnavailableException(String.format(
|
||||
"value [%1$s] from PID file [%2$s] was not a valid numerical PID", pidValue, pidFile), e);
|
||||
}
|
||||
finally {
|
||||
IOUtils.close(fileReader);
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
@SuppressWarnings("all")
|
||||
public static void writePid(File pidFile, int pid) throws IOException {
|
||||
Assert.isTrue(pidFile != null && (pidFile.isFile() || pidFile.createNewFile()), String.format(
|
||||
"File [%s] is not a valid file", pidFile));
|
||||
|
||||
Assert.isTrue(pid > 0, String.format("PID [%d] must greater than 0", pid));
|
||||
|
||||
PrintWriter fileWriter = new PrintWriter(new BufferedWriter(new FileWriter(pidFile, false), 16), true);
|
||||
|
||||
try {
|
||||
fileWriter.println(pid);
|
||||
}
|
||||
finally {
|
||||
pidFile.deleteOnExit();
|
||||
FileSystemUtils.close(fileWriter);
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static class DirectoryPidFileFilter extends PidFileFilter {
|
||||
|
||||
protected static final DirectoryPidFileFilter INSTANCE = new DirectoryPidFileFilter();
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public boolean accept(File path) {
|
||||
return (path != null && (path.isDirectory() || super.accept(path)));
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected static class PidFileFilter implements FileFilter {
|
||||
|
||||
protected static final PidFileFilter INSTANCE = new PidFileFilter();
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public boolean accept(File path) {
|
||||
return (path != null && path.isFile() && path.getName().toLowerCase().endsWith(".pid"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,347 @@
|
||||
/*
|
||||
* 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.tests.process;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.springframework.data.gemfire.tests.util.FileSystemUtils;
|
||||
import org.springframework.data.gemfire.tests.util.FileUtils;
|
||||
import org.springframework.data.gemfire.tests.util.IOUtils;
|
||||
import org.springframework.data.gemfire.tests.util.ThreadUtils;
|
||||
import org.springframework.data.gemfire.tests.util.ThrowableUtils;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* The ProcessWrapper class is a wrapper for a Process object representing an OS process and the ProcessBuilder used
|
||||
* to construct and start the process.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see Process
|
||||
* @see ProcessBuilder
|
||||
* @since 1.5.0
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class ProcessWrapper {
|
||||
|
||||
protected static final boolean DEFAULT_DAEMON_THREAD = true;
|
||||
|
||||
protected static final long DEFAULT_WAIT_TIME_MILLISECONDS = TimeUnit.SECONDS.toMillis(15);
|
||||
|
||||
private final List<ProcessInputStreamListener> listeners = new CopyOnWriteArrayList<>();
|
||||
|
||||
protected final Logger log = Logger.getLogger(getClass().getName());
|
||||
|
||||
private final Process process;
|
||||
private final ProcessConfiguration processConfiguration;
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public ProcessWrapper(Process process, ProcessConfiguration processConfiguration) {
|
||||
Assert.notNull(process, "Process must not be null");
|
||||
|
||||
Assert.notNull(processConfiguration, "The context and configuration meta-data providing details"
|
||||
+ " about the environment in which the process is running and how the process was configured and executed"
|
||||
+ " must not be null");
|
||||
|
||||
this.process = process;
|
||||
this.processConfiguration = processConfiguration;
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
private void init() {
|
||||
newThread("Process OUT Stream Reader Thread",
|
||||
newProcessInputStreamReaderRunnable(process.getInputStream())).start();
|
||||
|
||||
if (!isRedirectingErrorStream()) {
|
||||
newThread("Process ERR Stream Reader Thread",
|
||||
newProcessInputStreamReaderRunnable(process.getErrorStream())).start();
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected Runnable newProcessInputStreamReaderRunnable(InputStream in) {
|
||||
return () -> {
|
||||
if (isRunning()) {
|
||||
BufferedReader inputReader = new BufferedReader(new InputStreamReader(in));
|
||||
|
||||
try {
|
||||
for (String input = inputReader.readLine(); input != null; input = inputReader.readLine()) {
|
||||
for (ProcessInputStreamListener listener : listeners) {
|
||||
listener.onInput(input);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IOException ignore) {
|
||||
// Ignore IO error and just stop reading from the process input stream
|
||||
// An IO error occurred most likely because the process was terminated
|
||||
}
|
||||
finally {
|
||||
IOUtils.close(inputReader);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected Thread newThread(String name, Runnable task) {
|
||||
Assert.hasText(name, "Thread name must be specified");
|
||||
Assert.notNull(task, "Thread task must not be null");
|
||||
|
||||
Thread thread = new Thread(task, name);
|
||||
|
||||
thread.setDaemon(DEFAULT_DAEMON_THREAD);
|
||||
thread.setPriority(Thread.NORM_PRIORITY);
|
||||
|
||||
return thread;
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public boolean isAlive() {
|
||||
return ProcessUtils.isAlive(process);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public boolean isNotAlive() {
|
||||
return !isAlive();
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public List<String> getCommand() {
|
||||
return processConfiguration.getCommand();
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public String getCommandString() {
|
||||
return processConfiguration.getCommandString();
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public Map<String, String> getEnvironment() {
|
||||
return processConfiguration.getEnvironment();
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public int getPid() {
|
||||
return ProcessUtils.findAndReadPid(getWorkingDirectory());
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public int safeGetPid() {
|
||||
try {
|
||||
return getPid();
|
||||
}
|
||||
catch (PidUnavailableException ignore) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public boolean isRedirectingErrorStream() {
|
||||
return processConfiguration.isRedirectingErrorStream();
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public boolean isNotRunning() {
|
||||
return !isRunning();
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public boolean isRunning() {
|
||||
return ProcessUtils.isRunning(process);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public File getWorkingDirectory() {
|
||||
return processConfiguration.getWorkingDirectory();
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public int exitValue() {
|
||||
return process.exitValue();
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public int safeExitValue() {
|
||||
try {
|
||||
return exitValue();
|
||||
}
|
||||
catch (IllegalThreadStateException ignore) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public String readLogFile() throws IOException {
|
||||
File[] logFiles = FileSystemUtils.listFiles(getWorkingDirectory(),
|
||||
(path) -> (path != null && (path.isDirectory() || path.getAbsolutePath().endsWith(".log"))));
|
||||
|
||||
if (logFiles.length > 0) {
|
||||
return readLogFile(logFiles[0]);
|
||||
}
|
||||
else {
|
||||
throw new FileNotFoundException(String.format(
|
||||
"No log files found in process's [%d] working directory [%s]",
|
||||
safeGetPid(), getWorkingDirectory()));
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public String readLogFile(File log) throws IOException {
|
||||
return FileUtils.read(log);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public boolean register(ProcessInputStreamListener listener) {
|
||||
return (listener != null && listeners.add(listener));
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public void registerShutdownHook() {
|
||||
Runtime.getRuntime().addShutdownHook(new Thread(this::shutdown));
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public void signal() {
|
||||
try {
|
||||
OutputStream outputStream = process.getOutputStream();
|
||||
outputStream.write("\n".getBytes());
|
||||
outputStream.flush();
|
||||
}
|
||||
catch (IOException e) {
|
||||
log.warning("Failed to signal process");
|
||||
|
||||
if (log.isLoggable(Level.FINE)) {
|
||||
log.fine(ThrowableUtils.toString(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public void signalStop() {
|
||||
try {
|
||||
ProcessUtils.signalStop(process);
|
||||
}
|
||||
catch (IOException e) {
|
||||
log.warning("Failed to signal the process to stop");
|
||||
|
||||
if (log.isLoggable(Level.FINE)) {
|
||||
log.fine(ThrowableUtils.toString(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public int stop() {
|
||||
return stop(DEFAULT_WAIT_TIME_MILLISECONDS);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public int stop(long milliseconds) {
|
||||
if (isRunning()) {
|
||||
boolean interrupted = false;
|
||||
int exitValue = -1;
|
||||
int pid = safeGetPid();
|
||||
long timeout = (System.currentTimeMillis() + milliseconds);
|
||||
AtomicBoolean exited = new AtomicBoolean(false);
|
||||
|
||||
ExecutorService executorService = Executors.newSingleThreadExecutor();
|
||||
|
||||
try {
|
||||
Future<Integer> futureExitValue = executorService.submit(() -> {
|
||||
process.destroy();
|
||||
int localExitValue = process.waitFor();
|
||||
exited.set(true);
|
||||
return localExitValue;
|
||||
});
|
||||
|
||||
while (!exited.get() && System.currentTimeMillis() < timeout) {
|
||||
try {
|
||||
exitValue = futureExitValue.get(milliseconds, TimeUnit.MILLISECONDS);
|
||||
log.info(String.format("Process [%s] has stopped%n", pid));
|
||||
}
|
||||
catch (InterruptedException ignore) {
|
||||
interrupted = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (TimeoutException e) {
|
||||
exitValue = -1;
|
||||
log.warning(String.format("Process [%1$d] did not stop within the allotted timeout of %2$d seconds%n",
|
||||
pid, TimeUnit.MILLISECONDS.toSeconds(milliseconds)));
|
||||
}
|
||||
catch (Exception ignore) {
|
||||
// handles CancellationException, ExecutionException
|
||||
}
|
||||
finally {
|
||||
executorService.shutdownNow();
|
||||
|
||||
if (interrupted) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
return exitValue;
|
||||
}
|
||||
else {
|
||||
return exitValue();
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public int shutdown() {
|
||||
if (isRunning()) {
|
||||
log.info(String.format("Stopping process [%d]...%n", safeGetPid()));
|
||||
signalStop();
|
||||
waitFor();
|
||||
}
|
||||
|
||||
return stop();
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public boolean unregister(ProcessInputStreamListener listener) {
|
||||
return listeners.remove(listener);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public void waitFor() {
|
||||
waitFor(DEFAULT_WAIT_TIME_MILLISECONDS);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public void waitFor(long milliseconds) {
|
||||
ThreadUtils.timedWait(milliseconds, 500, this::isRunning);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* 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.tests.support;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.SQLFeatureNotSupportedException;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
/**
|
||||
* The DataSourceAdapter class is an implementation of the DataSource interface with unsupported operations by default.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see Connection
|
||||
* @see DataSource
|
||||
* @since 1.3.4
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public abstract class DataSourceAdapter implements DataSource {
|
||||
|
||||
private static final String UNSUPPORTED_OPERATION_EXCEPTION_MESSAGE = "Not Implemented";
|
||||
|
||||
@Override
|
||||
public Connection getConnection() throws SQLException {
|
||||
throw new UnsupportedOperationException(UNSUPPORTED_OPERATION_EXCEPTION_MESSAGE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Connection getConnection(final String username, final String password) throws SQLException {
|
||||
throw new UnsupportedOperationException(UNSUPPORTED_OPERATION_EXCEPTION_MESSAGE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PrintWriter getLogWriter() throws SQLException {
|
||||
throw new UnsupportedOperationException(UNSUPPORTED_OPERATION_EXCEPTION_MESSAGE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLogWriter(final PrintWriter out) throws SQLException {
|
||||
throw new UnsupportedOperationException(UNSUPPORTED_OPERATION_EXCEPTION_MESSAGE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLoginTimeout() throws SQLException {
|
||||
throw new UnsupportedOperationException(UNSUPPORTED_OPERATION_EXCEPTION_MESSAGE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLoginTimeout(final int seconds) throws SQLException {
|
||||
throw new UnsupportedOperationException(UNSUPPORTED_OPERATION_EXCEPTION_MESSAGE);
|
||||
}
|
||||
|
||||
//@Override
|
||||
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
|
||||
throw new UnsupportedOperationException(UNSUPPORTED_OPERATION_EXCEPTION_MESSAGE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWrapperFor(final Class<?> iface) throws SQLException {
|
||||
throw new UnsupportedOperationException(UNSUPPORTED_OPERATION_EXCEPTION_MESSAGE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T unwrap(final Class<T> iface) throws SQLException {
|
||||
throw new UnsupportedOperationException(UNSUPPORTED_OPERATION_EXCEPTION_MESSAGE);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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.tests.support;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
/**
|
||||
* The IdentifierSequence class is an Identifier (ID) generator generating unique IDs in sequence.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see System#currentTimeMillis()
|
||||
* @see AtomicLong
|
||||
* @since 1.5.3
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public abstract class IdentifierSequence {
|
||||
|
||||
private static final AtomicLong ID_SEQUENCE = new AtomicLong(System.currentTimeMillis());
|
||||
|
||||
public static long nextId() {
|
||||
return ID_SEQUENCE.incrementAndGet();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2017 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.tests.support;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* The {@link MapBuilder} class employs the Builder Software Design Pattern to build a {@link Map}.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see Map
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public class MapBuilder<KEY, VALUE> {
|
||||
|
||||
public static <KEY, VALUE> MapBuilder<KEY, VALUE> newMapBuilder() {
|
||||
return new MapBuilder<>();
|
||||
}
|
||||
|
||||
private final Map<KEY, VALUE> map = new HashMap<>();
|
||||
|
||||
public MapBuilder<KEY, VALUE> put(KEY key, VALUE value) {
|
||||
this.map.put(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public MapBuilder<KEY, VALUE> remove(KEY key) {
|
||||
this.map.remove(key);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Map<KEY, VALUE> build() {
|
||||
return this.map;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,249 @@
|
||||
/*
|
||||
* 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.tests.util;
|
||||
|
||||
import static org.springframework.data.gemfire.util.ArrayUtils.nullSafeArray;
|
||||
import static org.springframework.data.gemfire.util.CollectionUtils.nullSafeIterable;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* The {@link FileSystemUtils} class is a utility class encapsulating functionality to process
|
||||
* file system directories and files collectively.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see File
|
||||
* @see FileFilter
|
||||
* @see org.springframework.data.gemfire.test.support.FileUtils
|
||||
* @see org.springframework.data.gemfire.test.support.IOUtils
|
||||
* @since 1.5.0
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
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 TEMPORARY_DIRECTORY = new File(System.getProperty("java.io.tmpdir"));
|
||||
public static final File USER_HOME = new File(System.getProperty("user.home"));
|
||||
public static final File WORKING_DIRECTORY = new File(System.getProperty("user.dir"));
|
||||
|
||||
public static final File[] NO_FILES = new File[0];
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public static boolean deleteRecursive(File path) {
|
||||
return deleteRecursive(path, AllFilesFilter.INSTANCE);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public static boolean deleteRecursive(File path, FileFilter fileFilter) {
|
||||
boolean success = true;
|
||||
|
||||
if (isDirectory(path)) {
|
||||
for (File file : safeListFiles(path, fileFilter)) {
|
||||
success &= deleteRecursive(file);
|
||||
}
|
||||
}
|
||||
|
||||
return ((!exists(path) || path.delete()) && success);
|
||||
}
|
||||
|
||||
public static boolean exists(File path) {
|
||||
return (path != null && path.exists());
|
||||
}
|
||||
|
||||
// returns sub-directory just below working directory
|
||||
public static File getRootRelativeToWorkingDirectoryOrPath(File path) {
|
||||
File localPath = path;
|
||||
|
||||
if (isDirectory(localPath)) {
|
||||
while (localPath != null && !WORKING_DIRECTORY.equals(localPath.getParentFile())) {
|
||||
localPath = localPath.getParentFile();
|
||||
}
|
||||
}
|
||||
|
||||
return (localPath != null ? localPath : path);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public static File[] listFiles(File directory, FileFilter fileFilter) {
|
||||
Assert.isTrue(isDirectory(directory), String.format(
|
||||
"File [%s] does not refer to a valid directory", directory));
|
||||
|
||||
List<File> results = new ArrayList<File>();
|
||||
|
||||
for (File file : safeListFiles(directory, fileFilter)) {
|
||||
if (isDirectory(file)) {
|
||||
results.addAll(Arrays.asList(listFiles(file, fileFilter)));
|
||||
}
|
||||
else {
|
||||
results.add(file);
|
||||
}
|
||||
}
|
||||
|
||||
return results.toArray(new File[results.size()]);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public static File[] safeListFiles(File directory) {
|
||||
return safeListFiles(directory, AllFilesFilter.INSTANCE);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public static File[] safeListFiles(File directory, FileFilter fileFilter) {
|
||||
FileFilter resolvedFileFilter = (fileFilter != null ? fileFilter : AllFilesFilter.INSTANCE);
|
||||
File[] files = (isDirectory(directory) ? directory.listFiles(resolvedFileFilter) : null);
|
||||
return (files != null ? files : NO_FILES);
|
||||
}
|
||||
|
||||
public static class AllFilesFilter implements FileFilter {
|
||||
|
||||
public static final AllFilesFilter INSTANCE = new AllFilesFilter();
|
||||
|
||||
@Override
|
||||
public boolean accept(File pathname) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public static class CompositeFileFilter implements FileFilter {
|
||||
|
||||
private final FileFilter fileFilterOne;
|
||||
private final FileFilter fileFilterTwo;
|
||||
|
||||
private final LogicalOperator logicalOperator;
|
||||
|
||||
private CompositeFileFilter(FileFilter fileFilterOne, LogicalOperator operator, FileFilter fileFilterTwo) {
|
||||
this.fileFilterOne = fileFilterOne;
|
||||
this.logicalOperator = operator;
|
||||
this.fileFilterTwo = fileFilterTwo;
|
||||
}
|
||||
|
||||
protected static FileFilter compose(FileFilter fileFilterOne, LogicalOperator operator, FileFilter fileFilterTwo) {
|
||||
return (fileFilterOne == null ? fileFilterTwo : (fileFilterTwo == null ? fileFilterOne
|
||||
: new CompositeFileFilter(fileFilterOne, operator, fileFilterTwo)));
|
||||
}
|
||||
|
||||
public static FileFilter and(FileFilter... fileFilters) {
|
||||
return and(Arrays.asList(nullSafeArray(fileFilters, FileFilter.class)));
|
||||
}
|
||||
|
||||
public static FileFilter and(Iterable<FileFilter> fileFilters) {
|
||||
FileFilter current = null;
|
||||
|
||||
for (FileFilter fileFilter : nullSafeIterable(fileFilters)) {
|
||||
current = compose(current, LogicalOperator.AND, fileFilter);
|
||||
}
|
||||
|
||||
return current;
|
||||
}
|
||||
|
||||
public static FileFilter or(FileFilter... fileFilters) {
|
||||
return or(Arrays.asList(nullSafeArray(fileFilters, FileFilter.class)));
|
||||
}
|
||||
|
||||
public static FileFilter or(Iterable<FileFilter> fileFilters) {
|
||||
FileFilter current = null;
|
||||
|
||||
for (FileFilter fileFilter : nullSafeIterable(fileFilters)) {
|
||||
current = compose(current, LogicalOperator.OR, fileFilter);
|
||||
}
|
||||
|
||||
return current;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean accept(File pathname) {
|
||||
switch (this.logicalOperator) {
|
||||
case AND:
|
||||
return (fileFilterOne.accept(pathname) && fileFilterTwo.accept(pathname));
|
||||
case OR:
|
||||
return (fileFilterOne.accept(pathname) || fileFilterTwo.accept(pathname));
|
||||
default:
|
||||
throw new UnsupportedOperationException(String.format(
|
||||
"Logical operator [%s] is unsupported", this.logicalOperator));
|
||||
}
|
||||
}
|
||||
|
||||
enum LogicalOperator {
|
||||
AND, OR;
|
||||
}
|
||||
}
|
||||
|
||||
public static class DirectoryOnlyFilter implements FileFilter {
|
||||
|
||||
public static final DirectoryOnlyFilter INSTANCE = new DirectoryOnlyFilter();
|
||||
|
||||
@Override
|
||||
public boolean accept(File pathname) {
|
||||
return isDirectory(pathname);
|
||||
}
|
||||
}
|
||||
|
||||
public static final class FileExtensionFilter extends FileOnlyFilter {
|
||||
|
||||
private final String fileExtension;
|
||||
|
||||
public static FileExtensionFilter newFileExtensionFilter(String fileExtension) {
|
||||
return new FileExtensionFilter(fileExtension);
|
||||
}
|
||||
|
||||
public FileExtensionFilter(String fileExtension) {
|
||||
Assert.hasText(fileExtension, String.format("File extension [%s] must be specified", fileExtension));
|
||||
this.fileExtension = fileExtension;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean accept(File pathname) {
|
||||
return (super.accept(pathname) && pathname.getAbsolutePath().toLowerCase().endsWith(this.fileExtension));
|
||||
}
|
||||
}
|
||||
|
||||
public static class FileOnlyFilter implements FileFilter {
|
||||
|
||||
public static final FileOnlyFilter INSTANCE = new FileOnlyFilter();
|
||||
|
||||
@Override
|
||||
public boolean accept(File pathname) {
|
||||
return isFile(pathname);
|
||||
}
|
||||
}
|
||||
|
||||
public static class NegatingFileFilter implements FileFilter {
|
||||
|
||||
private final FileFilter delegate;
|
||||
|
||||
public static NegatingFileFilter newNegatingFileFilter(FileFilter delegate) {
|
||||
return new NegatingFileFilter(delegate);
|
||||
}
|
||||
|
||||
public NegatingFileFilter(FileFilter delegate) {
|
||||
Assert.notNull(delegate, "FileFilter must not be null");
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean accept(File pathname) {
|
||||
return !this.delegate.accept(pathname);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright 2010-2017 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.tests.util;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* The {@link FileUtils} class is an abstract utility class for processing file system files
|
||||
* by working with {@link File} objects.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see File
|
||||
* @see FileReader
|
||||
* @see FileWriter
|
||||
* @see org.springframework.data.gemfire.test.support.IOUtils
|
||||
* @since 1.5.0
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public abstract class FileUtils extends IOUtils {
|
||||
|
||||
public static final String FILE_SEPARATOR = System.getProperty("file.separator");
|
||||
public static final String LINE_SEPARATOR = System.getProperty("line.separator");
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public static boolean isDirectory(File path) {
|
||||
return (path != null && path.isDirectory());
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public static boolean isFile(File path) {
|
||||
return (path != null && path.isFile());
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public static File newFile(String pathname) {
|
||||
return new File(pathname);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public static File newFile(File parent, String pathname) {
|
||||
return new File(parent, pathname);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
@SuppressWarnings("all")
|
||||
public static String read(File file) throws IOException {
|
||||
Assert.isTrue(isFile(file), String.format("The file [%s] to read the contents from is not a valid file", file));
|
||||
|
||||
BufferedReader fileReader = new BufferedReader(new FileReader(file));
|
||||
|
||||
try {
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
|
||||
for (String line = fileReader.readLine(); line != null; line = fileReader.readLine()) {
|
||||
buffer.append(line);
|
||||
buffer.append(LINE_SEPARATOR);
|
||||
}
|
||||
|
||||
return buffer.toString().trim();
|
||||
}
|
||||
finally {
|
||||
close(fileReader);
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public static void write(File file, String contents) throws IOException {
|
||||
Assert.notNull(file, "File must not be null");
|
||||
|
||||
Assert.isTrue(StringUtils.hasText(contents), String.format(
|
||||
"The contents for File [%1$s] cannot be null or empty", file));
|
||||
|
||||
BufferedWriter fileWriter = null;
|
||||
|
||||
try {
|
||||
fileWriter = new BufferedWriter(new FileWriter(file));
|
||||
fileWriter.write(contents);
|
||||
fileWriter.flush();
|
||||
}
|
||||
finally {
|
||||
IOUtils.close(fileWriter);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright 2010-2017 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.tests.util;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.io.Serializable;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* The {@link IOUtils} class is an abstract utility class for working with IO operations.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see Closeable
|
||||
* @since 1.5.0
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public abstract class IOUtils {
|
||||
|
||||
protected static final Logger log = Logger.getLogger(IOUtils.class.getName());
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public static boolean close(Closeable closeable) {
|
||||
|
||||
if (closeable != null) {
|
||||
try {
|
||||
closeable.close();
|
||||
return true;
|
||||
}
|
||||
catch (IOException cause) {
|
||||
if (log.isLoggable(Level.FINE)) {
|
||||
log.fine(String.format("Failed to close the Closeable object (%1$s) due to an I/O error:%n%2$s",
|
||||
closeable, ThrowableUtils.toString(cause)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T deserializeObject(byte[] objectBytes) throws IOException, ClassNotFoundException {
|
||||
|
||||
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(objectBytes);
|
||||
|
||||
ObjectInputStream objectInputStream = null;
|
||||
|
||||
try {
|
||||
objectInputStream = new ObjectInputStream(byteArrayInputStream);
|
||||
|
||||
return (T) objectInputStream.readObject();
|
||||
}
|
||||
finally {
|
||||
IOUtils.close(objectInputStream);
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public static byte[] serializeObject(Serializable obj) throws IOException {
|
||||
|
||||
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
||||
|
||||
ObjectOutputStream objectOutputStream = null;
|
||||
|
||||
try {
|
||||
objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
|
||||
objectOutputStream.writeObject(obj);
|
||||
objectOutputStream.flush();
|
||||
|
||||
return byteArrayOutputStream.toByteArray();
|
||||
}
|
||||
finally {
|
||||
IOUtils.close(objectOutputStream);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright 2016 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.tests.util;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* {@link SocketUtils} is a utility class for managing {@link Socket} and {@link ServerSocket} objects.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see ServerSocket
|
||||
* @see Socket
|
||||
* @since 1.9.0
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public abstract class SocketUtils {
|
||||
|
||||
private static final Logger log = Logger.getLogger(SocketUtils.class.getName());
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public static boolean close(Socket socket) {
|
||||
try {
|
||||
if (socket != null) {
|
||||
socket.close();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (IOException ignore) {
|
||||
log.warning(String.format("Failed to close Socket [%s]", socket));
|
||||
log.warning(ThrowableUtils.toString(ignore));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public static boolean close(ServerSocket serverSocket) {
|
||||
try {
|
||||
if (serverSocket != null) {
|
||||
serverSocket.close();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (IOException ignore) {
|
||||
log.warning(String.format("Failed to close ServerSocket [%s]", serverSocket));
|
||||
log.warning(ThrowableUtils.toString(ignore));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* 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.tests.util;
|
||||
|
||||
/**
|
||||
* The StackTraceUtils class is a utility class for working with stack trace frames (elements) of the current Thread.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see StackTraceElement
|
||||
* @see Thread
|
||||
* @see org.springframework.data.gemfire.test.support.ThreadUtils
|
||||
* @since 1.5.3
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public abstract class StackTraceUtils extends ThreadUtils {
|
||||
|
||||
public static StackTraceElement getCaller() {
|
||||
return getCaller(Thread.currentThread());
|
||||
}
|
||||
|
||||
public static StackTraceElement getCaller(final Thread thread) {
|
||||
return thread.getStackTrace()[2];
|
||||
}
|
||||
|
||||
public static String getCallerName(final StackTraceElement element) {
|
||||
return String.format("%1$%s.%2$s", element.getClass().getName(), element.getMethodName());
|
||||
}
|
||||
|
||||
public static String getCallerSimpleName(final StackTraceElement element) {
|
||||
return String.format("%1$%s.%2$s", element.getClass().getSimpleName(), element.getMethodName());
|
||||
}
|
||||
|
||||
public static StackTraceElement getTestCaller() {
|
||||
return getTestCaller(Thread.currentThread());
|
||||
}
|
||||
|
||||
public static StackTraceElement getTestCaller(final Thread thread) {
|
||||
for (StackTraceElement stackTraceElement : thread.getStackTrace()) {
|
||||
if (isTestSuiteClass(stackTraceElement) && isTestCaseMethod(stackTraceElement)) {
|
||||
return stackTraceElement;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean isTestCaseMethod(final StackTraceElement element) {
|
||||
boolean result = element.getMethodName().toLowerCase().startsWith("test");
|
||||
|
||||
try {
|
||||
result |= element.getClass().getMethod(element.getMethodName()).isAnnotationPresent(org.junit.Test.class);
|
||||
}
|
||||
catch (NoSuchMethodException ignore) {
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static boolean isTestSuiteClass(final StackTraceElement element) {
|
||||
boolean result = element.getClass().getSimpleName().toLowerCase().endsWith("test");
|
||||
result |= element.getClass().isAssignableFrom(junit.framework.TestCase.class);
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* 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.tests.util;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* {@link ThreadUtils} is an abstract utility class for managing Java {@link Thread Threads}.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see Thread
|
||||
* @since 1.5.0
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public abstract class ThreadUtils {
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public static boolean sleep(long milliseconds) {
|
||||
try {
|
||||
Thread.sleep(milliseconds);
|
||||
return true;
|
||||
}
|
||||
catch (InterruptedException ignore) {
|
||||
Thread.currentThread().interrupt();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean timedWait(long duration) {
|
||||
return timedWait(duration, duration);
|
||||
}
|
||||
|
||||
public static boolean timedWait(long duration, long interval) {
|
||||
return timedWait(duration, interval, new WaitCondition() {
|
||||
@Override public boolean waiting() {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
public static boolean timedWait(long duration, long interval, WaitCondition waitCondition) {
|
||||
final long timeout = (System.currentTimeMillis() + duration);
|
||||
|
||||
interval = Math.min(interval, duration);
|
||||
|
||||
try {
|
||||
while (waitCondition.waiting() && (System.currentTimeMillis() < timeout)) {
|
||||
synchronized (waitCondition) {
|
||||
TimeUnit.MILLISECONDS.timedWait(waitCondition, interval);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
|
||||
return !waitCondition.waiting();
|
||||
}
|
||||
|
||||
// TODO rename interface to Condition and waiting() method to evaluate()
|
||||
public interface WaitCondition {
|
||||
boolean waiting();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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.tests.util;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
|
||||
/**
|
||||
* The ThrowableUtils class is a utility class for working with Throwable, Exception and Error objects.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see Error
|
||||
* @see Exception
|
||||
* @see Throwable
|
||||
* @since 1.5.0
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public abstract class ThrowableUtils {
|
||||
|
||||
public static String toString(final Throwable t) {
|
||||
StringWriter writer = new StringWriter();
|
||||
t.printStackTrace(new PrintWriter(writer));
|
||||
return writer.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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.tests.util;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.data.gemfire.util.CollectionUtils;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
|
||||
/**
|
||||
* The ZipUtils class is an abstract utility class for working with JAR and ZIP archives.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see File
|
||||
* @see ZipFile
|
||||
* @since 1.5.0
|
||||
*/
|
||||
public abstract class ZipUtils {
|
||||
|
||||
public static void unzip(final Resource zipResource, final File directory) throws IOException {
|
||||
Assert.notNull(zipResource, "The ZIP Resource must not be null!");
|
||||
|
||||
Assert.isTrue(directory != null && directory.isDirectory(), String.format(
|
||||
"The file system pathname (%1$s) is not a valid directory!", directory));
|
||||
|
||||
ZipFile zipFile = new ZipFile(zipResource.getFile(), ZipFile.OPEN_READ);
|
||||
|
||||
for (ZipEntry entry : CollectionUtils.iterable(zipFile.entries())) {
|
||||
if (entry.isDirectory()) {
|
||||
new File(directory, entry.getName()).mkdirs();
|
||||
}
|
||||
else {
|
||||
DataInputStream entryInputStream = new DataInputStream(zipFile.getInputStream(entry));
|
||||
|
||||
DataOutputStream entryOutputStream = new DataOutputStream(new FileOutputStream(
|
||||
new File(directory, entry.getName())));
|
||||
|
||||
try {
|
||||
FileCopyUtils.copy(entryInputStream, entryOutputStream);
|
||||
}
|
||||
finally {
|
||||
IOUtils.close(entryInputStream);
|
||||
IOUtils.close(entryOutputStream);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user