Add Annotation based configuration to configure a client's 'durable-client-id' & 'durable-client-timeout' properties along with keeping the client's server event queue alive.

This commit is contained in:
John Blum
2018-06-04 18:38:44 -07:00
parent 41a7e7bc55
commit 490d8c8ed9
3 changed files with 333 additions and 0 deletions

View File

@@ -0,0 +1,142 @@
/*
* Copyright 2018 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.geode.config.annotation;
import java.lang.annotation.Annotation;
import java.util.Optional;
import org.apache.geode.cache.client.ClientCache;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportAware;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.data.gemfire.config.annotation.ClientCacheConfigurer;
import org.springframework.data.gemfire.config.annotation.PeerCacheConfigurer;
import org.springframework.data.gemfire.config.annotation.support.AbstractAnnotationConfigSupport;
import org.springframework.util.StringUtils;
/**
* The {@link DurableClientConfiguration} class is a Spring {@link Configuration} class used to configure
* this {@link ClientCache} instance as a {@literal Durable Client} by setting the {@literal durable-client-id}
* and {@literal durable-client-timeout} properties in addition to enabling {@literal keepAlive}
* on {@link ClientCache} shutdown.
*
* @author John Blum
* @see org.apache.geode.cache.client.ClientCache
* @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.config.annotation.ClientCacheConfigurer
* @see org.springframework.data.gemfire.config.annotation.PeerCacheConfigurer
* @see org.springframework.data.gemfire.config.annotation.support.AbstractAnnotationConfigSupport
* @see org.springframework.geode.config.annotation.EnableDurableClient
* @since 1.0.0
*/
@Configuration
@SuppressWarnings("unused")
public class DurableClientConfiguration extends AbstractAnnotationConfigSupport implements ImportAware {
public static final boolean DEFAULT_KEEP_ALIVE = true;
public static final int DEFAULT_DURABLE_CLIENT_TIMEOUT = 300;
private Boolean keepAlive = DEFAULT_KEEP_ALIVE;
private Integer durableClientTimeout = DEFAULT_DURABLE_CLIENT_TIMEOUT;
private final Logger logger = LoggerFactory.getLogger(getClass());
private String durableClientId;
@Override
protected Class<? extends Annotation> getAnnotationType() {
return EnableDurableClient.class;
}
@Override
@SuppressWarnings("all")
public void setImportMetadata(AnnotationMetadata importMetadata) {
if (isAnnotationPresent(importMetadata)) {
AnnotationAttributes enableDurableClientAttributes = getAnnotationAttributes(importMetadata);
this.durableClientId = enableDurableClientAttributes.containsKey("id")
? enableDurableClientAttributes.getString("id")
: null;
this.durableClientTimeout = enableDurableClientAttributes.containsKey("timeout")
? enableDurableClientAttributes.getNumber("timeout")
: DEFAULT_DURABLE_CLIENT_TIMEOUT;
this.keepAlive = enableDurableClientAttributes.containsKey("keepAlive")
? enableDurableClientAttributes.getBoolean("keepAlive")
: DEFAULT_KEEP_ALIVE;
}
}
protected Optional<String> getDurableClientId() {
return Optional.ofNullable(this.durableClientId)
.filter(StringUtils::hasText);
}
protected Integer getDurableClientTimeout() {
return Optional.ofNullable(this.durableClientTimeout)
.orElse(DEFAULT_DURABLE_CLIENT_TIMEOUT);
}
public Boolean getKeepAlive() {
return Optional.ofNullable(this.keepAlive)
.orElse(DEFAULT_KEEP_ALIVE);
}
protected Logger getLogger() {
return this.logger;
}
@Bean
ClientCacheConfigurer clientCacheDurableClientConfigurer() {
return (beanName, clientCacheFactoryBean) -> getDurableClientId().ifPresent(durableClientId -> {
clientCacheFactoryBean.setDurableClientId(durableClientId);
clientCacheFactoryBean.setDurableClientTimeout(getDurableClientTimeout());
clientCacheFactoryBean.setKeepAlive(getKeepAlive());
});
}
@Bean
PeerCacheConfigurer peerCacheDurableClientConfigurer() {
return (beanName, cacheFactoryBean) -> getDurableClientId().ifPresent(durableClientId -> {
Logger logger = getLogger();
if (logger.isWarnEnabled()) {
logger.warn("Durable Client ID [{}] was set on a peer Cache instance, which will not have any effect",
durableClientId);
}
});
}
}

View File

@@ -0,0 +1,72 @@
/*
* Copyright 2018 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.geode.config.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.client.ClientCache;
import org.springframework.context.annotation.Import;
/**
* The {@link EnableDurableClient} annotation configures a {@link ClientCache} instance as a {@literal Durable Client}.
*
* @author John Blum
* @see java.lang.annotation.Documented
* @see java.lang.annotation.Inherited
* @see java.lang.annotation.Retention
* @see java.lang.annotation.Target
* @see org.apache.geode.cache.client.ClientCache
* @see org.springframework.context.annotation.Import
* @see org.springframework.geode.config.annotation.DurableClientConfiguration
* @since 1.0.0
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@Import(DurableClientConfiguration.class)
@SuppressWarnings("unused")
public @interface EnableDurableClient {
/**
* Used only for clients in a client/server installation. If set, this indicates that the client is durable
* and identifies the client. The ID is used by servers to reestablish any messaging that was interrupted
* by client downtime.
*/
String id();
/**
* Configure whether the server should keep the durable client's queues alive for the timeout period.
*
* Defaults to {@literal true}.
*/
boolean keepAlive() default DurableClientConfiguration.DEFAULT_KEEP_ALIVE;
/**
* Used only for clients in a client/server installation. Number of seconds this client can remain disconnected
* from its server and have the server continue to accumulate durable events for it.
*
* Defaults to {@literal 300 seconds}, or {@literal 5 minutes}.
*/
int timeout() default DurableClientConfiguration.DEFAULT_DURABLE_CLIENT_TIMEOUT;
}

View File

@@ -0,0 +1,119 @@
/*
* Copyright 2018 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.geode.config.annotation;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicReference;
import org.apache.geode.cache.GemFireCache;
import org.apache.geode.cache.client.ClientCache;
import org.junit.AfterClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.data.gemfire.client.ClientCacheFactoryBean;
import org.springframework.data.gemfire.config.annotation.ClientCacheApplication;
import org.springframework.data.gemfire.tests.integration.IntegrationTestsSupport;
import org.springframework.data.gemfire.tests.mock.annotation.EnableGemFireMockObjects;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
/**
* Integration tests for {@link EnableDurableClient} and {@link DurableClientConfiguration}.
*
* @author John Blum
* @see org.junit.Test
* @see org.mockito.Mockito
* @see org.apache.geode.cache.GemFireCache
* @see org.apache.geode.cache.client.ClientCache
* @see org.springframework.context.ConfigurableApplicationContext
* @see org.springframework.data.gemfire.client.ClientCacheFactoryBean
* @see org.springframework.data.gemfire.config.annotation.ClientCacheApplication
* @see org.springframework.data.gemfire.tests.integration.IntegrationTestsSupport
* @see org.springframework.data.gemfire.tests.mock.annotation.EnableGemFireMockObjects
* @see org.springframework.geode.config.annotation.DurableClientConfiguration
* @see org.springframework.geode.config.annotation.EnableDurableClient
* @see org.springframework.test.context.ContextConfiguration
* @see org.springframework.test.context.junit4.SpringRunner
* @since 1.0.0
*/
@RunWith(SpringRunner.class)
@ContextConfiguration
@SuppressWarnings("unused")
public class DurableClientIdConfigurationIntegrationTests extends IntegrationTestsSupport {
private static final AtomicReference<ConfigurableApplicationContext> applicationContextReference =
new AtomicReference<>(null);
private static final AtomicReference<ClientCache> clientCacheReference =
new AtomicReference<>(null);
@Autowired
private ConfigurableApplicationContext applicationContext;
@Autowired
private GemFireCache gemfireCache;
@Autowired
private ClientCacheFactoryBean clientCacheFactoryBean;
@AfterClass
public static void closeApplicationContext() {
Optional.ofNullable(applicationContextReference.get()).ifPresent(ConfigurableApplicationContext::close);
ClientCache clientCache = clientCacheReference.get();
assertThat(clientCache).isNotNull();
verify(clientCache, times(1)).close(eq(true));
}
@Test
public void durableClientWasConfiguredSuccessfully() {
assertThat(this.gemfireCache).isInstanceOf(ClientCache.class);
assertThat(this.gemfireCache.getDistributedSystem()).isNotNull();
assertThat(this.gemfireCache.getDistributedSystem().getProperties()).isNotNull();
assertThat(this.gemfireCache.getDistributedSystem().getProperties().getProperty("durable-client-id"))
.isEqualTo("abc123");
assertThat(this.gemfireCache.getDistributedSystem().getProperties().getProperty("durable-client-timeout"))
.isEqualTo("600");
applicationContextReference.set(this.applicationContext);
clientCacheReference.set((ClientCache) this.gemfireCache);
}
@Test
public void setClientCacheFactoryBeanSetsKeepAliveOnClose() {
assertThat(this.clientCacheFactoryBean).isNotNull();
assertThat(this.clientCacheFactoryBean.isKeepAlive()).isTrue();
}
@ClientCacheApplication
@EnableGemFireMockObjects
@EnableDurableClient(id = "abc123", timeout = 600)
static class TestConfiguration { }
}