SGF-559 - Add integration tests convering Geode Integrated Security framework configuration using Geode and Apache Shiro methods with SDG's Annotation configuration support.

This commit is contained in:
John Blum
2016-11-02 01:09:11 -07:00
parent 03a94283c2
commit 0dffb938fc
19 changed files with 1062 additions and 176 deletions

View File

@@ -102,9 +102,9 @@ public class GemfireTemplateQueriesOnGroupedPooledClientCacheRegionsIntegrationT
@BeforeClass
public static void setupGemFireCluster() throws Exception {
serverOne = ProcessExecutor.launch(createDirectory("serverOne"), GemFireCacheServerOneConfiguration.class);
waitForCacheServerToStart("localhost", 41414);
waitForServerToStart("localhost", 41414);
serverTwo = ProcessExecutor.launch(createDirectory("serverTwo"), GemFireCacheServerTwoConfiguration.class);
waitForCacheServerToStart("localhost", 42424);
waitForServerToStart("localhost", 42424);
}
@AfterClass

View File

@@ -0,0 +1,404 @@
/*
* 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.config.annotation;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
import java.io.IOException;
import java.io.Serializable;
import java.security.Principal;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import org.apache.geode.LogWriter;
import org.apache.geode.cache.CacheLoader;
import org.apache.geode.cache.CacheLoaderException;
import org.apache.geode.cache.GemFireCache;
import org.apache.geode.cache.LoaderHelper;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.client.ClientRegionShortcut;
import org.apache.geode.cache.client.ServerOperationException;
import org.apache.geode.distributed.DistributedMember;
import org.apache.geode.security.AuthInitialize;
import org.apache.geode.security.AuthenticationFailedException;
import org.apache.geode.security.NotAuthorizedException;
import org.apache.geode.security.ResourcePermission;
import org.apache.shiro.util.Assert;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.FixMethodOrder;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runners.MethodSorters;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Profile;
import org.springframework.data.gemfire.LocalRegionFactoryBean;
import org.springframework.data.gemfire.client.ClientRegionFactoryBean;
import org.springframework.data.gemfire.process.ProcessWrapper;
import org.springframework.data.gemfire.test.support.ClientServerIntegrationTestsSupport;
import org.springframework.data.gemfire.util.CollectionUtils;
import org.springframework.data.gemfire.util.PropertiesBuilder;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.util.StringUtils;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
/**
* Abstract base test class for implementing Apache Geode Integrated Security Integration Tests.
*
* @author John Blum
* @see org.junit.FixMethodOrder
* @see org.junit.Test
* @see lombok
* @see org.apache.geode.cache.GemFireCache
* @see org.springframework.data.gemfire.test.support.ClientServerIntegrationTestsSupport
* @since 1.0.0
*/
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@SuppressWarnings("unused")
public abstract class AbstractGeodeSecurityIntegrationTests extends ClientServerIntegrationTestsSupport {
protected static final int CACHE_SERVER_PORT = 13531;
protected static final Logger LOGGER = LoggerFactory.getLogger(AbstractGeodeSecurityIntegrationTests.class);
protected static final String CACHE_SERVER_HOST = "localhost";
protected static final String GEODE_SECURITY_PROFILE_PROPERTY = "geode.security.profile";
protected static final String SECURITY_PASSWORD_PROPERTY = "security-password";
protected static final String SECURITY_USERNAME_PROPERTY = "security-username";
private static final AtomicInteger RUN_COUNT = new AtomicInteger(0);
private static ProcessWrapper geodeServerProcess;
@BeforeClass
public static void runGeodeServer() throws IOException {
String geodeSecurityProfile = System.getProperty(GEODE_SECURITY_PROFILE_PROPERTY);
if (StringUtils.hasText(geodeSecurityProfile)) {
runGeodeServer(geodeSecurityProfile);
}
}
protected static ProcessWrapper runGeodeServer(String geodeSecurityProfile) throws IOException {
Assert.hasText(geodeSecurityProfile, String.format("The '%s' System property must be set",
GEODE_SECURITY_PROFILE_PROPERTY));
geodeServerProcess = run(GeodeServerConfiguration.class,
String.format("-Dgemfire.log-file=%s", logFile()),
String.format("-Dgemfire.log-level=%s", logLevel(TEST_GEMFIRE_LOG_LEVEL)),
String.format("-Dspring.profiles.active=apache-geode-server,%s", geodeSecurityProfile));
waitForServerToStart(CACHE_SERVER_HOST, CACHE_SERVER_PORT);
return geodeServerProcess;
}
@AfterClass
public static void stopGeodeServer() {
System.clearProperty(GEODE_SECURITY_PROFILE_PROPERTY);
stop(geodeServerProcess);
}
@Rule
public ExpectedException exception = ExpectedException.none();
@Resource(name = "Echo")
private Region<String, String> echo;
@Test
@DirtiesContext
public void authorizedUser() {
assertThat(echo.get("one")).isEqualTo("one");
assertThat(echo.put("two", "four")).isNull();
assertThat(echo.get("two")).isEqualTo("four");
}
@Test
public void unauthorizedUser() {
assertThat(echo.get("one")).isEqualTo("one");
exception.expect(ServerOperationException.class);
exception.expectCause(is(instanceOf(NotAuthorizedException.class)));
exception.expectMessage(containsString("analyst not authorized for DATA:WRITE:Echo:two"));
echo.put("two", "four");
}
protected static abstract class AuthInitializeSupport implements AuthInitialize {
/**
* @inheritDoc
*/
@Override
public void init(LogWriter systemLogger, LogWriter securityLogger) throws AuthenticationFailedException {
}
/**
* @inheritDoc
*/
@Override
public Properties getCredentials(Properties securityProperties, DistributedMember server, boolean isPeer)
throws AuthenticationFailedException {
return getCredentials(securityProperties);
}
/**
* @inheritDoc
*/
@Override
public void close() {
}
}
public static class GeodeClientAuthInitialize extends AuthInitializeSupport {
protected static final User ANALYST = User.newUser("analyst").with("p@55w0rd");
protected static final User SCIENTIST = User.newUser("scientist").with("w0rk!ng4u");
private final User user;
/* (non-Javadoc) */
public static GeodeClientAuthInitialize create() {
return new GeodeClientAuthInitialize(RUN_COUNT.incrementAndGet() < 2 ? SCIENTIST : ANALYST);
}
/* (non-Javadoc) */
public GeodeClientAuthInitialize(User user) {
Assert.notNull(user, "User cannot be null");
this.user = user;
}
/**
* @inheritDoc
*/
@Override
public Properties getCredentials(Properties securityProperties) {
User user = getUser();
return new PropertiesBuilder()
.setProperty(SECURITY_USERNAME_PROPERTY, user.getName())
.setProperty(SECURITY_PASSWORD_PROPERTY, user.getCredentials())
.build();
}
/* (non-Javadoc) */
protected User getUser() {
return this.user;
}
/**
* @inheritDoc
*/
@Override
public String toString() {
User user = getUser();
return String.format("%1$s:%2$s", user.getName(), user.getCredentials());
}
}
@ClientCacheApplication(name = "GeodeSecurityIntegrationTestsClient", logLevel = TEST_GEMFIRE_LOG_LEVEL,
servers = { @ClientCacheApplication.Server(port = CACHE_SERVER_PORT) })
@EnableAuth(clientAuthenticationInitializer = "org.springframework.data.gemfire.config.annotation.AbstractGeodeSecurityIntegrationTests$GeodeClientAuthInitialize.create")
@Profile("apache-geode-client")
static class GeodeClientConfiguration {
@Bean("Echo")
ClientRegionFactoryBean<String, String> echoRegion(GemFireCache gemfireCache) {
ClientRegionFactoryBean<String, String> echoRegion = new ClientRegionFactoryBean<>();
echoRegion.setCache(gemfireCache);
echoRegion.setClose(false);
echoRegion.setShortcut(ClientRegionShortcut.PROXY);
return echoRegion;
}
}
@CacheServerApplication(name = "GeodeSecurityIntegrationTestsServer", logLevel = TEST_GEMFIRE_LOG_LEVEL,
port = CACHE_SERVER_PORT)
@Import({
ApacheShiroIniGeodeSecurityIntegrationTests.ApacheShiroIniConfiguration.class,
ApacheShiroRealmGeodeSecurityIntegrationTests.ApacheShiroRealmConfiguration.class,
ApacheGeodeSecurityManagerGeodeSecurityIntegrationTests.ApacheGeodeSecurityManagerConfiguration.class
})
@Profile("apache-geode-server")
public static class GeodeServerConfiguration {
public static void main(String[] args) {
runSpringApplication(GeodeServerConfiguration.class, args);
}
@Autowired
private GemFireCache gemfireCache;
@Bean("Echo")
LocalRegionFactoryBean<String, String> echoRegion(GemFireCache gemfireCache) {
LocalRegionFactoryBean<String, String> echoRegion = new LocalRegionFactoryBean<>();
echoRegion.setCache(gemfireCache);
echoRegion.setCacheLoader(echoCacheLoader());
echoRegion.setClose(false);
echoRegion.setPersistent(false);
return echoRegion;
}
CacheLoader<String, String> echoCacheLoader() {
return new CacheLoader<String, String>() {
@Override
public String load(LoaderHelper<String, String> helper) throws CacheLoaderException {
return helper.getKey();
}
@Override
public void close() {
}
};
}
@PostConstruct
public void postProcess() {
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Geode Distributed System Properties [{}]",
CollectionUtils.toString(gemfireCache.getDistributedSystem().getProperties()));
}
}
}
@Getter
@EqualsAndHashCode(of = { "name", "credentials" })
@RequiredArgsConstructor(staticName = "newUser")
@SuppressWarnings("all")
public static class User implements Iterable<Role>, Principal, Serializable {
private Set<Role> roles = new HashSet<>();
@NonNull
private String name;
private String credentials;
/* (non-Javadoc) */
public boolean hasPermission(ResourcePermission permission) {
for (Role role : this) {
if (role.hasPermission(permission)) {
return true;
}
}
return false;
}
/* (non-Javadoc) */
public boolean hasRole(Role role) {
return this.roles.contains(role);
}
/**
* @inheritDoc
*/
@Override
public Iterator<Role> iterator() {
return Collections.unmodifiableSet(this.roles).iterator();
}
/**
* @inheritDoc
*/
@Override
public String toString() {
return getName();
}
/* (non-Javadoc) */
public User with(String credentials) {
this.credentials = credentials;
return this;
}
/* (non-Javadoc) */
public User with(Role... roles) {
Collections.addAll(this.roles, roles);
return this;
}
}
@Getter
@EqualsAndHashCode(of = "name")
@RequiredArgsConstructor(staticName = "newRole")
@SuppressWarnings("unsed")
public static class Role implements Iterable<ResourcePermission>, Serializable {
@NonNull
private String name;
private Set<ResourcePermission> permissions = new HashSet<>();
/* (non-Javadoc) */
public boolean hasPermission(ResourcePermission permission) {
for (ResourcePermission thisPermission : this) {
if (thisPermission.implies(permission)) {
return true;
}
}
return false;
}
/**
* @inheritDoc
*/
@Override
public Iterator<ResourcePermission> iterator() {
return Collections.unmodifiableSet(this.permissions).iterator();
}
/**
* @inheritDoc
*/
@Override
public String toString() {
return getName();
}
/* (non-Javadoc) */
public Role with(ResourcePermission... permissions) {
Collections.addAll(this.permissions, permissions);
return this;
}
}
}

View File

@@ -0,0 +1,144 @@
/*
* 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.config.annotation;
import static org.apache.geode.security.ResourcePermission.Operation;
import static org.apache.geode.security.ResourcePermission.Resource;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Properties;
import java.util.Set;
import org.apache.geode.security.AuthenticationFailedException;
import org.apache.geode.security.ResourcePermission;
import org.junit.BeforeClass;
import org.junit.runner.RunWith;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
/**
* Integration tests for Apache Geode Integrated Security using an application-specific, Apache Geode
* {@link org.apache.geode.security.SecurityManager}.
*
* @author John Blum
* @see org.springframework.data.gemfire.config.annotation.AbstractGeodeSecurityIntegrationTests
* @since 1.0.0
*/
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = AbstractGeodeSecurityIntegrationTests.GeodeClientConfiguration.class)
@ActiveProfiles("apache-geode-client")
public class ApacheGeodeSecurityManagerGeodeSecurityIntegrationTests extends AbstractGeodeSecurityIntegrationTests {
protected static final String GEODE_SECURITY_MANAGER_PROPERTY_CONFIGURATION_PROFILE =
"geode-security-manager-property-configuration";
@BeforeClass
public static void setup() throws IOException {
runGeodeServer(GEODE_SECURITY_MANAGER_PROPERTY_CONFIGURATION_PROFILE);
}
@Configuration
@EnableSecurity(securityManagerClassName = "org.springframework.data.gemfire.config.annotation.ApacheGeodeSecurityManagerGeodeSecurityIntegrationTests$TestGeodeSecurityManager")
@Profile(GEODE_SECURITY_MANAGER_PROPERTY_CONFIGURATION_PROFILE)
public static class ApacheGeodeSecurityManagerConfiguration {
}
protected interface GeodeSecurityRepository {
ResourcePermission CLUSTER_MANAGE = new ResourcePermission(Resource.CLUSTER, Operation.MANAGE);
ResourcePermission CLUSTER_READ = new ResourcePermission(Resource.CLUSTER, Operation.READ);
ResourcePermission CLUSTER_WRITE = new ResourcePermission(Resource.CLUSTER, Operation.WRITE);
ResourcePermission DATA_MANAGE = new ResourcePermission(Resource.DATA, Operation.MANAGE);
ResourcePermission DATA_READ = new ResourcePermission(Resource.DATA, Operation.READ);
ResourcePermission DATA_WRITE = new ResourcePermission(Resource.DATA, Operation.WRITE);
Role ADMIN = Role.newRole("ADMIN").with(CLUSTER_MANAGE, CLUSTER_READ, CLUSTER_WRITE);
Role DBA = Role.newRole("DBA").with(DATA_MANAGE, DATA_READ, DATA_WRITE);
Role DATA_SCIENTIST = Role.newRole("DATA_SCIENTIST").with(DATA_READ, DATA_WRITE);
Role DATA_ANALYST = Role.newRole("DATA_ANALYST").with(DATA_READ);
Role GUEST = Role.newRole("GUEST");
User root = User.newUser("root").with("s3cr3t!").with(ADMIN, DBA);
User scientist = User.newUser("scientist").with("w0rk!ng4u").with(DATA_SCIENTIST);
User analyst = User.newUser("analyst").with("p@55w0rd").with(DATA_ANALYST);
User guest = User.newUser("guest").with("guest").with(GUEST);
Set<User> users = new HashSet<>(Arrays.asList(root, scientist, analyst, guest));
default User findBy(String username) {
return users.stream().filter((user) -> user.getName().equals(username)).findFirst().get();
}
}
public static abstract class GeodeSecurityManagerSupport implements org.apache.geode.security.SecurityManager {
/**
* @inheritDoc
*/
@Override
public void init(Properties securityProps) {
}
/**
* @inheritDoc
*/
@Override
public void close() {
}
}
public static class TestGeodeSecurityManager extends GeodeSecurityManagerSupport {
private final GeodeSecurityRepository securityRepository;
public TestGeodeSecurityManager() {
this.securityRepository = new GeodeSecurityRepository() { };
}
/**
* @inheritDoc
*/
@Override
public Object authenticate(Properties credentials) throws AuthenticationFailedException {
String username = credentials.getProperty(SECURITY_USERNAME_PROPERTY);
String password = credentials.getProperty(SECURITY_PASSWORD_PROPERTY);
User user = securityRepository.findBy(username);
if (user == null || !user.getCredentials().equals(password)) {
throw new AuthenticationFailedException(String.format("Failed to authenticate user [%s]", username));
}
return user;
}
/**
* @inheritDoc
*/
@Override
public boolean authorize(Object principal, ResourcePermission permission) {
return (principal instanceof User && ((User) principal).hasPermission(permission));
}
}
}

View File

@@ -0,0 +1,54 @@
/*
* 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.config.annotation;
import java.io.IOException;
import org.junit.BeforeClass;
import org.junit.runner.RunWith;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
/**
* Integration tests for Apache Geode Integrated Security using an Apache Shiro INI security configuration resource.
*
* @author John Blum
* @see org.springframework.data.gemfire.config.annotation.AbstractGeodeSecurityIntegrationTests
* @since 1.0.0
*/
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = AbstractGeodeSecurityIntegrationTests.GeodeClientConfiguration.class)
@ActiveProfiles("apache-geode-client")
public class ApacheShiroIniGeodeSecurityIntegrationTests extends AbstractGeodeSecurityIntegrationTests {
protected static final String SHIRO_INI_CONFIGURATION_PROFILE = "shiro-ini-configuration";
@BeforeClass
public static void setup() throws IOException {
runGeodeServer(SHIRO_INI_CONFIGURATION_PROFILE);
}
@Configuration
@EnableSecurity(shiroIniResourcePath = "shiro.ini")
@Profile(SHIRO_INI_CONFIGURATION_PROFILE)
public static class ApacheShiroIniConfiguration {
}
}

View File

@@ -0,0 +1,67 @@
/*
* 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.config.annotation;
import java.io.IOException;
import org.apache.geode.internal.security.shiro.GeodePermissionResolver;
import org.apache.shiro.realm.text.PropertiesRealm;
import org.junit.BeforeClass;
import org.junit.runner.RunWith;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
/**
* Integration tests for Apache Geode Integrated Security using an Apache Shiro Realm security configuration
* as a Spring managed bean in a Spring {@link org.springframework.context.ApplicationContext}.
*
* @author John Blum
* @see org.springframework.data.gemfire.config.annotation.AbstractGeodeSecurityIntegrationTests
* @since 1.0.0
*/
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = AbstractGeodeSecurityIntegrationTests.GeodeClientConfiguration.class)
@ActiveProfiles("apache-geode-client")
public class ApacheShiroRealmGeodeSecurityIntegrationTests extends AbstractGeodeSecurityIntegrationTests {
protected static final String SHIRO_REALM_CONFIGURATION_PROFILE = "shiro-realm-configuration";
@BeforeClass
public static void setup() throws IOException {
runGeodeServer(SHIRO_REALM_CONFIGURATION_PROFILE);
}
@Configuration
@EnableSecurity
@Profile(SHIRO_REALM_CONFIGURATION_PROFILE)
@SuppressWarnings("unused")
public static class ApacheShiroRealmConfiguration {
@Bean
public PropertiesRealm shiroRealm() {
PropertiesRealm propertiesRealm = new PropertiesRealm();
propertiesRealm.setResourcePath("classpath:shiro.properties");
propertiesRealm.setPermissionResolver(new GeodePermissionResolver());
return propertiesRealm;
}
}
}

View File

@@ -17,17 +17,7 @@
package org.springframework.data.gemfire.config.annotation;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
import java.io.File;
import java.io.IOException;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import static org.assertj.core.api.Assertions.assertThat;
import javax.annotation.Resource;
@@ -43,18 +33,13 @@ import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.data.gemfire.PartitionedRegionFactoryBean;
import org.springframework.data.gemfire.client.ClientRegionFactoryBean;
import org.springframework.data.gemfire.process.ProcessExecutor;
import org.springframework.data.gemfire.process.ProcessWrapper;
import org.springframework.data.gemfire.test.support.FileSystemUtils;
import org.springframework.data.gemfire.test.support.IOUtils;
import org.springframework.data.gemfire.test.support.ThreadUtils;
import org.springframework.data.gemfire.test.support.ClientServerIntegrationTestsSupport;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.util.Assert;
import org.springframework.test.context.junit4.SpringRunner;
/**
* Test suite of test cases testing the contract and functionality of the {@link CacheServerApplication}
@@ -66,15 +51,15 @@ import org.springframework.util.Assert;
* @see org.springframework.data.gemfire.config.annotation.CacheServerApplication
* @see org.springframework.data.gemfire.config.annotation.ClientCacheApplication
* @see org.springframework.test.context.ContextConfiguration
* @see org.springframework.test.context.junit4.SpringJUnit4ClassRunner
* @see org.springframework.test.context.junit4.SpringRunner
* @see org.apache.geode.cache.client.ClientCache
* @see org.apache.geode.cache.server.CacheServer
* @since 1.9.0
*/
@RunWith(SpringJUnit4ClassRunner.class)
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = ClientServerCacheApplicationIntegrationTests.ClientCacheApplicationConfiguration.class)
@SuppressWarnings("all")
public class ClientServerCacheApplicationIntegrationTests {
public class ClientServerCacheApplicationIntegrationTests extends ClientServerIntegrationTestsSupport {
private static final int PORT = 12480;
@@ -82,56 +67,15 @@ public class ClientServerCacheApplicationIntegrationTests {
@BeforeClass
public static void setupGemFireServer() throws Exception {
String serverName = ClientServerCacheApplicationIntegrationTests.class.getSimpleName();
gemfireServerProcess = run(CacheServerApplicationConfiguration.class, String.format("-Dgemfire.name=%1$s",
asApplicationName(ClientServerCacheApplicationIntegrationTests.class)));
File serverWorkingDirectory = new File(FileSystemUtils.WORKING_DIRECTORY, serverName.toLowerCase());
Assert.isTrue(serverWorkingDirectory.isDirectory() || serverWorkingDirectory.mkdirs());
List<String> arguments = new ArrayList<String>();
arguments.add(String.format("-Dgemfire.name=%1$s", serverName));
gemfireServerProcess = ProcessExecutor.launch(serverWorkingDirectory,
CacheServerApplicationConfiguration.class,
arguments.toArray(new String[arguments.size()]));
waitForServerStart(TimeUnit.SECONDS.toMillis(20));
System.out.println("GemFire Cache Server Process for Client/Server cache application should be running...");
}
private static void waitForServerStart(final long milliseconds) {
ThreadUtils.timedWait(milliseconds, TimeUnit.SECONDS.toMillis(1), new ThreadUtils.WaitCondition() {
AtomicBoolean connected = new AtomicBoolean(false);
public boolean waiting() {
Socket socket = null;
try {
if (!connected.get()) {
socket = new Socket("localhost", PORT);
connected.set(true);
}
}
catch (IOException ignore) {
}
finally {
IOUtils.close(socket);
}
return !connected.get();
}
});
waitForServerToStart("localhost", PORT);
}
@AfterClass
public static void tearDownGemFireServer() {
gemfireServerProcess.shutdown();
if (Boolean.valueOf(System.getProperty("spring.data.gemfire.fork.clean", Boolean.TRUE.toString()))) {
org.springframework.util.FileSystemUtils.deleteRecursively(gemfireServerProcess.getWorkingDirectory());
}
stop(gemfireServerProcess);
}
@Autowired
@@ -141,22 +85,19 @@ public class ClientServerCacheApplicationIntegrationTests {
private Region<String, String> echo;
@Test
public void clientEchoProxyRegionEchoesKeysForValues() {
assertThat(echo.get("Hello"), is(equalTo("Hello")));
assertThat(echo.get("Test"), is(equalTo("Test")));
public void echoClientProxyRegionEchoesKeysForValues() {
assertThat(echo.get("Hello")).isEqualTo("Hello");
assertThat(echo.get("Test")).isEqualTo("Test");
}
@CacheServerApplication(name = "ClientServerCacheApplicationIntegrationTests", logLevel = "warn", port = 12480)
@CacheServerApplication(name = "ClientServerCacheApplicationIntegrationTests", logLevel = "warn", port = PORT)
public static class CacheServerApplicationConfiguration {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext =
new AnnotationConfigApplicationContext(CacheServerApplicationConfiguration.class);
applicationContext.registerShutdownHook();
runSpringApplication(CacheServerApplicationConfiguration.class, args);
}
@Bean(name = "Echo")
@Bean("Echo")
public PartitionedRegionFactoryBean<String, String> echoRegion(Cache gemfireCache) {
PartitionedRegionFactoryBean<String, String> echoRegion =
new PartitionedRegionFactoryBean<String, String>();
@@ -183,7 +124,7 @@ public class ClientServerCacheApplicationIntegrationTests {
}
}
@ClientCacheApplication(logLevel = "warn", servers = { @ClientCacheApplication.Server(port = 12480)})
@ClientCacheApplication(logLevel = "warn", servers = { @ClientCacheApplication.Server(port = PORT)})
static class ClientCacheApplicationConfiguration {
@Bean(name = "Echo")

View File

@@ -17,9 +17,7 @@
package org.springframework.data.gemfire.config.annotation;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
import static org.assertj.core.api.Assertions.assertThat;
import javax.annotation.Resource;
@@ -33,21 +31,20 @@ import org.junit.runner.RunWith;
import org.springframework.context.annotation.Bean;
import org.springframework.data.gemfire.PartitionedRegionFactoryBean;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.junit4.SpringRunner;
/**
* Test suite of test case testing the contract and functionality of the {@link PeerCacheApplication} SDG annotation
* for configuring and bootstrapping a GemFire peer cache instance.
* Integration tests for {@link PeerCacheApplication} SDG annotation.
*
* @author John Blum
* @see org.junit.Test
* @see org.springframework.data.gemfire.config.annotation.PeerCacheApplication
* @see org.springframework.test.context.ContextConfiguration
* @see org.springframework.test.context.junit4.SpringJUnit4ClassRunner
* @see org.springframework.test.context.junit4.SpringRunner
* @see org.apache.geode.cache.Cache
* @since 1.9.0
*/
@RunWith(SpringJUnit4ClassRunner.class)
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = PeerCacheApplicationIntegrationTests.PeerCacheApplicationConfiguration.class)
@SuppressWarnings("all")
public class PeerCacheApplicationIntegrationTests {
@@ -57,14 +54,14 @@ public class PeerCacheApplicationIntegrationTests {
@Test
public void echoPartitionRegionEchoesKeysAsValues() {
assertThat(echo.get("Hello"), is(equalTo("Hello")));
assertThat(echo.get("Test"), is(equalTo("Test")));
assertThat(echo.get("Hello")).isEqualTo("Hello");
assertThat(echo.get("Test")).isEqualTo("Test");
}
@PeerCacheApplication(name = "PeerCacheApplicationIntegrationTests", logLevel = "warn")
static class PeerCacheApplicationConfiguration {
@Bean(name = "Echo")
@Bean("Echo")
PartitionedRegionFactoryBean<String, String> echoRegion(Cache gemfireCache) {
PartitionedRegionFactoryBean<String, String> echoRegion =
new PartitionedRegionFactoryBean<String, String>();

View File

@@ -15,7 +15,7 @@
*
*/
package org.springframework.data.gemfire.config;
package org.springframework.data.gemfire.config.xml;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;

View File

@@ -22,6 +22,7 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import org.springframework.data.gemfire.test.support.FileSystemUtils;
import org.springframework.util.Assert;
@@ -31,6 +32,7 @@ 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
@@ -67,11 +69,7 @@ public abstract class ProcessExecutor {
ProcessWrapper processWrapper = new ProcessWrapper(process, ProcessConfiguration.create(processBuilder));
processWrapper.register(new ProcessInputStreamListener() {
@Override public void onInput(final String input) {
System.err.printf("[FORK-OUT] - %s%n", input);
}
});
processWrapper.register((input) -> System.err.printf("[FORK] - %s%n", input));
return processWrapper;
}
@@ -79,7 +77,7 @@ public abstract class ProcessExecutor {
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<String>();
List<String> command = new ArrayList<>();
List<String> programArgs = Collections.emptyList();
command.add(JAVA_EXE.getAbsolutePath());
@@ -90,7 +88,7 @@ public abstract class ProcessExecutor {
command.addAll(getSpringGemFireSystemProperties());
if (args != null) {
programArgs = new ArrayList<String>(args.length);
programArgs = new ArrayList<>(args.length);
for (String arg : args) {
if (isJvmOption(arg)) {
@@ -109,15 +107,10 @@ public abstract class ProcessExecutor {
}
protected static Collection<? extends String> getSpringGemFireSystemProperties() {
List<String> springGemfireSystemProperties = new ArrayList<String>();
for (String property : System.getProperties().stringPropertyNames()) {
if (property.startsWith(SPRING_GEMFIRE_SYSTEM_PROPERTY_PREFIX)) {
springGemfireSystemProperties.add(String.format("-D%1$s=%2$s", property, System.getProperty(property)));
}
}
return springGemfireSystemProperties;
return System.getProperties().stringPropertyNames().stream()
.filter(property -> 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) {

View File

@@ -18,14 +18,12 @@ package org.springframework.data.gemfire.process;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileFilter;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@@ -61,18 +59,20 @@ public class ProcessWrapper {
protected static final long DEFAULT_WAIT_TIME_MILLISECONDS = TimeUnit.SECONDS.toMillis(15);
private final List<ProcessInputStreamListener> listeners = new CopyOnWriteArrayList<ProcessInputStreamListener>();
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 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;
@@ -80,39 +80,42 @@ public class ProcessWrapper {
init();
}
/* (non-Javadoc) */
private void init() {
newThread("Process OUT Stream Reader", newProcessInputStreamReader(process.getInputStream())).start();
newThread("Process OUT Stream Reader Thread",
newProcessInputStreamReaderRunnable(process.getInputStream())).start();
if (!isRedirectingErrorStream()) {
newThread("Process ERR Stream Reader", newProcessInputStreamReader(process.getErrorStream())).start();
newThread("Process ERR Stream Reader Thread",
newProcessInputStreamReaderRunnable(process.getErrorStream())).start();
}
}
protected Runnable newProcessInputStreamReader(final InputStream in) {
return new Runnable() {
@Override public void run() {
if (isRunning()) {
BufferedReader inputReader = new BufferedReader(new InputStreamReader(in));
/* (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);
}
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);
}
}
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");
@@ -125,22 +128,37 @@ public class ProcessWrapper {
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();
@@ -150,22 +168,32 @@ public class ProcessWrapper {
}
}
/* (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();
@@ -175,38 +203,37 @@ public class ProcessWrapper {
}
}
/* (non-Javadoc) */
public String readLogFile() throws IOException {
File[] logFiles = FileSystemUtils.listFiles(getWorkingDirectory(), new FileFilter() {
@Override public boolean accept(File pathname) {
return (pathname != null && (pathname.isDirectory() || pathname.getAbsolutePath().endsWith(".log")));
}
});
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 working directory [%s]", getWorkingDirectory()));
"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(new Runnable() {
@Override public void run() {
shutdown();
}
}));
Runtime.getRuntime().addShutdownHook(new Thread(this::shutdown));
}
/* (non-Javadoc) */
public void signalStop() {
try {
ProcessUtils.signalStop(process);
@@ -220,28 +247,28 @@ public class ProcessWrapper {
}
}
/* (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;
final int pid = safeGetPid();
final long timeout = (System.currentTimeMillis() + milliseconds);
final AtomicBoolean exited = new AtomicBoolean(false);
int pid = safeGetPid();
long timeout = (System.currentTimeMillis() + milliseconds);
AtomicBoolean exited = new AtomicBoolean(false);
ExecutorService executorService = Executors.newSingleThreadExecutor();
try {
Future<Integer> futureExitValue = executorService.submit(new Callable<Integer>() {
@Override public Integer call() throws Exception {
process.destroy();
int exitValue = process.waitFor();
exited.set(true);
return exitValue;
}
Future<Integer> futureExitValue = executorService.submit(() -> {
process.destroy();
int localExitValue = process.waitFor();
exited.set(true);
return localExitValue;
});
while (!exited.get() && System.currentTimeMillis() < timeout) {
@@ -277,6 +304,7 @@ public class ProcessWrapper {
}
}
/* (non-Javadoc) */
public int shutdown() {
if (isRunning()) {
log.info(String.format("Stopping process [%d]...%n", safeGetPid()));
@@ -287,19 +315,18 @@ public class ProcessWrapper {
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, new ThreadUtils.WaitCondition() {
@Override public boolean waiting() {
return isRunning();
}
});
ThreadUtils.timedWait(milliseconds, 500, this::isRunning);
}
}

View File

@@ -42,9 +42,6 @@ import org.springframework.util.StringUtils;
* @author John Blum
* @see java.io.File
* @see java.lang.Process
* @see java.lang.management.ManagementFactory
* @see java.lang.management.RuntimeMXBean
* see com.sun.tools.attach.VirtualMachine
* @since 1.5.0
*/
@SuppressWarnings("unused")
@@ -54,6 +51,7 @@ public abstract class ProcessUtils {
protected static final String TERM_TOKEN = "<TERM/>";
/* (non-Javadoc) */
public static int currentPid() {
RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean();
String runtimeMXBeanName = runtimeMXBean.getName();
@@ -77,6 +75,12 @@ public abstract class ProcessUtils {
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()) {
@@ -87,9 +91,11 @@ public abstract class ProcessUtils {
return false;
*/
throw new UnsupportedOperationException("operation not supported");
}
/* (non-Javadoc) */
public static boolean isRunning(Process process) {
try {
process.exitValue();
@@ -100,6 +106,7 @@ public abstract class ProcessUtils {
}
}
/* (non-Javadoc) */
public static void signalStop(Process process) throws IOException {
if (isRunning(process)) {
OutputStream processOutputStream = process.getOutputStream();
@@ -108,12 +115,14 @@ public abstract class ProcessUtils {
}
}
/* (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);
@@ -126,6 +135,7 @@ public abstract class ProcessUtils {
return readPid(pidFile);
}
/* (non-Javadoc) */
@SuppressWarnings("all")
protected static File findPidFile(File workingDirectory) {
Assert.isTrue(FileSystemUtils.isDirectory(workingDirectory), String.format(
@@ -144,6 +154,7 @@ public abstract class ProcessUtils {
return null;
}
/* (non-Javadoc) */
@SuppressWarnings("all")
public static int readPid(File pidFile) {
Assert.isTrue(pidFile != null && pidFile.isFile(), String.format(
@@ -159,10 +170,10 @@ public abstract class ProcessUtils {
return Integer.parseInt(pidValue);
}
catch (FileNotFoundException e) {
throw new PidUnavailableException(String.format("PID file [%1$s] not found", pidFile), 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 [%1$s]", pidFile), e);
throw new PidUnavailableException(String.format("failed to read PID from file [%s]", pidFile), e);
}
catch (NumberFormatException e) {
throw new PidUnavailableException(String.format(
@@ -173,6 +184,7 @@ public abstract class ProcessUtils {
}
}
/* (non-Javadoc) */
@SuppressWarnings("all")
public static void writePid(File pidFile, int pid) throws IOException {
Assert.isTrue(pidFile != null && (pidFile.isFile() || pidFile.createNewFile()), String.format(
@@ -191,20 +203,28 @@ public abstract class ProcessUtils {
}
}
/* (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"));

View File

@@ -18,21 +18,32 @@
package org.springframework.data.gemfire.test.support;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.data.gemfire.util.ArrayUtils.asArray;
import java.io.File;
import java.io.IOException;
import java.net.Socket;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
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.process.ProcessExecutor;
import org.springframework.data.gemfire.process.ProcessWrapper;
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 org.apache.geode.cache.server.CacheServer
* @see org.springframework.context.annotation.AnnotationConfigApplicationContext
* @see org.springframework.data.gemfire.process.ProcessExecutor
* @see org.springframework.data.gemfire.process.ProcessWrapper
* @see org.springframework.data.gemfire.test.support.SocketUtils
* @see org.springframework.data.gemfire.test.support.ThreadUtils
* @since 1.9.0
@@ -41,37 +52,164 @@ import org.apache.geode.cache.server.CacheServer;
public class ClientServerIntegrationTestsSupport {
protected static final long DEFAULT_WAIT_DURATION = TimeUnit.SECONDS.toMillis(20);
protected static final long DEFAULT_WAIT_INTERVAL = 500L;
protected static final long DEFAULT_WAIT_INTERVAL = 500L; // milliseconds
protected static final String DEFAULT_GEMFIRE_LOG_FILE = "gemfire-server.log";
protected static final String DEFAULT_GEMFIRE_LOG_LEVEL = "config";
protected static final String DIRECTORY_DELETE_ON_EXIT_PROPERTY = "sdg.directory.delete-on-exit";
protected static final String GEMFIRE_LOG_FILE_PROPERTY = "gemfire.log.file";
protected static final String GEMFIRE_LOG_LEVEL_PROPERTY = "gemfire.log.level";
protected static final String PROCESS_RUN_MANUAL_PROPERTY = "sdg.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) {
File directory = new File(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();
directory.deleteOnExit();
if (isDeleteDirectoryOnExit()) {
directory.deleteOnExit();
}
return directory;
}
/* (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(DEFAULT_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(DEFAULT_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 (isProcessRunManual() ? null
: ProcessExecutor.launch(createDirectory(workingDirectory), type, arguments));
}
/* (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 (isProcessRunManual() ? null
: ProcessExecutor.launch(createDirectory(workingDirectory), classpath, type, arguments));
}
/* (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) {
if (process != null) {
process.stop(duration);
if (process.isNotRunning() && isDeleteDirectoryOnExit()) {
FileSystemUtils.deleteRecursive(process.getWorkingDirectory());
}
return process.isRunning();
}
return true;
}
/* (non-Javadoc) */
protected static boolean waitForCacheServerToStart(CacheServer cacheServer) {
return waitForCacheServerToStart(cacheServer.getBindAddress(), cacheServer.getPort(), DEFAULT_WAIT_DURATION);
return waitForServerToStart(cacheServer.getBindAddress(), cacheServer.getPort(), DEFAULT_WAIT_DURATION);
}
/* (non-Javadoc) */
protected static boolean waitForCacheServerToStart(CacheServer cacheServer, long duration) {
return waitForCacheServerToStart(cacheServer.getBindAddress(), cacheServer.getPort(), duration);
return waitForServerToStart(cacheServer.getBindAddress(), cacheServer.getPort(), duration);
}
/* (non-Javadoc) */
protected static boolean waitForCacheServerToStart(String host, int port) {
return waitForCacheServerToStart(host, port, DEFAULT_WAIT_DURATION);
protected static boolean waitForServerToStart(String host, int port) {
return waitForServerToStart(host, port, DEFAULT_WAIT_DURATION);
}
/* (non-Javadoc) */
protected static boolean waitForCacheServerToStart(final String host, final int port, long duration) {
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);

View File

@@ -52,7 +52,11 @@ public abstract class FileSystemUtils extends FileUtils {
}
}
return ((path == null || path.delete()) && success);
return ((!exists(path) || path.delete()) && success);
}
public static boolean exists(File path) {
return (path != null && path.exists());
}
// returns sub-directory just below working directory
@@ -124,5 +128,4 @@ public abstract class FileSystemUtils extends FileUtils {
return (pathname != null && pathname.isDirectory());
}
}
}