Restructure the Spring Boot for Apache Geode project to mirror Spring Boot's project structure.

Resolves gh-60.
This commit is contained in:
John Blum
2022-02-24 16:23:35 -08:00
parent 0e8e3baeeb
commit 192133b95e
576 changed files with 0 additions and 19 deletions

View File

@@ -0,0 +1,19 @@
apply plugin: 'io.spring.convention.spring-module'
description = "Apache Geode Extensions"
dependencies {
api "org.apache.geode:geode-core:$apacheGeodeVersion"
api "org.apache.geode:geode-cq:$apacheGeodeVersion"
api "org.apache.geode:geode-lucene:$apacheGeodeVersion"
api "org.apache.geode:geode-wan:$apacheGeodeVersion"
implementation "com.fasterxml.jackson.core:jackson-databind"
// See additional testImplementation dependencies declared in the testDependencies project extension
// defined in the DependencySetPlugin.
testImplementation "org.apache.geode:geode-membership:$apacheGeodeVersion"
testImplementation "org.apache.geode:geode-serialization:$apacheGeodeVersion"
}

View File

@@ -0,0 +1,104 @@
/*
* Copyright 2017-present 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
*
* https://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.cache;
import org.apache.geode.cache.CacheListener;
import org.apache.geode.cache.EntryEvent;
import org.apache.geode.cache.RegionEvent;
import org.apache.geode.cache.util.CacheListenerAdapter;
/**
* An {@link Class abstract base class} implementing the Apache Geode {@link CacheListener} interface
* by extending the {@link CacheListenerAdapter} base class, which processes all {@link EntryEvent EntryEvents}
* and {@link RegionEvent RegionEvents} using the same logic.
*
* @author John Blum
* @see org.apache.geode.cache.CacheListener
* @see org.apache.geode.cache.EntryEvent
* @see org.apache.geode.cache.RegionEvent
* @see org.apache.geode.cache.util.CacheListenerAdapter
* @since 1.1.0
*/
public abstract class AbstractCommonEventProcessingCacheListener<K, V> extends CacheListenerAdapter<K, V> {
@Override
public void afterCreate(EntryEvent<K, V> event) {
processEntryEvent(event, EntryEventType.CREATE);
}
@Override
public void afterDestroy(EntryEvent<K, V> event) {
processEntryEvent(event, EntryEventType.DESTROY);
}
@Override
public void afterInvalidate(EntryEvent<K, V> event) {
processEntryEvent(event, EntryEventType.INVALIDATE);
}
@Override
public void afterUpdate(EntryEvent<K, V> event) {
processEntryEvent(event, EntryEventType.UPDATE);
}
protected void processEntryEvent(EntryEvent<K, V> event, EntryEventType eventType) { }
@Override
public void afterRegionClear(RegionEvent<K, V> event) {
processRegionEvent(event, RegionEventType.CLEAR);
}
@Override
public void afterRegionCreate(RegionEvent<K, V> event) {
processRegionEvent(event, RegionEventType.CREATE);
}
@Override
public void afterRegionDestroy(RegionEvent<K, V> event) {
processRegionEvent(event, RegionEventType.DESTROY);
}
@Override
public void afterRegionInvalidate(RegionEvent<K, V> event) {
processRegionEvent(event, RegionEventType.INVALIDATE);
}
@Override
public void afterRegionLive(RegionEvent<K, V> event) {
processRegionEvent(event, RegionEventType.LIVE);
}
protected void processRegionEvent(RegionEvent<K, V> event, RegionEventType eventType) { }
public enum EntryEventType {
CREATE,
DESTROY,
INVALIDATE,
UPDATE;
}
public enum RegionEventType {
CLEAR,
CREATE,
DESTROY,
INVALIDATE,
LIVE;
}
}

View File

@@ -0,0 +1,155 @@
/*
* Copyright 2017-present 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
*
* https://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.cache;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicReference;
import org.apache.geode.cache.Cache;
import org.apache.geode.cache.CacheFactory;
import org.apache.geode.cache.GemFireCache;
import org.apache.geode.cache.RegionService;
import org.apache.geode.cache.client.ClientCache;
import org.apache.geode.cache.client.ClientCacheFactory;
import org.springframework.geode.util.CacheUtils;
/**
* The {@link SimpleCacheResolver} abstract class contains utility functions for resolving Apache Geode
* {@link GemFireCache} instances, such as a {@link ClientCache} or a {@literal peer} {@link Cache}.
*
* @author John Blum
* @see org.apache.geode.cache.Cache
* @see org.apache.geode.cache.CacheFactory
* @see org.apache.geode.cache.GemFireCache
* @see org.apache.geode.cache.RegionService
* @see org.apache.geode.cache.client.ClientCache
* @see org.apache.geode.cache.client.ClientCacheFactory
* @since 1.3.0
*/
@SuppressWarnings("unused")
public abstract class SimpleCacheResolver {
private static final AtomicReference<SimpleCacheResolver> instance = new AtomicReference<>(null);
/**
* Lazily constructs and gets an instance to the {@link SimpleCacheResolver}, as needed.
*
* @return an instance of the {@link SimpleCacheResolver}.
* @see #newSimpleCacheResolver()
*/
public static SimpleCacheResolver getInstance() {
return instance.updateAndGet(cacheResolver -> cacheResolver != null
? cacheResolver
: newSimpleCacheResolver());
}
// TODO Consider resolving the SimpleCacheResolver instance using Java's ServiceProvider API.
private static SimpleCacheResolver newSimpleCacheResolver() {
return new SimpleCacheResolver() { };
}
/**
* The 1st {@code resolve():Optional<? extends GemFireCache>} method signature avoids the cast
* and the @SuppressWarnings("unchecked") annotation, but puts the burden on the caller.
* The 2nd {@code resolve():Optional<T extends GemFireCache>} method signature requires a cast
* and the @SuppressWarnings("unchecked") annotation, but avoids putting the burden on the caller.
*/
private static void testCallResolve() {
Optional<ClientCache> clientCache = getInstance().resolve();
}
/**
* The resolution algorithm first tries to resolve an {@link Optional} {@link ClientCache} instance
* then a {@literal peer} {@link Cache} instance if a {@link ClientCache} is not present.
*
* If neither a {@link ClientCache} or {@literal peer} {@link Cache} is available, then {@link Optional#empty()}
* is returned. No {@link Throwable Exception} is thrown.
*
* @param <T> {@link Class subclass} of {@link GemFireCache}.
* @return a {@link ClientCache} or then a {@literal peer} {@link Cache} instance if present.
* @see org.apache.geode.cache.client.ClientCache
* @see org.apache.geode.cache.Cache
* @see java.util.Optional
* @see #resolveClientCache()
* @see #resolvePeerCache()
*/
//public static Optional<? extends GemFireCache> resolve() {
@SuppressWarnings("unchecked")
public <T extends GemFireCache> Optional<T> resolve() {
Optional<ClientCache> clientCache = resolveClientCache();
return (Optional<T>) (clientCache.isPresent() ? clientCache : resolvePeerCache());
}
/**
* Attempts to resolve an {@link Optional} {@link ClientCache} instance.
*
* @return an {@link Optional} {@link ClientCache} instance.
* @see org.springframework.geode.util.CacheUtils#isClientCache(RegionService)
* @see org.apache.geode.cache.client.ClientCacheFactory#getAnyInstance()
* @see org.apache.geode.cache.client.ClientCache
* @see java.util.Optional
*/
public Optional<ClientCache> resolveClientCache() {
try {
return Optional.ofNullable(ClientCacheFactory.getAnyInstance())
.filter(CacheUtils::isClientCache);
}
catch (Throwable ignore) {
return Optional.empty();
}
}
/**
* Attempts to resolve an {@link Optional} {@link Cache} instance.
*
* @return an {@link Optional} {@link Cache} instance.
* @see org.springframework.geode.util.CacheUtils#isPeerCache(RegionService)
* @see org.apache.geode.cache.CacheFactory#getAnyInstance()
* @see org.apache.geode.cache.Cache
* @see java.util.Optional
*/
public Optional<Cache> resolvePeerCache() {
try {
return Optional.ofNullable(CacheFactory.getAnyInstance())
.filter(CacheUtils::isPeerCache);
}
catch (Throwable ignore) {
return Optional.empty();
}
}
/**
* Requires an instance of either a {@link ClientCache} or a {@literal peer} {@link Cache}.
*
* @param <T> {@link Class subclass} of {@link GemFireCache} to resolve.
* @return an instance of either a {@link ClientCache} or a {@literal peer} {@link Cache}.
* @throws IllegalStateException if a cache instance cannot be resolved.
* @see org.apache.geode.cache.client.ClientCache
* @see org.apache.geode.cache.Cache
* @see org.apache.geode.cache.GemFireCache
* @see #resolve()
*/
public <T extends GemFireCache> T require() {
return this.<T>resolve()
.orElseThrow(() -> new IllegalStateException("GemFireCache not found"));
}
}

View File

@@ -0,0 +1,161 @@
/*
* Copyright 2017-present 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
*
* https://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.distributed.event;
import java.util.EventObject;
import java.util.Optional;
import org.apache.geode.cache.Cache;
import org.apache.geode.distributed.DistributedMember;
import org.apache.geode.distributed.DistributedSystem;
import org.apache.geode.distributed.internal.DistributionManager;
/**
* {@link EventObject} implementation indicating a membership event in the {@link DistributedSystem}.
*
* @author John Blum
* @param <T> specific {@link Class type} of {@link MembershipEvent}.
* @see java.util.EventObject
* @see org.apache.geode.cache.Cache
* @see org.apache.geode.distributed.DistributedMember
* @see org.apache.geode.distributed.DistributedSystem
* @see org.apache.geode.distributed.internal.DistributionManager
* @since 1.3.0
*/
@SuppressWarnings("unused")
public abstract class MembershipEvent<T extends MembershipEvent<T>> extends EventObject {
private DistributedMember distributedMember;
/**
* Asserts that the given {@link Object target} is not {@literal null}.
*
* @param <T> {@link Class type} of the {@link Object target}.
* @param target {@link Object} to evaluate.
* @param message {@link String} containing the message for the {@link IllegalArgumentException}.
* @param arguments array of {@link Object arguments} to populate the placeholders in the {@link String message}.
* @return the {@link Object target}.
* @throws IllegalArgumentException if {@link Object target} is {@literal null}.
*/
protected static <T> T assertNotNull(T target, String message, Object... arguments) {
if (target == null) {
throw new IllegalArgumentException(String.format(message, arguments));
}
return target;
}
/**
* Constructs a new instance of {@link MembershipEvent} initialized with the given {@link DistributionManager}.
*
* @param distributionManager {@link DistributionManager} used to acquire the {@link Cache}, which is used
* as the {@literal source} of this event.
* @throws IllegalArgumentException if {@link DistributionManager} is {@literal null}.
* @see org.apache.geode.distributed.internal.DistributionManager
*/
public MembershipEvent(DistributionManager distributionManager) {
super(assertNotNull(distributionManager, "DistributionManager must not be null"));
}
/**
* Returns an {@link Optional} reference to the {@literal peer} {@link Cache}.
*
* @return an {@link Optional} reference to the {@literal peer} {@link Cache}.
* @see org.apache.geode.cache.Cache
* @see #getDistributionManager()
* @see java.util.Optional
*/
public Optional<Cache> getCache() {
return Optional.ofNullable(getDistributionManager().getCache());
}
/**
* Returns an {@link Optional} reference to the {@link DistributedMember} that is the subject
* of this {@link MembershipEvent}.
*
* @return an {@link Optional} reference to the {@link DistributedMember} that is the subject of this event.
* @see org.apache.geode.distributed.DistributedMember
* @see #getDistributionManager()
* @see java.util.Optional
*/
public Optional<DistributedMember> getDistributedMember() {
return Optional.ofNullable(this.distributedMember);
}
/**
* Returns an {@link Optional} reference to the {@link DistributedSystem} (cluster) to which the {@literal peer}
* {@link Cache} is connected.
*
* @return an {@link Optional} reference to the {@link DistributedSystem}.
* @see org.apache.geode.distributed.DistributedSystem
* @see #getDistributionManager()
* @see java.util.Optional
*/
public Optional<DistributedSystem> getDistributedSystem() {
return Optional.ofNullable(getDistributionManager().getSystem());
}
/**
* Returns a reference to the configured {@link DistributionManager} which is use as the {@link #getSource() source}
* of this event.
*
* @return a reference to the {@link DistributionManager}.
* @see org.apache.geode.distributed.internal.DistributionManager
*/
public DistributionManager getDistributionManager() {
return (DistributionManager) getSource();
}
/**
* Returns the {@link Type} of this {@link MembershipEvent}, such as {@link Type#MEMBER_JOINED}.
*
* @return the {@link MembershipEvent.Type}.
*/
public Type getType() {
return Type.UNQUALIFIED;
}
/**
* Null-safe builder method used to configure the {@link DistributedMember member} that is the subject
* of this event.
*
* @param distributedMember {@link DistributedMember} that is the subject of this event.
* @return this {@link MembershipEvent}.
* @see org.apache.geode.distributed.DistributedMember
* @see #getDistributedMember()
*/
@SuppressWarnings("unchecked")
public T withMember(DistributedMember distributedMember) {
this.distributedMember = distributedMember;
return (T) this;
}
/**
* An {@link Enum enumeration} of different type of {@link MembershipEvent MembershipEvents}.
*/
public enum Type {
MEMBER_DEPARTED,
MEMBER_JOINED,
MEMBER_SUSPECT,
QUORUM_LOST,
UNQUALIFIED;
}
}

View File

@@ -0,0 +1,135 @@
/*
* Copyright 2017-present 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
*
* https://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.distributed.event;
import java.util.EventListener;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import org.apache.geode.cache.Cache;
import org.apache.geode.distributed.internal.DistributionManager;
import org.apache.geode.distributed.internal.InternalDistributedSystem;
import org.apache.geode.distributed.internal.MembershipListener;
import org.apache.geode.distributed.internal.membership.InternalDistributedMember;
import org.springframework.geode.distributed.event.support.MemberDepartedEvent;
import org.springframework.geode.distributed.event.support.MemberJoinedEvent;
import org.springframework.geode.distributed.event.support.MemberSuspectEvent;
import org.springframework.geode.distributed.event.support.QuorumLostEvent;
/**
* An abstract {@link MembershipListener} implementation using the
* <a href="https://en.wikipedia.org/wiki/Adapter_pattern">Adapter Software Design Pattern</a>
* to delegate membership event callbacks to event handlers for those membership events.
*
* @author John Blum
* @param <T> specific {@link Class sub-type} of this {@link MembershipListenerAdapter}.
* @see java.util.EventListener
* @see org.apache.geode.cache.Cache
* @see org.apache.geode.distributed.internal.DistributionManager
* @see org.apache.geode.distributed.internal.InternalDistributedSystem
* @see org.apache.geode.distributed.internal.MembershipListener
* @see org.apache.geode.distributed.internal.membership.InternalDistributedMember
* @since 1.3.0
*/
@SuppressWarnings("unused")
public abstract class MembershipListenerAdapter<T extends MembershipListenerAdapter<T>>
implements EventListener, MembershipListener {
/**
* @inheritDoc
*/
@Override
public final void memberDeparted(DistributionManager manager, InternalDistributedMember member, boolean crashed) {
MemberDepartedEvent event = new MemberDepartedEvent(manager)
.withMember(member)
.crashed(crashed);
handleMemberDeparted(event);
}
public void handleMemberDeparted(MemberDepartedEvent event) { }
/**
* @inheritDoc
*/
@Override
public final void memberJoined(DistributionManager manager, InternalDistributedMember member) {
MemberJoinedEvent event = new MemberJoinedEvent(manager)
.withMember(member);
handleMemberJoined(event);
}
public void handleMemberJoined(MemberJoinedEvent event) { }
/**
* @inheritDoc
*/
@Override
public final void memberSuspect(DistributionManager manager, InternalDistributedMember member,
InternalDistributedMember suspectMember, String reason) {
MemberSuspectEvent event = new MemberSuspectEvent(manager)
.withMember(member)
.withReason(reason)
.withSuspect(suspectMember);
handleMemberSuspect(event);
}
public void handleMemberSuspect(MemberSuspectEvent event) { }
/**
* @inheritDoc
*/
@Override
public final void quorumLost(DistributionManager manager, Set<InternalDistributedMember> failedMembers,
List<InternalDistributedMember> remainingMembers) {
QuorumLostEvent event = new QuorumLostEvent(manager)
.withFailedMembers(failedMembers)
.withRemainingMembers(remainingMembers);
handleQuorumLost(event);
}
public void handleQuorumLost(QuorumLostEvent event) { }
/**
* Registers this {@link MembershipListener} with the given {@literal peer} {@link Cache}.
*
* @param peerCache {@literal peer} {@link Cache} on which to register this {@link MembershipListener}.
* @return this {@link MembershipListenerAdapter}.
* @see org.apache.geode.cache.Cache
*/
@SuppressWarnings("unchecked")
public T register(Cache peerCache) {
Optional.ofNullable(peerCache)
.map(Cache::getDistributedSystem)
.filter(InternalDistributedSystem.class::isInstance)
.map(InternalDistributedSystem.class::cast)
.map(InternalDistributedSystem::getDistributionManager)
.ifPresent(distributionManager -> distributionManager
.addMembershipListener(this));
return (T) this;
}
}

View File

@@ -0,0 +1,81 @@
/*
* Copyright 2017-present 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
*
* https://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.distributed.event.support;
import org.apache.geode.distributed.DistributedMember;
import org.apache.geode.distributed.DistributedSystem;
import org.apache.geode.distributed.internal.DistributionManager;
import org.springframework.geode.distributed.event.MembershipEvent;
/**
* {@link MembershipEvent} fired when a {@link DistributedMember} departs from the {@link DistributedSystem}.
*
* @author John Blum
* @see org.apache.geode.distributed.DistributedMember
* @see org.apache.geode.distributed.DistributedSystem
* @see org.springframework.geode.distributed.event.MembershipEvent
* @since 1.3.0
*/
public class MemberDepartedEvent extends MembershipEvent<MemberDepartedEvent> {
private boolean crashed = false;
/**
* Constructs a new instance of {@link MemberDepartedEvent} initialized with the given {@link DistributionManager}.
*
* @param distributionManager {@link DistributionManager} used as the {@link #getSource() source} of this event;
* must not be {@literal null}.
* @throws IllegalArgumentException if {@link DistributionManager} is {@literal null}.
* @see org.apache.geode.distributed.internal.DistributionManager
*/
public MemberDepartedEvent(DistributionManager distributionManager) {
super(distributionManager);
}
/**
* Determines whether the peer member crashed when it departed from the {@link DistributedSystem} (cluster).
*
* @return a boolean value indicating whether the peer member crashed when it departed
* from the {@link DistributedSystem} (cluster).
*/
public boolean isCrashed() {
return this.crashed;
}
/**
* Builder method used to configure the {@link #isCrashed()} property indicating whether the peer member crashed
* when it departed from the {@link DistributedSystem}.
*
* @param crashed boolean value indicating whether the peer member crashed.
* @return this {@link MemberDepartedEvent}.
* @see #isCrashed()
*/
public MemberDepartedEvent crashed(boolean crashed) {
this.crashed = crashed;
return this;
}
/**
* @inheritDoc
*/
@Override
public final Type getType() {
return Type.MEMBER_DEPARTED;
}
}

View File

@@ -0,0 +1,54 @@
/*
* Copyright 2017-present 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
*
* https://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.distributed.event.support;
import org.apache.geode.distributed.DistributedMember;
import org.apache.geode.distributed.DistributedSystem;
import org.apache.geode.distributed.internal.DistributionManager;
import org.springframework.geode.distributed.event.MembershipEvent;
/**
* {@link MembershipEvent} fired when a {@link DistributedMember} joins the {@link DistributedSystem}.
*
* @author John Blum
* @see org.apache.geode.distributed.DistributedMember
* @see org.apache.geode.distributed.DistributedSystem
* @see org.springframework.geode.distributed.event.MembershipEvent
* @since 1.3.0
*/
public class MemberJoinedEvent extends MembershipEvent<MemberJoinedEvent> {
/**
* Constructs a new instance of {@link MemberJoinedEvent} initialized with the given {@link DistributionManager}.
*
* @param distributionManager {@link DistributionManager} used as the {@link #getSource() source} of this event;
* must not be {@literal null}.
* @throws IllegalArgumentException if {@link DistributionManager} is {@literal null}.
* @see org.apache.geode.distributed.internal.DistributionManager
*/
public MemberJoinedEvent(DistributionManager distributionManager) {
super(distributionManager);
}
/**
* @inheritDoc
*/
@Override
public Type getType() {
return Type.MEMBER_JOINED;
}
}

View File

@@ -0,0 +1,113 @@
/*
* Copyright 2017-present 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
*
* https://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.distributed.event.support;
import java.util.Optional;
import org.apache.geode.distributed.DistributedMember;
import org.apache.geode.distributed.DistributedSystem;
import org.apache.geode.distributed.internal.DistributionManager;
import org.springframework.geode.distributed.event.MembershipEvent;
/**
* {@link MembershipEvent} fired when a {@link DistributedMember} of the {@link DistributedSystem} is suspected
* of being unresponsive to other {@link DistributedMember peer members} in the cluster.
*
* @author John Blum
* @see org.apache.geode.distributed.DistributedMember
* @see org.apache.geode.distributed.DistributedSystem
* @see org.springframework.geode.distributed.event.MembershipEvent
* @since 1.3.0
*/
public class MemberSuspectEvent extends MembershipEvent<MemberSuspectEvent> {
private DistributedMember suspectMember;
private String reason;
/**
* Constructs a new instance of {@link MemberSuspectEvent} initialized with the given {@link DistributionManager}.
*
* @param distributionManager {@link DistributionManager} used as the {@link #getSource() source} of this event;
* must not be {@literal null}.
* @throws IllegalArgumentException if {@link DistributionManager} is {@literal null}.
* @see org.apache.geode.distributed.internal.DistributionManager
*/
public MemberSuspectEvent(DistributionManager distributionManager) {
super(distributionManager);
}
/**
* Returns an {@link Optional} {@link String reason} describing the suspicion of the peer member.
*
* @return an {@link Optional} {@link String reason} describing the suspicion of the peer member.
* @see java.util.Optional
*/
public Optional<String> getReason() {
return Optional.ofNullable(this.reason);
}
/**
* Returns an {@link Optional} {@link DistributedMember} identified as the suspect in the {@link MembershipEvent}.
*
* @return an {@link Optional} {@link DistributedMember} identified as the suspect in the {@link MembershipEvent}.
* @see org.apache.geode.distributed.DistributedMember
* @see java.util.Optional
*/
public Optional<DistributedMember> getSuspectMember() {
return Optional.ofNullable(this.suspectMember);
}
/**
* Builder method used to configure the {@link String reason} describing the suspicion of
* the {@link DistributedMember suspect member}.
*
* @param reason {@link String} describing the suspicion of the {@link DistributedMember peer member};
* may be {@literal null}.
* @return this {@link MemberSuspectEvent}.
* @see #getReason()
*/
public MemberSuspectEvent withReason(String reason) {
this.reason = reason;
return this;
}
/**
* Builder method used to configure the {@link DistributedMember peer member} that is the subject of the suspicion
* {@link MembershipEvent}.
*
* @param suspectMember {@link DistributedMember peer member} that is being suspected; may be {@literal null}.
* @return this {@link MemberSuspectEvent}.
* @see #getSuspectMember()
*/
public MemberSuspectEvent withSuspect(DistributedMember suspectMember) {
this.suspectMember = suspectMember;
return this;
}
/**
* @inheritDoc
*/
@Override
public final Type getType() {
return Type.MEMBER_SUSPECT;
}
}

View File

@@ -0,0 +1,161 @@
/*
* Copyright 2017-present 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
*
* https://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.distributed.event.support;
import java.util.Arrays;
import java.util.Collections;
import org.apache.geode.distributed.DistributedMember;
import org.apache.geode.distributed.DistributedSystem;
import org.apache.geode.distributed.internal.DistributionManager;
import org.springframework.geode.distributed.event.MembershipEvent;
/**
* {@link QuorumLostEvent} is fired for the losing side of the {@link DistributedSystem} when
* a network partition occurs.
*
* @author John Blum
* @see org.apache.geode.distributed.DistributedMember
* @see org.apache.geode.distributed.DistributedSystem
* @see org.springframework.geode.distributed.event.MembershipEvent
* @since 1.3.0
*/
public class QuorumLostEvent extends MembershipEvent<QuorumLostEvent> {
private Iterable<? extends DistributedMember> remainingMembers = Collections.emptyList();
private Iterable<? extends DistributedMember> failedMembers = Collections.emptySet();
/**
* Constructs a new instance of {@link QuorumLostEvent} initialized with the given {@link DistributionManager}.
*
* @param distributionManager {@link DistributionManager} used as the {@link #getSource() source} of this event;
* must not be {@literal null}.
* @throws IllegalArgumentException if {@link DistributionManager} is {@literal null}.
* @see org.apache.geode.distributed.internal.DistributionManager
*/
public QuorumLostEvent(DistributionManager distributionManager) {
super(distributionManager);
}
/**
* Gets the configured {@link Iterable} of failed {@link DistributedMember peer members}
* in the {@link DistributedSystem} that are on the losing side of a network partition.
*
* @return an {@link Iterable} of failed {@link DistributedMember peer members}; never {@literal null}.
* @see org.apache.geode.distributed.DistributedMember
* @see #getRemainingMembers()
* @see java.lang.Iterable
*/
public Iterable<? extends DistributedMember> getFailedMembers() {
return this.failedMembers;
}
/**
* Gets the configured {@link Iterable} of remaining {@link DistributedMember peer members}
* in the {@link DistributedSystem} that are on the winning side of a network partition.
*
* @return an {@link Iterable} of remaining {@link DistributedMember peer members}; never {@literal null}.
* @see org.apache.geode.distributed.DistributedMember
* @see #getFailedMembers()
* @see java.lang.Iterable
*/
public Iterable<? extends DistributedMember> getRemainingMembers() {
return this.remainingMembers;
}
/**
* Null-safe builder method used to configure an array of failing {@link DistributedMember peer members}
* in the {@link DistributedSystem} on the losing side of a network partition.
*
* @param failedMembers array of failed {@link DistributedMember peer members}; may be {@literal null}.
* @return this {@link QuorumLostEvent}.
* @see org.apache.geode.distributed.DistributedMember
* @see #withFailedMembers(Iterable)
* @see #getFailedMembers()
*/
public QuorumLostEvent withFailedMembers(DistributedMember... failedMembers) {
return withFailedMembers(failedMembers != null
? Arrays.asList(failedMembers)
: Collections.emptySet());
}
/**
* Null-safe builder method used to configure an {@link Iterable} of failing {@link DistributedMember peer members}
* in the {@link DistributedSystem} on the losing side of a network partition.
*
* @param failedMembers {@link Iterable} of failed {@link DistributedMember peer members}; may be {@literal null}.
* @return this {@link QuorumLostEvent}.
* @see org.apache.geode.distributed.DistributedMember
* @see #getFailedMembers()
* @see java.lang.Iterable
*/
public QuorumLostEvent withFailedMembers(Iterable<? extends DistributedMember> failedMembers) {
this.failedMembers = failedMembers != null
? failedMembers
: Collections.emptySet();
return this;
}
/**
* Null-safe builder method used to configure an array of remaining {@link DistributedMember peer members}
* in the {@link DistributedSystem} on the winning side of a network partition.
*
* @param remainingMembers array of remaining {@link DistributedMember peer members}; may be {@literal null}.
* @return this {@link QuorumLostEvent}.
* @see org.apache.geode.distributed.DistributedMember
* @see #withRemainingMembers(Iterable)
* @see #getRemainingMembers()
*/
public QuorumLostEvent withRemainingMembers(DistributedMember... remainingMembers) {
return withRemainingMembers(remainingMembers != null
? Arrays.asList(remainingMembers)
: Collections.emptyList());
}
/**
* Null-safe builder method used to configure an {@link Iterable} of remaining {@link DistributedMember peer members}
* in the {@link DistributedSystem} on the winning side of a network partition.
*
* @param remainingMembers {@link Iterable} of remaining {@link DistributedMember peer members};
* may be {@literal null}.
* @return this {@link QuorumLostEvent}.
* @see org.apache.geode.distributed.DistributedMember
* @see #getRemainingMembers()
* @see java.lang.Iterable
*/
public QuorumLostEvent withRemainingMembers(Iterable<? extends DistributedMember> remainingMembers) {
this.remainingMembers = remainingMembers != null
? remainingMembers
: Collections.emptyList();
return this;
}
/**
* @inheritDoc
*/
@Override
public final Type getType() {
return Type.QUORUM_LOST;
}
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright 2017-present 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
*
* https://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.pdx;
import org.apache.geode.pdx.PdxInstance;
/**
* A {@link RuntimeException} thrown to indicate that a PDX field of a {@link PdxInstance} is not writable.
*
* @author John Blum
* @see java.lang.RuntimeException
* @see org.apache.geode.pdx.PdxInstance
* @since 1.3.0
*/
@SuppressWarnings("unused")
public class PdxFieldNotWritableException extends RuntimeException {
public PdxFieldNotWritableException() { }
public PdxFieldNotWritableException(String message) {
super(message);
}
public PdxFieldNotWritableException(Throwable cause) {
super(cause);
}
public PdxFieldNotWritableException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@@ -0,0 +1,206 @@
/*
* Copyright 2017-present 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
*
* https://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.pdx;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicReference;
import org.apache.geode.cache.GemFireCache;
import org.apache.geode.cache.RegionService;
import org.apache.geode.pdx.PdxInstance;
import org.apache.geode.pdx.PdxInstanceFactory;
import org.springframework.geode.cache.SimpleCacheResolver;
/**
* The {@link PdxInstanceBuilder} class is a <a href="https://en.wikipedia.org/wiki/Builder_pattern">Builder</a>
* used to construct and initialize a {@link PdxInstance} from different sources.
*
* @author John Blum
* @see org.apache.geode.cache.GemFireCache
* @see org.apache.geode.cache.RegionService
* @see org.apache.geode.pdx.PdxInstance
* @see org.apache.geode.pdx.PdxInstanceFactory
* @see <a href="https://en.wikipedia.org/wiki/Builder_pattern">Builder Software Design Pattern</a>
* @since 1.3.0
*/
@SuppressWarnings("unused")
public class PdxInstanceBuilder {
/**
* Factory method used to construct a new instance of the {@link PdxInstanceBuilder} class.
*
* This factory method tries to resolve the {@link GemFireCache} instance for the caller
* by using {@link SimpleCacheResolver}.
*
* Alternatively, callers may provider their own {@link GemFireCache} instance by calling
* {@link #create(RegionService)}.
*
* @return a new instance of the {@link PdxInstanceBuilder}.
* @throws IllegalArgumentException if a {@link GemFireCache} instance is not present.
* @see #create(RegionService)
*/
public static PdxInstanceBuilder create() {
return create(SimpleCacheResolver.getInstance().require());
}
/**
* Factory method use to construct a new instance of the {@link PdxInstanceBuilder} class initialized with
* the given, required {@link RegionService} used by the Builder to performs its functions.
*
* @param regionService {@link RegionService} instance used by the {@link PdxInstanceBuilder} to perform
* its functions;
* must not be {@literal null}.
* @return an new instance of the {@link PdxInstanceBuilder}.
* @throws IllegalArgumentException if {@link GemFireCache} is {@literal null}.
* @see org.apache.geode.cache.GemFireCache
* @see #PdxInstanceBuilder(RegionService)
*/
public static PdxInstanceBuilder create(RegionService regionService) {
return new PdxInstanceBuilder(regionService);
}
private static void assertNotNull(Object target, String message, Object... arguments) {
if (target == null) {
throw new IllegalArgumentException(String.format(message, arguments));
}
}
private static <T> List<T> nullSafeList(List<T> list) {
return list != null ? list : Collections.emptyList();
}
private final RegionService regionService;
private PdxInstanceFactory pdxFactory;
/**
* Constructs a new instance of {@link PdxInstanceBuilder} initialized with the required {@link RegionService}.
*
* @param regionService {@link RegionService} instance used to perform the functions of the PDX Builder.
* @throws IllegalArgumentException if {@link RegionService} is {@literal null}.
* @see org.apache.geode.cache.RegionService
*/
protected PdxInstanceBuilder(RegionService regionService) {
assertNotNull(regionService, "RegionService must not be null");
this.regionService = regionService;
}
/**
* Returns a reference to the configured {@link RegionService} used to perform the operations of this PDX Builder.
*
* @return a reference to the configured {@link RegionService}; never {@literal null}.
*/
protected RegionService getRegionService() {
return this.regionService;
}
/**
* Copies the contents of the existing {@link PdxInstance} to a new {@link PdxInstance} built with this Builder.
*
* @param pdxInstance {@link PdxInstance} to copy.
* @return an instance of the {@link PdxInstanceFactory} used to {@link PdxInstanceFactory#create() create}
* the {@link PdxInstance}.
* @throws IllegalArgumentException if {@link PdxInstance} is {@literal null}.
* @see org.apache.geode.pdx.PdxInstance
* @see org.apache.geode.pdx.PdxInstanceFactory
*/
public PdxInstanceFactory copy(PdxInstance pdxInstance) {
assertNotNull(pdxInstance, "PdxInstance must not be null");
PdxInstanceFactory factory = getRegionService().createPdxInstanceFactory(pdxInstance.getClassName());
nullSafeList(pdxInstance.getFieldNames()).forEach(fieldName -> {
factory.writeObject(fieldName, pdxInstance.getField(fieldName));
if (pdxInstance.isIdentityField(fieldName)) {
factory.markIdentityField(fieldName);
}
});
return factory;
}
/**
* Constructs a new {@link PdxInstance} from the given, required source {@link Object}.
*
* @param source {@link Object} being serialized to PDX; must not be {@literal null}.
* @return a {@link Factory} used to create the {@link PdxInstance} from the given,
* required source {@link Object}, which was serialized to PDX.
* @throws IllegalArgumentException if {@link Object source} is {@literal null}.
* @see Factory
*/
public Factory from(Object source) {
assertNotNull(source, "Source object to serialize to PDX must not be null");
RegionService regionService = getRegionService();
Optional.of(regionService)
.filter(GemFireCache.class::isInstance)
.map(GemFireCache.class::cast)
.map(GemFireCache::getPdxReadSerialized)
.filter(Boolean.TRUE::equals)
.orElseThrow(() -> new IllegalStateException("PDX read-serialized must be set to true"));
PdxInstanceFactory factory = regionService.createPdxInstanceFactory(source.getClass().getName());
factory.writeObject("source", source);
AtomicReference<Object> resolvedSource = new AtomicReference<>(null);
return () -> Optional.of(factory)
.map(PdxInstanceFactory::create)
.map(pdxInstance -> resolvedSource.updateAndGet(it -> pdxInstance.getField("source")))
.filter(PdxInstance.class::isInstance)
.map(PdxInstance.class::cast)
.orElseThrow(() -> {
String message = String.format("Expected an instance of PDX but was an instance of type [%s];"
+ " Was PDX read-serialized set to true", nullSafeClassName(resolvedSource.get()));
return new IllegalArgumentException(message);
});
}
private String nullSafeClassName(Object target) {
return target != null ? target.getClass().getName() : null;
}
@FunctionalInterface
public interface Factory {
/**
* Creates a {@link PdxInstance}.
*
* @return the created {@link PdxInstance}.
* @throws IllegalArgumentException Depending on the implementation, an {@link IllegalArgumentException}
* may be thrown if the {@link Object created object} is not of type {@link PdxInstance}.
* @throws ClassCastException Depending on the implementation, a {@link ClassCastException} may be thrown
* if the {@link Object created object} is not of type {@link PdxInstance}.
* @see org.apache.geode.pdx.PdxInstance
*/
PdxInstance create();
}
}

View File

@@ -0,0 +1,500 @@
/*
* Copyright 2017-present 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
*
* https://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.pdx;
import static org.springframework.geode.util.GeodeAssertions.assertThat;
import java.io.DataOutput;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.json.JsonMapper;
import org.apache.geode.internal.Sendable;
import org.apache.geode.pdx.JSONFormatter;
import org.apache.geode.pdx.PdxInstance;
import org.apache.geode.pdx.WritablePdxInstance;
/**
* The {@link PdxInstanceWrapper} class is an implementation of the {@link PdxInstance} interface
* wrapping an existing {@link PdxInstance} object and decorating the functionality.
*
* @author John Blum
* @see java.util.function.Function
* @see com.fasterxml.jackson.databind.ObjectMapper
* @see org.apache.geode.pdx.JSONFormatter
* @see org.apache.geode.pdx.PdxInstance
* @see org.apache.geode.pdx.WritablePdxInstance
* @since 1.3.0
*/
public class PdxInstanceWrapper implements PdxInstance, Sendable {
public static final String AT_IDENTIFIER_FIELD_NAME = "@identifier";
public static final String AT_TYPE_FIELD_NAME = "@type";
public static final String CLASS_NAME_PROPERTY = "className";
public static final String ID_FIELD_NAME = "id";
protected static final String NO_FIELD_NAME = "";
protected static final String ARRAY_BEGIN = "[";
protected static final String ARRAY_END = "]";
protected static final String COMMA = ",";
protected static final String EMPTY_STRING = "";
protected static final String FIELD_TYPE_VALUE = "\"%1$s\"(%2$s): \"%3$s\"";
protected static final String INDENT_STRING = "\t";
protected static final String NEW_LINE = "\n";
protected static final String COMMA_NEW_LINE = "," + NEW_LINE;
protected static final String COMMA_SPACE = COMMA + " ";
protected static final String OBJECT_BEGIN = "{";
protected static final String OBJECT_END = "}";
/**
* Smart, {@literal null-safe} factory method used to evaluate the given {@link Object} and wrap the {@link Object}
* in a new instance of {@link PdxInstanceWrapper} if the {@link Object} is an instance of {@link PdxInstance}
* or return the given {@link Object} as is.
*
* @param target {@link Object} to evaluate
* @return the {@link Object} wrapped in a new instance of {@link PdxInstanceWrapper} if {@link Object}
* is an instance of {@link PdxInstance}, otherwise returns the given {@link Object}.
* @see org.apache.geode.pdx.PdxInstance
* @see java.lang.Object
* @see #from(PdxInstance)
*/
public static Object from(Object target) {
return target instanceof PdxInstance
? from((PdxInstance) target)
: target;
}
/**
* Factory method used to construct a new instance of {@link PdxInstanceWrapper} initialized with the given,
* required {@link PdxInstance} used to back the wrapper.
*
* @param pdxInstance {@link PdxInstance} object used to back this wrapper; must not be {@literal null}.
* @return a new instance of {@link PdxInstanceWrapper} initialized with the given {@link PdxInstance}.
* @throws IllegalArgumentException if {@link PdxInstance} is {@literal null}.
* @see org.apache.geode.pdx.PdxInstance
* @see #PdxInstanceWrapper(PdxInstance)
*/
public static PdxInstanceWrapper from(PdxInstance pdxInstance) {
return pdxInstance instanceof PdxInstanceWrapper
? (PdxInstanceWrapper) pdxInstance
: new PdxInstanceWrapper(pdxInstance);
}
/**
* Null-safe factory method used to unwrap the given {@link PdxInstance}.
*
* If the given {@link PdxInstance} is an instance of {@link PdxInstanceWrapper} then this factory method will
* unwrap the {@link PdxInstanceWrapper} returning the underlying, {@link PdxInstanceWrapper#getDelegate() delegate}
* {@link PdxInstance}. Otherwise, the given {@link PdxInstance} is returned.
*
* @param pdxInstance {@link PdxInstance} to unwrap; may be {@literal null}.
* @return the unwrapped {@link PdxInstance}.
* @see org.apache.geode.pdx.PdxInstance
* @see #getDelegate()
*/
public static PdxInstance unwrap(PdxInstance pdxInstance) {
return pdxInstance instanceof PdxInstanceWrapper
? ((PdxInstanceWrapper) pdxInstance).getDelegate()
: pdxInstance;
}
private final PdxInstance delegate;
/**
* Constructs a new instance of {@link PdxInstanceWrapper} initialized with the given, required {@link PdxInstance}
* object used to back this wrapper.
*
* @param pdxInstance {@link PdxInstance} object used to back this wrapper; must not be {@literal null}.
* @throws IllegalArgumentException if {@link PdxInstance} is {@literal null}.
* @see org.apache.geode.pdx.PdxInstance
*/
public PdxInstanceWrapper(PdxInstance pdxInstance) {
assertThat(pdxInstance).isNotNull();
this.delegate = pdxInstance;
}
/**
* Returns a reference to the configured, underlying {@link PdxInstance} backing this wrapper.
*
* @return a reference to the configured, underlying {@link PdxInstance} backing this wrapper;
* never {@literal null}.
* @see org.apache.geode.pdx.PdxInstance
*/
public PdxInstance getDelegate() {
return this.delegate;
}
/**
* Returns an {@link Optional} reference to a configured Jackson {@link ObjectMapper} used to
* deserialize the {@link String JSON} generated from {@link PdxInstance PDX} back into an {@link Object}.
*
* This method is meant ot be overridden by {@link Class subclasses}.
*
* @return an {@link Optional} {@link ObjectMapper}.
* @see com.fasterxml.jackson.databind.ObjectMapper
* @see java.util.Optional
*/
protected Optional<ObjectMapper> getObjectMapper() {
ObjectMapper objectMapper = newJsonMapperBuilder()
.configure(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES, false)
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS, true)
.build()
.findAndRegisterModules();
return Optional.of(objectMapper);
}
/**
* Constructs a new instance of Jackson's {@link JsonMapper}.
*
* @return a new instance of Jackson's {@link JsonMapper}; never {@literal null}.
* @see com.fasterxml.jackson.databind.json.JsonMapper
*/
JsonMapper.Builder newJsonMapperBuilder() {
return JsonMapper.builder();
}
/**
* @inheritDoc
*/
@Override
public String getClassName() {
return getDelegate().getClassName();
}
/**
* @inheritDoc
*/
@Override
public boolean isDeserializable() {
return getDelegate().isDeserializable();
}
/**
* @inheritDoc
*/
@Override
public boolean isEnum() {
return getDelegate().isEnum();
}
/**
* @inheritDoc
*/
@Override
public Object getField(String fieldName) {
return getDelegate().getField(fieldName);
}
/**
* @inheritDoc
*/
@Override
public List<String> getFieldNames() {
return getDelegate().getFieldNames();
}
/**
* Determines the {@link Object identifier} for, or {@link PdxInstance#isIdentityField(String) identity} of,
* this {@link PdxInstance}.
*
* @return the {@link Object identifier} for this {@link PdxInstance}; never {@literal null}.
* @throws IllegalStateException if the {@link PdxInstance} does not have an id.
* @see #isIdentityField(String)
* @see #getField(String)
* @see #getFieldNames()
* @see #getId()
*/
public Object getIdentifier() {
Optional<String> identityFieldName = nullSafeList(getFieldNames()).stream()
.filter(this::hasText)
.filter(this::isIdentityField)
.findFirst();
return identityFieldName
.map(this::getField)
.orElseGet(this::getId);
}
/**
* Searches for a PDX {@link String field name} called {@literal id} on this {@link PdxInstance}
* and returns its {@link Object value} as the {@link Object identifier} for,
* or {@link PdxInstance#isIdentityField(String) identity} of, this {@link PdxInstance}.
*
* @return the {@link Object value} of the {@literal id} {@link String field} on this {@link PdxInstance}.
* @throws IllegalStateException if this {@link PdxInstance} does not have an id.
* @see #getAtIdentifier()
* @see #getField(String)
* @see #hasField(String)
*/
protected Object getId() {
return hasField(ID_FIELD_NAME)
? getField(ID_FIELD_NAME)
: getAtIdentifier();
}
/**
* Searches for a PDX {@link String field} declared by the {@literal @identifier} metadata {@link String field}
* on this {@link PdxInstance} and returns the {@link Object value} of this {@link String field}
* as the {@link Object identifier} for, or {@link PdxInstance#isIdentityField(String) identity} of,
* this {@link PdxInstance}.
*
* @return the {@link Object value} of the {@link String field} declared in the {@literal @identifier} metadata
* {@link String field} on this {@link PdxInstance}.
* @throws IllegalStateException if the {@link PdxInstance} does not have an id.
* @see org.apache.geode.pdx.PdxInstance
*/
protected Object getAtIdentifier() {
return Optional.of(AT_IDENTIFIER_FIELD_NAME)
.filter(this::hasField)
.map(this::getField)
.map(String::valueOf)
.filter(this::hasField)
.map(this::getField)
.orElseThrow(() -> new IllegalStateException(String.format("PdxInstance for type [%1$s] has no %2$s",
getClassName(), resolveMessageForIdentifierError(this))));
}
private String resolveMessageForIdentifierError(PdxInstance pdxInstance) {
String message = "declared identifier";
if (pdxInstance.hasField(ID_FIELD_NAME)) {
message = "id";
}
else if (pdxInstance.hasField(AT_IDENTIFIER_FIELD_NAME)) {
Object atIdentifierFieldValue = pdxInstance.getField(AT_IDENTIFIER_FIELD_NAME);
String resolvedIdentifierFieldName = Objects.nonNull(atIdentifierFieldValue)
? atIdentifierFieldValue.toString().trim()
: NO_FIELD_NAME;
boolean identifierFieldNameWasDeclaredAndIsValid = pdxInstance.hasField(resolvedIdentifierFieldName);
Object identifier = identifierFieldNameWasDeclaredAndIsValid
? pdxInstance.getField(resolvedIdentifierFieldName)
: null;
String ifMessage = "value [%s] for field [%s] declared in [%s]";
String elseMessage = "field [%s] declared in [%s]";
message = identifierFieldNameWasDeclaredAndIsValid
? String.format(ifMessage, identifier, resolvedIdentifierFieldName, AT_IDENTIFIER_FIELD_NAME)
: String.format(elseMessage, resolvedIdentifierFieldName, AT_IDENTIFIER_FIELD_NAME);
}
return message;
}
/**
* @inheritDoc
*/
@Override
public boolean isIdentityField(String fieldName) {
return getDelegate().isIdentityField(fieldName);
}
/**
* Materializes an {@link Object} from the PDX bytes described by this {@link PdxInstance}.
*
* If these PDX bytes describe an {@link Object} parsed from JSON, then the JSON is reconstructed from
* this {@link PdxInstance} and mapped to an instance of the {@link Class type} identified by
* the {@literal @type} metadata PDX {@link String field} using Jackson's {@link ObjectMapper}.
*
* @return an {@link Object} constructed from the PDX bytes described by this {@link PdxInstance}.
* @see com.fasterxml.jackson.databind.ObjectMapper
* @see java.lang.Object
* @see #getObjectMapper()
*/
@Override
public Object getObject() {
return getObjectMapper()
.filter(objectMapper -> JSONFormatter.JSON_CLASSNAME.equals(getClassName()))
.filter(objectMapper -> hasField(AT_TYPE_FIELD_NAME))
.<Object>map(objectMapper -> {
try {
String typeName = String.valueOf(getField(AT_TYPE_FIELD_NAME));
Class<?> type = Class.forName(typeName);
String json = jsonFormatterToJson(getDelegate());
return objectMapper.readValue(json, type);
}
catch (Throwable ignore) {
// TODO Log Throwable?
return null;
}
})
.orElseGet(() -> getDelegate().getObject());
}
/**
* Calls {@link JSONFormatter#toJSON(PdxInstance)} to convert the {@link PdxInstance} into {@link String JSON}.
*
* @param pdxInstance {@link PdxInstance} to convert to {@link String JSON}.
* @return {@link String JSON} generated from the given {@link PdxInstance}.
* @see org.apache.geode.pdx.JSONFormatter#toJSON(PdxInstance)
* @see org.apache.geode.pdx.PdxInstance
*/
String jsonFormatterToJson(PdxInstance pdxInstance) {
return JSONFormatter.toJSON(pdxInstance);
}
/**
* @inheritDoc
*/
@Override
public WritablePdxInstance createWriter() {
return getDelegate().createWriter();
}
/**
* @inheritDoc
*/
@Override
public boolean hasField(String fieldName) {
return getDelegate().hasField(fieldName);
}
/**
* @inheritDoc
*/
@Override
public void sendTo(DataOutput out) throws IOException {
PdxInstance delegate = getDelegate();
if (delegate instanceof Sendable) {
((Sendable) delegate).sendTo(out);
}
}
/**
* Returns a {@link String} representation of this {@link PdxInstance}.
*
* @return a {@link String} representation of this {@link PdxInstance}.
* @see java.lang.String
*/
@Override
public String toString() {
//return getDelegate().toString();
return toString(this);
}
private String toString(PdxInstance pdx) {
return toString(pdx, "");
}
private String toString(PdxInstance pdx, String indent) {
if (Objects.nonNull(pdx)) {
StringBuilder buffer = new StringBuilder(OBJECT_BEGIN).append(NEW_LINE);
String fieldIndent = indent + INDENT_STRING;
buffer.append(fieldIndent).append(formatFieldValue(CLASS_NAME_PROPERTY, pdx.getClassName()));
for (String fieldName : nullSafeList(pdx.getFieldNames())) {
Object fieldValue = pdx.getField(fieldName);
String valueString = toStringObject(fieldValue, fieldIndent);
buffer.append(COMMA_NEW_LINE);
buffer.append(fieldIndent).append(formatFieldValue(fieldName, valueString));
}
buffer.append(NEW_LINE).append(indent).append(OBJECT_END);
return buffer.toString();
}
else {
return null;
}
}
private String toStringArray(Object value, String indent) {
Object[] array = (Object[]) value;
StringBuilder buffer = new StringBuilder(ARRAY_BEGIN);
boolean addComma = false;
for (Object element : array) {
buffer.append(addComma ? COMMA_SPACE : EMPTY_STRING);
buffer.append(toStringObject(element, indent));
addComma = true;
}
buffer.append(ARRAY_END);
return buffer.toString();
}
private String toStringObject(Object value, String indent) {
return isPdxInstance(value) ? toString((PdxInstance) value, indent)
: isArray(value) ? toStringArray(value, indent)
: String.valueOf(value);
}
private String formatFieldValue(String fieldName, Object fieldValue) {
return String.format(FIELD_TYPE_VALUE, fieldName, nullSafeType(fieldValue), fieldValue);
}
private boolean hasText(String value) {
return value != null && !value.trim().isEmpty();
}
private boolean isArray(Object value) {
return Objects.nonNull(value) && value.getClass().isArray();
}
private boolean isPdxInstance(Object value) {
return value instanceof PdxInstance;
}
private <T> List<T> nullSafeList(List<T> list) {
return list != null ? list : Collections.emptyList();
}
private Class<?> nullSafeType(Object value) {
return value != null ? value.getClass() : Object.class;
}
}

View File

@@ -0,0 +1,61 @@
/*
* Copyright 2017-present 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
*
* https://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.security;
import java.util.Properties;
import org.apache.geode.distributed.DistributedMember;
import org.apache.geode.security.AuthInitialize;
import org.apache.geode.security.AuthenticationFailedException;
import org.springframework.geode.util.GeodeConstants;
/**
* Simple, test {@link AuthInitialize} implementation.
*
* @author John Blum
* @see org.apache.geode.security.AuthInitialize
* @since 1.0.0
*/
@SuppressWarnings("unused")
public class TestAuthInitialize implements AuthInitialize {
private static final String DEFAULT_USERNAME = "test";
private static final String DEFAULT_PASSWORD = DEFAULT_USERNAME;
public static TestAuthInitialize create() {
return new TestAuthInitialize();
}
@Override
public Properties getCredentials(Properties securityProperties, DistributedMember server, boolean isPeer)
throws AuthenticationFailedException {
Properties credentials = new Properties();
credentials.setProperty(GeodeConstants.USERNAME,
securityProperties.getProperty(GeodeConstants.USERNAME, DEFAULT_USERNAME));
credentials.setProperty(GeodeConstants.PASSWORD,
securityProperties.getProperty(GeodeConstants.PASSWORD, DEFAULT_PASSWORD));
return credentials;
}
@Override
public void close() { }
}

View File

@@ -0,0 +1,108 @@
/*
* Copyright 2017-present 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
*
* https://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.security;
import java.io.Serializable;
import java.security.Principal;
import java.util.Properties;
import org.apache.geode.security.AuthenticationFailedException;
import org.springframework.geode.util.GeodeConstants;
/**
* Simple, test {@link org.apache.geode.security.SecurityManager}.
*
* @author John Blum
* @see java.security.Principal
* @see java.util.Properties
* @see org.apache.geode.security.SecurityManager
* @since 1.0.0
*/
@SuppressWarnings("unused")
public class TestSecurityManager implements org.apache.geode.security.SecurityManager {
@Override
public Object authenticate(Properties credentials) throws AuthenticationFailedException {
String username = credentials.getProperty(GeodeConstants.USERNAME);
String password = credentials.getProperty(GeodeConstants.PASSWORD);
if (!String.valueOf(username).equals(password)) {
throw new AuthenticationFailedException(String.format("User [%s] could not be authenticated", username));
}
return User.create(username);
}
public static class User implements Comparable<User>, Principal, Serializable {
public static User create(String name) {
return new User(name);
}
private final String name;
public User(String name) {
if (name == null || name.trim().isEmpty()) {
throw new IllegalArgumentException("Username is required");
}
this.name = name;
}
public String getName() {
return this.name;
}
@Override
public int compareTo(User user) {
return this.getName().compareTo(user.getName());
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof User)) {
return false;
}
User that = (User) obj;
return this.getName().equals(that.getName());
}
@Override
public int hashCode() {
int hashValue = 17;
hashValue = 37 * hashValue + getName().hashCode();
return hashValue;
}
@Override
public String toString() {
return getName();
}
}
}

View File

@@ -0,0 +1,259 @@
/*
* Copyright 2017-present 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
*
* https://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.util;
import static org.springframework.geode.util.GeodeAssertions.assertThat;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import org.apache.geode.cache.Cache;
import org.apache.geode.cache.DataPolicy;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.RegionAttributes;
import org.apache.geode.cache.RegionService;
import org.apache.geode.cache.client.ClientCache;
import org.apache.geode.cache.client.Pool;
import org.apache.geode.internal.cache.GemFireCacheImpl;
/**
* Abstract utility class for working with Apache Geode cache instances, such as {@link ClientCache}
* and {@literal peer} {@link Cache} instances.
*
* @author John Blum
* @see org.apache.geode.cache.Cache
* @see org.apache.geode.cache.DataPolicy
* @see org.apache.geode.cache.Region
* @see org.apache.geode.cache.RegionAttributes
* @see org.apache.geode.cache.RegionService
* @see org.apache.geode.cache.client.ClientCache
* @see org.apache.geode.cache.client.Pool
* @since 1.3.0
*/
public abstract class CacheUtils {
/**
* Collects all {@link Object values} from the given {@link Region}.
*
* This method is capable of pulling {@link Object values} from either {@literal client}
* or {@literal peer} {@link Region Regions}.
*
* @param <T> {@link Class type} of the {@link Region} {@link Object values}.
* @param region {@link Region} from which to collect the {@link Object values}.
* @return a {@link Collection} of all {@link Object values} from the given {@link Region}.
* @throws IllegalArgumentException if {@link Region} is {@literal null}.
* @see org.apache.geode.cache.Region
* @see java.util.Collection
*/
// TODO Add Predicate-based Filtering
public static <T> Collection<T> collectValues(Region<?, T> region) {
assertThat(region).isNotNull();
return isClientRegion(region)
? clientRegionValues(region)
: localRegionValues(region);
}
/**
* Collects values from the given {@literal client} {@link Region}.
*
* @param <T> {@link Class type} of the {@link Region Region's} values.
* @param region {@link Region} from which to collect values.
* @return a {@link Collection} of the {@literal client} {@link Region Region's} values.
* @see org.apache.geode.cache.Region
* @see #clientRegionValuesFromServer(Region)
* @see #localRegionValues(Region)
* @see #isProxyRegion(Region)
*/
private static <T> Collection<T> clientRegionValues(Region<?, T> region) {
return isProxyRegion(region)
? clientRegionValuesFromServer(region)
: localRegionValues(region);
}
/**
* Collections all values from the {@literal client} {@link Region} by pulling the values down from the server.
*
* @param <T> {@link Class type} of the {@link Region Region's} values.
* @param region {@link Region} from which to collect values.
* @return a {@link Collection} containing the values from the {@literal client} {@link Region} on the server.
* @see org.apache.geode.cache.Region#keySetOnServer()
* @see #getAll(Region, Set)
*/
private static <T> Collection<T> clientRegionValuesFromServer(Region<?, T> region) {
Set<?> keys = nullSafeSet(region.keySetOnServer());
return !keys.isEmpty() ? getAll(region, keys) : Collections.emptySet();
}
/**
* Gets all values from the given {@link Region} mapped to the specified {@link Set keys}.
*
* @param <T> {@link Class type} of the {@link Region Region's} values.
* @param region {@link Region} from which to get all values.
* @param keys {@link Set} of keys targeting the values to retrieve.
* @return a {@link Collection} of the {@link Region Region's} values.
* @see org.apache.geode.cache.Region#getAll(Collection)
*/
private static <T> Collection<T> getAll(Region<?, T> region, Set<?> keys) {
return nullSafeMap(region.getAll(keys)).values();
// Fallback procedure if region.getAll(keys) is buggered
//return keys.stream().map(region::get).collect(Collectors.toSet());
}
/**
* Collects values from the given {@link Region}, locally.
*
* @param <T> {@link Class type} of the {@link Region Region's} values.
* @param region {@link Region} from which to collect values.
* @return a {@link Collection} of the {@link Region Region's} values.
* @see org.apache.geode.cache.Region#values()
*/
private static <T> Collection<T> localRegionValues(Region<?, T> region) {
return region.values();
}
private static boolean hasText(String value) {
return value != null && !value.trim().isEmpty();
}
private static <K, V> Map<K, V> nullSafeMap(Map<K, V> map) {
return map != null ? map :Collections.emptyMap();
}
private static <T> Set<T> nullSafeSet(Set<T> set) {
return set != null ? set : Collections.emptySet();
}
/**
* Null-safe method to determine whether the given {@link RegionService} is an instance of {@link ClientCache}.
*
* The problem is, {@link GemFireCacheImpl} implements both the (peer) {@link Cache}
* and {@link ClientCache} interfaces. #sigh
*
* @param regionService {@link RegionService} to evaluate.
* @return a boolean value indicating whether the {@link RegionService} an instance of {@link ClientCache}.
* @see org.apache.geode.cache.client.ClientCache
* @see org.apache.geode.cache.RegionService
*/
public static boolean isClientCache(RegionService regionService) {
boolean result = regionService instanceof ClientCache;
if (regionService instanceof GemFireCacheImpl) {
result &= ((GemFireCacheImpl) regionService).isClient();
}
return result;
}
/**
* Null-safe method to determine whether the given {@link Region} is a {@literal client} {@link Region}
* in a {@link ClientCache}.
*
* @param region {@link Region} to evaluate.
* @return a boolean value indicating whether the given {@link Region} is a {@literal client} {@link Region}.
* @see org.apache.geode.cache.Region
* @see #isClientCache(RegionService)
*/
public static boolean isClientRegion(Region<?, ?> region) {
return region != null
&& (isClientCache(region.getRegionService()) || isRegionWithPool(region));
}
/**
* Null-safe method to determine whether the given {@link RegionService} is an instance of
* a {@literal peer} {@link Cache}.
*
* The problem is, {@link GemFireCacheImpl} implements both the (peer) {@link Cache}
* and {@link ClientCache} interfaces. #sigh
*
* @param regionService {@link RegionService} to evaluate.
* @return a boolean value indicating whether the {@link RegionService} is an instance of
* a {@literal peer} {@link Cache}.
* @see org.apache.geode.cache.RegionService
* @see org.apache.geode.cache.Cache
*/
public static boolean isPeerCache(RegionService regionService) {
boolean result = regionService instanceof Cache;
if (regionService instanceof GemFireCacheImpl) {
result &= !((GemFireCacheImpl) regionService).isClient();
}
return result;
}
/**
* Null-safe method to determine whether the given {@link Region} is a {@literal peer} {@link Region}
* in a {@literal peer} {@link Cache}.
*
* @param region {@link Region} to evaluate.
* @return a boolean value indicating whether the given {@link Region} is a {@literal peer} {@link Region}.
* @see org.apache.geode.cache.Region
* @see #isPeerCache(RegionService)
*/
public static boolean isPeerRegion(Region<?, ?> region) {
return region != null && !isClientRegion(region);
}
/**
* Null-safe method to determine whether the given {@link Region} is a [client] {@literal PROXY} {@link Region}.
*
* The {@link Region} is a {@literal PROXY} if the {@link DataPolicy} is {@link DataPolicy#EMPTY}
* or the {@link Region} has a configured {@link Pool}.
*
* @param region {@link Region} to evaluate.
* @return a boolean value to determine whether the {@link Region} is a [client] {@literal PROXY} {@link Region}.
* @see org.apache.geode.cache.Region
* @see #isRegionWithPool(Region)
*/
public static boolean isProxyRegion(Region<?, ?> region) {
return region != null
&& region.getAttributes() != null
&& (DataPolicy.EMPTY.equals(region.getAttributes().getDataPolicy())
|| isRegionWithPool(region));
}
/**
* Null-safe method to determine whether the given {@link Region} was configured with a {@link Pool}.
*
* A {@link Region} configured with a {@link Pool} (by {@link String name} specified in the {@link RegionAttributes})
* is a strong indicator that the {@link Region} is a {@literal client} {@link Region}.
*
* @param region {@link Region} to evaluate.
* @return a boolean to determine whether the given {@link Region} was configured with a {@link Pool}.
* @see org.apache.geode.cache.client.Pool
* @see org.apache.geode.cache.Region
*/
public static boolean isRegionWithPool(Region<?, ?> region) {
return Optional.ofNullable(region)
.map(Region::getAttributes)
.map(RegionAttributes::getPoolName)
.filter(CacheUtils::hasText)
.isPresent();
}
}

View File

@@ -0,0 +1,146 @@
/*
* Copyright 2017-present 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
*
* https://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.util;
import java.util.Objects;
import org.apache.geode.cache.GemFireCache;
import org.apache.geode.cache.Region;
import org.apache.geode.distributed.internal.InternalDistributedSystem;
import org.apache.geode.internal.cache.AbstractRegion;
import org.apache.geode.internal.cache.GemFireCacheImpl;
/**
* Abstract utility class containing different assertions for Apache Geode objects, such as a {@link GemFireCache}
* or {@link Region}, and so on.
*
* @author John Blum
* @see org.apache.geode.cache.GemFireCache
* @see org.apache.geode.cache.Region
* @since 1.3.0
*/
@SuppressWarnings("unused")
public abstract class GeodeAssertions {
/**
* Asserts that given {@link Object} upholds certain contractual obligations.
*
* @param <T> {@link Class type} of the given {@link Object}.
* @param obj {@link Object} being evaluated in the assertion.
* @return a new instance of {@link AssertThat} using the given {@link Object}
* as the {@link AssertThat#getSubject() subject} of the assertion.
* @see java.lang.Object
* @see AssertThat
*/
public static <T> AssertThat<T> assertThat(T obj) {
return () -> obj;
}
private static void assertIsInstanceOf(Object target, Class<?> type) {
if (!type.isInstance(target)) {
throw new AssertionError(String.format("[%1$s] is not an instance of [%2$s]",
nullSafeTypeName(target), nullSafeTypeName(type)));
}
}
private static void assertIsNotInstanceOf(Object target, Class<?> type) {
if (type.isInstance(target)) {
throw new AssertionError(String.format("[%1%s] is an instance of [%2$s]",
nullSafeTypeName(target), nullSafeTypeName(type)));
}
}
private static void assertIsNotNull(Object target) {
if (Objects.isNull(target)) {
throw new IllegalArgumentException("Argument must not be null");
}
}
private static Class<?> nullSafeType(Object obj) {
return obj != null ? obj.getClass() : null;
}
private static String nullSafeTypeName(Class<?> type) {
return type != null ? type.getName() : null;
}
public static String nullSafeTypeName(Object obj) {
return nullSafeTypeName(nullSafeType(obj));
}
/**
* The {@link AssertThat} {@link FunctionalInterface interface} defines a contract for making assertion about
* a given {@link Object} used as the {@link #getSubject() subject} of the assert statement.
*
* @param <T> {@link Class type} of the {@link Object} that is the {@link #getSubject() subject} of the assertion.
*/
@FunctionalInterface
public interface AssertThat<T> {
/**
* Returns the {@link Object} used as the subject of this assertion.
*
* @return the {@link Object} used as the subject of this assertion.
* @see java.lang.Object
*/
T getSubject();
/**
* Asserts the {@link #getSubject() subject} is not {@literal null}.
*/
default void isNotNull() {
assertIsNotNull(getSubject());
}
/**
* Asserts the {@link #getSubject()} is an instance of {@link GemFireCacheImpl}.
*/
default void isInstanceOfGemFireCacheImpl() {
assertIsInstanceOf(getSubject(), GemFireCacheImpl.class);
}
/**
* Asserts the {@link #getSubject()} is an instance of {@link InternalDistributedSystem}.
*/
default void isInstanceOfInternalDistributedSystem() {
assertIsInstanceOf(getSubject(), InternalDistributedSystem.class);
}
/**
* Asserts the {@link #getSubject()} is not an instance of {@link AbstractRegion}.
*/
default void isNotInstanceOfAbstractRegion() {
assertIsNotInstanceOf(getSubject(), AbstractRegion.class);
}
/**
* Asserts the {@link #getSubject()} is not an instance of {@link GemFireCacheImpl}.
*/
default void isNotInstanceOfGemFireCacheImpl() {
assertIsNotInstanceOf(getSubject(), GemFireCacheImpl.class);
}
/**
* Asserts the {@link #getSubject()} is not an instance of {@link InternalDistributedSystem}.
*/
default void isNotInstanceOfInternalDistributedSystem() {
assertIsNotInstanceOf(getSubject(), InternalDistributedSystem.class);
}
}
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright 2017-present 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
*
* https://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.util;
import org.apache.geode.distributed.ConfigurationProperties;
import org.apache.geode.management.internal.security.ResourceConstants;
/**
* Interface encapsulating common Apache Geode constants used by SBDG.
*
* @author John Blum
* @see org.apache.geode.distributed.ConfigurationProperties
* @since 1.3.0
*/
public interface GeodeConstants {
String GEMFIRE_PROPERTY_PREFIX = "gemfire.";
// Logging Constants (referring to Properties)
String LOG_DISK_SPACE_LIMIT = ConfigurationProperties.LOG_DISK_SPACE_LIMIT;
String LOG_FILE = ConfigurationProperties.LOG_FILE;
String LOG_FILE_SIZE_LIMIT = ConfigurationProperties.LOG_FILE_SIZE_LIMIT;
String LOG_LEVEL = ConfigurationProperties.LOG_LEVEL;
// Security Constants (referring to Properties)
String PASSWORD = ResourceConstants.PASSWORD;
String USERNAME = ResourceConstants.USER_NAME;
}

View File

@@ -0,0 +1,84 @@
/*
* Copyright 2017-present 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
*
* https://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.util.function;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Iterator;
/**
* {@link Iterable} of {@link Method} invocation {@link Object arguments}.
*
* @author John Blum
* @see java.lang.Iterable
* @since 1.3.0
*/
public class InvocationArguments implements Iterable<Object> {
public static InvocationArguments from(Object... arguments) {
return new InvocationArguments(arguments);
}
private final Object[] arguments;
/**
* Constructs a new instance of {@link InvocationArguments} initialized with the given array of
* {@link Object arguments}.
*
* @param arguments array of {@link Object arguments} indicating the values passed to the {@link Method} invocation
* parameters; may be {@literal null}.
*/
public InvocationArguments(Object[] arguments) {
this.arguments = arguments != null ? arguments : new Object[0];
}
protected Object[] getArguments() {
return this.arguments;
}
@SuppressWarnings("unchecked")
protected <T> T getArgumentAt(int index) {
return (T) getArguments()[index];
}
@Override
public Iterator<Object> iterator() {
return new Iterator<Object>() {
int index = 0;
@Override
public boolean hasNext() {
return this.index < InvocationArguments.this.getArguments().length;
}
@Override
public Object next() {
return InvocationArguments.this.getArguments()[this.index++];
}
};
}
public int size() {
return this.arguments.length;
}
@Override
public String toString() {
return Arrays.toString(getArguments());
}
}

View File

@@ -0,0 +1,56 @@
/*
* Copyright 2017-present 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
*
* https://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.util.function;
import java.util.Objects;
import java.util.function.Consumer;
/**
* {@link Consumer} like interface accepting 3 arguments.
*
* @author John Blum
* @see java.util.function.Consumer
* @since 1.3.0
*/
@FunctionalInterface
public interface TriConsumer<T, U, V> {
/**
* Performs a given operation on the 3 arguments.
*
* @param t first {@link Object argument}.
* @param u second {@link Object argument}.
* @param v third {@link Object argument}.
*/
void accept(T t, U u, V v);
/**
* Composes this {@link TriConsumer} with the given {@link TriConsumer} after this {@link TriConsumer}.
*
* @param after {@link TriConsumer} to composed with this {@link TriConsumer}; must not be {@literal null}.
* @return a new {@link TriConsumer} with the given {@link TriConsumer} composed after this {@link TriConsumer}.
* @throws NullPointerException if {@link TriConsumer} is {@literal null}.
*/
default TriConsumer<T, U, V> andThen(TriConsumer<T, U, V> after) {
Objects.requireNonNull(after);
return (t, u, v) -> {
accept(t, u, v);
after.accept(t, u, v);
};
}
}

View File

@@ -0,0 +1,31 @@
/*
* Copyright 2017-present 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
*
* https://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.util.function;
import java.util.function.Consumer;
/**
* {@link Consumer} implementation accepting a tuple of {@link InvocationArguments arguments}.
*
* @author John Blum
* @see java.util.function.Consumer
* @see org.springframework.geode.util.function.InvocationArguments
* @since 1.3.0
*/
@SuppressWarnings("unused")
public interface TupleConsumer extends Consumer<InvocationArguments> {
}

View File

@@ -0,0 +1,114 @@
/*
* Copyright 2017-present 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
*
* https://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.app;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.Optional;
import java.util.Properties;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.client.ClientCache;
import org.apache.geode.cache.client.ClientCacheFactory;
import org.apache.geode.cache.client.ClientRegionShortcut;
/**
* An example Apache Geode {@link ClientCache} application.
*
* @author John Blum
* @see java.util.Properties
* @see org.apache.geode.cache.Region
* @see org.apache.geode.cache.client.ClientCache
* @see org.apache.geode.cache.client.ClientCacheFactory
* @since 1.3.0
*/
@SuppressWarnings("unused")
public class ApacheGeodeClientApplication implements Runnable {
private static final ClientRegionShortcut CLIENT_REGION_SHORTCUT = ClientRegionShortcut.LOCAL;
private static final String APPLICATION_NAME = ApacheGeodeClientApplication.class.getSimpleName();
private static final String GEMFIRE_LOG_LEVEL = "info";
private static final String[] EMPTY_ARGUMENTS = {};
public static void main(String[] args) {
new ApacheGeodeClientApplication(args).run();
}
private final String[] arguments;
public ApacheGeodeClientApplication(String[] arguments) {
this.arguments = arguments != null ? arguments : EMPTY_ARGUMENTS;
}
protected String[] getArguments() {
return this.arguments;
}
@Override
public void run() {
run(getArguments());
}
public void run(String[] arguments) {
ClientCache clientCache = registerShutdownHook(newClientCache(gemfireProperties()));
Region<Object, Object> example = newClientRegion(clientCache, "Example");
doDataAccessOperationsTest(example);
}
protected Properties gemfireProperties() {
Properties gemfireProperties = new Properties();
gemfireProperties.setProperty("name", APPLICATION_NAME);
gemfireProperties.setProperty("log-level", GEMFIRE_LOG_LEVEL);
// See: https://issues.apache.org/jira/browse/GEODE-7891
//gemfireProperties.setProperty("geode.disallow-internal-messages-without-credentials", Boolean.TRUE.toString());
//gemfireProperties.setProperty("tombstone-gc-threshold", "100");
return gemfireProperties;
}
protected ClientCache newClientCache(Properties gemfireProperties) {
return new ClientCacheFactory(gemfireProperties).create();
}
protected <K, V> Region<K, V> newClientRegion(ClientCache clientCache, String regionName) {
return clientCache.<K, V>createClientRegionFactory(CLIENT_REGION_SHORTCUT).create(regionName);
}
@SuppressWarnings("unchecked")
protected <K, V> Region<K, V> doDataAccessOperationsTest(Region<Object, Object> region) {
assertThat(region.put(1, "TEST")).isNull();
assertThat(region.get(1)).isEqualTo("TEST");
return (Region<K, V>) region;
}
protected ClientCache registerShutdownHook(ClientCache clientCache) {
Runtime.getRuntime().addShutdownHook(new Thread(() ->
Optional.ofNullable(clientCache).ifPresent(ClientCache::close),
"ClientCache Shutdown Hook"));
return clientCache;
}
}

View File

@@ -0,0 +1,169 @@
/*
* Copyright 2017-present 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
*
* https://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.cache;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import org.apache.geode.cache.EntryEvent;
import org.apache.geode.cache.RegionEvent;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Spy;
import org.mockito.junit.MockitoJUnitRunner;
/**
* Unit Tests for {@link AbstractCommonEventProcessingCacheListener}.
*
* @author John Blum
* @see org.junit.Test
* @see org.mockito.Mockito
* @see org.apache.geode.cache.CacheListener
* @since 1.0.0
*/
@RunWith(MockitoJUnitRunner.class)
public class AbstractCommonEventProcessingCacheListenerUnitTests {
@Mock
private EntryEvent<Object, Object> mockEntryEvent;
@Mock
private RegionEvent<Object, Object> mockRegionEvent;
@Spy
private AbstractCommonEventProcessingCacheListener<Object, Object> cacheListener;
@Test
public void afterCreateCallsProcessEntryEventWithCreate() {
this.cacheListener.afterCreate(this.mockEntryEvent);
verify(this.cacheListener, times(1)).afterCreate(eq(this.mockEntryEvent));
verify(this.cacheListener, times(1)).processEntryEvent(eq(this.mockEntryEvent),
eq(AbstractCommonEventProcessingCacheListener.EntryEventType.CREATE));
verifyNoMoreInteractions(this.cacheListener);
}
@Test
public void afterDestroyCallsProcessEntryEventWithDestroy() {
this.cacheListener.afterDestroy(this.mockEntryEvent);
verify(this.cacheListener, times(1)).afterDestroy(eq(this.mockEntryEvent));
verify(this.cacheListener, times(1)).processEntryEvent(eq(this.mockEntryEvent),
eq(AbstractCommonEventProcessingCacheListener.EntryEventType.DESTROY));
verifyNoMoreInteractions(this.cacheListener);
}
@Test
public void afterInvalidateCallsProcessEntryEventWithInvalidate() {
this.cacheListener.afterInvalidate(this.mockEntryEvent);
verify(this.cacheListener, times(1)).afterInvalidate(eq(this.mockEntryEvent));
verify(this.cacheListener, times(1)).processEntryEvent(eq(this.mockEntryEvent),
eq(AbstractCommonEventProcessingCacheListener.EntryEventType.INVALIDATE));
verifyNoMoreInteractions(this.cacheListener);
}
@Test
public void afterUpdateCallsProcessEntryEventWithUpdate() {
this.cacheListener.afterUpdate(this.mockEntryEvent);
verify(this.cacheListener, times(1)).afterUpdate(eq(this.mockEntryEvent));
verify(this.cacheListener, times(1)).processEntryEvent(eq(this.mockEntryEvent),
eq(AbstractCommonEventProcessingCacheListener.EntryEventType.UPDATE));
verifyNoMoreInteractions(this.cacheListener);
}
@Test
public void afterRegionClearCallsProcessRegionEventWithClear() {
this.cacheListener.afterRegionClear(this.mockRegionEvent);
verify(this.cacheListener, times(1)).afterRegionClear(eq(this.mockRegionEvent));
verify(this.cacheListener, times(1)).processRegionEvent(eq(this.mockRegionEvent),
eq(AbstractCommonEventProcessingCacheListener.RegionEventType.CLEAR));
verifyNoMoreInteractions(this.cacheListener);
}
@Test
public void afterRegionCreateCallsProcessRegionEventWithCreate() {
this.cacheListener.afterRegionCreate(this.mockRegionEvent);
verify(this.cacheListener, times(1)).afterRegionCreate(eq(this.mockRegionEvent));
verify(this.cacheListener, times(1)).processRegionEvent(eq(this.mockRegionEvent),
eq(AbstractCommonEventProcessingCacheListener.RegionEventType.CREATE));
verifyNoMoreInteractions(this.cacheListener);
}
@Test
public void afterRegionDestroyCallsProcessRegionEventWithCreate() {
this.cacheListener.afterRegionDestroy(this.mockRegionEvent);
verify(this.cacheListener, times(1)).afterRegionDestroy(eq(this.mockRegionEvent));
verify(this.cacheListener, times(1)).processRegionEvent(eq(this.mockRegionEvent),
eq(AbstractCommonEventProcessingCacheListener.RegionEventType.DESTROY));
verifyNoMoreInteractions(this.cacheListener);
}
@Test
public void afterRegionInvalidateCallsProcessRegionEventWithInvalidate() {
this.cacheListener.afterRegionInvalidate(this.mockRegionEvent);
verify(this.cacheListener, times(1)).afterRegionInvalidate(eq(this.mockRegionEvent));
verify(this.cacheListener, times(1)).processRegionEvent(eq(this.mockRegionEvent),
eq(AbstractCommonEventProcessingCacheListener.RegionEventType.INVALIDATE));
verifyNoMoreInteractions(this.cacheListener);
}
@Test
public void afterRegionLiveCallsProcessRegionEventWithLive() {
this.cacheListener.afterRegionLive(this.mockRegionEvent);
verify(this.cacheListener, times(1)).afterRegionLive(eq(this.mockRegionEvent));
verify(this.cacheListener, times(1)).processRegionEvent(eq(this.mockRegionEvent),
eq(AbstractCommonEventProcessingCacheListener.RegionEventType.LIVE));
verifyNoMoreInteractions(this.cacheListener);
}
}

View File

@@ -0,0 +1,73 @@
/*
* Copyright 2017-present 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
*
* https://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.cache;
import static org.assertj.core.api.Assertions.assertThat;
import org.junit.Test;
/**
* Unit Tests for {@link SimpleCacheResolver}.
*
* @author John Blum
* @see org.junit.Test
* @see org.apache.geode.cache.Cache
* @see org.apache.geode.cache.GemFireCache
* @see org.apache.geode.cache.client.ClientCache
* @see org.springframework.geode.cache.SimpleCacheResolver
* @since 1.3.0
*/
public class SimpleCacheResolverUnitTests {
@Test
public void getInstanceReturnsASingleInstance() {
SimpleCacheResolver cacheResolver = SimpleCacheResolver.getInstance();
assertThat(cacheResolver).isNotNull();
assertThat(cacheResolver).isSameAs(SimpleCacheResolver.getInstance());
}
@Test
public void resolveWhenNoCacheIsPresentReturnsEmptyOptional() {
assertThat(SimpleCacheResolver.getInstance().resolve().orElse(null)).isNull();
}
@Test
public void resolveClientCacheWhenNoClientCacheIsPresentReturnsEmptyOptional() {
assertThat(SimpleCacheResolver.getInstance().resolveClientCache().orElse(null)).isNull();
}
@Test
public void resolvePeerCacheWhenNoPeerCacheIsPresentReturnsEmptyOptional() {
assertThat(SimpleCacheResolver.getInstance().resolvePeerCache().orElse(null)).isNull();
}
@Test(expected = IllegalStateException.class)
public void requireCacheWhenNoCacheIsPresentThrowsIllegalStateException() {
try {
SimpleCacheResolver.getInstance().require();
}
catch (IllegalStateException expected) {
assertThat(expected).hasMessage("GemFireCache not found");
assertThat(expected).hasNoCause();
throw expected;
}
}
}

View File

@@ -0,0 +1,74 @@
/*
* Copyright 2017-present 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
*
* https://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.cache;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.Optional;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.apache.geode.cache.client.ClientCache;
import org.apache.geode.cache.client.ClientCacheFactory;
/**
* Integration Tests for {@link SimpleCacheResolver} using an Apache Geode {@link ClientCache}.
*
* @author John Blum
* @see org.junit.Test
* @see org.apache.geode.cache.client.ClientCache
* @see org.apache.geode.cache.client.ClientCacheFactory
* @see org.springframework.geode.cache.SimpleCacheResolver
* @since 1.3.0
*/
public class SimpleClientCacheResolverIntegrationTests {
private static ClientCache clientCache;
@BeforeClass
public static void createClientCache() {
clientCache = new ClientCacheFactory().create();
assertThat(clientCache).isNotNull();
}
@AfterClass
public static void destroyClientCache() {
Optional.ofNullable(clientCache).ifPresent(ClientCache::close);
clientCache = null;
}
@Test
public void resolveReturnsClientCache() {
assertThat(SimpleCacheResolver.getInstance().resolve().orElse(null)).isSameAs(clientCache);
}
@Test
public void resolveClientCacheReturnsClientCache() {
assertThat(SimpleCacheResolver.getInstance().resolveClientCache().orElse(null)).isSameAs(clientCache);
}
@Test
public void resolvePeerCacheReturnsEmptyOptional() {
assertThat(SimpleCacheResolver.getInstance().resolvePeerCache().orElse(null)).isNull();
}
@Test
public void requireReturnsClientCache() {
assertThat(SimpleCacheResolver.getInstance().<ClientCache>require()).isSameAs(clientCache);
}
}

View File

@@ -0,0 +1,73 @@
/*
* Copyright 2017-present 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
*
* https://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.cache;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.Optional;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.apache.geode.cache.Cache;
import org.apache.geode.cache.CacheFactory;
/**
* Integration Tests for {@link SimpleCacheResolver} using an Apache Geode {@literal peer} {@link Cache}.
*
* @author John Blum
* @see org.junit.Test
* @see org.apache.geode.cache.Cache
* @see org.apache.geode.cache.CacheFactory
* @see org.springframework.geode.cache.SimpleCacheResolver
* @since 1.3.0
*/
public class SimplePeerCacheResolverIntegrationTests {
private static Cache peerCache;
@BeforeClass
public static void createPeerCache() {
peerCache = new CacheFactory().create();
assertThat(peerCache).isNotNull();
}
@AfterClass
public static void destroyPeerCache() {
Optional.ofNullable(peerCache).ifPresent(Cache::close);
}
@Test
public void resolveReturnsPeerCache() {
assertThat(SimpleCacheResolver.getInstance().resolve().orElse(null)).isSameAs(peerCache);
}
@Test
public void resolveClientCacheReturnsEmptyOptional() {
assertThat(SimpleCacheResolver.getInstance().resolveClientCache().orElse(null)).isNull();
}
@Test
public void resolvePeerCacheReturnsPeerCache() {
assertThat(SimpleCacheResolver.getInstance().resolvePeerCache().orElse(null)).isSameAs(peerCache);
}
@Test
public void requireReturnsPeerCache() {
assertThat(SimpleCacheResolver.getInstance().<Cache>require()).isSameAs(peerCache);
}
}

View File

@@ -0,0 +1,130 @@
/*
* Copyright 2017-present 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
*
* https://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.distributed.event;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import org.junit.Test;
import org.apache.geode.distributed.DistributedMember;
import org.apache.geode.distributed.internal.DistributionManager;
import org.apache.geode.distributed.internal.InternalDistributedSystem;
import org.apache.geode.internal.cache.InternalCache;
/**
* Unit Tests for {@link MembershipEvent}.
*
* @author John Blum
* @see org.junit.Test
* @see org.mockito.Mockito
* @see org.apache.geode.distributed.DistributedMember
* @see org.apache.geode.distributed.DistributedSystem
* @see org.apache.geode.distributed.internal.DistributionManager
* @see org.springframework.geode.distributed.event.MembershipEvent
* @since 1.3.0
*/
public class MembershipEventUnitTests {
@Test
public void assertNotNullWithNonNullValue() {
assertThat(MembershipEvent.assertNotNull("test", "message")).isEqualTo("test");
}
@Test(expected = IllegalArgumentException.class)
public void assertNotNullWithNullValue() {
try {
MembershipEvent.assertNotNull(null, "Message with argument [%s]", "test");
}
catch (IllegalArgumentException expected) {
assertThat(expected).hasMessage("Message with argument [test]");
assertThat(expected).hasNoCause();
throw expected;
}
}
@Test
public void constructsNewMembershipEventWithDistributionManager() {
DistributionManager mockDistributionManager = mock(DistributionManager.class);
InternalCache mockCache = mock(InternalCache.class);
InternalDistributedSystem mockDistributedSystem = mock(InternalDistributedSystem.class);
doReturn(mockCache).when(mockDistributionManager).getCache();
doReturn(mockDistributedSystem).when(mockDistributionManager).getSystem();
MembershipEvent<TestMembershipEvent> event = new TestMembershipEvent(mockDistributionManager);
assertThat(event).isNotNull();
assertThat(event.getCache().orElse(null)).isEqualTo(mockCache);
assertThat(event.getDistributedMember().orElse(null)).isNull();
assertThat(event.getDistributedSystem().orElse(null)).isEqualTo(mockDistributedSystem);
assertThat(event.getDistributionManager()).isEqualTo(mockDistributionManager);
assertThat(event.getType()).isEqualTo(MembershipEvent.Type.UNQUALIFIED);
verify(mockDistributionManager, times(1)).getCache();
verify(mockDistributionManager, times(1)).getSystem();
verifyNoMoreInteractions(mockDistributionManager);
}
@Test
public void withMemberReturnsMember() {
DistributedMember mockDistributedMember = mock(DistributedMember.class);
DistributionManager mockDistributionManager = mock(DistributionManager.class);
MembershipEvent<TestMembershipEvent> event = new TestMembershipEvent(mockDistributionManager);
assertThat(event).isNotNull();
assertThat(event.getDistributedMember().orElse(null)).isNull();
assertThat(event.withMember(mockDistributedMember)).isSameAs(event);
assertThat(event.getDistributedMember().orElse(null)).isEqualTo(mockDistributedMember);
assertThat(event.withMember(null)).isSameAs(event);
assertThat(event.getDistributedMember().orElse(null)).isNull();
}
@Test(expected = IllegalArgumentException.class)
public void constructWithNullDistributionManagerThrowsIllegalArgumentException() {
try {
new TestMembershipEvent(null);
}
catch (IllegalArgumentException expected) {
assertThat(expected).hasMessage("DistributionManager must not be null");
assertThat(expected).hasNoCause();
throw expected;
}
}
static class TestMembershipEvent extends MembershipEvent<TestMembershipEvent> {
TestMembershipEvent(DistributionManager distributionManager) {
super(distributionManager);
}
}
}

View File

@@ -0,0 +1,259 @@
/*
* Copyright 2017-present 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
*
* https://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.distributed.event;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isA;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.apache.geode.cache.Cache;
import org.apache.geode.distributed.DistributedSystem;
import org.apache.geode.distributed.internal.DistributionManager;
import org.apache.geode.distributed.internal.InternalDistributedSystem;
import org.apache.geode.distributed.internal.membership.InternalDistributedMember;
import org.springframework.geode.distributed.event.support.MemberDepartedEvent;
import org.springframework.geode.distributed.event.support.MemberJoinedEvent;
import org.springframework.geode.distributed.event.support.MemberSuspectEvent;
import org.springframework.geode.distributed.event.support.QuorumLostEvent;
/**
* Unit Tests for {@link MembershipListenerAdapter}.
*
* @author John Blum
* @see org.junit.Test
* @see org.mockito.Mockito
* @see org.mockito.junit.MockitoJUnitRunner
* @see org.apache.geode.cache.Cache
* @see org.apache.geode.distributed.DistributedMember
* @see org.apache.geode.distributed.DistributedSystem
* @see org.apache.geode.distributed.internal.DistributionManager
* @see org.apache.geode.distributed.internal.membership.InternalDistributedMember
* @see MembershipListenerAdapter
* @since 1.3.0
*/
@RunWith(MockitoJUnitRunner.class)
public class MembershipListenerAdapterUnitTests {
@Mock
private DistributionManager mockDistributionManager;
@Mock
private InternalDistributedMember mockDistributedMember;
@Mock
private InternalDistributedSystem mockDistributedSystem;
@SuppressWarnings("unchecked")
private <T> Set<T> asSet(T... elements) {
return new HashSet<>(Arrays.asList(elements));
}
@Test
public void memberDepartedCallsHandleMemberDeparted() {
MembershipListenerAdapter<?> listener = spy(new TestMembershipListener());
doAnswer(invocation -> {
MemberDepartedEvent event = invocation.getArgument(0);
assertThat(event).isNotNull();
assertThat(event.isCrashed()).isTrue();
assertThat(event.getDistributedMember().orElse(null)).isEqualTo(this.mockDistributedMember);
assertThat(event.getDistributionManager()).isEqualTo(this.mockDistributionManager);
assertThat(event.getType()).isEqualTo(MembershipEvent.Type.MEMBER_DEPARTED);
return null;
}).when(listener).handleMemberDeparted(any(MemberDepartedEvent.class));
listener.memberDeparted(this.mockDistributionManager, this.mockDistributedMember, true);
verify(listener, times(1)).handleMemberDeparted(isA(MemberDepartedEvent.class));
}
@Test
public void memberJoinedCallsHandleMemberJoined() {
MembershipListenerAdapter<?> listener = spy(new TestMembershipListener());
doAnswer(invocation -> {
MemberJoinedEvent event = invocation.getArgument(0);
assertThat(event).isNotNull();
assertThat(event.getDistributedMember().orElse(null)).isEqualTo(this.mockDistributedMember);
assertThat(event.getDistributionManager()).isEqualTo(this.mockDistributionManager);
assertThat(event.getType()).isEqualTo(MembershipEvent.Type.MEMBER_JOINED);
return null;
}).when(listener).handleMemberJoined(any(MemberJoinedEvent.class));
listener.memberJoined(this.mockDistributionManager, this.mockDistributedMember);
verify(listener, times(1)).handleMemberJoined(isA(MemberJoinedEvent.class));
}
@Test
public void memberSuspectCallsHandleMemberSuspect() {
InternalDistributedMember suspectMember = mock(InternalDistributedMember.class);
MembershipListenerAdapter<?> listener = spy(new TestMembershipListener());
doAnswer(invocation -> {
MemberSuspectEvent event = invocation.getArgument(0);
assertThat(event).isNotNull();
assertThat(event.getDistributedMember().orElse(null)).isEqualTo(this.mockDistributedMember);
assertThat(event.getDistributionManager()).isEqualTo(this.mockDistributionManager);
assertThat(event.getReason().orElse(null)).isEqualTo("The system sucks!");
assertThat(event.getSuspectMember().orElse(null)).isEqualTo(suspectMember);
assertThat(event.getType()).isEqualTo(MembershipEvent.Type.MEMBER_SUSPECT);
return null;
}).when(listener).handleMemberSuspect(any(MemberSuspectEvent.class));
listener.memberSuspect(this.mockDistributionManager, this.mockDistributedMember, suspectMember,
"The system sucks!");
verify(listener, times(1)).handleMemberSuspect(isA(MemberSuspectEvent.class));
}
@Test
public void quorumLostCallsHandleQuorumLost() {
InternalDistributedMember mockMemberOne = mock(InternalDistributedMember.class);
InternalDistributedMember mockMemberTwo = mock(InternalDistributedMember.class);
MembershipListenerAdapter<?> listener = spy(new TestMembershipListener());
doAnswer(invocation -> {
QuorumLostEvent event = invocation.getArgument(0);
assertThat(event).isNotNull();
assertThat(event.getDistributedMember().orElse(null)).isNull();
assertThat(event.getDistributionManager()).isEqualTo(this.mockDistributionManager);
assertThat(event.getFailedMembers()).isEqualTo(asSet(mockMemberOne, mockMemberTwo));
assertThat(event.getRemainingMembers()).isEqualTo(Collections.singletonList(this.mockDistributedMember));
assertThat(event.getType()).isEqualTo(MembershipEvent.Type.QUORUM_LOST);
return null;
}).when(listener).handleQuorumLost(any(QuorumLostEvent.class));
listener.quorumLost(this.mockDistributionManager, asSet(mockMemberOne, mockMemberTwo),
Collections.singletonList(this.mockDistributedMember));
verify(listener, times(1)).handleQuorumLost(isA(QuorumLostEvent.class));
}
@Test
public void registersListenerWithPeerCache() {
Cache mockCache = mock(Cache.class);
doReturn(this.mockDistributedSystem).when(mockCache).getDistributedSystem();
doReturn(this.mockDistributionManager).when(this.mockDistributedSystem).getDistributionManager();
MembershipListenerAdapter<?> listener = new TestMembershipListener();
assertThat(listener.register(mockCache)).isSameAs(listener);
verify(mockCache, times(1)).getDistributedSystem();
verify(this.mockDistributedSystem, times(1)).getDistributionManager();
verify(this.mockDistributionManager, times(1))
.addMembershipListener(eq(listener));
}
@Test
public void registerListenerWithNullCacheIsNullSafe() {
MembershipListenerAdapter<?> listener = new TestMembershipListener();
assertThat(listener.register(null)).isSameAs(listener);
}
@Test
public void registerListenerWithNullDistributedSystemIsNullSafe() {
Cache mockCache = mock(Cache.class);
MembershipListenerAdapter<?> listener = new TestMembershipListener();
assertThat(listener.register(mockCache)).isSameAs(listener);
verify(mockCache, times(1)).getDistributedSystem();
}
@Test
public void registerListenerWithNonInternalDistributedSystemIsSafe() {
Cache mockCache = mock(Cache.class);
DistributedSystem mockDistributedSystem = mock(DistributedSystem.class);
doReturn(mockDistributedSystem).when(mockCache).getDistributedSystem();
MembershipListenerAdapter<?> listener = new TestMembershipListener();
assertThat(listener.register(mockCache)).isSameAs(listener);
verify(mockCache, times(1)).getDistributedSystem();
verifyNoInteractions(mockDistributedSystem);
}
@Test
public void registerListenerWithNullDistributionManagerIsNullSafe() {
Cache mockCache = mock(Cache.class);
doReturn(this.mockDistributedSystem).when(mockCache).getDistributedSystem();
MembershipListenerAdapter<?> listener = new TestMembershipListener();
assertThat(listener.register(mockCache)).isSameAs(listener);
verify(mockCache, times(1)).getDistributedSystem();
verify(this.mockDistributedSystem, times(1)).getDistributionManager();
}
static class TestMembershipListener extends MembershipListenerAdapter<TestMembershipListener> { }
}

View File

@@ -0,0 +1,69 @@
/*
* Copyright 2017-present 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
*
* https://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.distributed.event.support;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verifyNoInteractions;
import org.junit.Test;
import org.apache.geode.distributed.internal.DistributionManager;
import org.springframework.geode.distributed.event.MembershipEvent;
/**
* Unit Tests for {@link MemberDepartedEvent}.
*
* @author John Blum
* @see org.junit.Test
* @see org.mockito.Mockito
* @see org.springframework.geode.distributed.event.MembershipEvent
* @see org.springframework.geode.distributed.event.support.MemberDepartedEvent
* @since 1.3.0
*/
public class MemberDepartedEventUnitTests {
@Test
public void constructMemberDepartedEvent() {
DistributionManager mockDistributionManager = mock(DistributionManager.class);
MemberDepartedEvent event = new MemberDepartedEvent(mockDistributionManager);
assertThat(event).isNotNull();
assertThat(event.getDistributionManager()).isEqualTo(mockDistributionManager);
assertThat(event.isCrashed()).isFalse();
assertThat(event.getType()).isEqualTo(MembershipEvent.Type.MEMBER_DEPARTED);
verifyNoInteractions(mockDistributionManager);
}
@Test
public void setAndGetCrashed() {
DistributionManager mockDistributionManager = mock(DistributionManager.class);
MemberDepartedEvent event = new MemberDepartedEvent(mockDistributionManager);
assertThat(event).isNotNull();
assertThat(event.isCrashed()).isFalse();
assertThat(event.crashed(true)).isEqualTo(event);
assertThat(event.isCrashed()).isTrue();
assertThat(event.crashed(false)).isEqualTo(event);
assertThat(event.isCrashed()).isFalse();
}
}

View File

@@ -0,0 +1,50 @@
/*
* Copyright 2017-present 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
*
* https://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.distributed.event.support;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import org.junit.Test;
import org.apache.geode.distributed.internal.DistributionManager;
import org.springframework.geode.distributed.event.MembershipEvent;
/**
* Unit Tests for {@link MemberJoinedEvent}.
*
* @author John Blum
* @see org.junit.Test
* @see org.mockito.Mockito
* @see org.springframework.geode.distributed.event.MembershipEvent
* @see org.springframework.geode.distributed.event.support.MemberJoinedEvent
* @since 1.3.0
*/
public class MemberJoinedEventUnitTests {
@Test
public void constructsMemberJoinedEvent() {
DistributionManager mockDistributionManager = mock(DistributionManager.class);
MemberJoinedEvent event = new MemberJoinedEvent(mockDistributionManager);
assertThat(event).isNotNull();
assertThat(event.getDistributionManager()).isEqualTo(mockDistributionManager);
assertThat(event.getType()).isEqualTo(MembershipEvent.Type.MEMBER_JOINED);
}
}

View File

@@ -0,0 +1,95 @@
/*
* Copyright 2017-present 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
*
* https://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.distributed.event.support;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verifyNoInteractions;
import org.junit.Test;
import org.apache.geode.distributed.DistributedMember;
import org.apache.geode.distributed.internal.DistributionManager;
import org.springframework.geode.distributed.event.MembershipEvent;
/**
* Unit Tests for {@link MemberSuspectEvent}.
*
* @author John Blum
* @see org.junit.Test
* @see org.mockito.Mockito
* @see org.apache.geode.distributed.DistributedMember
* @see org.springframework.geode.distributed.event.MembershipEvent
* @see org.springframework.geode.distributed.event.support.MemberSuspectEvent
* @since 1.3.0
*/
public class MemberSuspectEventUnitTests {
@Test
public void constructsMemberSuspectEvent() {
DistributionManager mockDistributionManager = mock(DistributionManager.class);
MemberSuspectEvent event = new MemberSuspectEvent(mockDistributionManager);
assertThat(event).isNotNull();
assertThat(event.getDistributionManager()).isEqualTo(mockDistributionManager);
assertThat(event.getReason().orElse(null)).isNull();
assertThat(event.getSuspectMember().orElse(null)).isNull();
assertThat(event.getType()).isEqualTo(MembershipEvent.Type.MEMBER_SUSPECT);
verifyNoInteractions(mockDistributionManager);
}
@Test
public void setAndGetReason() {
DistributionManager mockDistributionManager = mock(DistributionManager.class);
MemberSuspectEvent event = new MemberSuspectEvent(mockDistributionManager);
assertThat(event).isNotNull();
assertThat(event.getReason().orElse(null)).isNull();
assertThat(event.withReason("TEST")).isSameAs(event);
assertThat(event.getReason().orElse(null)).isEqualTo("TEST");
assertThat(event.withReason(null)).isSameAs(event);
assertThat(event.getReason().orElse(null)).isNull();
assertThat(event.withReason(" ")).isSameAs(event);
assertThat(event.getReason().orElse(null)).isEqualTo(" ");
assertThat(event.withReason(null)).isSameAs(event);
assertThat(event.getReason().orElse(null)).isNull();
assertThat(event.withReason("")).isSameAs(event);
assertThat(event.getReason().orElse(null)).isEqualTo("");
}
@Test
public void setAndGetSuspectMember() {
DistributedMember mockDistributedMember = mock(DistributedMember.class);
DistributionManager mockDistributionManager = mock(DistributionManager.class);
MemberSuspectEvent event = new MemberSuspectEvent(mockDistributionManager);
assertThat(event).isNotNull();
assertThat(event.getSuspectMember().orElse(null)).isNull();
assertThat(event.withSuspect(mockDistributedMember)).isSameAs(event);
assertThat(event.getSuspectMember().orElse(null)).isEqualTo(mockDistributedMember);
assertThat(event.withSuspect(null)).isSameAs(event);
assertThat(event.getSuspectMember().orElse(null)).isNull();
}
}

View File

@@ -0,0 +1,140 @@
/*
* Copyright 2017-present 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
*
* https://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.distributed.event.support;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verifyNoInteractions;
import java.util.Collections;
import org.junit.Test;
import org.apache.geode.distributed.DistributedMember;
import org.apache.geode.distributed.internal.DistributionManager;
import org.springframework.geode.distributed.event.MembershipEvent;
/**
* Unit Tests for {@link QuorumLostEvent}.
*
* @author John Blum
* @see org.junit.Test
* @see org.mockito.Mockito
* @see org.apache.geode.distributed.DistributedMember
* @see org.springframework.geode.distributed.event.MembershipEvent
* @see org.springframework.geode.distributed.event.support.QuorumLostEvent
* @since 1.3.0
*/
public class QuorumLostEventUnitTests {
@SuppressWarnings("unchecked")
private <T> Iterable<T> normalize(Iterable<? extends T> iterable) {
return (Iterable<T>) iterable;
}
@Test
public void constructsQuorumLostEvent() {
DistributionManager mockDistributionManager = mock(DistributionManager.class);
QuorumLostEvent event = new QuorumLostEvent(mockDistributionManager);
assertThat(event).isNotNull();
assertThat(event.getDistributionManager()).isEqualTo(mockDistributionManager);
assertThat(event.getFailedMembers()).isEmpty();
assertThat(event.getRemainingMembers()).isEmpty();
assertThat(event.getType()).isEqualTo(MembershipEvent.Type.QUORUM_LOST);
verifyNoInteractions(mockDistributionManager);
}
@Test
public void setAndGetFailedMembers() {
DistributedMember mockMemberOne = mock(DistributedMember.class);
DistributedMember mockMemberTwo = mock(DistributedMember.class);
DistributionManager mockDistributionManager = mock(DistributionManager.class);
QuorumLostEvent event = new QuorumLostEvent(mockDistributionManager);
assertThat(event).isNotNull();
assertThat(event.getFailedMembers()).isNotNull();
assertThat(event.getFailedMembers()).isEmpty();
assertThat(event.withFailedMembers(mockMemberOne, mockMemberTwo)).isSameAs(event);
assertThat(this.<DistributedMember>normalize(event.getFailedMembers()))
.containsExactlyInAnyOrder(mockMemberOne, mockMemberTwo);
assertThat(event.withFailedMembers((DistributedMember[]) null)).isSameAs(event);
assertThat(event.getFailedMembers()).isNotNull();
assertThat(event.getFailedMembers()).isEmpty();
assertThat(event.withFailedMembers(mockMemberOne)).isSameAs(event);
assertThat(this.<DistributedMember>normalize(event.getFailedMembers())).containsExactly(mockMemberOne);
assertThat(event.withFailedMembers((Iterable<? extends DistributedMember>) null)).isSameAs(event);
assertThat(event.getFailedMembers()).isNotNull();
assertThat(event.getFailedMembers()).isEmpty();
assertThat(event.withFailedMembers(Collections.singleton(mockMemberTwo))).isSameAs(event);
assertThat(this.<DistributedMember>normalize(event.getFailedMembers())).containsExactly(mockMemberTwo);
assertThat(event.withFailedMembers()).isSameAs(event);
assertThat(event.getFailedMembers()).isNotNull();
assertThat(event.getFailedMembers()).isEmpty();
}
@Test
public void setAndGetRemainingMembers() {
DistributedMember mockMemberOne = mock(DistributedMember.class);
DistributedMember mockMemberTwo = mock(DistributedMember.class);
DistributionManager mockDistributionManager = mock(DistributionManager.class);
QuorumLostEvent event = new QuorumLostEvent(mockDistributionManager);
assertThat(event).isNotNull();
assertThat(event.getRemainingMembers()).isNotNull();
assertThat(event.getRemainingMembers()).isEmpty();
assertThat(event.withRemainingMembers(mockMemberOne, mockMemberTwo)).isSameAs(event);
assertThat(this.<DistributedMember>normalize(event.getRemainingMembers()))
.containsExactlyInAnyOrder(mockMemberOne, mockMemberTwo);
assertThat(event.withRemainingMembers((DistributedMember[]) null)).isSameAs(event);
assertThat(event.getRemainingMembers()).isNotNull();
assertThat(event.getRemainingMembers()).isEmpty();
assertThat(event.withRemainingMembers(mockMemberOne)).isSameAs(event);
assertThat(this.<DistributedMember>normalize(event.getRemainingMembers()))
.containsExactlyInAnyOrder(mockMemberOne);
assertThat(event.withRemainingMembers((Iterable<? extends DistributedMember>) null)).isSameAs(event);
assertThat(event.getRemainingMembers()).isNotNull();
assertThat(event.getRemainingMembers()).isEmpty();
assertThat(event.withRemainingMembers(Collections.singletonList(mockMemberTwo))).isSameAs(event);
assertThat(this.<DistributedMember>normalize(event.getRemainingMembers()))
.containsExactlyInAnyOrder(mockMemberTwo);
assertThat(event.withRemainingMembers()).isSameAs(event);
assertThat(event.getRemainingMembers()).isNotNull();
assertThat(event.getRemainingMembers()).isEmpty();
}
}

View File

@@ -0,0 +1,382 @@
/*
* Copyright 2017-present 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
*
* https://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.pdx;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import java.util.Arrays;
import org.junit.Test;
import org.mockito.InOrder;
import org.mockito.Mockito;
import org.apache.geode.cache.GemFireCache;
import org.apache.geode.cache.RegionService;
import org.apache.geode.pdx.PdxInstance;
import org.apache.geode.pdx.PdxInstanceFactory;
/**
* Unit Tests for {@link PdxInstanceBuilder}.
*
* @author John Blum
* @see org.junit.Test
* @see org.mockito.Mockito
* @see org.apache.geode.cache.GemFireCache
* @see org.apache.geode.cache.RegionService
* @see org.apache.geode.pdx.PdxInstance
* @see org.apache.geode.pdx.PdxInstanceFactory
* @see org.springframework.geode.pdx.PdxInstanceBuilder
* @since 1.3.0
*/
public class PdxInstanceBuilderUnitTests {
@Test
public void constructPdxInstanceBuilderInitializedWithRegionService() {
RegionService mockRegionService = mock(RegionService.class);
PdxInstanceBuilder builder = new PdxInstanceBuilder(mockRegionService);
assertThat(builder).isNotNull();
assertThat(builder.getRegionService()).isEqualTo(mockRegionService);
verifyNoInteractions(mockRegionService);
}
@Test(expected = IllegalArgumentException.class)
public void constructPdxInstanceBuilderWithNullRegionService() {
try {
new PdxInstanceBuilder(null);
}
catch (IllegalArgumentException expected) {
assertThat(expected).hasMessage("RegionService must not be null");
assertThat(expected).hasNoCause();
throw expected;
}
}
@Test
public void createPdxInstanceBuilderWithRegionService() {
RegionService mockRegionService = mock(RegionService.class);
PdxInstanceBuilder builder = PdxInstanceBuilder.create(mockRegionService);
assertThat(builder).isNotNull();
assertThat(builder.getRegionService()).isEqualTo(mockRegionService);
verifyNoMoreInteractions(mockRegionService);
}
@Test(expected = IllegalStateException.class)
public void createPdxInstanceBuilderWithUnresolvableRegionService() {
try {
PdxInstanceBuilder.create();
}
catch (IllegalStateException expected) {
assertThat(expected).hasMessage("GemFireCache not found");
assertThat(expected).hasNoCause();
throw expected;
}
}
@Test
public void copyPdxInstance() {
PdxInstance mockPdxInstance = mock(PdxInstance.class);
doReturn("example.app.test.model.Type").when(mockPdxInstance).getClassName();
doReturn("testOne").when(mockPdxInstance).getField(eq("fieldOne"));
doReturn("testTwo").when(mockPdxInstance).getField(eq("fieldTwo"));
doReturn(1).when(mockPdxInstance).getField(eq("id"));
doReturn(Arrays.asList("fieldOne", "id", "fieldTwo")).when(mockPdxInstance).getFieldNames();
doReturn(true).when(mockPdxInstance).isIdentityField(eq("id"));
PdxInstanceFactory mockPdxInstanceFactory = mock(PdxInstanceFactory.class);
RegionService mockRegionService = mock(RegionService.class);
doReturn(mockPdxInstanceFactory).when(mockRegionService)
.createPdxInstanceFactory(eq("example.app.test.model.Type"));
PdxInstanceBuilder builder = PdxInstanceBuilder.create(mockRegionService);
assertThat(builder).isNotNull();
assertThat(builder.getRegionService()).isEqualTo(mockRegionService);
assertThat(builder.copy(mockPdxInstance)).isEqualTo(mockPdxInstanceFactory);
InOrder inOrder = Mockito.inOrder(mockPdxInstance, mockPdxInstanceFactory, mockRegionService);
inOrder.verify(mockPdxInstance, times(1)).getClassName();
inOrder.verify(mockRegionService, times(1))
.createPdxInstanceFactory(eq("example.app.test.model.Type"));
inOrder.verify(mockPdxInstance, times(1)).getFieldNames();
inOrder.verify(mockPdxInstance, times(1)).getField(eq("fieldOne"));
inOrder.verify(mockPdxInstanceFactory, times(1))
.writeObject(eq("fieldOne"), eq("testOne"));
inOrder.verify(mockPdxInstance, times(1)).isIdentityField(eq("fieldOne"));
inOrder.verify(mockPdxInstance, times(1)).getField(eq("id"));
inOrder.verify(mockPdxInstanceFactory, times(1))
.writeObject(eq("id"), eq(1));
inOrder.verify(mockPdxInstance, times(1)).isIdentityField(eq("id"));
inOrder.verify(mockPdxInstanceFactory, times(1)).markIdentityField(eq("id"));
inOrder.verify(mockPdxInstance, times(1)).getField(eq("fieldTwo"));
inOrder.verify(mockPdxInstanceFactory, times(1))
.writeObject(eq("fieldTwo"), eq("testTwo"));
inOrder.verify(mockPdxInstance, times(1)).isIdentityField(eq("fieldTwo"));
}
@Test
public void copyPdxInstanceWhenGetFieldsNamesReturnsNullIsNullSafe() {
PdxInstance mockPdxInstance = mock(PdxInstance.class);
PdxInstanceFactory mockPdxInstanceFactory = mock(PdxInstanceFactory.class);
RegionService mockRegionService = mock(RegionService.class);
doReturn("example.app.test.model.Type").when(mockPdxInstance).getClassName();
doReturn(null).when(mockPdxInstance).getFieldNames();
doReturn(mockPdxInstanceFactory).when(mockRegionService)
.createPdxInstanceFactory(eq("example.app.test.model.Type"));
PdxInstanceBuilder builder = PdxInstanceBuilder.create(mockRegionService);
assertThat(builder).isNotNull();
assertThat(builder.getRegionService()).isEqualTo(mockRegionService);
assertThat(builder.copy(mockPdxInstance)).isEqualTo(mockPdxInstanceFactory);
verify(mockPdxInstance, times(1)).getFieldNames();
verifyNoInteractions(mockPdxInstanceFactory);
verify(mockRegionService, times(1))
.createPdxInstanceFactory(eq("example.app.test.model.Type"));
}
@Test(expected = IllegalArgumentException.class)
public void copyNullPdxInstanceThrowsIllegalArgumentException() {
RegionService mockRegionService = mock(RegionService.class);
try {
PdxInstanceBuilder.create(mockRegionService).copy(null);
}
catch (IllegalArgumentException expected) {
assertThat(expected).hasMessage("PdxInstance must not be null");
assertThat(expected).hasNoCause();
throw expected;
}
finally {
verifyNoInteractions(mockRegionService);
}
}
@Test
public void fromSourceObject() {
Object source = new Object();
GemFireCache mockCache = mock(GemFireCache.class);
PdxInstance mockPdxInstanceHolder = mock(PdxInstance.class);
PdxInstance mockPdxInstanceSource = mock(PdxInstance.class);
PdxInstanceFactory mockPdxInstanceFactory = mock(PdxInstanceFactory.class);
doReturn(true).when(mockCache).getPdxReadSerialized();
doReturn(mockPdxInstanceFactory).when(mockCache).createPdxInstanceFactory(eq(source.getClass().getName()));
doReturn(mockPdxInstanceHolder).when(mockPdxInstanceFactory).create();
doReturn(mockPdxInstanceSource).when(mockPdxInstanceHolder).getField(eq("source"));
PdxInstanceBuilder builder = PdxInstanceBuilder.create(mockCache);
assertThat(builder).isNotNull();
assertThat(builder.getRegionService()).isEqualTo(mockCache);
PdxInstanceBuilder.Factory factory = builder.from(source);
assertThat(factory).isNotNull();
assertThat(factory.create()).isEqualTo(mockPdxInstanceSource);
verify(mockCache, times(1)).getPdxReadSerialized();
verify(mockCache, times(1)).createPdxInstanceFactory(eq(source.getClass().getName()));
verify(mockPdxInstanceFactory, times(1)).writeObject(eq("source"), eq(source));
verify(mockPdxInstanceFactory, times(1)).create();
verify(mockPdxInstanceHolder, times(1)).getField(eq("source"));
verifyNoInteractions(mockPdxInstanceSource);
}
@Test(expected = IllegalArgumentException.class)
public void fromNullSourceObjectThrowsIllegalArgumentException() {
RegionService mockRegionService = mock(RegionService.class);
try {
PdxInstanceBuilder.create(mockRegionService).from(null);
}
catch (IllegalArgumentException expected) {
assertThat(expected).hasMessage("Source object to serialize to PDX must not be null");
assertThat(expected).hasNoCause();
throw expected;
}
finally {
verifyNoInteractions(mockRegionService);
}
}
@Test(expected = IllegalStateException.class)
public void fromNonNullSourceObjectWhenCacheIsNotPresent() {
RegionService mockRegionService = mock(RegionService.class);
try {
PdxInstanceBuilder.create(mockRegionService).from("TEST");
}
catch (IllegalStateException expected) {
assertThat(expected).hasMessage("PDX read-serialized must be set to true");
assertThat(expected).hasNoCause();
throw expected;
}
finally {
verifyNoInteractions(mockRegionService);
}
}
@Test(expected = IllegalStateException.class)
public void fromNonNullSourceObjectWhenCachePdxReadSerializedIsFalse() {
GemFireCache mockCache = mock(GemFireCache.class);
doReturn(false).when(mockCache).getPdxReadSerialized();
try {
PdxInstanceBuilder.create(mockCache).from("TEST");
}
catch (IllegalStateException expected) {
assertThat(expected).hasMessage("PDX read-serialized must be set to true");
assertThat(expected).hasNoCause();
throw expected;
}
finally {
verify(mockCache, times(1)).getPdxReadSerialized();
}
}
@Test(expected = IllegalArgumentException.class)
public void fromNonNullSourceObjectReturningNonPdxInstanceThrowsIllegalArgumentException() {
Object source = new Object();
GemFireCache mockCache = mock(GemFireCache.class);
PdxInstance mockPdxInstanceHolder = mock(PdxInstance.class);
PdxInstanceFactory mockPdxInstanceFactory = mock(PdxInstanceFactory.class);
doReturn(true).when(mockCache).getPdxReadSerialized();
doReturn(mockPdxInstanceFactory).when(mockCache).createPdxInstanceFactory(eq(source.getClass().getName()));
doReturn(mockPdxInstanceHolder).when(mockPdxInstanceFactory).create();
doReturn(source).when(mockPdxInstanceHolder).getField(eq("source"));
PdxInstanceBuilder builder = PdxInstanceBuilder.create(mockCache);
assertThat(builder).isNotNull();
assertThat(builder.getRegionService()).isEqualTo(mockCache);
PdxInstanceBuilder.Factory factory = builder.from(source);
assertThat(factory).isNotNull();
try {
factory.create();
}
catch (IllegalArgumentException expected) {
assertThat(expected).hasMessage("Expected an instance of PDX but was an instance of type [%s];"
+ " Was PDX read-serialized set to true", source.getClass().getName());
assertThat(expected).hasNoCause();
throw expected;
}
finally {
verify(mockCache, times(1)).getPdxReadSerialized();
verify(mockCache, times(1)).createPdxInstanceFactory(eq(source.getClass().getName()));
verify(mockPdxInstanceFactory, times(1)).writeObject(eq("source"), eq(source));
verify(mockPdxInstanceHolder, times(1)).getField(eq("source"));
}
}
@Test(expected = IllegalArgumentException.class)
public void fromNonNullSourceObjectReturningNullOnPdxInstanceFactoryCreateIsNullSafe() {
Object source = mock(Object.class);
GemFireCache mockCache = mock(GemFireCache.class);
PdxInstanceFactory mockPdxInstanceFactory = mock(PdxInstanceFactory.class);
doReturn(true).when(mockCache).getPdxReadSerialized();
doReturn(mockPdxInstanceFactory).when(mockCache).createPdxInstanceFactory(eq(source.getClass().getName()));
doReturn(null).when(mockPdxInstanceFactory).create();
PdxInstanceBuilder builder = PdxInstanceBuilder.create(mockCache);
assertThat(builder).isNotNull();
assertThat(builder.getRegionService()).isEqualTo(mockCache);
PdxInstanceBuilder.Factory factory = builder.from(source);
assertThat(factory).isNotNull();
try {
factory.create();
}
catch (IllegalArgumentException expected) {
assertThat(expected).hasMessage("Expected an instance of PDX but was an instance of type [null];"
+ " Was PDX read-serialized set to true");
assertThat(expected).hasNoCause();
throw expected;
}
finally {
verify(mockCache, times(1)).getPdxReadSerialized();
verify(mockCache, times(1)).createPdxInstanceFactory(eq(source.getClass().getName()));
verify(mockPdxInstanceFactory, times(1)).writeObject(eq("source"), eq(source));
verifyNoInteractions(source);
}
}
}

View File

@@ -0,0 +1,813 @@
/*
* Copyright 2017-present 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
*
* https://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.pdx;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import java.io.DataOutput;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import com.fasterxml.jackson.core.JsonGenerationException;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.json.JsonMapper;
import org.junit.Test;
import org.apache.geode.internal.Sendable;
import org.apache.geode.pdx.JSONFormatter;
import org.apache.geode.pdx.PdxInstance;
import org.apache.geode.pdx.WritablePdxInstance;
/**
* Unit Tests for {@link PdxInstanceWrapper}.
*
* @author John Blum
* @see org.junit.Test
* @see org.mockito.Mockito
* @see com.fasterxml.jackson.core.JsonGenerator
* @see com.fasterxml.jackson.databind.ObjectMapper
* @see com.fasterxml.jackson.databind.json.JsonMapper
* @see org.apache.geode.pdx.JSONFormatter
* @see org.apache.geode.pdx.PdxInstance
* @see org.apache.geode.pdx.WritablePdxInstance
* @since 1.3.0
*/
public class PdxInstanceWrapperUnitTests {
@Test
public void constructPdxInstanceWrapper() {
PdxInstance mockPdxInstance = mock(PdxInstance.class);
PdxInstanceWrapper wrapper = new PdxInstanceWrapper(mockPdxInstance);
assertThat(wrapper).isNotNull();
assertThat(wrapper.getDelegate()).isEqualTo(mockPdxInstance);
verifyNoInteractions(mockPdxInstance);
}
@Test(expected = IllegalArgumentException.class)
public void constructPdxInstanceWrapperWithNull() {
try {
new PdxInstanceWrapper(null);
}
catch (IllegalArgumentException expected) {
assertThat(expected).hasMessage("Argument must not be null");
assertThat(expected).hasNoCause();
throw expected;
}
}
@Test
public void fromObjectIsObject() {
assertThat(PdxInstanceWrapper.from("TEST")).isEqualTo("TEST");
}
@Test
public void fromNullIsNull() {
assertThat(PdxInstanceWrapper.from((Object) null)).isEqualTo(null);
}
@Test
public void fromPdxInstanceIsWrapper() {
PdxInstance mockPdxInstance = mock(PdxInstance.class);
PdxInstanceWrapper wrapper = PdxInstanceWrapper.from(mockPdxInstance);
assertThat(wrapper).isNotNull();
assertThat(wrapper.getDelegate()).isEqualTo(mockPdxInstance);
verifyNoInteractions(mockPdxInstance);
}
@Test
public void fromPdxInstanceWrapperIsSameWrapper() {
PdxInstanceWrapper mockWrapper = mock(PdxInstanceWrapper.class);
assertThat(PdxInstanceWrapper.from(mockWrapper)).isSameAs(mockWrapper);
verifyNoInteractions(mockWrapper);
}
@Test
public void unwrapPdxInstanceWrapperReturnsPdxInstanceDelegate() {
PdxInstance mockPdxInstance = mock(PdxInstance.class);
PdxInstanceWrapper wrapper = PdxInstanceWrapper.from(mockPdxInstance);
assertThat(wrapper).isNotNull();
assertThat(wrapper.getDelegate()).isEqualTo(mockPdxInstance);
assertThat(PdxInstanceWrapper.unwrap(wrapper)).isEqualTo(mockPdxInstance);
verifyNoInteractions(mockPdxInstance);
}
@Test
public void unwrapPdxInstanceReturnsPdxInstance() {
PdxInstance mockPdxInstance = mock(PdxInstance.class);
assertThat(PdxInstanceWrapper.unwrap(mockPdxInstance)).isEqualTo(mockPdxInstance);
verifyNoInteractions(mockPdxInstance);
}
@Test
public void unwrapNullIsNullSafeAndReturnsNull() {
assertThat(PdxInstanceWrapper.unwrap(null)).isNull();
}
@Test
public void objectMapperConfigurationIsCorrect() {
PdxInstance mockPdxInstance = mock(PdxInstance.class);
PdxInstanceWrapper wrapper = spy(PdxInstanceWrapper.from(mockPdxInstance));
ObjectMapper mockObjectMapper = mock(ObjectMapper.class);
JsonMapper.Builder mockJsonMapperBuilder = mock(JsonMapper.Builder.class);
JsonMapper mockJsonMapper = mock(JsonMapper.class);
doReturn(mockJsonMapperBuilder).when(wrapper).newJsonMapperBuilder();
doReturn(mockJsonMapperBuilder).when(mockJsonMapperBuilder).configure(any(DeserializationFeature.class), anyBoolean());
doReturn(mockJsonMapperBuilder).when(mockJsonMapperBuilder).configure(any(MapperFeature.class), anyBoolean());
doReturn(mockJsonMapper).when(mockJsonMapperBuilder).build();
doReturn(mockObjectMapper).when(mockJsonMapper).findAndRegisterModules();
assertThat(wrapper.getDelegate()).isEqualTo(mockPdxInstance);
ObjectMapper objectMapper = wrapper.getObjectMapper().orElse(null);
assertThat(objectMapper).isNotNull();
verify(mockJsonMapperBuilder, times(1)).configure(eq(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES), eq(false));
verify(mockJsonMapperBuilder, times(1)).configure(eq(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES), eq(false));
verify(mockJsonMapperBuilder, times(1)).configure(eq(MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS), eq(true));
verify(mockJsonMapperBuilder, times(1)).build();
verify(mockJsonMapper, times(1)).findAndRegisterModules();
verifyNoMoreInteractions(mockJsonMapperBuilder, mockJsonMapper);
verifyNoInteractions(mockPdxInstance, mockObjectMapper);
}
@Test
public void getClassNameCallsPdxInstanceGetClassName() {
PdxInstance mockPdxInstance = mock(PdxInstance.class);
doReturn("example.app.test.model.Type").when(mockPdxInstance).getClassName();
assertThat(PdxInstanceWrapper.from(mockPdxInstance).getClassName()).isEqualTo("example.app.test.model.Type");
verify(mockPdxInstance, times(1)).getClassName();
verifyNoMoreInteractions(mockPdxInstance);
}
@Test
public void isDeserializaleCallsPdxInstanceIsDeserializable() {
PdxInstance mockPdxInstance = mock(PdxInstance.class);
doReturn(true).when(mockPdxInstance).isDeserializable();
assertThat(PdxInstanceWrapper.from(mockPdxInstance).isDeserializable()).isTrue();
verify(mockPdxInstance, times(1)).isDeserializable();
verifyNoMoreInteractions(mockPdxInstance);
}
@Test
public void isEnumCallsPdxInstanceIsEnum() {
PdxInstance mockPdxInstance = mock(PdxInstance.class);
doReturn(false).when(mockPdxInstance).isEnum();
assertThat(PdxInstanceWrapper.from(mockPdxInstance).isEnum()).isFalse();
verify(mockPdxInstance, times(1)).isEnum();
verifyNoMoreInteractions(mockPdxInstance);
}
@Test
public void isIdentityFieldCallsPdxInstanceIsIdentityField() {
PdxInstance mockPdxInstance = mock(PdxInstance.class);
doReturn(false).when(mockPdxInstance).isIdentityField(anyString());
doReturn(true).when(mockPdxInstance).isIdentityField("id");
PdxInstanceWrapper wrapper = PdxInstanceWrapper.from(mockPdxInstance);
assertThat(wrapper.isIdentityField("id")).isTrue();
assertThat(wrapper.isIdentityField("randomField")).isFalse();
verify(mockPdxInstance, times(1)).isIdentityField(eq("id"));
verify(mockPdxInstance, times(1)).isIdentityField(eq("randomField"));
verifyNoMoreInteractions(mockPdxInstance);
}
@Test
public void getFieldCallPdxInstanceGetField() {
PdxInstance mockPdxInstance = mock(PdxInstance.class);
doReturn("TestValue").when(mockPdxInstance).getField(eq("TestField"));
assertThat(PdxInstanceWrapper.from(mockPdxInstance).getField("TestField")).isEqualTo("TestValue");
verify(mockPdxInstance, times(1)).getField(eq("TestField"));
verifyNoMoreInteractions(mockPdxInstance);
}
@Test
public void getFieldNameCallsPdxInstanceGetFieldNames() {
List<String> fieldNames = Arrays.asList("FieldOne", "FieldTwo");
PdxInstance mockPdxInstance = mock(PdxInstance.class);
doReturn(fieldNames).when(mockPdxInstance).getFieldNames();
assertThat(PdxInstanceWrapper.from(mockPdxInstance).getFieldNames()).isEqualTo(fieldNames);
verify(mockPdxInstance, times(1)).getFieldNames();
verifyNoMoreInteractions(mockPdxInstance);
}
@Test
public void getIdentifierFromPdxInstanceHavingAnIdentity() {
PdxInstance mockPdxInstance = mock(PdxInstance.class);
doReturn(Arrays.asList("age", "id", "name")).when(mockPdxInstance).getFieldNames();
doReturn(true).when(mockPdxInstance).isIdentityField(eq("id"));
doReturn(42).when(mockPdxInstance).getField(eq("id"));
PdxInstanceWrapper wrapper = spy(new PdxInstanceWrapper(mockPdxInstance));
assertThat(wrapper.getIdentifier()).isEqualTo(42);
verify(wrapper, never()).getId();
verify(mockPdxInstance, times(1)).getFieldNames();
verify(mockPdxInstance, times(1)).isIdentityField(eq("age"));
verify(mockPdxInstance, times(1)).isIdentityField(eq("id"));
verify(mockPdxInstance, never()).isIdentityField(eq("name"));
verify(mockPdxInstance, times(1)).getField(eq("id"));
verifyNoMoreInteractions(mockPdxInstance);
}
@Test
public void getIdentifierFromPdxInstanceHavingNoFields() {
PdxInstance mockPdxInstance = mock(PdxInstance.class);
PdxInstanceWrapper wrapper = spy(new PdxInstanceWrapper(mockPdxInstance));
doReturn(null).when(mockPdxInstance).getFieldNames();
doReturn(69).when(wrapper).getId();
assertThat(wrapper.getIdentifier()).isEqualTo(69);
verify(wrapper, times(1)).getId();
verify(mockPdxInstance, times(1)).getFieldNames();
verify(mockPdxInstance, never()).isIdentityField(anyString());
verify(mockPdxInstance, never()).getField(anyString());
verifyNoMoreInteractions(mockPdxInstance);
}
@Test
public void getIdentifierFromPdxInstanceHavingNoIdentityFields() {
PdxInstance mockPdxInstance = mock(PdxInstance.class);
PdxInstanceWrapper wrapper = spy(new PdxInstanceWrapper(mockPdxInstance));
doReturn(Arrays.asList("", "age", null, "name", " ")).when(mockPdxInstance).getFieldNames();
doReturn(false).when(mockPdxInstance).isIdentityField(any());
doReturn(99).when(wrapper).getId();
assertThat(wrapper.getIdentifier()).isEqualTo(99);
verify(wrapper, times(1)).getId();
verify(mockPdxInstance, times(1)).getFieldNames();
verify(mockPdxInstance, times(1)).isIdentityField(eq("age"));
verify(mockPdxInstance, times(1)).isIdentityField(eq("name"));
verify(mockPdxInstance, never()).isIdentityField(isNull());
verify(mockPdxInstance, never()).isIdentityField(eq(""));
verify(mockPdxInstance, never()).isIdentityField(eq(" "));
verify(mockPdxInstance, never()).getField(anyString());
verifyNoMoreInteractions(mockPdxInstance);
}
@Test(expected = IllegalStateException.class)
public void getIdentifierFromPdxInstanceWithNoIdentifier() {
PdxInstance mockPdxInstance = mock(PdxInstance.class);
PdxInstanceWrapper wrapper = spy(new PdxInstanceWrapper(mockPdxInstance));
doReturn(Collections.singletonList("name")).when(mockPdxInstance).getFieldNames();
doReturn(false).when(mockPdxInstance).isIdentityField(anyString());
doThrow(new IllegalStateException("NO ID")).when(wrapper).getId();
try {
wrapper.getIdentifier();
}
catch (IllegalStateException expected) {
assertThat(expected).hasMessage("NO ID");
assertThat(expected).hasNoCause();
throw expected;
}
finally {
verify(mockPdxInstance, times(1)).getFieldNames();
verify(mockPdxInstance, times(1)).isIdentityField(eq("name"));
verify(mockPdxInstance, never()).getField(anyString());
verify(wrapper, times(1)).getId();
verifyNoMoreInteractions(mockPdxInstance);
}
}
@Test
public void getIdFromPdxInstanceHavingIdField() {
PdxInstance mockPdxInstance = mock(PdxInstance.class);
PdxInstanceWrapper wrapper = spy(new PdxInstanceWrapper(mockPdxInstance));
doReturn(true).when(mockPdxInstance).hasField(eq(PdxInstanceWrapper.ID_FIELD_NAME));
doReturn(42).when(mockPdxInstance).getField(eq(PdxInstanceWrapper.ID_FIELD_NAME));
assertThat(wrapper.getId()).isEqualTo(42);
verify(mockPdxInstance, times(1)).hasField(eq(PdxInstanceWrapper.ID_FIELD_NAME));
verify(mockPdxInstance, times(1)).getField(eq(PdxInstanceWrapper.ID_FIELD_NAME));
verify(wrapper, never()).getAtIdentifier();
verifyNoMoreInteractions(mockPdxInstance);
}
@Test
public void getIdFromPdxInstanceHavingIdFieldWithNoValueReturnsNull() {
PdxInstance mockPdxInstance = mock(PdxInstance.class);
PdxInstanceWrapper wrapper = spy(new PdxInstanceWrapper(mockPdxInstance));
doReturn(true).when(mockPdxInstance).hasField(eq(PdxInstanceWrapper.ID_FIELD_NAME));
doReturn(null).when(mockPdxInstance).getField(eq(PdxInstanceWrapper.ID_FIELD_NAME));
assertThat(wrapper.getId()).isNull();
verify(mockPdxInstance, times(1)).hasField(eq(PdxInstanceWrapper.ID_FIELD_NAME));
verify(mockPdxInstance, times(1)).getField(eq(PdxInstanceWrapper.ID_FIELD_NAME));
verify(wrapper, never()).getAtIdentifier();
verifyNoMoreInteractions(mockPdxInstance);
}
@Test
public void getIdFromPdxInstanceWithNoIdFieldCallsGetAtIdentifier() {
PdxInstance mockPdxInstance = mock(PdxInstance.class);
PdxInstanceWrapper wrapper = spy(new PdxInstanceWrapper(mockPdxInstance));
doReturn(false).when(mockPdxInstance).hasField(any());
doReturn(99).when(wrapper).getAtIdentifier();
assertThat(wrapper.getId()).isEqualTo(99);
verify(mockPdxInstance, times(1)).hasField(eq(PdxInstanceWrapper.ID_FIELD_NAME));
verify(mockPdxInstance, never()).getField(eq(PdxInstanceWrapper.ID_FIELD_NAME));
verify(wrapper, times(1)).getAtIdentifier();
verifyNoMoreInteractions(mockPdxInstance);
}
@Test
public void getAtIdentifierFromPdxInstance() {
PdxInstance mockPdxInstance = mock(PdxInstance.class);
PdxInstanceWrapper wrapper = spy(new PdxInstanceWrapper(mockPdxInstance));
doReturn(true).when(mockPdxInstance).hasField(eq(PdxInstanceWrapper.AT_IDENTIFIER_FIELD_NAME));
doReturn(true).when(mockPdxInstance).hasField(eq("isbn"));
doReturn("isbn").when(mockPdxInstance).getField(eq(PdxInstanceWrapper.AT_IDENTIFIER_FIELD_NAME));
doReturn("123456789").when(mockPdxInstance).getField(eq("isbn"));
assertThat(wrapper.getAtIdentifier()).isEqualTo("123456789");
verify(mockPdxInstance, times(1))
.hasField(eq(PdxInstanceWrapper.AT_IDENTIFIER_FIELD_NAME));
verify(mockPdxInstance, times(1))
.getField(eq(PdxInstanceWrapper.AT_IDENTIFIER_FIELD_NAME));
verify(mockPdxInstance, times(1)).hasField(eq("isbn"));
verify(mockPdxInstance, times(1)).getField(eq("isbn"));
verifyNoMoreInteractions(mockPdxInstance);
}
@Test(expected = IllegalStateException.class)
public void getAtIdentifierFromPdxInstanceWithNoDeclaredIdentity() {
PdxInstance mockPdxInstance = mock(PdxInstance.class);
PdxInstanceWrapper wrapper = spy(new PdxInstanceWrapper(mockPdxInstance));
doReturn(Account.class.getName()).when(mockPdxInstance).getClassName();
doReturn(false).when(mockPdxInstance).hasField(any());
try {
wrapper.getAtIdentifier();
}
catch (IllegalStateException expected) {
assertThat(expected).hasMessage("PdxInstance for type [%s] has no declared identifier",
Account.class.getName());
assertThat(expected).hasNoCause();
throw expected;
}
finally {
verify(mockPdxInstance, times(1)).getClassName();
verify(mockPdxInstance, times(2))
.hasField(eq(PdxInstanceWrapper.AT_IDENTIFIER_FIELD_NAME));
verify(mockPdxInstance, times(1))
.hasField(eq(PdxInstanceWrapper.ID_FIELD_NAME));
verify(mockPdxInstance, never()).getField(anyString());
verifyNoMoreInteractions(mockPdxInstance);
}
}
@Test(expected = IllegalStateException.class)
public void getAtIdentifierFromPdxInstanceWithNoId() {
PdxInstance mockPdxInstance = mock(PdxInstance.class);
PdxInstanceWrapper wrapper = spy(new PdxInstanceWrapper(mockPdxInstance));
doReturn(Account.class.getName()).when(mockPdxInstance).getClassName();
doReturn(true).when(mockPdxInstance).hasField(eq(PdxInstanceWrapper.ID_FIELD_NAME));
try {
wrapper.getAtIdentifier();
}
catch (IllegalStateException expected) {
assertThat(expected).hasMessage("PdxInstance for type [%s] has no id",
Account.class.getName());
assertThat(expected).hasNoCause();
throw expected;
}
finally {
verify(mockPdxInstance, times(1)).getClassName();
verify(mockPdxInstance, times(1))
.hasField(eq(PdxInstanceWrapper.AT_IDENTIFIER_FIELD_NAME));
verify(mockPdxInstance, times(1))
.hasField(eq(PdxInstanceWrapper.ID_FIELD_NAME));
verify(mockPdxInstance, never()).getField(any());
verifyNoMoreInteractions(mockPdxInstance);
}
}
@Test(expected = IllegalStateException.class)
public void getAtIdentifierFromPdxInstanceWithValidAtIdentifierAndIdentifierFieldButNoId() {
PdxInstance mockPdxInstance = mock(PdxInstance.class);
PdxInstanceWrapper wrapper = spy(new PdxInstanceWrapper(mockPdxInstance));
doReturn(Person.class.getName()).when(mockPdxInstance).getClassName();
doReturn(true).when(mockPdxInstance).hasField(eq(PdxInstanceWrapper.AT_IDENTIFIER_FIELD_NAME));
doReturn(false).when(mockPdxInstance).hasField(eq(PdxInstanceWrapper.ID_FIELD_NAME));
doReturn(true).when(mockPdxInstance).hasField(eq("ssn"));
doReturn("ssn").when(mockPdxInstance).getField(eq(PdxInstanceWrapper.AT_IDENTIFIER_FIELD_NAME));
doReturn(null).when(mockPdxInstance).getField(eq("ssn"));
try {
wrapper.getAtIdentifier();
}
catch (IllegalStateException expected) {
String expectedMessage = "PdxInstance for type [%s] has no value [null] for field [ssn] declared in [%s]";
assertThat(expected).hasMessage(expectedMessage, Person.class.getName(),
PdxInstanceWrapper.AT_IDENTIFIER_FIELD_NAME);
assertThat(expected).hasNoCause();
throw expected;
}
finally {
verify(mockPdxInstance, times(1)).getClassName();
verify(mockPdxInstance, times(2)).hasField(eq(PdxInstanceWrapper.AT_IDENTIFIER_FIELD_NAME));
verify(mockPdxInstance, times(1)).hasField(eq(PdxInstanceWrapper.ID_FIELD_NAME));
verify(mockPdxInstance, times(2)).hasField(eq("ssn"));
verify(mockPdxInstance, times(2)).getField(eq(PdxInstanceWrapper.AT_IDENTIFIER_FIELD_NAME));
verify(mockPdxInstance, times(2)).getField(eq("ssn"));
verifyNoMoreInteractions(mockPdxInstance);
}
}
@Test(expected = IllegalStateException.class)
public void getAtIdentifierFromPdxInstanceWithAtIdentifierReferringToInvalidIdentifierField() {
PdxInstance mockPdxInstance = mock(PdxInstance.class);
PdxInstanceWrapper wrapper = spy(new PdxInstanceWrapper(mockPdxInstance));
doReturn(Person.class.getName()).when(mockPdxInstance).getClassName();
doReturn(true).when(mockPdxInstance).hasField(eq(PdxInstanceWrapper.AT_IDENTIFIER_FIELD_NAME));
doReturn(false).when(mockPdxInstance).hasField(eq(PdxInstanceWrapper.ID_FIELD_NAME));
doReturn(false).when(mockPdxInstance).hasField(eq("ssn"));
doReturn("ssn").when(mockPdxInstance).getField(eq(PdxInstanceWrapper.AT_IDENTIFIER_FIELD_NAME));
try {
wrapper.getAtIdentifier();
}
catch (IllegalStateException expected) {
assertThat(expected).hasMessage("PdxInstance for type [%s] has no field [ssn] declared in [%s]",
Person.class.getName(), PdxInstanceWrapper.AT_IDENTIFIER_FIELD_NAME);
assertThat(expected).hasNoCause();
throw expected;
}
finally {
verify(mockPdxInstance, times(1)).getClassName();
verify(mockPdxInstance, times(2)).hasField(eq(PdxInstanceWrapper.AT_IDENTIFIER_FIELD_NAME));
verify(mockPdxInstance, times(1)).hasField(eq(PdxInstanceWrapper.ID_FIELD_NAME));
verify(mockPdxInstance, times(2)).hasField(eq("ssn"));
verify(mockPdxInstance, times(2)).getField(eq(PdxInstanceWrapper.AT_IDENTIFIER_FIELD_NAME));
verify(mockPdxInstance, never()).getField(eq("ssn"));
verifyNoMoreInteractions(mockPdxInstance);
}
}
@Test
public void getObjectReturnsObject() throws JsonProcessingException {
Account mockAccount = mock(Account.class);
ObjectMapper mockObjectMapper = mock(ObjectMapper.class);
PdxInstance mockPdxInstance = mock(PdxInstance.class);
String json = String.format("{ \"@type\": \"%s\", \"name\": \"Savings\"}", Account.class.getName());
doReturn(JSONFormatter.JSON_CLASSNAME).when(mockPdxInstance).getClassName();
doReturn(true).when(mockPdxInstance).hasField(eq(PdxInstanceWrapper.AT_TYPE_FIELD_NAME));
doReturn(Account.class.getName()).when(mockPdxInstance).getField(eq(PdxInstanceWrapper.AT_TYPE_FIELD_NAME));
PdxInstanceWrapper wrapper = spy(PdxInstanceWrapper.from(mockPdxInstance));
assertThat(wrapper).isNotNull();
assertThat(wrapper.getDelegate()).isEqualTo(mockPdxInstance);
doReturn(Optional.of(mockObjectMapper)).when(wrapper).getObjectMapper();
doReturn(json).when(wrapper).jsonFormatterToJson(eq(mockPdxInstance));
doReturn(mockAccount).when(mockObjectMapper).readValue(eq(json), eq(Account.class));
assertThat(wrapper.getObject()).isEqualTo(mockAccount);
verify(wrapper, atLeastOnce()).getDelegate();
verify(wrapper, times(1)).getObjectMapper();
verify(mockPdxInstance, times(1)).getClassName();
verify(mockPdxInstance, times(1)).hasField(eq(PdxInstanceWrapper.AT_TYPE_FIELD_NAME));
verify(mockPdxInstance, times(1)).getField(eq(PdxInstanceWrapper.AT_TYPE_FIELD_NAME));
verify(wrapper, times(1)).jsonFormatterToJson(eq(mockPdxInstance));
verify(mockObjectMapper, times(1)).readValue(eq(json), eq(Account.class));
verify(mockPdxInstance, never()).getObject();
verifyNoMoreInteractions(mockObjectMapper, mockPdxInstance);
}
@Test
@SuppressWarnings("unchecked")
public void getObjectCallsPdxInstanceGetObjectWhenAtTypeFieldIsNotPresent() throws JsonProcessingException {
Object value = new Object();
ObjectMapper mockObjectMapper = mock(ObjectMapper.class);
PdxInstance mockPdxInstance = mock(PdxInstance.class);
doReturn(JSONFormatter.JSON_CLASSNAME).when(mockPdxInstance).getClassName();
doReturn(false).when(mockPdxInstance).hasField(eq(PdxInstanceWrapper.AT_TYPE_FIELD_NAME));
doReturn(value).when(mockPdxInstance).getObject();
PdxInstanceWrapper wrapper = spy(PdxInstanceWrapper.from(mockPdxInstance));
assertThat(wrapper).isNotNull();
assertThat(wrapper.getDelegate()).isEqualTo(mockPdxInstance);
doReturn(Optional.of(mockObjectMapper)).when(wrapper).getObjectMapper();
assertThat(wrapper.getObject()).isEqualTo(value);
verify(wrapper, atLeastOnce()).getDelegate();
verify(wrapper, times(1)).getObjectMapper();
verify(mockPdxInstance, times(1)).getClassName();
verify(mockPdxInstance, times(1)).hasField(eq(PdxInstanceWrapper.AT_TYPE_FIELD_NAME));
verify(mockPdxInstance, never()).getField(anyString());
verify(wrapper, never()).jsonFormatterToJson(any());
verify(mockObjectMapper, never()).readValue(anyString(), any(Class.class));
verify(mockPdxInstance, times(1)).getObject();
verifyNoMoreInteractions(mockObjectMapper, mockPdxInstance);
}
@Test
@SuppressWarnings("unchecked")
public void getObjectCallsPdxInstanceGetObjectWhenClassNameIsNotGemFireJson() throws JsonProcessingException {
ObjectMapper mockObjectMapper = mock(ObjectMapper.class);
PdxInstance mockPdxInstance = mock(PdxInstance.class);
doReturn("non.existing.class.Name").when(mockPdxInstance).getClassName();
doReturn("TEST").when(mockPdxInstance).getObject();
PdxInstanceWrapper wrapper = spy(PdxInstanceWrapper.from(mockPdxInstance));
assertThat(wrapper).isNotNull();
assertThat(wrapper.getDelegate()).isEqualTo(mockPdxInstance);
doReturn(Optional.of(mockObjectMapper)).when(wrapper).getObjectMapper();
assertThat(wrapper.getObject()).isEqualTo("TEST");
verify(wrapper, atLeastOnce()).getDelegate();
verify(wrapper, times(1)).getObjectMapper();
verify(mockPdxInstance, times(1)).getClassName();
verify(mockPdxInstance, never()).hasField(anyString());
verify(mockPdxInstance, never()).getField(anyString());
verify(wrapper, never()).jsonFormatterToJson(any());
verify(mockObjectMapper, never()).readValue(anyString(), any(Class.class));
verify(mockPdxInstance, times(1)).getObject();
verifyNoMoreInteractions(mockObjectMapper, mockPdxInstance);
}
@Test
@SuppressWarnings("unchecked")
public void getObjectCallsPdxInstanceGetObjectWhenExceptionIsThrown() throws JsonProcessingException {
Object value = new Object();
ObjectMapper mockObjectMapper = mock(ObjectMapper.class);
PdxInstance mockPdxInstance = mock(PdxInstance.class);
String json = String.format("{ \"@type\": \"%s\", \"name\": \"Checking\"}", Account.class.getName());
doReturn(JSONFormatter.JSON_CLASSNAME).when(mockPdxInstance).getClassName();
doReturn(true).when(mockPdxInstance).hasField(eq(PdxInstanceWrapper.AT_TYPE_FIELD_NAME));
doReturn(Account.class.getName()).when(mockPdxInstance).getField(eq(PdxInstanceWrapper.AT_TYPE_FIELD_NAME));
doReturn(value).when(mockPdxInstance).getObject();
PdxInstanceWrapper wrapper = spy(PdxInstanceWrapper.from(mockPdxInstance));
assertThat(wrapper).isNotNull();
assertThat(wrapper.getDelegate()).isEqualTo(mockPdxInstance);
doReturn(Optional.of(mockObjectMapper)).when(wrapper).getObjectMapper();
doReturn(json).when(wrapper).jsonFormatterToJson(eq(mockPdxInstance));
doThrow(new JsonGenerationException("TEST", mock(JsonGenerator.class)))
.when(mockObjectMapper).readValue(anyString(), any(Class.class));
assertThat(wrapper.getObject()).isEqualTo(value);
verify(wrapper, times(1)).getObjectMapper();
verify(mockPdxInstance, times(1)).getClassName();
verify(mockPdxInstance, times(1)).hasField(eq(PdxInstanceWrapper.AT_TYPE_FIELD_NAME));
verify(mockPdxInstance, times(1)).getField(eq(PdxInstanceWrapper.AT_TYPE_FIELD_NAME));
verify(wrapper, atLeastOnce()).getDelegate();
verify(wrapper, times(1)).jsonFormatterToJson(eq(mockPdxInstance));
verify(mockObjectMapper, times(1)).readValue(eq(json), eq(Account.class));
verify(mockPdxInstance, times((1))).getObject();
verifyNoMoreInteractions(mockObjectMapper, mockPdxInstance);
}
@Test
public void getObjectCallsPdxInstanceGetObjectWhenObjectMapperIsNotPresent() {
PdxInstance mockPdxInstance = mock(PdxInstance.class);
doReturn("non.existing.class.Name").when(mockPdxInstance).getClassName();
doReturn("MOCK").when(mockPdxInstance).getObject();
PdxInstanceWrapper wrapper = spy(PdxInstanceWrapper.from(mockPdxInstance));
assertThat(wrapper).isNotNull();
assertThat(wrapper.getDelegate()).isEqualTo(mockPdxInstance);
doReturn(Optional.empty()).when(wrapper).getObjectMapper();
assertThat(wrapper.getObject()).isEqualTo("MOCK");
verify(wrapper, atLeastOnce()).getDelegate();
verify(wrapper, times(1)).getObjectMapper();
verify(wrapper, never()).jsonFormatterToJson(any());
verify(mockPdxInstance, times(1)).getObject();
verifyNoMoreInteractions(mockPdxInstance);
}
@Test
public void createWriterCallsPdxInstanceCreateWriter() {
PdxInstance mockPdxInstance = mock(PdxInstance.class);
WritablePdxInstance mockWritablePdxInstance = mock(WritablePdxInstance.class);
doReturn(mockWritablePdxInstance).when(mockPdxInstance).createWriter();
assertThat(PdxInstanceWrapper.from(mockPdxInstance).createWriter()).isEqualTo(mockWritablePdxInstance);
verify(mockPdxInstance, times(1)).createWriter();
verifyNoMoreInteractions(mockPdxInstance);
}
@Test
public void hasFieldCallsPdxInstanceHasField() {
PdxInstance mockPdxInstance = mock(PdxInstance.class);
doReturn(true).when(mockPdxInstance).hasField("name");
assertThat(PdxInstanceWrapper.from(mockPdxInstance).hasField("name")).isTrue();
verify(mockPdxInstance, times(1)).hasField(eq("name"));
verifyNoMoreInteractions(mockPdxInstance);
}
@Test
public void sendToCallsPdxInstanceSendTo() throws IOException {
SendablePdxInstance mockSendablePdxInstance = mock(SendablePdxInstance.class);
PdxInstanceWrapper wrapper = PdxInstanceWrapper.from(mockSendablePdxInstance);
DataOutput mockOut = mock(DataOutput.class);
assertThat(wrapper).isNotNull();
assertThat(wrapper.getDelegate()).isEqualTo(mockSendablePdxInstance);
wrapper.sendTo(mockOut);
verify(mockSendablePdxInstance, times(1)).sendTo(eq(mockOut));
verifyNoMoreInteractions(mockSendablePdxInstance);
verifyNoMoreInteractions(mockOut);
}
interface Account {
@SuppressWarnings("unused")
String getName();
}
interface Person { }
interface SendablePdxInstance extends PdxInstance, Sendable { }
}

View File

@@ -0,0 +1,101 @@
/*
* Copyright 2017-present 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
*
* https://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.security;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.Properties;
import org.junit.Test;
import org.springframework.geode.util.GeodeConstants;
/**
* Unit Tests for {@link TestAuthInitialize}.
*
* @author John Blum
* @see java.util.Properties
* @see org.junit.Test
* @see org.springframework.geode.security.TestAuthInitialize
* @since 1.0.0
*/
public class TestAuthInitializeUnitTests {
private final TestAuthInitialize authInitialize = TestAuthInitialize.create();
private boolean isSet(String value) {
return !(value == null || value.trim().isEmpty());
}
private Properties newSecurityProperties(String username, String password) {
Properties securityProperties = new Properties();
if (isSet(username)) {
securityProperties.setProperty(GeodeConstants.USERNAME, username);
}
if (isSet(password)) {
securityProperties.setProperty(GeodeConstants.PASSWORD, password);
}
return securityProperties;
}
@Test
public void getCredentialsUsesProperties() {
Properties securityProperties = newSecurityProperties("testUser", "s3cr3t");
Properties credentials = this.authInitialize.getCredentials(securityProperties, null, false);
assertThat(credentials).isNotNull();
assertThat(credentials.getProperty(GeodeConstants.USERNAME)).isEqualTo("testUser");
assertThat(credentials.getProperty(GeodeConstants.PASSWORD)).isEqualTo("s3cr3t");
}
@Test
public void getCredentialsUsesProvidedUsernameAndDefaultPassword() {
Properties securityProperties = newSecurityProperties("testUser", null);
Properties credentials = this.authInitialize.getCredentials(securityProperties, null, false);
assertThat(credentials).isNotNull();
assertThat(credentials.getProperty(GeodeConstants.USERNAME)).isEqualTo("testUser");
assertThat(credentials.getProperty(GeodeConstants.PASSWORD)).isEqualTo("test");
}
@Test
public void getCredentialsUsesProvidedPasswordAndDefaultUsername() {
Properties securityProperties = newSecurityProperties(null, "s3cr3t");
Properties credentials = this.authInitialize.getCredentials(securityProperties, null, false);
assertThat(credentials).isNotNull();
assertThat(credentials.getProperty(GeodeConstants.USERNAME)).isEqualTo("test");
assertThat(credentials.getProperty(GeodeConstants.PASSWORD)).isEqualTo("s3cr3t");
}
@Test
public void getCredentialsUsesDefaultUsernameAndPassword() {
Properties securityProperties = newSecurityProperties(null, null);
Properties credentials = this.authInitialize.getCredentials(securityProperties, null, false);
assertThat(credentials).isNotNull();
assertThat(credentials.getProperty(GeodeConstants.USERNAME)).isEqualTo("test");
assertThat(credentials.getProperty(GeodeConstants.PASSWORD)).isEqualTo("test");
}
}

View File

@@ -0,0 +1,103 @@
/*
* Copyright 2017-present 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
*
* https://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.security;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import java.security.Principal;
import java.util.Properties;
import org.junit.Test;
import org.apache.geode.security.AuthenticationFailedException;
import org.apache.geode.security.ResourcePermission;
import org.springframework.geode.util.GeodeConstants;
/**
* Unit Tests for {@link org.springframework.geode.security.TestSecurityManager}.
*
* @author John Blum
* @see org.junit.Test
* @see org.mockito.Mockito
* @see org.springframework.geode.security.TestSecurityManager
* @since 1.0.0
*/
public class TestSecurityManagerUnitTests {
private TestSecurityManager securityManager = new TestSecurityManager();
private Properties newSecurityProperties(String username, String password) {
Properties securityProperties = new Properties();
securityProperties.setProperty(GeodeConstants.USERNAME, username);
securityProperties.setProperty(GeodeConstants.PASSWORD, password);
return securityProperties;
}
@Test
public void userAuthenticates() {
Object user = this.securityManager.authenticate(newSecurityProperties("test", "test"));
assertThat(user).isInstanceOf(TestSecurityManager.User.class);
assertThat(((TestSecurityManager.User) user).getName()).isEqualTo("test");
}
@Test(expected = AuthenticationFailedException.class)
public void userDoesNotAuthenticateBecauseUsernamePasswordAreCaseSensitive() {
try {
this.securityManager.authenticate(newSecurityProperties("TestUser", "testuser"));
}
catch (AuthenticationFailedException expected) {
assertThat(expected).hasMessage("User [TestUser] could not be authenticated");
assertThat(expected).hasNoCause();
throw expected;
}
}
@Test(expected = AuthenticationFailedException.class)
public void userDoesNotAuthenticateWhenUsernamePasswordDoNotMatch() {
try {
this.securityManager.authenticate(newSecurityProperties("testUser", "testPassword"));
}
catch (AuthenticationFailedException expected) {
assertThat(expected).hasMessage("User [testUser] could not be authenticated");
assertThat(expected).hasNoCause();
throw expected;
}
}
@Test
public void userIsAlwaysAuthorized() {
ResourcePermission clusterManage =
new ResourcePermission(ResourcePermission.Resource.CLUSTER, ResourcePermission.Operation.MANAGE);
assertThat(this.securityManager.authorize(null, clusterManage)).isTrue();
assertThat(this.securityManager.authorize(new TestSecurityManager.User("test"), clusterManage)).isTrue();
assertThat(this.securityManager.authorize(mock(Principal.class), clusterManage)).isTrue();
}
}

View File

@@ -0,0 +1,564 @@
/*
* Copyright 2017-present 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
*
* https://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.util;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import org.junit.Test;
import org.apache.geode.cache.Cache;
import org.apache.geode.cache.DataPolicy;
import org.apache.geode.cache.GemFireCache;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.RegionAttributes;
import org.apache.geode.cache.RegionService;
import org.apache.geode.cache.client.ClientCache;
import org.apache.geode.internal.cache.GemFireCacheImpl;
/**
* Unit Tests for {@link CacheUtils}.
*
* @author John Blum
* @see org.junit.Test
* @see org.mockito.Mockito
* @see org.apache.geode.cache.Cache
* @see org.apache.geode.cache.GemFireCache
* @see org.apache.geode.cache.Region
* @see org.apache.geode.cache.RegionAttributes
* @see org.apache.geode.cache.RegionService
* @see org.apache.geode.cache.client.ClientCache
* @since 1.3.0
*/
@SuppressWarnings("unchecked")
public class CacheUtilsUnitTests {
@Test
public void collectValuesFromClientRegion() {
Region<Object, Object> mockRegion = mock(Region.class);
RegionAttributes<Object, Object> mockRegionAttributes = mock(RegionAttributes.class);
RegionService mockRegionService = mock(ClientCache.class);
Set<?> keySetOnServer = new TreeSet<>(Arrays.asList(1, 2, 3));
Map<Object, Object> keysValues = new HashMap<>();
keysValues.put(1, "one");
keysValues.put(2, "two");
keysValues.put(3, "three");
doReturn(mockRegionAttributes).when(mockRegion).getAttributes();
doReturn(mockRegionService).when(mockRegion).getRegionService();
doReturn(keySetOnServer).when(mockRegion).keySetOnServer();
doReturn(keysValues).when(mockRegion).getAll(eq(keySetOnServer));
doReturn(DataPolicy.EMPTY).when(mockRegionAttributes).getDataPolicy();
assertThat(CacheUtils.collectValues(mockRegion)).containsExactlyInAnyOrder(keysValues.values().toArray());
verify(mockRegion, times(2)).getAttributes();
verify(mockRegion, times(1)).getRegionService();
verify(mockRegion, times(1)).keySetOnServer();
verify(mockRegion, times(1)).getAll(eq(keySetOnServer));
verify(mockRegion, never()).values();
verify(mockRegionAttributes, times(1)).getDataPolicy();
verify(mockRegionAttributes, never()).getPoolName();
verifyNoInteractions(mockRegionService);
verifyNoMoreInteractions(mockRegion);
}
@Test
public void collectValuesFromClientRegionWhenClientRegionGetAllKeysReturnsNullMapIsNullSafe() {
Region<Object, Object> mockRegion = mock(Region.class);
RegionAttributes<Object, Object> mockRegionAttributes = mock(RegionAttributes.class);
RegionService mockRegionService = mock(ClientCache.class);
Set<Object> keySetOnServer = new TreeSet<>(Arrays.asList(1, 2));
doReturn(mockRegionAttributes).when(mockRegion).getAttributes();
doReturn(mockRegionService).when(mockRegion).getRegionService();
doReturn(keySetOnServer).when(mockRegion).keySetOnServer();
doReturn(null).when(mockRegion).getAll(eq(keySetOnServer));
doReturn(DataPolicy.NORMAL).when(mockRegionAttributes).getDataPolicy();
doReturn("Car").when(mockRegionAttributes).getPoolName();
Collection<Object> values = CacheUtils.collectValues(mockRegion);
assertThat(values).isNotNull();
assertThat(values).isEmpty();
verify(mockRegion, times(3)).getAttributes();
verify(mockRegion, times(1)).getRegionService();
verify(mockRegion, times(1)).keySetOnServer();
verify(mockRegion, times(1)).getAll(eq(keySetOnServer));
verify(mockRegion, never()).values();
verify(mockRegionAttributes, times(1)).getDataPolicy();
verify(mockRegionAttributes, times(1)).getPoolName();
verifyNoInteractions(mockRegionService);
verifyNoMoreInteractions(mockRegion);
}
@Test
public void collectValuesFromClientRegionWhenClientRegionKeySetOnServerReturnsNullSetIsNullSafe() {
Region<Object, Object> mockRegion = mock(Region.class);
RegionAttributes<Object, Object> mockRegionAttributes = mock(RegionAttributes.class);
RegionService mockRegionService = mock(ClientCache.class);
doReturn(mockRegionAttributes).when(mockRegion).getAttributes();
doReturn(mockRegionService).when(mockRegion).getRegionService();
doReturn(null).when(mockRegion).keySetOnServer();
doReturn(DataPolicy.PARTITION).when(mockRegionAttributes).getDataPolicy();
doReturn("Dead").when(mockRegionAttributes).getPoolName();
Collection<Object> values = CacheUtils.collectValues(mockRegion);
assertThat(values).isNotNull();
assertThat(values).isEmpty();
verify(mockRegion, times(3)).getAttributes();
verify(mockRegion, times(1)).getRegionService();
verify(mockRegion, times(1)).keySetOnServer();
verify(mockRegion, never()).getAll(any());
verify(mockRegion, never()).values();
verify(mockRegionAttributes, times(1)).getDataPolicy();
verify(mockRegionAttributes, times(1)).getPoolName();
verifyNoInteractions(mockRegionService);
verifyNoMoreInteractions(mockRegion);
}
@Test
public void collectValuesFromClientRegionWhenClientRegionGetRegionServiceReturnsNullIsNullSafe() {
Collection<Object> values = Arrays.asList("one", "two", "three");
Region<Object, Object> mockRegion = mock(Region.class);
doReturn(null).when(mockRegion).getRegionService();
doReturn(values).when(mockRegion).values();
assertThat(CacheUtils.collectValues(mockRegion)).containsExactlyInAnyOrder(values.toArray());
verify(mockRegion, times(1)).getRegionService();
verify(mockRegion, times(1)).values();
}
@Test
public void collectValuesFromLocalClientRegion() {
Region<?, String> mockRegion = mock(Region.class);
RegionAttributes<?, String> mockRegionAttributes = mock(RegionAttributes.class);
RegionService mockRegionService = mock(ClientCache.class);
doReturn(mockRegionAttributes).when(mockRegion).getAttributes();
doReturn(mockRegionService).when(mockRegion).getRegionService();
doReturn(DataPolicy.NORMAL).when(mockRegionAttributes).getDataPolicy();
doReturn(" ").when(mockRegionAttributes).getPoolName();
doReturn(Arrays.asList("one", "two")).when(mockRegion).values();
Collection<String> values = CacheUtils.collectValues(mockRegion);
assertThat(values).isNotNull();
assertThat(values).containsExactly("one", "two");
verify(mockRegion, times(1)).getRegionService();
verify(mockRegion, times(3)).getAttributes();
verify(mockRegion, times(1)).values();
verify(mockRegion, never()).keySetOnServer();
verify(mockRegion, never()).getAll(any());
verify(mockRegionAttributes, times(1)).getDataPolicy();
verify(mockRegionAttributes, times(1)).getPoolName();
verifyNoInteractions(mockRegionService);
}
@Test
public void collectValuesFromPeerRegion() {
Region<Object, Object> mockRegion = mock(Region.class);
RegionService mockRegionService = mock(Cache.class);
Collection<Object> values = Arrays.asList("one", "two", "three");
doReturn(mockRegionService).when(mockRegion).getRegionService();
doReturn(values).when(mockRegion).values();
assertThat(CacheUtils.collectValues(mockRegion)).containsExactlyInAnyOrder(values.toArray());
verify(mockRegion, times(1)).getRegionService();
verify(mockRegion, times(1)).values();
verifyNoInteractions(mockRegionService);
}
@Test(expected = IllegalArgumentException.class)
public void collectValuesWithNullRegionThrowsIllegalArgumentException() {
try {
CacheUtils.collectValues(null);
}
catch (IllegalArgumentException expected) {
assertThat(expected).hasMessageStartingWith("Argument must not be null");
assertThat(expected).hasNoCause();
throw expected;
}
}
@Test
public void isClientCacheWithClientCache() {
assertThat(CacheUtils.isClientCache(mock(ClientCache.class))).isTrue();
}
@Test
public void isClientCacheWithGemFireCacheImplWhenIsClientReturnsTrue() {
GemFireCacheImpl mockCache = mock(GemFireCacheImpl.class);
doReturn(true).when(mockCache).isClient();
assertThat(CacheUtils.isClientCache(mockCache)).isTrue();
verify(mockCache, times(1)).isClient();
verifyNoMoreInteractions(mockCache);
}
@Test
public void isClientCacheWithGemFireCacheImplWhenIsClientReturnsFalse() {
GemFireCacheImpl mockCache = mock(GemFireCacheImpl.class);
doReturn(false).when(mockCache).isClient();
assertThat(CacheUtils.isClientCache(mockCache)).isFalse();
verify(mockCache, times(1)).isClient();
verifyNoMoreInteractions(mockCache);
}
@Test
public void isClientCacheWithNonClientCache() {
assertThat(CacheUtils.isClientCache(mock(Cache.class))).isFalse();
assertThat(CacheUtils.isClientCache(mock(GemFireCache.class))).isFalse();
assertThat(CacheUtils.isClientCache(mock(RegionService.class))).isFalse();
}
@Test
public void isClientCacheWithNull() {
assertThat(CacheUtils.isClientCache(null)).isFalse();
}
@Test
public void isClientRegionWithClientRegionInClientCache() {
Region<?, ?> mockRegion = mock(Region.class);
RegionService mockRegionService = mock(ClientCache.class);
doReturn(mockRegionService).when(mockRegion).getRegionService();
assertThat(CacheUtils.isClientRegion(mockRegion)).isTrue();
assertThat(CacheUtils.isPeerRegion(mockRegion)).isFalse();
verify(mockRegion, times(2)).getRegionService();
verifyNoMoreInteractions(mockRegion);
verifyNoInteractions(mockRegionService);
}
@Test
public void isClientRegionWithClientRegionDeterminedByPoolName() {
Region<?, ?> mockRegion = mock(Region.class);
RegionAttributes<?, ?> mockRegionAttributes = mock(RegionAttributes.class);
RegionService mockRegionService = mock(RegionService.class);
doReturn(mockRegionAttributes).when(mockRegion).getAttributes();
doReturn(mockRegionService).when(mockRegion).getRegionService();
doReturn("TestPool").when(mockRegionAttributes).getPoolName();
assertThat(CacheUtils.isClientRegion(mockRegion)).isTrue();
assertThat(CacheUtils.isPeerRegion(mockRegion)).isFalse();
verify(mockRegion, times(2)).getRegionService();
verify(mockRegion, times(2)).getAttributes();
verify(mockRegionAttributes, times(2)).getPoolName();
verifyNoMoreInteractions(mockRegion, mockRegionAttributes);
verifyNoInteractions(mockRegionService);
}
@Test
public void isClientRegionWithRegionHavingNoPoolName() {
Region<?, ?> mockRegion = mock(Region.class);
RegionAttributes<?, ?> mockRegionAttributes = mock(RegionAttributes.class);
doReturn(mockRegionAttributes).when(mockRegion).getAttributes();
doReturn(null).when(mockRegion).getRegionService();
doReturn(" ").when(mockRegionAttributes).getPoolName();
assertThat(CacheUtils.isClientRegion(mockRegion)).isFalse();
assertThat(CacheUtils.isPeerRegion(mockRegion)).isTrue();
verify(mockRegion, times(2)).getRegionService();
verify(mockRegion, times(2)).getAttributes();
verify(mockRegionAttributes, times(2)).getPoolName();
verifyNoMoreInteractions(mockRegion, mockRegionAttributes);
}
@Test
public void isClientRegionWithRegionHavingNoRegionAttributes() {
Region<?, ?> mockRegion = mock(Region.class);
doReturn(null).when(mockRegion).getAttributes();
doReturn(null).when(mockRegion).getRegionService();
assertThat(CacheUtils.isClientRegion(mockRegion)).isFalse();
assertThat(CacheUtils.isPeerRegion(mockRegion)).isTrue();
verify(mockRegion, times(2)).getRegionService();
verify(mockRegion, times(2)).getAttributes();
verifyNoMoreInteractions(mockRegion);
}
@Test
public void isClientRegionWithPeerRegionInPeerCache() {
Region<?, ?> mockRegion = mock(Region.class);
RegionService mockRegionService = mock(Cache.class);
doReturn(mockRegionService).when(mockRegion).getRegionService();
assertThat(CacheUtils.isClientRegion(mockRegion)).isFalse();
assertThat(CacheUtils.isPeerRegion(mockRegion)).isTrue();
verify(mockRegion, times(2)).getRegionService();
verify(mockRegion, times(2)).getAttributes();
verifyNoMoreInteractions(mockRegion);
}
@Test
public void isClientRegionWithNull() {
assertThat(CacheUtils.isClientRegion(null)).isFalse();
}
@Test
public void isPeerCacheWithPeerCache() {
assertThat(CacheUtils.isPeerCache(mock(Cache.class))).isTrue();
}
@Test
public void isPeerCacheWithGemFireCacheImplWhenIsClientReturnsTrue() {
GemFireCacheImpl mockCache = mock(GemFireCacheImpl.class);
doReturn(true).when(mockCache).isClient();
assertThat(CacheUtils.isPeerCache(mockCache)).isFalse();
verify(mockCache, times(1)).isClient();
verifyNoMoreInteractions(mockCache);
}
@Test
public void isPeerCacheWithGemFireCacheImplWhenIsClientReturnsFalse() {
GemFireCacheImpl mockCache = mock(GemFireCacheImpl.class);
doReturn(false).when(mockCache).isClient();
assertThat(CacheUtils.isPeerCache(mockCache)).isTrue();
verify(mockCache, times(1)).isClient();
verifyNoMoreInteractions(mockCache);
}
@Test
public void isPeerCacheWithNonPeerCache() {
assertThat(CacheUtils.isPeerCache(mock(ClientCache.class))).isFalse();
assertThat(CacheUtils.isPeerCache(mock(GemFireCache.class))).isFalse();
assertThat(CacheUtils.isPeerCache(mock(RegionService.class))).isFalse();
}
@Test
public void isPeerCacheWithNull() {
assertThat(CacheUtils.isPeerCache(null)).isFalse();
}
@Test
public void isPeerRegionWithNull() {
assertThat(CacheUtils.isPeerRegion(null)).isFalse();
}
@Test
public void isProxyRegionWithProxyRegionBasedOnDataPolicy() {
Region<?, ?> mockRegion = mock(Region.class);
RegionAttributes<?, ?> mockRegionAttributes = mock(RegionAttributes.class);
doReturn(mockRegionAttributes).when(mockRegion).getAttributes();
doReturn(DataPolicy.EMPTY).when(mockRegionAttributes).getDataPolicy();
assertThat(CacheUtils.isProxyRegion(mockRegion)).isTrue();
verify(mockRegion, times(2)).getAttributes();
verify(mockRegionAttributes, times(1)).getDataPolicy();
verify(mockRegionAttributes, never()).getPoolName();
}
@Test
public void isProxyRegionWithProxyRegionBasedOnPoolConfiguration() {
Region<?, ?> mockRegion = mock(Region.class);
RegionAttributes<?, ?> mockRegionAttributes = mock(RegionAttributes.class);
doReturn(mockRegionAttributes).when(mockRegion).getAttributes();
doReturn(DataPolicy.NORMAL).when(mockRegionAttributes).getDataPolicy();
doReturn("TestPool").when(mockRegionAttributes).getPoolName();
assertThat(CacheUtils.isProxyRegion(mockRegion)).isTrue();
verify(mockRegion, times(3)).getAttributes();
verify(mockRegionAttributes, times(1)).getDataPolicy();
verify(mockRegionAttributes, times(1)).getPoolName();
}
@Test
public void isProxyRegionWithNonProxyRegion() {
Region<?, ?> mockRegion = mock(Region.class);
RegionAttributes<?, ?> mockRegionAttributes = mock(RegionAttributes.class);
doReturn(mockRegionAttributes).when(mockRegion).getAttributes();
doReturn(DataPolicy.NORMAL).when(mockRegionAttributes).getDataPolicy();
doReturn(" ").when(mockRegionAttributes).getPoolName();
assertThat(CacheUtils.isProxyRegion(mockRegion)).isFalse();
verify(mockRegion, times(3)).getAttributes();
verify(mockRegionAttributes, times(1)).getDataPolicy();
verify(mockRegionAttributes, times(1)).getPoolName();
}
@Test
public void isProxyRegionWithRegionHavingNoAttributesIsNullSafe() {
Region<?, ?> mockRegion = mock(Region.class);
assertThat(CacheUtils.isProxyRegion(mockRegion)).isFalse();
verify(mockRegion, times(1)).getAttributes();
verifyNoMoreInteractions(mockRegion);
}
@Test
public void isProxyRegionWithNullRegionIsNullSafe() {
assertThat(CacheUtils.isProxyRegion(null)).isFalse();
}
@Test
public void isRegionWithPoolWithPooledRegion() {
Region<?, ?> mockRegion = mock(Region.class);
RegionAttributes<?, ?> mockRegionAttributes = mock(RegionAttributes.class);
doReturn(mockRegionAttributes).when(mockRegion).getAttributes();
doReturn("Swimming").when(mockRegionAttributes).getPoolName();
assertThat(CacheUtils.isRegionWithPool(mockRegion)).isTrue();
verify(mockRegion, times(1)).getAttributes();
verify(mockRegionAttributes, times(1)).getPoolName();
}
@Test
public void isRegionWithPoolUsingRegionHavingNoAttributesIsNullSafe() {
Region<?, ?> mockRegion = mock(Region.class);
assertThat(CacheUtils.isRegionWithPool(mockRegion)).isFalse();
verify(mockRegion, times(1)).getAttributes();
verifyNoMoreInteractions(mockRegion);
}
@Test
public void isRegionWithPoolUsingNullRegionIsNullSafe() {
assertThat(CacheUtils.isRegionWithPool(null)).isFalse();
}
private void testIsRegionWithPoolUsingRegionWithInvalidPoolNameReturnsFalse(String poolName) {
Region<?, ?> mockRegion = mock(Region.class);
RegionAttributes<?, ?> mockRegionAttributes = mock(RegionAttributes.class);
doReturn(mockRegionAttributes).when(mockRegion).getAttributes();
doReturn(poolName).when(mockRegionAttributes).getPoolName();
assertThat(CacheUtils.isRegionWithPool(mockRegion)).isFalse();
verify(mockRegion, times(1)).getAttributes();
verify(mockRegionAttributes, times(1)).getPoolName();
}
@Test
public void isRegionWithPoolUsingRegionConfiguredWithBlankPoolName() {
testIsRegionWithPoolUsingRegionWithInvalidPoolNameReturnsFalse(" ");
}
@Test
public void isRegionWithPoolUsingRegionConfiguredWithEmptyPoolName() {
testIsRegionWithPoolUsingRegionWithInvalidPoolNameReturnsFalse("");
}
@Test
public void isRegionWithPoolUsingRegionConfiguredWithNullPoolName() {
testIsRegionWithPoolUsingRegionWithInvalidPoolNameReturnsFalse(null);
}
}

View File

@@ -0,0 +1,97 @@
/*
* Copyright 2017-present 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
*
* https://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.util.function;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.Arrays;
import org.junit.Test;
/**
* Unit Tests for {@link InvocationArguments).
*
* @author John Blum
* @see org.junit.Test
* @see org.springframework.geode.util.function.InvocationArguments
* @since 1.3.0
*/
public class InvocationArgumentsUnitTests {
@Test
public void constructsInvocationArgumentsWithArguments() {
Object[] arguments = { true, 'c', 1, Math.PI, "test" };
InvocationArguments invocationArguments = new InvocationArguments(arguments);
assertThat(invocationArguments).isNotNull();
assertThat(invocationArguments.size()).isEqualTo(arguments.length);
assertThat(Arrays.equals(invocationArguments.getArguments(), arguments)).isTrue();
assertThat(invocationArguments.<Boolean>getArgumentAt(0)).isEqualTo(true);
assertThat(invocationArguments.<Character>getArgumentAt(1)).isEqualTo('c');
assertThat(invocationArguments.<Integer>getArgumentAt(2)).isEqualTo(1);
assertThat(invocationArguments.<Double>getArgumentAt(3)).isEqualTo(Math.PI);
assertThat(invocationArguments.<String>getArgumentAt(4)).isEqualTo("test");
}
@Test
public void fromConstructsNewInvocationArguments() {
InvocationArguments arguments = InvocationArguments.from("test", 1, false);
assertThat(arguments).isNotNull();
assertThat(arguments).hasSize(3);
assertThat(arguments.size()).isEqualTo(3);
assertThat(arguments).containsExactly("test", 1, false);
assertThat(arguments.<String>getArgumentAt(0)).isEqualTo("test");
assertThat(arguments.<Integer>getArgumentAt(1)).isEqualTo(1);
assertThat(arguments.<Boolean>getArgumentAt(2)).isEqualTo(false);
}
@Test
public void constructInvocationArgumentsWithNull() {
InvocationArguments arguments = new InvocationArguments(null);
assertThat(arguments).isNotNull();
assertThat(arguments).hasSize(0);
assertThat(arguments.getArguments()).isNotNull();
}
@Test
public void iteratesArguments() {
Object[] arguments = { true, 1, "test" };
InvocationArguments invocationArguments = new InvocationArguments(arguments);
int index = 0;
for (Object argument : invocationArguments) {
assertThat(argument).isEqualTo(arguments[index++]);
}
}
@Test
public void toStringIsCorrect() {
InvocationArguments arguments = InvocationArguments.from("one", "two", "three");
assertThat(arguments).isNotNull();
assertThat(arguments.toString()).isEqualTo("[one, two, three]");
}
}

View File

@@ -0,0 +1,75 @@
/*
* Copyright 2017-present 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
*
* https://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.util.function;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doCallRealMethod;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import org.junit.Test;
import org.mockito.InOrder;
/**
* Unit Tests for {@link TriConsumer}.
*
* @author John Blum
* @see org.junit.Test
* @see org.mockito.Mockito
* @see org.springframework.geode.util.function.TriConsumer
* @since 1.3.0
*/
public class TriConsumerUnitTests {
@Test
@SuppressWarnings({ "rawtypes", "unchecked" })
public void andThenComposesTriConsumers() {
TriConsumer consumerOne = mock(TriConsumer.class);
TriConsumer consumerTwo = mock(TriConsumer.class);
doCallRealMethod().when(consumerOne).andThen(any(TriConsumer.class));
TriConsumer composedConsumer = consumerOne.andThen(consumerTwo);
assertThat(composedConsumer).isNotNull();
assertThat(composedConsumer).isNotSameAs(consumerOne);
assertThat(composedConsumer).isNotSameAs(consumerTwo);
composedConsumer.accept("one", "two", "three");
InOrder order = inOrder(consumerOne, consumerTwo);
order.verify(consumerOne, times(1))
.accept(eq("one"), eq("two"), eq("three"));
order.verify(consumerTwo, times(1))
.accept(eq("one"), eq("two"), eq("three"));
}
@Test(expected = NullPointerException.class)
@SuppressWarnings({ "rawtypes", "unchecked" })
public void andThenWithNull() {
TriConsumer consumer = mock(TriConsumer.class);
doCallRealMethod().when(consumer).andThen(any());
consumer.andThen(null);
}
}

View File

@@ -0,0 +1,76 @@
/*
* Copyright 2017-present 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
*
* https://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.util.function;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doCallRealMethod;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import java.util.function.Consumer;
import org.junit.Test;
import org.mockito.InOrder;
/**
* Unit Tests for {@link TupleConsumer}.
*
* @author John Blum
* @see java.util.function.Consumer
* @see org.junit.Test
* @see org.mockito.Mockito
* @see org.springframework.geode.util.function.TupleConsumer
* @since 1.3.0
*/
public class TupleConsumerUnitTests {
@Test
public void andThenComposesTupleConsumers() {
TupleConsumer consumerOne = mock(TupleConsumer.class);
TupleConsumer consumerTwo = mock(TupleConsumer.class);
doCallRealMethod().when(consumerOne).andThen(any(TupleConsumer.class));
Consumer<InvocationArguments> composedConsumer = consumerOne.andThen(consumerTwo);
assertThat(composedConsumer).isNotNull();
assertThat(composedConsumer).isNotSameAs(consumerOne);
assertThat(composedConsumer).isNotSameAs(consumerTwo);
InvocationArguments arguments = InvocationArguments.from("test", 1, true);
composedConsumer.accept(arguments);
InOrder order = inOrder(consumerOne, consumerTwo);
order.verify(consumerOne, times(1)).accept(eq(arguments));
order.verify(consumerTwo, times(1)).accept(eq(arguments));
}
@Test(expected = NullPointerException.class)
public void andThenWithNull() {
TupleConsumer consumer = mock(TupleConsumer.class);
doCallRealMethod().when(consumer).andThen(any());
consumer.andThen(null);
}
}

View File

@@ -0,0 +1,16 @@
apply plugin: 'io.spring.convention.spring-module'
description = "Spring Boot Actuator Auto-Configuration for Apache Geode"
dependencies {
api project(":spring-geode-actuator")
api project(":spring-geode-autoconfigure")
compileOnly "com.google.code.findbugs:jsr305:$findbugsVersion"
// See additional testImplementation dependencies declared in the testDependencies project extension
// defined in the DependencySetPlugin.
testImplementation "org.springframework.boot:spring-boot-starter-test"
}

View File

@@ -0,0 +1,65 @@
/*
* Copyright 2017-present 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
*
* https://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.boot.actuate.autoconfigure;
import org.apache.geode.cache.GemFireCache;
import org.springframework.boot.actuate.autoconfigure.health.ConditionalOnEnabledHealthIndicator;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.gemfire.CacheFactoryBean;
import org.springframework.geode.boot.actuate.autoconfigure.config.BaseGeodeHealthIndicatorConfiguration;
import org.springframework.geode.boot.actuate.autoconfigure.config.ClientCacheHealthIndicatorConfiguration;
import org.springframework.geode.boot.actuate.autoconfigure.config.PeerCacheHealthIndicatorConfiguration;
import org.springframework.geode.boot.autoconfigure.ClientCacheAutoConfiguration;
/**
* Spring Boot {@link EnableAutoConfiguration auto-configuration} for Apache Geode
* {@link HealthIndicator HealthIndicators}.
*
* @author John Blum
* @see org.apache.geode.cache.GemFireCache
* @see org.springframework.boot.actuate.autoconfigure.health.ConditionalOnEnabledHealthIndicator
* @see org.springframework.boot.actuate.autoconfigure.health.HealthContributorAutoConfiguration
* @see org.springframework.boot.autoconfigure.EnableAutoConfiguration
* @see org.springframework.context.annotation.Configuration
* @see org.springframework.context.annotation.Import
* @see org.springframework.data.gemfire.CacheFactoryBean
* @see org.springframework.geode.boot.actuate.autoconfigure.config.BaseGeodeHealthIndicatorConfiguration
* @see org.springframework.geode.boot.actuate.autoconfigure.config.ClientCacheHealthIndicatorConfiguration
* @see org.springframework.geode.boot.actuate.autoconfigure.config.PeerCacheHealthIndicatorConfiguration
* @see org.springframework.geode.boot.autoconfigure.ClientCacheAutoConfiguration
* @since 1.0.0
*/
@Configuration
@AutoConfigureAfter(ClientCacheAutoConfiguration.class)
@ConditionalOnBean(GemFireCache.class)
@ConditionalOnClass(CacheFactoryBean.class)
@ConditionalOnEnabledHealthIndicator("geode")
@Import({
BaseGeodeHealthIndicatorConfiguration.class,
ClientCacheHealthIndicatorConfiguration.class,
PeerCacheHealthIndicatorConfiguration.class,
})
@SuppressWarnings("unused")
public class GeodeHealthIndicatorAutoConfiguration {
}

View File

@@ -0,0 +1,72 @@
/*
* Copyright 2017-present 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
*
* https://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.boot.actuate.autoconfigure.config;
import org.apache.geode.cache.Cache;
import org.apache.geode.cache.GemFireCache;
import org.apache.geode.cache.client.ClientCache;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.geode.boot.actuate.GeodeCacheHealthIndicator;
import org.springframework.geode.boot.actuate.GeodeDiskStoresHealthIndicator;
import org.springframework.geode.boot.actuate.GeodeIndexesHealthIndicator;
import org.springframework.geode.boot.actuate.GeodeRegionsHealthIndicator;
/**
* Spring {@link Configuration} class declaring Spring beans for general Apache Geode peer {@link Cache}
* and {@link ClientCache} {@link HealthIndicator HealthIndicators}.
*
* @author John Blum
* @see org.apache.geode.cache.Cache
* @see org.apache.geode.cache.GemFireCache
* @see org.apache.geode.cache.client.ClientCache
* @see org.springframework.boot.actuate.health.HealthIndicator
* @see org.springframework.context.ApplicationContext
* @see org.springframework.context.annotation.Bean
* @see org.springframework.context.annotation.Configuration
* @see org.springframework.geode.boot.actuate.GeodeCacheHealthIndicator
* @see org.springframework.geode.boot.actuate.GeodeDiskStoresHealthIndicator
* @see org.springframework.geode.boot.actuate.GeodeIndexesHealthIndicator
* @see org.springframework.geode.boot.actuate.GeodeRegionsHealthIndicator
* @since 1.0.0
*/
@Configuration
@SuppressWarnings("unused")
public class BaseGeodeHealthIndicatorConfiguration {
@Bean("GeodeCacheHealthIndicator")
GeodeCacheHealthIndicator cacheHealthIndicator(GemFireCache gemfireCache) {
return new GeodeCacheHealthIndicator(gemfireCache);
}
@Bean("GeodeDiskStoresHealthIndicator")
GeodeDiskStoresHealthIndicator diskStoresHealthIndicator(ApplicationContext applicationContext) {
return new GeodeDiskStoresHealthIndicator(applicationContext);
}
@Bean("GeodeIndexesHealthIndicator")
GeodeIndexesHealthIndicator indexesHealthIndicator(ApplicationContext applicationContext) {
return new GeodeIndexesHealthIndicator(applicationContext);
}
@Bean("GeodeRegionsHealthIndicator")
GeodeRegionsHealthIndicator regionsHealthIndicator(GemFireCache gemfireCache) {
return new GeodeRegionsHealthIndicator(gemfireCache);
}
}

View File

@@ -0,0 +1,78 @@
/*
* Copyright 2017-present 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
*
* https://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.boot.actuate.autoconfigure.config;
import org.apache.geode.cache.Cache;
import org.apache.geode.cache.GemFireCache;
import org.apache.geode.cache.client.ClientCache;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.data.gemfire.listener.ContinuousQueryListenerContainer;
import org.springframework.data.gemfire.util.CacheUtils;
import org.springframework.geode.boot.actuate.GeodeContinuousQueriesHealthIndicator;
import org.springframework.geode.boot.actuate.GeodePoolsHealthIndicator;
/**
* Spring {@link Configuration} class declaring Spring beans for Apache Geode {@link ClientCache}
* {@link HealthIndicator HealthIndicators}.
*
* @author John Blum
* @see org.apache.geode.cache.GemFireCache
* @see org.apache.geode.cache.client.ClientCache
* @see org.springframework.boot.actuate.health.HealthIndicator
* @see org.springframework.context.annotation.Bean
* @see org.springframework.context.annotation.Configuration
* @see org.springframework.geode.boot.actuate.GeodeContinuousQueriesHealthIndicator
* @see org.springframework.geode.boot.actuate.GeodePoolsHealthIndicator
* @since 1.0.0
*/
@Configuration
@Conditional(ClientCacheHealthIndicatorConfiguration.ClientCacheCondition.class)
@SuppressWarnings("unused")
public class ClientCacheHealthIndicatorConfiguration {
@Bean("GeodeContinuousQueryHealthIndicator")
GeodeContinuousQueriesHealthIndicator continuousQueriesHealthIndicator(
@Autowired(required = false) ContinuousQueryListenerContainer continuousQueryListenerContainer) {
return new GeodeContinuousQueriesHealthIndicator(continuousQueryListenerContainer);
}
@Bean("GeodePoolsHealthIndicator")
GeodePoolsHealthIndicator poolsHealthIndicator(GemFireCache gemfireCache) {
return new GeodePoolsHealthIndicator(gemfireCache);
}
public static final class ClientCacheCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Cache peerCache = CacheUtils.getCache();
ClientCache clientCache = CacheUtils.getClientCache();
return clientCache != null || peerCache == null;
}
}
}

View File

@@ -0,0 +1,146 @@
/*
* Copyright 2017-present 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
*
* https://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.boot.actuate.autoconfigure.config;
import java.util.Optional;
import org.apache.geode.cache.Cache;
import org.apache.geode.cache.GemFireCache;
import org.apache.geode.cache.client.ClientCache;
import org.apache.geode.cache.server.CacheServer;
import org.apache.geode.cache.server.ServerLoadProbe;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.data.gemfire.server.CacheServerFactoryBean;
import org.springframework.data.gemfire.util.CacheUtils;
import org.springframework.geode.boot.actuate.GeodeAsyncEventQueuesHealthIndicator;
import org.springframework.geode.boot.actuate.GeodeCacheServersHealthIndicator;
import org.springframework.geode.boot.actuate.GeodeGatewayReceiversHealthIndicator;
import org.springframework.geode.boot.actuate.GeodeGatewaySendersHealthIndicator;
import org.springframework.geode.boot.actuate.health.support.ActuatorServerLoadProbeWrapper;
import org.springframework.geode.core.util.ObjectUtils;
import org.springframework.lang.Nullable;
/**
* Spring {@link Configuration} class declaring Spring beans for Apache Geode peer {@link Cache}
* {@link HealthIndicator HealthIndicators}.
*
* @author John Blum
* @see org.apache.geode.cache.Cache
* @see org.apache.geode.cache.GemFireCache
* @see org.springframework.beans.factory.config.BeanPostProcessor
* @see org.springframework.boot.actuate.health.HealthIndicator
* @see org.springframework.context.annotation.Bean
* @see org.springframework.context.annotation.Configuration
* @see org.springframework.geode.boot.actuate.GeodeAsyncEventQueuesHealthIndicator
* @see org.springframework.geode.boot.actuate.GeodeCacheServersHealthIndicator
* @see org.springframework.geode.boot.actuate.GeodeGatewayReceiversHealthIndicator
* @see org.springframework.geode.boot.actuate.GeodeGatewaySendersHealthIndicator
* @since 1.0.0
*/
@Configuration
@Conditional(PeerCacheHealthIndicatorConfiguration.PeerCacheCondition.class)
@SuppressWarnings("unused")
public class PeerCacheHealthIndicatorConfiguration {
@Bean("GeodeAsyncEventQueuesHealthIndicator")
GeodeAsyncEventQueuesHealthIndicator asyncEventQueuesHealthIndicator(GemFireCache gemfireCache) {
return new GeodeAsyncEventQueuesHealthIndicator(gemfireCache);
}
@Bean("GeodeCacheServersHealthIndicator")
GeodeCacheServersHealthIndicator cacheServersHealthIndicator(GemFireCache gemfireCache) {
return new GeodeCacheServersHealthIndicator(gemfireCache);
}
@Bean("GeodeGatewayReceiversHealthIndicator")
GeodeGatewayReceiversHealthIndicator gatewayReceiversHealthIndicator(GemFireCache gemfireCache) {
return new GeodeGatewayReceiversHealthIndicator(gemfireCache);
}
@Bean("GeodeGatewaySendersHealthIndicator")
GeodeGatewaySendersHealthIndicator gatewaySendersHealthIndicator(GemFireCache gemfireCache) {
return new GeodeGatewaySendersHealthIndicator(gemfireCache);
}
@Bean
BeanPostProcessor cacheServerLoadProbeWrappingBeanPostProcessor() {
return new BeanPostProcessor() {
@Nullable @Override @SuppressWarnings("all")
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof CacheServerFactoryBean) {
CacheServerFactoryBean cacheServerFactoryBean = (CacheServerFactoryBean) bean;
ServerLoadProbe serverLoadProbe =
ObjectUtils.<ServerLoadProbe>get(bean, "serverLoadProbe");
if (serverLoadProbe != null) {
cacheServerFactoryBean.setServerLoadProbe(wrap(serverLoadProbe));
}
}
return bean;
}
@Nullable @Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof CacheServer) {
CacheServer cacheServer = (CacheServer) bean;
Optional.ofNullable(cacheServer.getLoadProbe())
.filter(it -> !(it instanceof ActuatorServerLoadProbeWrapper))
.filter(it -> cacheServer.getLoadPollInterval() > 0)
.filter(it -> !cacheServer.isRunning())
.ifPresent(serverLoadProbe ->
cacheServer.setLoadProbe(new ActuatorServerLoadProbeWrapper(serverLoadProbe)));
}
return bean;
}
private ServerLoadProbe wrap(ServerLoadProbe serverLoadProbe) {
return new ActuatorServerLoadProbeWrapper(serverLoadProbe);
}
};
}
public static final class PeerCacheCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Cache peerCache = CacheUtils.getCache();
ClientCache clientCache = CacheUtils.getClientCache();
return peerCache != null || clientCache == null;
}
}
}

View File

@@ -0,0 +1,2 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.geode.boot.actuate.autoconfigure.GeodeHealthIndicatorAutoConfiguration

View File

@@ -0,0 +1,138 @@
/*
* Copyright 2017-present 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
*
* https://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.boot.actuate.autoconfigure;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;
import java.util.Map;
import org.apache.geode.cache.Cache;
import org.apache.geode.cache.server.CacheServer;
import org.apache.geode.cache.server.ServerLoad;
import org.apache.geode.cache.server.ServerLoadProbe;
import org.apache.geode.cache.server.ServerMetrics;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.Status;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Bean;
import org.springframework.data.gemfire.config.annotation.PeerCacheApplication;
import org.springframework.data.gemfire.server.CacheServerFactoryBean;
import org.springframework.data.gemfire.tests.integration.IntegrationTestsSupport;
import org.springframework.data.gemfire.tests.mock.CacheServerMockObjects;
import org.springframework.data.gemfire.tests.mock.annotation.EnableGemFireMockObjects;
import org.springframework.geode.boot.actuate.GeodeCacheServersHealthIndicator;
import org.springframework.geode.boot.actuate.health.support.ActuatorServerLoadProbeWrapper;
import org.springframework.test.context.junit4.SpringRunner;
/**
* The GeodeCacheServerHealthIndicatorAutoConfigurationIntegrationTests class...
*
* @author John Blum
* @since 1.0.0
*/
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
@SuppressWarnings("unused")
public class GeodeCacheServerHealthIndicatorAutoConfigurationIntegrationTests extends IntegrationTestsSupport {
private static final String GEODE_LOG_LEVEL = "error";
@Autowired
private GeodeCacheServersHealthIndicator healthIndicator;
@Test
public void mockCacheServerHealthCheckWithServerLoadDetails() {
Health health = this.healthIndicator.health();
assertThat(health).isNotNull();
assertThat(health.getStatus()).isEqualTo(Status.UP);
Map<String, Object> healthDetails = health.getDetails();
assertThat(healthDetails).isNotNull();
assertThat(healthDetails).isNotEmpty();
assertThat(healthDetails).containsEntry("geode.cache.server.count", 1);
assertThat(healthDetails).containsEntry("geode.cache.server.0.port", 48484);
assertThat(healthDetails).containsEntry("geode.cache.server.0.load.connection-load", 0.65f);
assertThat(healthDetails).containsEntry("geode.cache.server.0.load.load-per-connection", 0.35f);
assertThat(healthDetails).containsEntry("geode.cache.server.0.load.load-per-subscription-connection", 0.75f);
assertThat(healthDetails).containsEntry("geode.cache.server.0.load.subscription-connection-load", 0.55f);
assertThat(healthDetails).containsEntry("geode.cache.server.0.metrics.client-count", 21);
assertThat(healthDetails).containsEntry("geode.cache.server.0.metrics.max-connection-count", 800);
assertThat(healthDetails).containsEntry("geode.cache.server.0.metrics.open-connection-count", 400);
assertThat(healthDetails).containsEntry("geode.cache.server.0.metrics.subscription-connection-count", 200);
}
@SpringBootApplication
@EnableGemFireMockObjects
@PeerCacheApplication(
name = "GeodeCacheServerHealthIndicatorAutoConfigurationIntegrationTests",
logLevel = GEODE_LOG_LEVEL
)
static class TestConfiguration {
@Bean("MockCacheServer")
CacheServerFactoryBean mockCacheServer(Cache gemfireCache) {
CacheServerFactoryBean mockCacheServer = new CacheServerFactoryBean();
mockCacheServer.setCache(gemfireCache);
mockCacheServer.setPort(48484);
mockCacheServer.setServerLoadProbe(mockServerLoadProbe());
return mockCacheServer;
}
@Bean("MockServerLoadProbe")
ServerLoadProbe mockServerLoadProbe() {
return CacheServerMockObjects.mockServerLoadProbe();
}
@Bean
ApplicationRunner runner(ServerLoadProbe mockServerLoadProbe,
@Qualifier("MockCacheServer") CacheServer mockCacheServer) {
return args -> {
assertThat(mockCacheServer.getLoadProbe()).isInstanceOf(ActuatorServerLoadProbeWrapper.class);
ServerMetrics mockServerMetrics = CacheServerMockObjects.mockServerMetrics(21,
400, 800, 200);
ServerLoad mockServerLoad = CacheServerMockObjects.mockServerLoad(0.65f,
0.35f, 0.75f, 0.55f);
when(mockServerLoadProbe.getLoad(eq(mockServerMetrics))).thenReturn(mockServerLoad);
ServerLoadProbe serverLoadProbe = mockCacheServer.getLoadProbe();
if (serverLoadProbe != null) {
serverLoadProbe.getLoad(mockServerMetrics);
}
};
}
}
}

View File

@@ -0,0 +1,18 @@
apply plugin: 'io.spring.convention.spring-module'
description = "Spring Boot Actuator for Apache Geode"
dependencies {
api project(":spring-geode")
api "org.springframework.boot:spring-boot-starter-actuator"
provided "org.apache.geode:geode-logging:$apacheGeodeVersion"
provided "org.apache.geode:geode-serialization:$apacheGeodeVersion"
// See additional testImplementation dependencies declared in the testDependencies project extension
// defined in the DependencySetPlugin.
testImplementation "org.springframework.boot:spring-boot-starter-test"
}

View File

@@ -0,0 +1,111 @@
/*
* Copyright 2017-present 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
*
* https://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.boot.actuate;
import java.util.Collections;
import java.util.Objects;
import java.util.Set;
import org.apache.geode.cache.Cache;
import org.apache.geode.cache.GemFireCache;
import org.apache.geode.cache.asyncqueue.AsyncEventQueue;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.data.gemfire.util.CacheUtils;
import org.springframework.geode.boot.actuate.health.AbstractGeodeHealthIndicator;
/**
* The {@link GeodeAsyncEventQueuesHealthIndicator} class is a Spring Boot {@link HealthIndicator} providing details
* about the health of Apache Geode {@link AsyncEventQueue AsyncEventQueues}.
*
* @author John Blum
* @see org.apache.geode.cache.Cache
* @see org.apache.geode.cache.GemFireCache
* @see org.apache.geode.cache.asyncqueue.AsyncEventQueue
* @see org.springframework.boot.actuate.health.Health
* @see org.springframework.boot.actuate.health.HealthIndicator
* @see org.springframework.geode.boot.actuate.health.AbstractGeodeHealthIndicator
* @since 1.0.0
*/
@SuppressWarnings("unused")
public class GeodeAsyncEventQueuesHealthIndicator extends AbstractGeodeHealthIndicator {
/**
* Default constructor to construct an uninitialized instance of {@link GeodeAsyncEventQueuesHealthIndicator},
* which will not provide any health information.
*/
public GeodeAsyncEventQueuesHealthIndicator() {
super("Async Event Queues health check failed");
}
/**
* Constructs an instance of the {@link GeodeAsyncEventQueuesHealthIndicator} initialized with a reference to
* the {@link GemFireCache} instance.
*
* @param gemfireCache reference to the {@link GemFireCache} instance used to collect health information.
* @throws IllegalArgumentException if {@link GemFireCache} is {@literal null}.
* @see org.apache.geode.cache.GemFireCache
*/
public GeodeAsyncEventQueuesHealthIndicator(GemFireCache gemfireCache) {
super(gemfireCache);
}
@Override
protected void doHealthCheck(Health.Builder builder) {
if (getGemFireCache().filter(CacheUtils::isPeer).isPresent()) {
Set<AsyncEventQueue> asyncEventQueues = getGemFireCache()
.map(Cache.class::cast)
.map(Cache::getAsyncEventQueues)
.orElseGet(Collections::emptySet);
builder.withDetail("geode.async-event-queue.count", asyncEventQueues.size());
asyncEventQueues.stream()
.filter(Objects::nonNull)
.forEach(asyncEventQueue -> {
String asyncEventQueueId = asyncEventQueue.getId();
builder.withDetail(asyncEventQueueKey(asyncEventQueueId, "batch-conflation-enabled"), toYesNoString(asyncEventQueue.isBatchConflationEnabled()))
.withDetail(asyncEventQueueKey(asyncEventQueueId, "batch-size"), asyncEventQueue.getBatchSize())
.withDetail(asyncEventQueueKey(asyncEventQueueId, "batch-time-interval"), asyncEventQueue.getBatchTimeInterval())
.withDetail(asyncEventQueueKey(asyncEventQueueId, "disk-store-name"), asyncEventQueue.getDiskStoreName())
.withDetail(asyncEventQueueKey(asyncEventQueueId, "disk-synchronous"), toYesNoString(asyncEventQueue.isDiskSynchronous()))
.withDetail(asyncEventQueueKey(asyncEventQueueId, "dispatcher-threads"), asyncEventQueue.getDispatcherThreads())
.withDetail(asyncEventQueueKey(asyncEventQueueId, "forward-expiration-destroy"), toYesNoString(asyncEventQueue.isForwardExpirationDestroy()))
.withDetail(asyncEventQueueKey(asyncEventQueueId, "max-queue-memory"), asyncEventQueue.getMaximumQueueMemory())
.withDetail(asyncEventQueueKey(asyncEventQueueId, "order-policy"), asyncEventQueue.getOrderPolicy())
.withDetail(asyncEventQueueKey(asyncEventQueueId, "parallel"), toYesNoString(asyncEventQueue.isParallel()))
.withDetail(asyncEventQueueKey(asyncEventQueueId, "persistent"), toYesNoString(asyncEventQueue.isPersistent()))
.withDetail(asyncEventQueueKey(asyncEventQueueId, "primary"), toYesNoString(asyncEventQueue.isPrimary()))
.withDetail(asyncEventQueueKey(asyncEventQueueId, "size"), asyncEventQueue.size());
});
builder.up();
return;
}
builder.unknown();
}
private String asyncEventQueueKey(String id, String suffix) {
return String.format("geode.async-event-queue.%1$s.%2$s", id, suffix);
}
}

View File

@@ -0,0 +1,179 @@
/*
* Copyright 2017-present 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
*
* https://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.boot.actuate;
import java.net.URL;
import java.util.function.Function;
import org.apache.geode.CancelCriterion;
import org.apache.geode.cache.GemFireCache;
import org.apache.geode.cache.control.ResourceManager;
import org.apache.geode.distributed.DistributedMember;
import org.apache.geode.distributed.DistributedSystem;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.boot.actuate.health.Status;
import org.springframework.data.gemfire.util.CollectionUtils;
import org.springframework.geode.boot.actuate.health.AbstractGeodeHealthIndicator;
import org.springframework.util.StringUtils;
/**
* The {@link GeodeCacheHealthIndicator} class is a Spring Boot {@link HealthIndicator} providing details about
* the health of the {@link GemFireCache}, the {@link DistributedSystem}, this {@link DistributedMember}
* and the {@link ResourceManager}.
*
* @author John Blum
* @see java.net.URL
* @see java.util.Optional
* @see java.util.function.Function
* @see org.apache.geode.cache.GemFireCache
* @see org.apache.geode.cache.control.ResourceManager
* @see org.apache.geode.distributed.DistributedMember
* @see org.apache.geode.distributed.DistributedSystem
* @see org.springframework.boot.actuate.health.Health
* @see org.springframework.boot.actuate.health.HealthIndicator
* @see org.springframework.geode.boot.actuate.health.AbstractGeodeHealthIndicator
* @since 1.0.0
*/
@SuppressWarnings("unused")
public class GeodeCacheHealthIndicator extends AbstractGeodeHealthIndicator {
private final Function<Health.Builder, Health.Builder> gemfireHealthIndicatorFunctions = withCacheDetails()
.andThen(withDistributedSystemDetails())
.andThen(withDistributedMemberDetails())
.andThen(withResourceManagerDetails());
/**
* Default constructor to construct an uninitialized instance of {@link GeodeCacheHealthIndicator},
* which will not provide any health information.
*/
public GeodeCacheHealthIndicator() {
super("(Client) Cache health check failed");
}
/**
* Constructs an instance of the {@link GeodeCacheHealthIndicator} initialized with a reference to
* the {@link GemFireCache} instance.
*
* @param gemfireCache reference to the {@link GemFireCache} instance used to collect health information.
* @throws IllegalArgumentException if {@link GemFireCache} is {@literal null}.
* @see org.apache.geode.cache.GemFireCache
*/
public GeodeCacheHealthIndicator(GemFireCache gemfireCache) {
super(gemfireCache);
}
/**
* Return a collection of {@link Function Functions} that apply {@link HealthIndicator} information
* about the {@link GemFireCache} to the {@link Health} aggregate object.
*
* @return a collection of {@link Function Functions} applying {@link HealthIndicator} information
* about the {@link GemFireCache} to a {@link Health} object.
* @see org.springframework.boot.actuate.health.Health.Builder
* @see java.util.function.Function
*/
protected Function<Health.Builder, Health.Builder> getGemfireHealthIndicatorFunctions() {
return this.gemfireHealthIndicatorFunctions;
}
@Override
protected void doHealthCheck(Health.Builder builder) {
if (getGemFireCache().isPresent()) {
getGemfireHealthIndicatorFunctions().apply(builder);
builder.status(getGemFireCache().map(GemFireCache::isClosed).orElse(true)
? Status.DOWN : Status.UP);
return;
}
builder.unknown();
}
private Function<Health.Builder, Health.Builder> withCacheDetails() {
return healthBuilder -> healthBuilder.withDetail("geode.cache.name", getGemFireCache().map(GemFireCache::getName).orElse(""))
.withDetail("geode.cache.closed", getGemFireCache().map(GemFireCache::isClosed).map(this::toYesNoString).orElse("Yes"))
.withDetail("geode.cache.cancel-in-progress", getGemFireCache()
.map(GemFireCache::getCancelCriterion)
.filter(CancelCriterion::isCancelInProgress)
.isPresent() ? "Yes" : "No");
}
private Function<Health.Builder, Health.Builder> withDistributedMemberDetails() {
return healthBuilder -> getGemFireCache()
.map(GemFireCache::getDistributedSystem)
.map(DistributedSystem::getDistributedMember)
.map(distributedMember -> healthBuilder
.withDetail("geode.distributed-member.id", distributedMember.getId())
.withDetail("geode.distributed-member.name", distributedMember.getName())
.withDetail("geode.distributed-member.groups", distributedMember.getGroups())
.withDetail("geode.distributed-member.host", distributedMember.getHost())
.withDetail("geode.distributed-member.process-id", distributedMember.getProcessId())
)
.orElse(healthBuilder);
}
private Function<Health.Builder, Health.Builder> withDistributedSystemDetails() {
return healthBuilder -> getGemFireCache()
.map(GemFireCache::getDistributedSystem)
.map(distributedSystem -> healthBuilder
.withDetail("geode.distributed-system.member-count", toMemberCount(distributedSystem))
.withDetail("geode.distributed-system.connection", toConnectedNoConnectedString(distributedSystem.isConnected()))
.withDetail("geode.distributed-system.reconnecting", toYesNoString(distributedSystem.isReconnecting()))
.withDetail("geode.distributed-system.properties-location", toString(DistributedSystem.getPropertiesFileURL()))
.withDetail("geode.distributed-system.security-properties-location", toString(DistributedSystem.getSecurityPropertiesFileURL()))
)
.orElse(healthBuilder);
}
private Function<Health.Builder, Health.Builder> withResourceManagerDetails() {
return healthBuilder -> getGemFireCache()
.map(GemFireCache::getResourceManager)
.map(resourceManager -> healthBuilder
.withDetail("geode.resource-manager.critical-heap-percentage", resourceManager.getCriticalHeapPercentage())
.withDetail("geode.resource-manager.critical-off-heap-percentage", resourceManager.getCriticalOffHeapPercentage())
.withDetail("geode.resource-manager.eviction-heap-percentage", resourceManager.getEvictionHeapPercentage())
.withDetail("geode.resource-manager.eviction-off-heap-percentage", resourceManager.getEvictionOffHeapPercentage())
)
.orElse(healthBuilder);
}
private String emptyIfUnset(String value) {
return StringUtils.hasText(value) ? value : "";
}
private String toConnectedNoConnectedString(Boolean connected) {
return Boolean.TRUE.equals(connected) ? "Connected" : "Not Connected";
}
private int toMemberCount(DistributedSystem distributedSystem) {
return CollectionUtils.nullSafeSize(distributedSystem.getAllOtherMembers()) + 1;
}
private String toString(URL url) {
String urlString = url != null ? url.toExternalForm() : null;
return emptyIfUnset(urlString);
}
}

View File

@@ -0,0 +1,147 @@
/*
* Copyright 2017-present 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
*
* https://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.boot.actuate;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.geode.cache.Cache;
import org.apache.geode.cache.GemFireCache;
import org.apache.geode.cache.server.CacheServer;
import org.apache.geode.cache.server.ServerLoad;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.data.gemfire.util.CacheUtils;
import org.springframework.geode.boot.actuate.health.AbstractGeodeHealthIndicator;
import org.springframework.geode.boot.actuate.health.support.ActuatorServerLoadProbeWrapper;
/**
* The {@link GeodeCacheServersHealthIndicator} class is a Spring Boot {@link HealthIndicator} providing details about
* the health of Apache Geode {@link CacheServer CacheServers}.
*
* @author John Blum
* @see org.apache.geode.cache.Cache
* @see org.apache.geode.cache.GemFireCache
* @see org.apache.geode.cache.server.CacheServer
* @see org.springframework.boot.actuate.health.Health
* @see org.springframework.boot.actuate.health.HealthIndicator
* @see org.springframework.geode.boot.actuate.health.AbstractGeodeHealthIndicator
* @see org.springframework.geode.boot.actuate.health.support.ActuatorServerLoadProbeWrapper
* @since 1.0.0
*/
@SuppressWarnings("unused")
public class GeodeCacheServersHealthIndicator extends AbstractGeodeHealthIndicator {
/**
* Default constructor to construct an uninitialized instance of {@link GeodeCacheServersHealthIndicator},
* which will not provide any health information.
*/
public GeodeCacheServersHealthIndicator() {
super("Cache Servers health check failed");
}
/**
* Constructs an instance of the {@link GeodeCacheServersHealthIndicator} initialized with a reference to
* the {@link GemFireCache} instance.
*
* @param gemfireCache reference to the {@link GemFireCache} instance used to collect health information.
* @throws IllegalArgumentException if {@link GemFireCache} is {@literal null}.
* @see org.apache.geode.cache.GemFireCache
*/
public GeodeCacheServersHealthIndicator(GemFireCache gemfireCache) {
super(gemfireCache);
}
@Override
protected void doHealthCheck(Health.Builder builder) {
if (getGemFireCache().filter(CacheUtils::isPeer).isPresent()) {
AtomicInteger globalIndex = new AtomicInteger(0);
List<CacheServer> cacheServers = getGemFireCache()
.map(Cache.class::cast)
.map(Cache::getCacheServers)
.orElseGet(Collections::emptyList);
builder.withDetail("geode.cache.server.count", cacheServers.size());
cacheServers.stream()
.filter(Objects::nonNull)
.forEach(cacheServer -> {
int cacheServerIndex = globalIndex.getAndIncrement();
builder.withDetail(cacheServerKey(cacheServerIndex, "bind-address"), cacheServer.getBindAddress())
.withDetail(cacheServerKey(cacheServerIndex, "hostname-for-clients"), cacheServer.getHostnameForClients())
.withDetail(cacheServerKey(cacheServerIndex, "load-poll-interval"), cacheServer.getLoadPollInterval())
.withDetail(cacheServerKey(cacheServerIndex, "max-connections"), cacheServer.getMaxConnections())
.withDetail(cacheServerKey(cacheServerIndex, "max-message-count"), cacheServer.getMaximumMessageCount())
.withDetail(cacheServerKey(cacheServerIndex, "max-threads"), cacheServer.getMaxThreads())
.withDetail(cacheServerKey(cacheServerIndex, "max-time-between-pings"), cacheServer.getMaximumTimeBetweenPings())
.withDetail(cacheServerKey(cacheServerIndex, "message-time-to-live"), cacheServer.getMessageTimeToLive())
.withDetail(cacheServerKey(cacheServerIndex, "port"), cacheServer.getPort())
.withDetail(cacheServerKey(cacheServerIndex, "running"), toYesNoString(cacheServer.isRunning()))
.withDetail(cacheServerKey(cacheServerIndex, "socket-buffer-size"), cacheServer.getSocketBufferSize())
.withDetail(cacheServerKey(cacheServerIndex, "tcp-no-delay"), toYesNoString(cacheServer.getTcpNoDelay()));
Optional.ofNullable(cacheServer.getLoadProbe())
.filter(ActuatorServerLoadProbeWrapper.class::isInstance)
.map(ActuatorServerLoadProbeWrapper.class::cast)
.flatMap(ActuatorServerLoadProbeWrapper::getCurrentServerMetrics)
.ifPresent(serverMetrics -> {
builder.withDetail(cacheServerMetricsKey(cacheServerIndex, "client-count"), serverMetrics.getClientCount())
.withDetail(cacheServerMetricsKey(cacheServerIndex, "max-connection-count"), serverMetrics.getMaxConnections())
.withDetail(cacheServerMetricsKey(cacheServerIndex, "open-connection-count"), serverMetrics.getConnectionCount())
.withDetail(cacheServerMetricsKey(cacheServerIndex, "subscription-connection-count"), serverMetrics.getSubscriptionConnectionCount());
ServerLoad serverLoad = cacheServer.getLoadProbe().getLoad(serverMetrics);
if (serverLoad != null) {
builder.withDetail(cacheServerLoadKey(cacheServerIndex, "connection-load"), serverLoad.getConnectionLoad())
.withDetail(cacheServerLoadKey(cacheServerIndex, "load-per-connection"), serverLoad.getLoadPerConnection())
.withDetail(cacheServerLoadKey(cacheServerIndex, "subscription-connection-load"), serverLoad.getSubscriptionConnectionLoad())
.withDetail(cacheServerLoadKey(cacheServerIndex, "load-per-subscription-connection"), serverLoad.getLoadPerSubscriptionConnection());
}
});
});
builder.up();
return;
}
builder.unknown();
}
private String cacheServerKey(int index, String suffix) {
return String.format("geode.cache.server.%d.%s", index, suffix);
}
private String cacheServerLoadKey(int index, String suffix) {
return String.format("geode.cache.server.%d.load.%s", index, suffix);
}
private String cacheServerMetricsKey(int index, String suffix) {
return String.format("geode.cache.server.%d.metrics.%s", index, suffix);
}
}

View File

@@ -0,0 +1,174 @@
/*
* Copyright 2017-present 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
*
* https://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.boot.actuate;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import org.apache.geode.cache.query.CqQuery;
import org.apache.geode.cache.query.CqState;
import org.apache.geode.cache.query.CqStatistics;
import org.apache.geode.cache.query.Query;
import org.apache.geode.cache.query.QueryService;
import org.apache.geode.cache.query.QueryStatistics;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.data.gemfire.listener.ContinuousQueryListenerContainer;
import org.springframework.geode.boot.actuate.health.AbstractGeodeHealthIndicator;
/**
* The {@link GeodeContinuousQueriesHealthIndicator} class is a Spring Boot {@link HealthIndicator} providing details
* about the health of the registered Apache Geode {@link CqQuery Continuous Queries}.
*
* @author John Blum
* @see org.apache.geode.cache.query.CqQuery
* @see org.apache.geode.cache.query.Query
* @see org.apache.geode.cache.query.QueryService
* @see org.springframework.boot.actuate.health.Health
* @see org.springframework.boot.actuate.health.HealthIndicator
* @see org.springframework.data.gemfire.listener.ContinuousQueryListenerContainer
* @see org.springframework.geode.boot.actuate.health.AbstractGeodeHealthIndicator
* @since 1.0.0
*/
@SuppressWarnings("unused")
public class GeodeContinuousQueriesHealthIndicator extends AbstractGeodeHealthIndicator {
private final ContinuousQueryListenerContainer continuousQueryListenerContainer;
/**
* Default constructor to construct an uninitialized instance of {@link GeodeContinuousQueriesHealthIndicator},
* which will not provide any health information.
*/
public GeodeContinuousQueriesHealthIndicator() {
super("Continuous Queries health check failed");
this.continuousQueryListenerContainer = null;
}
/**
* Constructs an instance of the {@link GeodeContinuousQueriesHealthIndicator} initialized with a reference to
* the {@link ContinuousQueryListenerContainer}.
*
* @param continuousQueryListenerContainer reference to the SDG {@link ContinuousQueryListenerContainer}.
* @see org.springframework.data.gemfire.listener.ContinuousQueryListenerContainer
*/
public GeodeContinuousQueriesHealthIndicator(ContinuousQueryListenerContainer continuousQueryListenerContainer) {
super("Continuous Queries health check enabled");
this.continuousQueryListenerContainer = continuousQueryListenerContainer;
}
/**
* Returns an {@link Optional} reference to the configured {@link ContinuousQueryListenerContainer}.
*
* @return an {@link Optional} reference to the configured {@link ContinuousQueryListenerContainer}.
* @see org.springframework.data.gemfire.listener.ContinuousQueryListenerContainer
* @see java.util.Optional
*/
protected Optional<ContinuousQueryListenerContainer> getContinuousQueryListenerContainer() {
return Optional.ofNullable(this.continuousQueryListenerContainer);
}
@Override
protected void doHealthCheck(Health.Builder builder) {
if (getContinuousQueryListenerContainer().isPresent()) {
Optional<QueryService> queryService = getContinuousQueryListenerContainer()
.map(ContinuousQueryListenerContainer::getQueryService);
List<CqQuery> continuousQueries = queryService
.map(QueryService::getCqs)
.map(Arrays::asList)
.orElseGet(Collections::emptyList);
builder.withDetail("geode.continuous-query.count", continuousQueries.size());
queryService
.map(QueryService::getCqStatistics)
.ifPresent(cqServiceStatistics ->
builder.withDetail("geode.continuous-query.number-of-active", cqServiceStatistics.numCqsActive())
.withDetail("geode.continuous-query.number-of-closed", cqServiceStatistics.numCqsClosed())
.withDetail("geode.continuous-query.number-of-created", cqServiceStatistics.numCqsCreated())
.withDetail("geode.continuous-query.number-of-stopped", cqServiceStatistics.numCqsStopped())
.withDetail("geode.continuous-query.number-on-client", cqServiceStatistics.numCqsOnClient())
);
continuousQueries.stream()
.filter(Objects::nonNull)
.forEach(continuousQuery -> {
String continuousQueryName = continuousQuery.getName();
builder.withDetail(continuousQueryKey(continuousQueryName,"oql-query-string"), continuousQuery.getQueryString())
.withDetail(continuousQueryKey(continuousQueryName, "closed"), toYesNoString(continuousQuery.isClosed()))
.withDetail(continuousQueryKey(continuousQueryName, "closing"), toYesNoString(continuousQuery.getState()))
.withDetail(continuousQueryKey(continuousQueryName, "durable"), toYesNoString(continuousQuery.isDurable()))
.withDetail(continuousQueryKey(continuousQueryName, "running"), toYesNoString(continuousQuery.isRunning()))
.withDetail(continuousQueryKey(continuousQueryName, "stopped"), toYesNoString(continuousQuery.isStopped()));
Query query = continuousQuery.getQuery();
if (query != null) {
QueryStatistics queryStatistics = query.getStatistics();
if (queryStatistics != null) {
builder.withDetail(continuousQueryQueryKey(continuousQueryName, "number-of-executions"), queryStatistics.getNumExecutions())
.withDetail(continuousQueryQueryKey(continuousQueryName, "total-execution-time"), queryStatistics.getTotalExecutionTime());
}
}
CqStatistics continuousQueryStatistics = continuousQuery.getStatistics();
if (continuousQueryStatistics != null) {
builder.withDetail(continuousQueryStatisticsKey(continuousQueryName, "number-of-deletes"), continuousQueryStatistics.numDeletes())
.withDetail(continuousQueryStatisticsKey(continuousQueryName, "number-of-events"), continuousQueryStatistics.numEvents())
.withDetail(continuousQueryStatisticsKey(continuousQueryName, "number-of-inserts"), continuousQueryStatistics.numInserts())
.withDetail(continuousQueryStatisticsKey(continuousQueryName, "number-of-updates"), continuousQueryStatistics.numUpdates());
}
});
builder.up();
return;
}
builder.unknown();
}
private String continuousQueryKey(String continuousQueryName, String suffix) {
return String.format("geode.continuous-query.%1$s.%2$s", continuousQueryName, suffix);
}
private String continuousQueryQueryKey(String continuousQueryName, String suffix) {
return String.format("geode.continuous-query.%1$s.query.%2$s", continuousQueryName, suffix);
}
private String continuousQueryStatisticsKey(String continuousQueryName, String suffix) {
return String.format("geode.continuous-query.%1$s.statistics.%2$s", continuousQueryName, suffix);
}
private String toYesNoString(CqState continuousQueryState) {
return continuousQueryState != null ? toYesNoString(continuousQueryState.isClosing()) : UNKNOWN;
}
}

View File

@@ -0,0 +1,141 @@
/*
* Copyright 2017-present 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
*
* https://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.boot.actuate;
import java.io.File;
import java.util.Arrays;
import java.util.Collections;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import org.apache.geode.cache.DiskStore;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.context.ApplicationContext;
import org.springframework.data.gemfire.util.ArrayUtils;
import org.springframework.geode.boot.actuate.health.AbstractGeodeHealthIndicator;
import org.springframework.util.Assert;
/**
* The {@link GeodeDiskStoresHealthIndicator} class is a Spring Boot {@link HealthIndicator} providing details about
* the health of Apache Geode {@link DiskStore DiskStores}.
*
* @author John Blum
* @see org.apache.geode.cache.DiskStore
* @see org.springframework.boot.actuate.health.Health
* @see org.springframework.boot.actuate.health.HealthIndicator
* @see org.springframework.context.ApplicationContext
* @see org.springframework.geode.boot.actuate.health.AbstractGeodeHealthIndicator
* @since 1.0.0
*/
@SuppressWarnings("unused")
public class GeodeDiskStoresHealthIndicator extends AbstractGeodeHealthIndicator {
private final ApplicationContext applicationContext;
/**
* Default constructor to construct an uninitialized instance of {@link GeodeDiskStoresHealthIndicator},
* which will not provide any health information.
*/
public GeodeDiskStoresHealthIndicator() {
super("Disk Stores health check failed");
this.applicationContext = null;
}
/**
* Constructs an instance of the {@link GeodeDiskStoresHealthIndicator} initialized with a reference to
* the {@link ApplicationContext} instance.
*
* @param applicationContext reference to the Spring {@link ApplicationContext}.
* @throws IllegalArgumentException if {@link ApplicationContext} is {@literal null}.
* @see org.springframework.context.ApplicationContext
*/
public GeodeDiskStoresHealthIndicator(ApplicationContext applicationContext) {
super("Disk Stores health check enabled");
Assert.notNull(applicationContext, "ApplicationContext is required");
this.applicationContext = applicationContext;
}
/**
* Returns an {@link Optional} reference to the Spring {@link ApplicationContext}.
*
* @return an {@link Optional} reference to the Spring {@link ApplicationContext}.
* @see org.springframework.context.ApplicationContext
* @see java.util.Optional
*/
protected Optional<ApplicationContext> getApplicationContext() {
return Optional.ofNullable(this.applicationContext);
}
@Override
protected void doHealthCheck(Health.Builder builder) {
if (getApplicationContext().isPresent()) {
Map<String, DiskStore> diskStores = getApplicationContext()
.map(it -> it.getBeansOfType(DiskStore.class))
.orElseGet(Collections::emptyMap);
builder.withDetail("geode.disk-store.count", diskStores.size());
diskStores.values().forEach(diskStore -> {
String diskStoreName = diskStore.getName();
builder.withDetail(diskStoreKey(diskStoreName, "allow-force-compaction"), toYesNoString(diskStore.getAllowForceCompaction()))
.withDetail(diskStoreKey(diskStoreName, "auto-compact"), toYesNoString(diskStore.getAutoCompact()))
.withDetail(diskStoreKey(diskStoreName, "compaction-threshold"), diskStore.getCompactionThreshold())
.withDetail(diskStoreKey(diskStoreName, "disk-directories"), toFileAbsolutePathStrings(diskStore.getDiskDirs()))
.withDetail(diskStoreKey(diskStoreName, "disk-directory-sizes"), Arrays.toString(nullSafeArray(diskStore.getDiskDirSizes())))
.withDetail(diskStoreKey(diskStoreName, "disk-usage-critical-percentage"), diskStore.getDiskUsageCriticalPercentage())
.withDetail(diskStoreKey(diskStoreName, "disk-usage-warning-percentage"), diskStore.getDiskUsageWarningPercentage())
.withDetail(diskStoreKey(diskStoreName, "max-oplog-size"), diskStore.getMaxOplogSize())
.withDetail(diskStoreKey(diskStoreName, "queue-size"), diskStore.getQueueSize())
.withDetail(diskStoreKey(diskStoreName, "time-interval"), diskStore.getTimeInterval())
.withDetail(diskStoreKey(diskStoreName, "uuid"), diskStore.getDiskStoreUUID().toString())
.withDetail(diskStoreKey(diskStoreName, "write-buffer-size"), diskStore.getWriteBufferSize());
});
builder.up();
return;
}
builder.unknown();
}
private String diskStoreKey(String diskStoreName, String suffix) {
return String.format("geode.disk-store.%1$s.%2$s", diskStoreName, suffix);
}
private int[] nullSafeArray(int[] array) {
return array != null ? array : new int[0];
}
private String toFileAbsolutePathStrings(File... files) {
return Arrays.toString(Arrays.stream(ArrayUtils.nullSafeArray(files, File.class))
.filter(Objects::nonNull)
.map(File::getAbsolutePath)
.distinct()
.toArray());
}
}

View File

@@ -0,0 +1,109 @@
/*
* Copyright 2017-present 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
*
* https://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.boot.actuate;
import java.util.Collections;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.geode.cache.Cache;
import org.apache.geode.cache.GemFireCache;
import org.apache.geode.cache.wan.GatewayReceiver;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.data.gemfire.util.CacheUtils;
import org.springframework.geode.boot.actuate.health.AbstractGeodeHealthIndicator;
/**
* The {@link GeodeGatewayReceiversHealthIndicator} class is a Spring Boot {@link HealthIndicator} providing details
* about the health of Apache Geode {@link GatewayReceiver GatewayReceivers}.
*
* @author John Blum
* @see org.apache.geode.cache.Cache
* @see org.apache.geode.cache.GemFireCache
* @see org.apache.geode.cache.wan.GatewayReceiver
* @see org.springframework.boot.actuate.health.Health
* @see org.springframework.boot.actuate.health.HealthIndicator
* @see org.springframework.geode.boot.actuate.health.AbstractGeodeHealthIndicator
* @since 1.0.0
*/
@SuppressWarnings("unused")
public class GeodeGatewayReceiversHealthIndicator extends AbstractGeodeHealthIndicator {
/**
* Default constructor to construct an uninitialized instance of {@link GeodeGatewayReceiversHealthIndicator},
* which will not provide any health information.
*/
public GeodeGatewayReceiversHealthIndicator() {
super("Gateway Receivers health check failed");
}
/**
* Constructs an instance of the {@link GeodeGatewayReceiversHealthIndicator} initialized with a reference to
* the {@link GemFireCache} instance.
*
* @param gemfireCache reference to the {@link GemFireCache} instance used to collect health information.
* @throws IllegalArgumentException if {@link GemFireCache} is {@literal null}.
* @see org.apache.geode.cache.GemFireCache
*/
public GeodeGatewayReceiversHealthIndicator(GemFireCache gemfireCache) {
super(gemfireCache);
}
@Override
protected void doHealthCheck(Health.Builder builder) {
if (getGemFireCache().filter(CacheUtils::isPeer).isPresent()) {
AtomicInteger globalIndex = new AtomicInteger(0);
Set<GatewayReceiver> gatewayReceivers = getGemFireCache()
.map(Cache.class::cast)
.map(Cache::getGatewayReceivers)
.orElseGet(Collections::emptySet);
builder.withDetail("geode.gateway-receiver.count", gatewayReceivers.size());
gatewayReceivers.stream()
.filter(Objects::nonNull)
.forEach(gatewayReceiver -> {
int index = globalIndex.getAndIncrement();
builder.withDetail(gatewayReceiverKey(index, "bind-address"), gatewayReceiver.getBindAddress())
.withDetail(gatewayReceiverKey(index, "end-port"), gatewayReceiver.getEndPort())
.withDetail(gatewayReceiverKey(index, "host"), gatewayReceiver.getHost())
.withDetail(gatewayReceiverKey(index, "max-time-between-pings"), gatewayReceiver.getMaximumTimeBetweenPings())
.withDetail(gatewayReceiverKey(index, "port"), gatewayReceiver.getPort())
.withDetail(gatewayReceiverKey(index, "running"), toYesNoString(gatewayReceiver.isRunning()))
.withDetail(gatewayReceiverKey(index, "socket-buffer-size"), gatewayReceiver.getSocketBufferSize())
.withDetail(gatewayReceiverKey(index, "start-port"), gatewayReceiver.getStartPort());
});
builder.up();
return;
}
builder.unknown();
}
private String gatewayReceiverKey(int index, String suffix) {
return String.format("geode.gateway-receiver.%d.%s", index, suffix);
}
}

View File

@@ -0,0 +1,120 @@
/*
* Copyright 2017-present 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
*
* https://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.boot.actuate;
import java.util.Collections;
import java.util.Objects;
import java.util.Set;
import org.apache.geode.cache.Cache;
import org.apache.geode.cache.GemFireCache;
import org.apache.geode.cache.wan.GatewaySender;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.data.gemfire.util.CacheUtils;
import org.springframework.geode.boot.actuate.health.AbstractGeodeHealthIndicator;
import org.springframework.util.StringUtils;
/**
* The {@link GeodeGatewaySendersHealthIndicator} class is a Spring Boot {@link HealthIndicator} providing details about
* the health of Apache Geode {@link GatewaySender GatewaySenders}.
*
* @author John Blum
* @see org.apache.geode.cache.Cache
* @see org.apache.geode.cache.GemFireCache
* @see org.apache.geode.cache.wan.GatewaySender
* @see org.springframework.boot.actuate.health.Health
* @see org.springframework.boot.actuate.health.HealthIndicator
* @see org.springframework.geode.boot.actuate.health.AbstractGeodeHealthIndicator
* @since 1.0.0
*/
@SuppressWarnings("unused")
public class GeodeGatewaySendersHealthIndicator extends AbstractGeodeHealthIndicator {
/**
* Default constructor to construct an uninitialized instance of {@link GeodeGatewaySendersHealthIndicator},
* which will not provide any health information.
*/
public GeodeGatewaySendersHealthIndicator() {
super("Gateway Senders health check failed");
}
/**
* Constructs an instance of the {@link GeodeGatewaySendersHealthIndicator} initialized with a reference to
* the {@link GemFireCache} instance.
*
* @param gemfireCache reference to the {@link GemFireCache} instance used to collect health information.
* @throws IllegalArgumentException if {@link GemFireCache} is {@literal null}.
* @see org.apache.geode.cache.GemFireCache
*/
public GeodeGatewaySendersHealthIndicator(GemFireCache gemfireCache) {
super(gemfireCache);
}
@Override
protected void doHealthCheck(Health.Builder builder) {
if (getGemFireCache().filter(CacheUtils::isPeer).isPresent()) {
Set<GatewaySender> gatewaySenders = getGemFireCache()
.map(Cache.class::cast)
.map(Cache::getGatewaySenders)
.orElseGet(Collections::emptySet);
builder.withDetail("geode.gateway-sender.count", gatewaySenders.size());
gatewaySenders.stream()
.filter(Objects::nonNull)
.forEach(gatewaySender -> {
String gatewaySenderId = gatewaySender.getId();
builder.withDetail(gatewaySendersKey(gatewaySenderId, "alert-threshold"), gatewaySender.getAlertThreshold())
.withDetail(gatewaySendersKey(gatewaySenderId, "batch-conflation-enabled"), toYesNoString(gatewaySender.isBatchConflationEnabled()))
.withDetail(gatewaySendersKey(gatewaySenderId, "batch-size"), gatewaySender.getBatchSize())
.withDetail(gatewaySendersKey(gatewaySenderId, "batch-time-interval"), gatewaySender.getBatchTimeInterval())
.withDetail(gatewaySendersKey(gatewaySenderId, "disk-store-name"), emptyIfUnset(gatewaySender.getDiskStoreName()))
.withDetail(gatewaySendersKey(gatewaySenderId, "disk-synchronous"), toYesNoString(gatewaySender.isDiskSynchronous()))
.withDetail(gatewaySendersKey(gatewaySenderId, "dispatcher-threads"), gatewaySender.getDispatcherThreads())
.withDetail(gatewaySendersKey(gatewaySenderId, "max-queue-memory"), gatewaySender.getMaximumQueueMemory())
.withDetail(gatewaySendersKey(gatewaySenderId, "max-parallelism-for-replicated-region"), gatewaySender.getMaxParallelismForReplicatedRegion())
.withDetail(gatewaySendersKey(gatewaySenderId, "order-policy"), gatewaySender.getOrderPolicy())
.withDetail(gatewaySendersKey(gatewaySenderId, "parallel"), toYesNoString(gatewaySender.isParallel()))
.withDetail(gatewaySendersKey(gatewaySenderId, "paused"), toYesNoString(gatewaySender.isPaused()))
.withDetail(gatewaySendersKey(gatewaySenderId, "persistent"), toYesNoString(gatewaySender.isPersistenceEnabled()))
.withDetail(gatewaySendersKey(gatewaySenderId, "remote-distributed-system-id"), gatewaySender.getRemoteDSId())
.withDetail(gatewaySendersKey(gatewaySenderId, "running"), toYesNoString(gatewaySender.isRunning()))
.withDetail(gatewaySendersKey(gatewaySenderId, "socket-buffer-size"), gatewaySender.getSocketBufferSize())
.withDetail(gatewaySendersKey(gatewaySenderId, "socket-read-timeout"), gatewaySender.getSocketReadTimeout());
});
builder.up();
return;
}
builder.unknown();
}
private String emptyIfUnset(String value) {
return StringUtils.hasText(value) ? value : "";
}
private String gatewaySendersKey(String id, String suffix) {
return String.format("geode.gateway-sender.%1$s.%2$s", id, suffix);
}
}

View File

@@ -0,0 +1,154 @@
/*
* Copyright 2017-present 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
*
* https://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.boot.actuate;
import java.util.Collections;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.query.Index;
import org.apache.geode.cache.query.IndexStatistics;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.context.ApplicationContext;
import org.springframework.geode.boot.actuate.health.AbstractGeodeHealthIndicator;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* The {@link GeodeIndexesHealthIndicator} class is a Spring Boot {@link HealthIndicator} providing details about
* the health of Apache Geode {@link Region} OQL {@link Index Indexes}.
*
* @author John Blum
* @see org.apache.geode.cache.Region
* @see org.apache.geode.cache.query.Index
* @see org.springframework.boot.actuate.health.Health
* @see org.springframework.boot.actuate.health.HealthIndicator
* @see org.springframework.context.ApplicationContext
* @see org.springframework.geode.boot.actuate.health.AbstractGeodeHealthIndicator
* @since 1.0.0
*/
@SuppressWarnings("unused")
public class GeodeIndexesHealthIndicator extends AbstractGeodeHealthIndicator {
private final ApplicationContext applicationContext;
/**
* Default constructor to construct an uninitialized instance of {@link GeodeIndexesHealthIndicator},
* which will not provide any health information.
*/
public GeodeIndexesHealthIndicator() {
super("Indexes health check failed");
this.applicationContext = null;
}
/**
* Constructs an instance of the {@link GeodeIndexesHealthIndicator} initialized with a reference to
* the {@link ApplicationContext} instance.
*
* @param applicationContext reference to the Spring {@link ApplicationContext}.
* @throws IllegalArgumentException if {@link ApplicationContext} is {@literal null}.
* @see org.springframework.context.ApplicationContext
*/
public GeodeIndexesHealthIndicator(ApplicationContext applicationContext) {
super("Indexes health check enabled");
Assert.notNull(applicationContext, "ApplicationContext is required");
this.applicationContext = applicationContext;
}
/**
* Returns an {@link Optional} reference to the Spring {@link ApplicationContext}.
*
* @return an {@link Optional} reference to the Spring {@link ApplicationContext}.
* @see org.springframework.context.ApplicationContext
* @see java.util.Optional
*/
protected Optional<ApplicationContext> getApplicationContext() {
return Optional.ofNullable(this.applicationContext);
}
@Override
protected void doHealthCheck(Health.Builder builder) {
if (getApplicationContext().isPresent()) {
Map<String, Index> indexes = getApplicationContext()
.map(it -> it.getBeansOfType(Index.class))
.orElseGet(Collections::emptyMap);
builder.withDetail("geode.index.count", indexes.size());
indexes.values().stream()
.filter(Objects::nonNull)
.forEach(index -> {
String indexName = index.getName();
builder.withDetail(indexKey(indexName, "from-clause"), index.getFromClause())
.withDetail(indexKey(indexName, "indexed-expression"), index.getIndexedExpression())
.withDetail(indexKey(indexName, "projection-attributes"), index.getProjectionAttributes())
.withDetail(indexKey(indexName, "region"), toRegionPath(index.getRegion()))
.withDetail(indexKey(indexName, "type"), String.valueOf(index.getType()));
IndexStatistics indexStatistics = index.getStatistics();
if (indexStatistics != null) {
builder.withDetail(indexStatisticsKey(indexName, "number-of-bucket-indexes"), indexStatistics.getNumberOfBucketIndexes())
.withDetail(indexStatisticsKey(indexName, "number-of-keys"), indexStatistics.getNumberOfKeys())
.withDetail(indexStatisticsKey(indexName, "number-of-map-index-keys"), indexStatistics.getNumberOfMapIndexKeys())
.withDetail(indexStatisticsKey(indexName, "number-of-values"), indexStatistics.getNumberOfValues())
.withDetail(indexStatisticsKey(indexName, "number-of-updates"), indexStatistics.getNumUpdates())
.withDetail(indexStatisticsKey(indexName, "read-lock-count"), indexStatistics.getReadLockCount())
.withDetail(indexStatisticsKey(indexName, "total-update-time"), indexStatistics.getTotalUpdateTime())
.withDetail(indexStatisticsKey(indexName, "total-uses"), indexStatistics.getTotalUses());
}
});
builder.up();
return;
}
builder.unknown();
}
private String emptyIfUnset(String value) {
return StringUtils.hasText(value) ? value : "";
}
private String indexKey(String indexName, String suffix) {
return String.format("geode.index.%1$s.%2$s", indexName, suffix);
}
private String indexStatisticsKey(String indexName, String suffix) {
return String.format("geode.index.%1$s.statistics.%2$s", indexName, suffix);
}
@SuppressWarnings("rawtypes")
private String toRegionPath(Region region) {
String regionPath = region != null ? region.getFullPath() : null;
return emptyIfUnset(regionPath);
}
}

View File

@@ -0,0 +1,141 @@
/*
* Copyright 2017-present 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
*
* https://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.boot.actuate;
import static org.springframework.data.gemfire.util.CollectionUtils.nullSafeList;
import static org.springframework.data.gemfire.util.CollectionUtils.nullSafeMap;
import java.net.InetSocketAddress;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import org.apache.geode.cache.GemFireCache;
import org.apache.geode.cache.client.ClientCache;
import org.apache.geode.cache.client.Pool;
import org.apache.geode.cache.client.PoolManager;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.data.gemfire.util.CacheUtils;
import org.springframework.geode.boot.actuate.health.AbstractGeodeHealthIndicator;
import org.springframework.util.StringUtils;
/**
* The {@link GeodePoolsHealthIndicator} class is a Spring Boot {@link HealthIndicator} providing details about
* the health of the configured Apache Geode client {@link Pool Pools}.
*
* @author John Blum
* @see org.apache.geode.cache.GemFireCache
* @see org.apache.geode.cache.client.Pool
* @see org.apache.geode.cache.client.PoolManager
* @see org.springframework.boot.actuate.health.Health
* @see org.springframework.boot.actuate.health.HealthIndicator
* @see org.springframework.geode.boot.actuate.health.AbstractGeodeHealthIndicator
* @since 1.0.0
*/
@SuppressWarnings("unused")
public class GeodePoolsHealthIndicator extends AbstractGeodeHealthIndicator {
/**
* Default constructor to construct an uninitialized instance of {@link GeodePoolsHealthIndicator},
* which will not provide any health information.
*/
public GeodePoolsHealthIndicator() {
super("Pools health check failed");
}
/**
* Constructs an instance of the {@link GeodePoolsHealthIndicator} initialized with a reference to
* the {@link GemFireCache} instance.
*
* @param gemfireCache reference to the {@link GemFireCache} instance used to collect health information.
* @throws IllegalArgumentException if {@link GemFireCache} is {@literal null}.
* @see org.apache.geode.cache.GemFireCache
*/
public GeodePoolsHealthIndicator(GemFireCache gemfireCache) {
super(gemfireCache);
}
@Override
protected void doHealthCheck(Health.Builder builder) {
if (getGemFireCache().filter(CacheUtils::isClient).isPresent()) {
Map<String, Pool> pools = nullSafeMap(findAllPools());
builder.withDetail("geode.pool.count", pools.size());
pools.values().stream()
.filter(Objects::nonNull)
.forEach(pool -> {
String poolName = pool.getName();
builder.withDetail(poolKey(poolName, "destroyed"), toYesNoString(pool.isDestroyed()))
.withDetail(poolKey(poolName, "free-connection-timeout"), pool.getFreeConnectionTimeout())
.withDetail(poolKey(poolName, "idle-timeout"), pool.getIdleTimeout())
.withDetail(poolKey(poolName, "load-conditioning-interval"), pool.getLoadConditioningInterval())
.withDetail(poolKey(poolName, "locators"), toCommaDelimitedHostAndPortsString(pool.getLocators()))
.withDetail(poolKey(poolName, "max-connections"), pool.getMaxConnections())
.withDetail(poolKey(poolName, "min-connections"), pool.getMinConnections())
.withDetail(poolKey(poolName, "multi-user-authentication"), toYesNoString(pool.getMultiuserAuthentication()))
.withDetail(poolKey(poolName, "online-locators"), toCommaDelimitedHostAndPortsString(pool.getOnlineLocators()))
.withDetail(poolKey(poolName, "ping-interval"), pool.getPingInterval())
.withDetail(poolKey(poolName, "pr-single-hop-enabled"), toYesNoString(pool.getPRSingleHopEnabled()))
.withDetail(poolKey(poolName, "read-timeout"), pool.getReadTimeout())
.withDetail(poolKey(poolName, "retry-attempts"), pool.getRetryAttempts())
.withDetail(poolKey(poolName, "server-group"), pool.getServerGroup())
.withDetail(poolKey(poolName, "servers"), toCommaDelimitedHostAndPortsString(pool.getServers()))
.withDetail(poolKey(poolName, "socket-buffer-size"), pool.getSocketBufferSize())
.withDetail(poolKey(poolName, "statistic-interval"), pool.getStatisticInterval())
.withDetail(poolKey(poolName, "subscription-ack-interval"), pool.getSubscriptionAckInterval())
.withDetail(poolKey(poolName, "subscription-enabled"), toYesNoString(pool.getSubscriptionEnabled()))
.withDetail(poolKey(poolName, "subscription-message-tracking-timeout"), pool.getSubscriptionMessageTrackingTimeout())
.withDetail(poolKey(poolName, "subscription-redundancy"), pool.getSubscriptionRedundancy());
//.withDetail(poolKey(poolName, "thread-local-connections"), toYesNoString(pool.getThreadLocalConnections()));
getGemFireCache()
.map(ClientCache.class::cast)
.filter(CacheUtils::isDurable)
.ifPresent(it -> builder.withDetail(poolKey(poolName, "pending-event-count"), pool.getPendingEventCount()));
});
builder.up();
return;
}
builder.unknown();
}
Map<String, Pool> findAllPools() {
return PoolManager.getAll();
}
private String poolKey(String poolName, String suffix) {
return String.format("geode.pool.%1$s.%2$s", poolName, suffix);
}
private String toCommaDelimitedHostAndPortsString(List<InetSocketAddress> socketAddresses) {
return StringUtils.collectionToCommaDelimitedString(nullSafeList(socketAddresses).stream()
.filter(Objects::nonNull)
.map(socketAddress -> String.format("%1$s:%2$d", socketAddress.getHostName(), socketAddress.getPort()))
.collect(Collectors.toList()));
}
}

View File

@@ -0,0 +1,284 @@
/*
* Copyright 2017-present 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
*
* https://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.boot.actuate;
import java.util.Collections;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.stream.Collectors;
import org.apache.geode.cache.EvictionAlgorithm;
import org.apache.geode.cache.EvictionAttributes;
import org.apache.geode.cache.ExpirationAttributes;
import org.apache.geode.cache.GemFireCache;
import org.apache.geode.cache.PartitionAttributes;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.RegionAttributes;
import org.apache.geode.internal.cache.LocalDataSet;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.geode.boot.actuate.health.AbstractGeodeHealthIndicator;
import org.springframework.geode.boot.actuate.health.support.RegionStatisticsResolver;
import org.springframework.util.StringUtils;
/**
* The {@link GeodeRegionsHealthIndicator} class is a Spring Boot {@link HealthIndicator} providing details about
* the health of the {@link GemFireCache} {@link Region Regions}.
*
* @author John Blum
* @see org.apache.geode.cache.GemFireCache
* @see org.apache.geode.cache.Region
* @see org.springframework.boot.actuate.health.Health
* @see org.springframework.boot.actuate.health.HealthIndicator
* @see org.springframework.geode.boot.actuate.health.AbstractGeodeHealthIndicator
* @since 1.0.0
*/
@SuppressWarnings("unused")
public class GeodeRegionsHealthIndicator extends AbstractGeodeHealthIndicator {
private final BiConsumer<Region<?, ?>, Health.Builder> gemfireRegionHealthIndicatorConsumers = withRegionDetails()
.andThen(withPartitionRegionDetails())
.andThen(withRegionEvictionPolicyDetails())
.andThen(withRegionExpirationPolicyDetails())
.andThen(withRegionStatisticsDetails());
/**
* Default constructor to construct an uninitialized instance of {@link GeodeRegionsHealthIndicator},
* which will not provide any health information.
*/
public GeodeRegionsHealthIndicator() {
super("Regions health check failed");
}
/**
* Constructs an instance of the {@link GeodeRegionsHealthIndicator} initialized with a reference to
* the {@link GemFireCache} instance.
*
* @param gemfireCache reference to the {@link GemFireCache} instance used to collect health information.
* @throws IllegalArgumentException if {@link GemFireCache} is {@literal null}.
* @see org.apache.geode.cache.GemFireCache
*/
public GeodeRegionsHealthIndicator(GemFireCache gemfireCache) {
super(gemfireCache);
}
/**
* Returns the collection of {@link BiConsumer} objects that applies health details about the {@link GemFireCache}
* {@link Region Regions} to the {@link Health} object.
*
* @return the collection of {@link BiConsumer} objects that applies health details about the {@link GemFireCache}
* {@link Region Regions} to the {@link Health} object.
* @see org.springframework.boot.actuate.health.Health
* @see org.apache.geode.cache.Region
* @see java.util.function.BiConsumer
*/
protected BiConsumer<Region<?, ?>, Health.Builder> getGemfireRegionHealthIndicatorConsumers() {
return this.gemfireRegionHealthIndicatorConsumers;
}
@Override
protected void doHealthCheck(Health.Builder builder) {
if (getGemFireCache().isPresent()) {
Set<Region<?, ?>> rootRegions = getGemFireCache()
.map(GemFireCache::rootRegions)
.orElseGet(Collections::emptySet);
builder.withDetail("geode.cache.regions", rootRegions.stream()
.filter(Objects::nonNull)
.map(Region::getFullPath)
.sorted()
.collect(Collectors.toList()));
builder.withDetail("geode.cache.regions.count", rootRegions.stream().filter(Objects::nonNull).count());
rootRegions.stream()
.filter(Objects::nonNull)
.forEach(region -> getGemfireRegionHealthIndicatorConsumers().accept(region, builder));
builder.up();
return;
}
builder.unknown();
}
private BiConsumer<Region<?, ?>, Health.Builder> withRegionDetails() {
return (region, builder) -> {
String regionName = region.getName();
builder.withDetail(cacheRegionKey(regionName, "full-path"), region.getFullPath());
if (isRegionAttributesPresent(region)) {
RegionAttributes<?, ?> regionAttributes = region.getAttributes();
builder.withDetail(cacheRegionKey(regionName, "cloning-enabled"), toYesNoString(regionAttributes.getCloningEnabled()))
.withDetail(cacheRegionKey(regionName, "data-policy"), String.valueOf(regionAttributes.getDataPolicy()))
.withDetail(cacheRegionKey(regionName, "initial-capacity"), regionAttributes.getInitialCapacity())
.withDetail(cacheRegionKey(regionName, "load-factor"), regionAttributes.getLoadFactor())
.withDetail(cacheRegionKey(regionName, "key-constraint"), nullSafeClassName(regionAttributes.getKeyConstraint()))
.withDetail(cacheRegionKey(regionName, "off-heap"), toYesNoString(regionAttributes.getOffHeap()))
.withDetail(cacheRegionKey(regionName, "pool-name"), emptyIfUnset(regionAttributes.getPoolName()))
.withDetail(cacheRegionKey(regionName, "scope"), String.valueOf(regionAttributes.getScope()))
.withDetail(cacheRegionKey(regionName, "statistics-enabled"), toYesNoString(regionAttributes.getStatisticsEnabled()))
.withDetail(cacheRegionKey(regionName, "value-constraint"), nullSafeClassName(regionAttributes.getValueConstraint())); }
};
}
private BiConsumer<Region<?, ?>, Health.Builder> withPartitionRegionDetails() {
return (region, builder) -> {
if (isRegionAttributesPresent(region)) {
PartitionAttributes<?, ?> partitionAttributes = region.getAttributes().getPartitionAttributes();
if (partitionAttributes != null) {
String regionName = region.getName();
builder.withDetail(cachePartitionRegionKey(regionName, "collocated-with"), emptyIfUnset(partitionAttributes.getColocatedWith()))
.withDetail(cachePartitionRegionKey(regionName, "local-max-memory"), partitionAttributes.getLocalMaxMemory())
.withDetail(cachePartitionRegionKey(regionName, "redundant-copies"), partitionAttributes.getRedundantCopies())
//.withDetail(cachePartitionRegionKey(regionName, "total-max-memory"), partitionAttributes.getTotalMaxMemory())
.withDetail(cachePartitionRegionKey(regionName, "total-number-of-buckets"), partitionAttributes.getTotalNumBuckets());
}
}
};
}
private BiConsumer<Region<?, ?>, Health.Builder> withRegionEvictionPolicyDetails() {
return (region, builder) -> {
if (isRegionAttributesPresent(region)) {
EvictionAttributes evictionAttributes = region.getAttributes().getEvictionAttributes();
if (evictionAttributes != null) {
String regionName = region.getName();
builder.withDetail(cacheRegionEvictionKey(regionName, "action"), String.valueOf(evictionAttributes.getAction()))
.withDetail(cacheRegionEvictionKey(regionName, "algorithm"), String.valueOf(evictionAttributes.getAlgorithm()));
EvictionAlgorithm evictionAlgorithm = evictionAttributes.getAlgorithm();
// NOTE: Eviction Maximum does not apply when Eviction Algorithm is Heap LRU.
if (evictionAlgorithm != null && !evictionAlgorithm.isLRUHeap()) {
builder.withDetail(cacheRegionEvictionKey(regionName,"maximum"),
evictionAttributes.getMaximum());
}
}
}
};
}
private BiConsumer<Region<?, ?>, Health.Builder> withRegionExpirationPolicyDetails() {
return (region, builder) -> {
if (isRegionAttributesPresent(region)) {
String regionName = region.getName();
RegionAttributes<?, ?> regionAttributes = region.getAttributes();
ExpirationAttributes entryTimeToLive = regionAttributes.getEntryTimeToLive();
if (entryTimeToLive != null) {
builder.withDetail(cacheRegionExpirationKey(regionName, "entry.ttl.action"), String.valueOf(entryTimeToLive.getAction()))
.withDetail(cacheRegionExpirationKey(regionName, "entry.ttl.timeout"), entryTimeToLive.getTimeout());
}
ExpirationAttributes entryIdleTimeout = regionAttributes.getEntryIdleTimeout();
if (entryIdleTimeout != null) {
builder.withDetail(cacheRegionExpirationKey(regionName, "entry.tti.action"), String.valueOf(entryIdleTimeout.getAction()))
.withDetail(cacheRegionExpirationKey(regionName, "entry.tti.timeout"), entryIdleTimeout.getTimeout());
}
}
};
}
private BiConsumer<Region<?, ?>, Health.Builder> withRegionStatisticsDetails() {
return (region, builder) -> {
String regionName = region.getName();
Optional.of(region)
.filter(this::isNotLocalDataSet)
.filter(this::isStatisticsEnabled)
.map(RegionStatisticsResolver::resolve)
.ifPresent(cacheStatistics -> builder
.withDetail(cacheRegionStatisticsKey(regionName, "cache-statistics-type"), nullSafeClassName(cacheStatistics.getClass()))
.withDetail(cacheRegionStatisticsKey(regionName, "hit-count"), cacheStatistics.getHitCount())
.withDetail(cacheRegionStatisticsKey(regionName, "hit-ratio"), cacheStatistics.getHitRatio())
.withDetail(cacheRegionStatisticsKey(regionName, "last-accessed-time"), cacheStatistics.getLastAccessedTime())
.withDetail(cacheRegionStatisticsKey(regionName, "last-modified-time"), cacheStatistics.getLastModifiedTime())
.withDetail(cacheRegionStatisticsKey(regionName, "miss-count"), cacheStatistics.getMissCount()));
};
}
private boolean isLocalDataSet(Region<?, ?> region) {
return region instanceof LocalDataSet;
}
private boolean isNotLocalDataSet(Region<?, ?> region) {
return !isLocalDataSet(region);
}
private boolean isRegionAttributesPresent(Region<?, ?> region) {
return region != null && region.getAttributes() != null;
}
private boolean isStatisticsEnabled(Region<?, ?> region) {
return isRegionAttributesPresent(region) && region.getAttributes().getStatisticsEnabled();
}
private String cachePartitionRegionKey(String regionName, String suffix) {
return cacheRegionKey(regionName, String.format("partition.%s", suffix));
}
private String cacheRegionKey(String regionName, String suffix) {
return String.format("geode.cache.regions.%1$s.%2$s", regionName, suffix);
}
private String cacheRegionEvictionKey(String regionName, String suffix) {
return cacheRegionKey(regionName, String.format("eviction.%s", suffix));
}
private String cacheRegionExpirationKey(String regionName, String suffix) {
return cacheRegionKey(regionName, String.format("expiration.%s", suffix));
}
private String cacheRegionStatisticsKey(String regionName, String suffix) {
return cacheRegionKey(regionName, String.format("statistics.%s", suffix));
}
private String emptyIfUnset(String value) {
return StringUtils.hasText(value) ? value : "";
}
}

View File

@@ -0,0 +1,97 @@
/*
* Copyright 2017-present 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
*
* https://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.boot.actuate.health;
import java.util.Optional;
import org.apache.geode.cache.GemFireCache;
import org.springframework.boot.actuate.health.AbstractHealthIndicator;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.util.Assert;
/**
* The {@link AbstractGeodeHealthIndicator} class is an abstract base class encapsulating functionality common to all
* Apache Geode {@link HealthIndicator} objects.
*
* @author John Blum
* @see org.apache.geode.cache.GemFireCache
* @see org.springframework.boot.actuate.health.AbstractHealthIndicator
* @see org.springframework.boot.actuate.health.HealthIndicator
* @since 1.0.0
*/
@SuppressWarnings("unused")
public abstract class AbstractGeodeHealthIndicator extends AbstractHealthIndicator {
protected static final String UNKNOWN = "unknown";
private final GemFireCache gemfireCache;
/**
* Default constructor to construct an uninitialized instance of {@link AbstractGeodeHealthIndicator},
* which will not provide any health information.
*/
public AbstractGeodeHealthIndicator(String healthCheckedFailedMessage) {
super(healthCheckedFailedMessage);
this.gemfireCache = null;
}
/**
* Constructs an instance of the {@link AbstractGeodeHealthIndicator} initialized with a reference to
* the {@link GemFireCache} instance.
*
* @param gemfireCache reference to the {@link GemFireCache} instance used to collect health information.
* @throws IllegalArgumentException if {@link GemFireCache} is {@literal null}.
* @see org.apache.geode.cache.GemFireCache
*/
public AbstractGeodeHealthIndicator(GemFireCache gemfireCache) {
Assert.notNull(gemfireCache, "GemFireCache must not be null");
this.gemfireCache = gemfireCache;
}
/**
* Returns a reference to the {@link GemFireCache} instance.
*
* @return a reference to the {@link GemFireCache} instance.
* @see org.apache.geode.cache.GemFireCache
*/
protected Optional<GemFireCache> getGemFireCache() {
return Optional.ofNullable(this.gemfireCache);
}
/**
* Determines the {@link String name} of the {@link Class} type safely by handling {@literal null}.
*
* @param type {@link Class} type to evaluate.
* @return the {@link String name} of the {@link Class} type.
* @see java.lang.Class#getName()
*/
protected String nullSafeClassName(Class<?> type) {
return type != null ? type.getName() : "";
}
/**
* Converts a {@link Boolean} value into a {@literal yes} / {@literal no} {@link String}.
*
* @param value {@link Boolean} value to convert.
* @return a {@literal yes} / {@literal no} response for the given {@link Boolean} value.
*/
protected String toYesNoString(Boolean value) {
return Boolean.TRUE.equals(value) ? "Yes" : "No";
}
}

View File

@@ -0,0 +1,107 @@
/*
* Copyright 2017-present 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
*
* https://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.boot.actuate.health.support;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicReference;
import org.apache.geode.cache.server.ServerLoad;
import org.apache.geode.cache.server.ServerLoadProbe;
import org.apache.geode.cache.server.ServerMetrics;
import org.springframework.util.Assert;
/**
* The ActuatorServerLoadProbeWrapper class is an implementation of Apache Geode's {@link ServerLoadProbe} interface
* used to capture the current {@link ServerMetrics} and access the latest {@link ServerLoad} details.
*
* @author John Blum
* @see org.apache.geode.cache.server.ServerLoad
* @see org.apache.geode.cache.server.ServerLoadProbe
* @see org.apache.geode.cache.server.ServerMetrics
* @since 1.0.0
*/
@SuppressWarnings("unused")
public class ActuatorServerLoadProbeWrapper implements ServerLoadProbe {
private AtomicReference<ServerMetrics> currentServerMetrics = new AtomicReference<>(null);
private final ServerLoadProbe delegate;
/**
* Constructs a new instance of {@link ActuatorServerLoadProbeWrapper} initialized with the required
* {@link ServerLoadProbe} used as the delegate.
*
* @param serverLoadProbe required {@link ServerLoadProbe}.
* @throws IllegalArgumentException if {@link ServerLoadProbe} is {@literal null}.
* @see org.apache.geode.cache.server.ServerLoadProbe
*/
public ActuatorServerLoadProbeWrapper(ServerLoadProbe serverLoadProbe) {
Assert.notNull(serverLoadProbe, "ServerLoaderProbe is required");
this.delegate = serverLoadProbe;
}
/**
* Returns the current, most up-to-date details on the {@link ServerLoad} if possible.
*
* @return the current {@link ServerLoad}.
* @see org.apache.geode.cache.server.ServerLoad
* @see #getCurrentServerMetrics()
* @see java.util.Optional
*/
public Optional<ServerLoad> getCurrentServerLoad() {
return getCurrentServerMetrics().map(getDelegate()::getLoad);
}
/**
* Returns the current, provided {@link ServerMetrics} if available.
*
* @return the current, provided {@link ServerMetrics} if available.
*/
public Optional<ServerMetrics> getCurrentServerMetrics() {
return Optional.ofNullable(this.currentServerMetrics.get());
}
/**
* Returns the underlying, wrapped {@link ServerLoadProbe} backing this instance.
*
* @return the underlying, wrapped {@link ServerLoadProbe}.
* @see org.apache.geode.cache.server.ServerLoadProbe
*/
protected ServerLoadProbe getDelegate() {
return this.delegate;
}
@Override
public ServerLoad getLoad(ServerMetrics metrics) {
this.currentServerMetrics.set(metrics);
return getDelegate().getLoad(metrics);
}
@Override
public void open() {
getDelegate().open();
}
@Override
public void close() {
getDelegate().close();
}
}

View File

@@ -0,0 +1,161 @@
/*
* Copyright 2017-present 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
*
* https://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.boot.actuate.health.support;
import java.util.Collections;
import java.util.Optional;
import java.util.Set;
import org.apache.geode.cache.CacheStatistics;
import org.apache.geode.cache.DataPolicy;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.StatisticsDisabledException;
import org.apache.geode.cache.partition.PartitionRegionHelper;
import org.apache.geode.internal.cache.BucketRegion;
import org.apache.geode.internal.cache.PartitionedRegion;
import org.apache.geode.internal.cache.PartitionedRegionDataStore;
import org.springframework.data.gemfire.util.RegionUtils;
import org.springframework.util.Assert;
/**
* The {@link RegionStatisticsResolver} class is a utility class for resolving the {@link CacheStatistics}
* for a {@link Region}, regardless of {@link Region} type, or more specifically {@link Region Region's}
* {@link DataPolicy data management policy}.
*
* @author John Blum
* @see org.apache.geode.cache.CacheStatistics
* @see org.apache.geode.cache.Region
* @see org.apache.geode.cache.partition.PartitionRegionHelper
* @see org.apache.geode.internal.cache.BucketRegion
* @see org.apache.geode.internal.cache.PartitionedRegion
* @since 1.0.0
*/
@SuppressWarnings("unused")
public class RegionStatisticsResolver {
public static CacheStatistics resolve(Region<?, ?> region) {
return region != null
? PartitionRegionHelper.isPartitionedRegion(region)
? new PartitionRegionCacheStatistics(region)
: region.getStatistics()
: null;
}
protected static class PartitionRegionCacheStatistics implements CacheStatistics {
private final PartitionedRegion partitionRegion;
private float hitRatio = 0.0f;
private long hitCount = 0L;
private long lastAccessedTime = 0L;
private long lastModifiedTime = 0L;
private long missCount = 0L;
protected PartitionRegionCacheStatistics(Region<?, ?> region) {
Assert.isInstanceOf(PartitionedRegion.class, region, () ->
String.format("Region [%1$s] must be of type [%2$s]", RegionUtils.toRegionPath(region),
PartitionedRegion.class.getName()));
this.partitionRegion = computeStatistics((PartitionedRegion) region);
}
protected PartitionedRegion computeStatistics(PartitionedRegion region) {
float totalHitRatio = 0.0f;
int totalCount = 0;
long totalHitCount = 0L;
long maxLastAccessedTime = 0L;
long maxLastModifiedTime = 0L;
long totalMissCount = 0L;
Set<BucketRegion> bucketRegions = Optional.of(region)
.map(PartitionedRegion::getDataStore)
.map(PartitionedRegionDataStore::getAllLocalBucketRegions)
.orElseGet(Collections::emptySet);
for (BucketRegion bucket : bucketRegions) {
CacheStatistics bucketStatistics = bucket.getStatistics();
if (bucketStatistics != null) {
totalCount++;
totalHitCount += bucketStatistics.getHitCount();
totalHitRatio += bucketStatistics.getHitRatio();
maxLastAccessedTime = Math.max(maxLastAccessedTime, bucketStatistics.getLastAccessedTime());
maxLastModifiedTime = Math.max(maxLastModifiedTime, bucketStatistics.getLastModifiedTime());
totalMissCount += bucketStatistics.getMissCount();
}
}
if (totalCount > 0) {
this.hitCount = totalHitCount / totalCount;
this.hitRatio = totalHitRatio / totalCount;
this.lastAccessedTime = maxLastAccessedTime;
this.lastModifiedTime = maxLastModifiedTime;
this.missCount = totalMissCount / totalCount;
}
return region;
}
protected PartitionedRegion getPartitionRegion() {
return this.partitionRegion;
}
@Override
public long getHitCount() throws StatisticsDisabledException {
return this.hitCount;
}
@Override
public float getHitRatio() throws StatisticsDisabledException {
return this.hitRatio;
}
@Override
public long getLastAccessedTime() throws StatisticsDisabledException {
return this.lastAccessedTime;
}
@Override
public long getLastModifiedTime() {
return this.lastModifiedTime;
}
@Override
public long getMissCount() throws StatisticsDisabledException {
return this.missCount;
}
@Override
public void resetCounts() throws StatisticsDisabledException {
this.hitCount = 0L;
this.hitRatio = 0.0f;
this.lastAccessedTime = 0L;
this.lastModifiedTime = 0L;
this.missCount = 0L;
}
}
}

View File

@@ -0,0 +1,159 @@
/*
* Copyright 2017-present 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
*
* https://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.boot.actuate;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.apache.geode.cache.Cache;
import org.apache.geode.cache.GemFireCache;
import org.apache.geode.cache.asyncqueue.AsyncEventQueue;
import org.apache.geode.cache.client.ClientCache;
import org.apache.geode.cache.wan.GatewaySender;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.Status;
import org.springframework.data.gemfire.tests.mock.AsyncEventQueueMockObjects;
/**
* Unit tests for {@link GeodeAsyncEventQueuesHealthIndicator}.
*
* @author John Blum
* @see org.junit.Test
* @see org.mockito.Mock
* @see org.mockito.Mockito
* @see org.mockito.junit.MockitoJUnitRunner
* @see org.apache.geode.cache.Cache
* @see org.apache.geode.cache.asyncqueue.AsyncEventQueue
* @see org.springframework.boot.actuate.health.Health
* @see org.springframework.boot.actuate.health.HealthIndicator
* @see org.springframework.data.gemfire.tests.mock.AsyncEventQueueMockObjects
* @see org.springframework.geode.boot.actuate.GeodeAsyncEventQueuesHealthIndicator
* @since 1.0.0
*/
@RunWith(MockitoJUnitRunner.class)
public class GeodeAsyncEventQueuesHealthIndicatorUnitTests {
@Mock
private Cache mockCache;
private GeodeAsyncEventQueuesHealthIndicator asyncEventQueuesHealthIndicator;
@Before
public void setup() {
this.asyncEventQueuesHealthIndicator = new GeodeAsyncEventQueuesHealthIndicator(this.mockCache);
}
@Test
public void healthCheckCapturesDetails() throws Exception {
Set<AsyncEventQueue> mockAsyncEventQueues = new HashSet<>();
mockAsyncEventQueues.add(AsyncEventQueueMockObjects.mockAsyncEventQueue("aeqOne", true,
250, 10000, "testDiskStoreOne", true, 16,
true, 65536, GatewaySender.OrderPolicy.THREAD, true,
true, true, 1024));
mockAsyncEventQueues.add(AsyncEventQueueMockObjects.mockAsyncEventQueue("aeqTwo", false,
100, 1000, "testDiskStoreTwo", false, 8,
false, 32768, GatewaySender.OrderPolicy.KEY, false,
true, false, 8192));
when(this.mockCache.getAsyncEventQueues()).thenReturn(mockAsyncEventQueues);
Health.Builder builder = new Health.Builder();
this.asyncEventQueuesHealthIndicator.doHealthCheck(builder);
Health health = builder.build();
assertThat(health).isNotNull();
assertThat(health.getStatus()).isEqualTo(Status.UP);
Map<String, Object> healthDetails = health.getDetails();
assertThat(healthDetails).isNotNull();
assertThat(healthDetails).isNotEmpty();
assertThat(healthDetails).containsEntry("geode.async-event-queue.count", mockAsyncEventQueues.size());
assertThat(healthDetails).containsEntry("geode.async-event-queue.aeqOne.batch-conflation-enabled", "Yes");
assertThat(healthDetails).containsEntry("geode.async-event-queue.aeqOne.batch-size", 250);
assertThat(healthDetails).containsEntry("geode.async-event-queue.aeqOne.batch-time-interval", 10000);
assertThat(healthDetails).containsEntry("geode.async-event-queue.aeqOne.disk-store-name", "testDiskStoreOne");
assertThat(healthDetails).containsEntry("geode.async-event-queue.aeqOne.disk-synchronous", "Yes");
assertThat(healthDetails).containsEntry("geode.async-event-queue.aeqOne.dispatcher-threads", 16);
assertThat(healthDetails).containsEntry("geode.async-event-queue.aeqOne.forward-expiration-destroy", "Yes");
assertThat(healthDetails).containsEntry("geode.async-event-queue.aeqOne.max-queue-memory", 65536);
assertThat(healthDetails).containsEntry("geode.async-event-queue.aeqOne.order-policy", GatewaySender.OrderPolicy.THREAD);
assertThat(healthDetails).containsEntry("geode.async-event-queue.aeqOne.parallel", "Yes");
assertThat(healthDetails).containsEntry("geode.async-event-queue.aeqOne.persistent", "Yes");
assertThat(healthDetails).containsEntry("geode.async-event-queue.aeqOne.primary", "Yes");
assertThat(healthDetails).containsEntry("geode.async-event-queue.aeqOne.size", 1024);
assertThat(healthDetails).containsEntry("geode.async-event-queue.aeqTwo.batch-conflation-enabled", "No");
assertThat(healthDetails).containsEntry("geode.async-event-queue.aeqTwo.batch-size", 100);
assertThat(healthDetails).containsEntry("geode.async-event-queue.aeqTwo.batch-time-interval", 1000);
assertThat(healthDetails).containsEntry("geode.async-event-queue.aeqTwo.disk-store-name", "testDiskStoreTwo");
assertThat(healthDetails).containsEntry("geode.async-event-queue.aeqTwo.disk-synchronous", "No");
assertThat(healthDetails).containsEntry("geode.async-event-queue.aeqTwo.dispatcher-threads", 8);
assertThat(healthDetails).containsEntry("geode.async-event-queue.aeqTwo.forward-expiration-destroy", "No");
assertThat(healthDetails).containsEntry("geode.async-event-queue.aeqTwo.max-queue-memory", 32768);
assertThat(healthDetails).containsEntry("geode.async-event-queue.aeqTwo.order-policy", GatewaySender.OrderPolicy.KEY);
assertThat(healthDetails).containsEntry("geode.async-event-queue.aeqTwo.parallel", "No");
assertThat(healthDetails).containsEntry("geode.async-event-queue.aeqTwo.persistent", "Yes");
assertThat(healthDetails).containsEntry("geode.async-event-queue.aeqTwo.primary", "No");
assertThat(healthDetails).containsEntry("geode.async-event-queue.aeqTwo.size", 8192);
verify(this.mockCache, times(1)).getAsyncEventQueues();
}
public void testHealthCheckFailsWhenGemFireCacheIsInvalid(GemFireCache gemfireCache) throws Exception {
GeodeAsyncEventQueuesHealthIndicator healthIndicator = gemfireCache != null
? new GeodeAsyncEventQueuesHealthIndicator(gemfireCache)
: new GeodeAsyncEventQueuesHealthIndicator();
Health.Builder builder = new Health.Builder();
healthIndicator.doHealthCheck(builder);
Health health = builder.build();
assertThat(health).isNotNull();
assertThat(health.getDetails()).isEmpty();
assertThat(health.getStatus()).isEqualTo(Status.UNKNOWN);
}
@Test
public void healthCheckFailsWhenGemFireCacheIsNotPeerCache() throws Exception {
testHealthCheckFailsWhenGemFireCacheIsInvalid(mock(ClientCache.class));
}
@Test
public void healthCheckFailsWhenGemFireCacheIsNotPresent() throws Exception {
testHealthCheckFailsWhenGemFireCacheIsInvalid(null);
}
}

View File

@@ -0,0 +1,165 @@
/*
* Copyright 2017-present 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
*
* https://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.boot.actuate;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.Arrays;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import org.apache.geode.CancelCriterion;
import org.apache.geode.cache.GemFireCache;
import org.apache.geode.cache.control.ResourceManager;
import org.apache.geode.distributed.DistributedMember;
import org.apache.geode.distributed.DistributedSystem;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.Status;
import org.springframework.data.gemfire.tests.mock.CacheMockObjects;
/**
* Unit tests for {@link GeodeCacheHealthIndicator}.
*
* @author John Blum
* @see org.junit.Test
* @see org.mockito.Mock
* @see org.mockito.Mockito
* @see org.mockito.junit.MockitoJUnitRunner
* @see org.apache.geode.CancelCriterion
* @see org.apache.geode.cache.GemFireCache
* @see org.apache.geode.cache.control.ResourceManager
* @see org.apache.geode.distributed.DistributedMember
* @see org.apache.geode.distributed.DistributedSystem
* @see org.springframework.boot.actuate.health.Health
* @see org.springframework.boot.actuate.health.HealthIndicator
* @see org.springframework.data.gemfire.tests.mock.CacheMockObjects
* @see org.springframework.geode.boot.actuate.GeodeCacheHealthIndicator
* @since 1.0.0
*/
@RunWith(MockitoJUnitRunner.class)
public class GeodeCacheHealthIndicatorUnitTests {
@Mock
private GemFireCache mockGemFireCache;
private GeodeCacheHealthIndicator cacheHealthIndicator;
@Before
public void setup() {
this.cacheHealthIndicator = new GeodeCacheHealthIndicator(this.mockGemFireCache);
}
@SuppressWarnings("all")
private Set<DistributedMember> mockDistributedMembers(int size) {
return IntStream.range(0, size)
.mapToObj(it -> mock(DistributedMember.class))
.collect(Collectors.toSet());
}
@Test
public void healthCheckCapturesDetails() throws Exception {
DistributedMember mockDistributedMember =
CacheMockObjects.mockDistributedMember("TestMember", "TestGroup", "MockGroup");
when(mockDistributedMember.getHost()).thenReturn("Skullbox");
when(mockDistributedMember.getProcessId()).thenReturn(12345);
DistributedSystem mockDistributedSystem = CacheMockObjects.mockDistributedSystem(mockDistributedMember);
when(mockDistributedSystem.getAllOtherMembers()).thenAnswer(invocation -> mockDistributedMembers(8));
when(mockDistributedSystem.isConnected()).thenReturn(true);
when(mockDistributedSystem.isReconnecting()).thenReturn(false);
ResourceManager mockResourceManager = CacheMockObjects.mockResourceManager(0.9f,
0.95f, 0.85f, 0.9f);
GemFireCache mockGemFireCache = CacheMockObjects.mockGemFireCache(this.mockGemFireCache,
"MockGemFireCache", mockDistributedSystem, mockResourceManager);
CancelCriterion mockCancelCriterion = mock(CancelCriterion.class);
when(mockCancelCriterion.isCancelInProgress()).thenReturn(false);
when(mockGemFireCache.getCancelCriterion()).thenReturn(mockCancelCriterion);
when(mockGemFireCache.isClosed()).thenReturn(false);
Health.Builder builder = new Health.Builder();
this.cacheHealthIndicator.doHealthCheck(builder);
Health health = builder.build();
assertThat(health).isNotNull();
assertThat(health.getStatus()).isEqualTo(Status.UP);
Map<String, Object> healthDetails = health.getDetails();
assertThat(healthDetails).isNotNull();
assertThat(healthDetails).isNotEmpty();
assertThat(healthDetails).containsEntry("geode.cache.name", "MockGemFireCache");
assertThat(healthDetails).containsEntry("geode.cache.closed", "No");
assertThat(healthDetails).containsEntry("geode.cache.cancel-in-progress", "No");
assertThat(healthDetails).containsKey("geode.distributed-member.id");
assertThat(String.valueOf(healthDetails.get("geode.distributed-member.id"))).isNotEqualToIgnoringCase("null");
assertThat(healthDetails).containsEntry("geode.distributed-member.name", "TestMember");
assertThat(healthDetails).containsEntry("geode.distributed-member.groups", Arrays.asList("TestGroup", "MockGroup"));
assertThat(healthDetails).containsEntry("geode.distributed-member.host", "Skullbox");
assertThat(healthDetails).containsEntry("geode.distributed-member.process-id", 12345);
assertThat(healthDetails).containsEntry("geode.distributed-system.member-count", 9);
assertThat(healthDetails).containsEntry("geode.distributed-system.connection", "Connected");
assertThat(healthDetails).containsEntry("geode.distributed-system.reconnecting", "No");
//assertThat(healthDetails).containsKey("geode.distributed-member.properties-location");
//assertThat(healthDetails).containsKey("geode.distributed-member.security-properties-location");
assertThat(healthDetails).containsEntry("geode.resource-manager.critical-heap-percentage", 0.9f);
assertThat(healthDetails).containsEntry("geode.resource-manager.critical-off-heap-percentage", 0.95f);
assertThat(healthDetails).containsEntry("geode.resource-manager.eviction-heap-percentage", 0.85f);
assertThat(healthDetails).containsEntry("geode.resource-manager.eviction-off-heap-percentage", 0.9f);
verify(this.mockGemFireCache, times(1)).getCancelCriterion();
verify(this.mockGemFireCache, times(2)).getDistributedSystem();
verify(this.mockGemFireCache, times(1)).getResourceManager();
verify(mockDistributedSystem, times(1)).getDistributedMember();
}
@Test
public void healthCheckFailsWhenGemFireCacheIsNotPresent() throws Exception {
GeodeCacheHealthIndicator healthIndicator = new GeodeCacheHealthIndicator();
Health.Builder builder = new Health.Builder();
healthIndicator.doHealthCheck(builder);
Health health = builder.build();
assertThat(health).isNotNull();
assertThat(health.getDetails()).isEmpty();
assertThat(health.getStatus()).isEqualTo(Status.UNKNOWN);
}
}

View File

@@ -0,0 +1,165 @@
/*
* Copyright 2017-present 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
*
* https://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.boot.actuate;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.apache.geode.cache.Cache;
import org.apache.geode.cache.GemFireCache;
import org.apache.geode.cache.client.ClientCache;
import org.apache.geode.cache.server.CacheServer;
import org.apache.geode.cache.server.ServerLoadProbe;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.Status;
import org.springframework.data.gemfire.tests.mock.CacheServerMockObjects;
/**
* Unit tests for {@link GeodeCacheServersHealthIndicator}.
*
* @author John Blum
* @see org.junit.Test
* @see org.mockito.Mock
* @see org.mockito.Mockito
* @see org.mockito.junit.MockitoJUnitRunner
* @see org.apache.geode.cache.Cache
* @see org.apache.geode.cache.GemFireCache
* @see org.apache.geode.cache.server.CacheServer
* @see org.apache.geode.cache.server.ServerLoadProbe
* @see org.springframework.boot.actuate.health.Health
* @see org.springframework.boot.actuate.health.HealthIndicator
* @see org.springframework.data.gemfire.tests.mock.CacheServerMockObjects
* @see org.springframework.geode.boot.actuate.GeodeCacheServersHealthIndicator
* @since 1.0.0
*/
@RunWith(MockitoJUnitRunner.class)
public class GeodeCacheServersHealthIndicatorUnitTests {
@Mock
private Cache mockCache;
private GeodeCacheServersHealthIndicator cacheServersHealthIndicator;
@Before
public void setup() {
this.cacheServersHealthIndicator = new GeodeCacheServersHealthIndicator(this.mockCache);
}
@Test
public void healthCheckCapturesDetails() throws Exception {
List<CacheServer> mockCacheServers = new ArrayList<>();
ServerLoadProbe mockServerLoadProbe = mock(ServerLoadProbe.class);
mockCacheServers.add(CacheServerMockObjects.mockCacheServer("10.11.111.1", null,
"Mailbox", 15000L, mockServerLoadProbe, 100, 500,
8, 20000, 30000, 41414, true, 16384,
true));
mockCacheServers.add(CacheServerMockObjects.mockCacheServer("10.12.120.2", null,
"Skullbox", 10000L, mockServerLoadProbe, 250, 50,
16, 5000, 15000, 42424, false, 8192,
false));
when(this.mockCache.getCacheServers()).thenReturn(mockCacheServers);
Health.Builder builder = new Health.Builder();
this.cacheServersHealthIndicator.doHealthCheck(builder);
Health health = builder.build();
assertThat(health).isNotNull();
assertThat(health.getStatus()).isEqualTo(Status.UP);
Map<String, Object> healthDetails = health.getDetails();
assertThat(healthDetails).isNotNull();
assertThat(healthDetails).isNotEmpty();
assertThat(healthDetails).containsEntry("geode.cache.server.count", 2);
assertThat(healthDetails).containsEntry("geode.cache.server.0.bind-address", "10.11.111.1");
assertThat(healthDetails).containsEntry("geode.cache.server.0.hostname-for-clients", "Mailbox");
assertThat(healthDetails).containsEntry("geode.cache.server.0.load-poll-interval", 15000L);
assertThat(healthDetails).containsEntry("geode.cache.server.0.max-connections", 100);
assertThat(healthDetails).containsEntry("geode.cache.server.0.max-message-count", 500);
assertThat(healthDetails).containsEntry("geode.cache.server.0.max-threads", 8);
assertThat(healthDetails).containsEntry("geode.cache.server.0.max-time-between-pings", 20000);
assertThat(healthDetails).containsEntry("geode.cache.server.0.message-time-to-live", 30000);
assertThat(healthDetails).containsEntry("geode.cache.server.0.port", 41414);
assertThat(healthDetails).containsEntry("geode.cache.server.0.running", "Yes");
assertThat(healthDetails).containsEntry("geode.cache.server.0.socket-buffer-size", 16384);
assertThat(healthDetails).containsEntry("geode.cache.server.0.tcp-no-delay", "Yes");
assertThat(healthDetails).doesNotContainKeys("geode.cache.server.0.client-subscription-config",
"geode.cache.server.0.metrics.client-count", "geode.cache.server.0.load.connection-load");
assertThat(healthDetails).containsEntry("geode.cache.server.1.bind-address", "10.12.120.2");
assertThat(healthDetails).containsEntry("geode.cache.server.1.hostname-for-clients", "Skullbox");
assertThat(healthDetails).containsEntry("geode.cache.server.1.load-poll-interval", 10000L);
assertThat(healthDetails).containsEntry("geode.cache.server.1.max-connections", 250);
assertThat(healthDetails).containsEntry("geode.cache.server.1.max-message-count", 50);
assertThat(healthDetails).containsEntry("geode.cache.server.1.max-threads", 16);
assertThat(healthDetails).containsEntry("geode.cache.server.1.max-time-between-pings", 5000);
assertThat(healthDetails).containsEntry("geode.cache.server.1.message-time-to-live", 15000);
assertThat(healthDetails).containsEntry("geode.cache.server.1.port", 42424);
assertThat(healthDetails).containsEntry("geode.cache.server.1.running", "No");
assertThat(healthDetails).containsEntry("geode.cache.server.1.socket-buffer-size", 8192);
assertThat(healthDetails).containsEntry("geode.cache.server.1.tcp-no-delay", "No");
assertThat(healthDetails).doesNotContainKeys("geode.cache.server.1.client-subscription-config",
"geode.cache.server.1.metrics.client-count", "geode.cache.server.1.load.connection-load");
verify(this.mockCache, times(1)).getCacheServers();
}
private void testHealthCheckFailsWhenGemFireCacheIsInvalid(GemFireCache gemfireCache) throws Exception {
GeodeCacheServersHealthIndicator healthIndicator = gemfireCache != null
? new GeodeCacheServersHealthIndicator(gemfireCache)
: new GeodeCacheServersHealthIndicator();
Health.Builder builder = new Health.Builder();
healthIndicator.doHealthCheck(builder);
Health health = builder.build();
assertThat(health).isNotNull();
assertThat(health.getDetails()).isEmpty();
assertThat(health.getStatus()).isEqualTo(Status.UNKNOWN);
}
@Test
public void healthCheckFailsWhenGemFireCacheIsNotPeerCache() throws Exception {
testHealthCheckFailsWhenGemFireCacheIsInvalid(mock(ClientCache.class));
}
@Test
public void healthCheckFailsWhenGemFireCacheIsNotPresent() throws Exception {
testHealthCheckFailsWhenGemFireCacheIsInvalid(null);
}
}

View File

@@ -0,0 +1,175 @@
/*
* Copyright 2017-present 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
*
* https://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.boot.actuate;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.Map;
import org.apache.geode.cache.query.CqQuery;
import org.apache.geode.cache.query.CqServiceStatistics;
import org.apache.geode.cache.query.CqState;
import org.apache.geode.cache.query.CqStatistics;
import org.apache.geode.cache.query.Query;
import org.apache.geode.cache.query.QueryService;
import org.apache.geode.cache.query.QueryStatistics;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.Status;
import org.springframework.data.gemfire.listener.ContinuousQueryListenerContainer;
/**
* Unit tests for {@link GeodeContinuousQueriesHealthIndicator}.
*
* @author John Blum
* @see org.junit.Test
* @see org.mockito.Mock
* @see org.mockito.Mockito
* @see org.mockito.junit.MockitoJUnitRunner
* @see org.apache.geode.cache.query.CqQuery
* @see org.apache.geode.cache.query.Query
* @see org.apache.geode.cache.query.QueryService
* @see org.springframework.boot.actuate.health.Health
* @see org.springframework.boot.actuate.health.HealthIndicator
* @see org.springframework.data.gemfire.listener.ContinuousQueryListenerContainer
* @see org.springframework.geode.boot.actuate.GeodeContinuousQueriesHealthIndicator
* @since 1.0.0
*/
@RunWith(MockitoJUnitRunner.class)
public class GeodeContinuousQueriesHealthIndicatorUnitTests {
private GeodeContinuousQueriesHealthIndicator continuousQueriesHealthIndicator;
@Mock
private QueryService mockQueryService;
@Before
public void setup() {
ContinuousQueryListenerContainer container = new ContinuousQueryListenerContainer();
container.setQueryService(this.mockQueryService);
this.continuousQueriesHealthIndicator = new GeodeContinuousQueriesHealthIndicator(container);
}
@Test
public void healthCheckCapturesDetails() throws Exception {
CqQuery mockContinuousQuery = mock(CqQuery.class, "MockContinuousQuery");
when(mockContinuousQuery.getName()).thenReturn("MockContinuousQuery");
when(mockContinuousQuery.getQueryString()).thenReturn("SELECT * FROM /Example WHERE status = 'RUNNING'");
when(mockContinuousQuery.isClosed()).thenReturn(false);
when(mockContinuousQuery.isDurable()).thenReturn(true);
when(mockContinuousQuery.isRunning()).thenReturn(true);
when(mockContinuousQuery.isStopped()).thenReturn(false);
CqState mockContinuousQueryState = mock(CqState.class);
when(mockContinuousQueryState.isClosing()).thenReturn(false);
when(mockContinuousQuery.getState()).thenReturn(mockContinuousQueryState);
CqStatistics mockContinuousQueryStatistics = mock(CqStatistics.class);
when(mockContinuousQueryStatistics.numDeletes()).thenReturn(1024L);
when(mockContinuousQueryStatistics.numEvents()).thenReturn(4096000L);
when(mockContinuousQueryStatistics.numInserts()).thenReturn(8192L);
when(mockContinuousQueryStatistics.numUpdates()).thenReturn(1638400L);
when(mockContinuousQuery.getStatistics()).thenReturn(mockContinuousQueryStatistics);
Query mockQuery = mock(Query.class);
QueryStatistics mockQueryStatistics = mock(QueryStatistics.class);
when(mockQueryStatistics.getNumExecutions()).thenReturn(1024L);
when(mockQueryStatistics.getTotalExecutionTime()).thenReturn(123456789L);
when(mockQuery.getStatistics()).thenReturn(mockQueryStatistics);
when(mockContinuousQuery.getQuery()).thenReturn(mockQuery);
CqQuery[] mockContinuousQueries = { mockContinuousQuery };
when(this.mockQueryService.getCqs()).thenReturn(mockContinuousQueries);
CqServiceStatistics mockContinuousQueryServiceStatistics = mock(CqServiceStatistics.class);
when(mockContinuousQueryServiceStatistics.numCqsActive()).thenReturn(42L);
when(mockContinuousQueryServiceStatistics.numCqsClosed()).thenReturn(8L);
when(mockContinuousQueryServiceStatistics.numCqsCreated()).thenReturn(51L);
when(mockContinuousQueryServiceStatistics.numCqsStopped()).thenReturn(16L);
when(mockContinuousQueryServiceStatistics.numCqsOnClient()).thenReturn(64L);
when(this.mockQueryService.getCqStatistics()).thenReturn(mockContinuousQueryServiceStatistics);
Health.Builder builder = new Health.Builder();
this.continuousQueriesHealthIndicator.doHealthCheck(builder);
Health health = builder.build();
assertThat(health).isNotNull();
assertThat(health.getStatus()).isEqualTo(Status.UP);
Map<String, Object> healthDetails = health.getDetails();
assertThat(healthDetails).isNotNull();
assertThat(healthDetails).isNotEmpty();
assertThat(healthDetails).containsEntry("geode.continuous-query.count", mockContinuousQueries.length);
assertThat(healthDetails).containsEntry("geode.continuous-query.number-of-active", 42L);
assertThat(healthDetails).containsEntry("geode.continuous-query.number-of-closed", 8L);
assertThat(healthDetails).containsEntry("geode.continuous-query.number-of-created", 51L);
assertThat(healthDetails).containsEntry("geode.continuous-query.number-of-stopped", 16L);
assertThat(healthDetails).containsEntry("geode.continuous-query.number-on-client", 64L);
assertThat(healthDetails).containsEntry("geode.continuous-query.MockContinuousQuery.oql-query-string", "SELECT * FROM /Example WHERE status = 'RUNNING'");
assertThat(healthDetails).containsEntry("geode.continuous-query.MockContinuousQuery.closed", "No");
assertThat(healthDetails).containsEntry("geode.continuous-query.MockContinuousQuery.closing", "No");
assertThat(healthDetails).containsEntry("geode.continuous-query.MockContinuousQuery.durable", "Yes");
assertThat(healthDetails).containsEntry("geode.continuous-query.MockContinuousQuery.running", "Yes");
assertThat(healthDetails).containsEntry("geode.continuous-query.MockContinuousQuery.stopped", "No");
assertThat(healthDetails).containsEntry("geode.continuous-query.MockContinuousQuery.query.number-of-executions", 1024L);
assertThat(healthDetails).containsEntry("geode.continuous-query.MockContinuousQuery.query.total-execution-time", 123456789L);
assertThat(healthDetails).containsEntry("geode.continuous-query.MockContinuousQuery.statistics.number-of-deletes", 1024L);
assertThat(healthDetails).containsEntry("geode.continuous-query.MockContinuousQuery.statistics.number-of-events", 4096000L);
assertThat(healthDetails).containsEntry("geode.continuous-query.MockContinuousQuery.statistics.number-of-inserts", 8192L);
assertThat(healthDetails).containsEntry("geode.continuous-query.MockContinuousQuery.statistics.number-of-updates", 1638400L);
verify(this.mockQueryService, times(1)).getCqs();
}
@Test
public void healthCheckFailsWhenContinuousQueryListenerContainerIsNotPresent() throws Exception {
GeodeContinuousQueriesHealthIndicator healthIndicator = new GeodeContinuousQueriesHealthIndicator();
Health.Builder builder = new Health.Builder();
healthIndicator.doHealthCheck(builder);
Health health = builder.build();
assertThat(health).isNotNull();
assertThat(health.getDetails()).isEmpty();
assertThat(health.getStatus()).isEqualTo(Status.UNKNOWN);
}
}

View File

@@ -0,0 +1,153 @@
/*
* Copyright 2017-present 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
*
* https://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.boot.actuate;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
import org.apache.geode.cache.DiskStore;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.Status;
import org.springframework.context.ApplicationContext;
import org.springframework.data.gemfire.tests.mock.DiskStoreMockObjects;
import org.springframework.data.gemfire.util.ArrayUtils;
/**
* Unit tests for {@link GeodeDiskStoresHealthIndicator}.
*
* @author John Blum
* @see org.junit.Test
* @see org.mockito.Mock
* @see org.mockito.Mockito
* @see org.mockito.junit.MockitoJUnitRunner
* @see org.apache.geode.cache.DiskStore
* @see org.springframework.boot.actuate.health.Health
* @see org.springframework.boot.actuate.health.HealthIndicator
* @see org.springframework.context.ApplicationContext
* @see org.springframework.data.gemfire.tests.mock.DiskStoreMockObjects
* @see org.springframework.geode.boot.actuate.GeodeDiskStoresHealthIndicator
* @since 1.0.0
*/
@RunWith(MockitoJUnitRunner.class)
public class GeodeDiskStoresHealthIndicatorUnitTests {
@Mock
private ApplicationContext mockApplicationContext;
private GeodeDiskStoresHealthIndicator diskStoresHealthIndicator;
@Before
public void setup() {
this.diskStoresHealthIndicator = new GeodeDiskStoresHealthIndicator(this.mockApplicationContext);
}
@Test
public void healthCheckCapturesDetails() throws Exception {
File mockDirectoryOne = mock(File.class);
File mockDirectoryTwo = mock(File.class);
when(mockDirectoryOne.getAbsolutePath()).thenReturn("/ext/gemfire/disk/stores/one");
when(mockDirectoryTwo.getAbsolutePath()).thenReturn("/ext/gemfire/disk/stores/two");
int[] diskDirectorySizes = { 1024, 8192 };
Map<String, DiskStore> mockDiskStores = new HashMap<>();
mockDiskStores.put("MockDiskStoreOne", DiskStoreMockObjects.mockDiskStore("MockDiskStoreOne",
true, true, 90,
ArrayUtils.asArray(mockDirectoryOne, mockDirectoryTwo), diskDirectorySizes, 0.95f,
0.90f, 1024000L, 16384, 5000L, 32768));
mockDiskStores.put("MockDiskStoreTwo", DiskStoreMockObjects.mockDiskStore("MockDiskStoreTwo",
false, true, 50,null, null,
0.90f,0.80f, 2048000L, 4096,
15000L, 8192));
when(this.mockApplicationContext.getBeansOfType(DiskStore.class)).thenReturn(mockDiskStores);
Health.Builder builder = new Health.Builder();
this.diskStoresHealthIndicator.doHealthCheck(builder);
Health health = builder.build();
assertThat(health).isNotNull();
assertThat(health.getStatus()).isEqualTo(Status.UP);
Map<String, Object> healthDetails = health.getDetails();
assertThat(healthDetails).isNotNull();
assertThat(healthDetails).isNotEmpty();
assertThat(healthDetails).containsEntry("geode.disk-store.count", mockDiskStores.size());
assertThat(healthDetails).containsEntry("geode.disk-store.MockDiskStoreOne.allow-force-compaction", "Yes");
assertThat(healthDetails).containsEntry("geode.disk-store.MockDiskStoreOne.auto-compact", "Yes");
assertThat(healthDetails).containsEntry("geode.disk-store.MockDiskStoreOne.compaction-threshold", 90);
assertThat(healthDetails).containsEntry("geode.disk-store.MockDiskStoreOne.disk-directories", "[/ext/gemfire/disk/stores/one, /ext/gemfire/disk/stores/two]");
assertThat(healthDetails).containsEntry("geode.disk-store.MockDiskStoreOne.disk-directory-sizes", "[1024, 8192]");
assertThat(healthDetails).containsEntry("geode.disk-store.MockDiskStoreOne.disk-usage-critical-percentage", 0.95f);
assertThat(healthDetails).containsEntry("geode.disk-store.MockDiskStoreOne.disk-usage-warning-percentage", 0.90f);
assertThat(healthDetails).containsEntry("geode.disk-store.MockDiskStoreOne.max-oplog-size", 1024000L);
assertThat(healthDetails).containsEntry("geode.disk-store.MockDiskStoreOne.queue-size", 16384);
assertThat(healthDetails).containsEntry("geode.disk-store.MockDiskStoreOne.time-interval", 5000L);
assertThat(healthDetails).containsKey("geode.disk-store.MockDiskStoreOne.uuid");
assertThat(healthDetails).containsEntry("geode.disk-store.MockDiskStoreOne.write-buffer-size", 32768);
assertThat(healthDetails).containsEntry("geode.disk-store.MockDiskStoreTwo.allow-force-compaction", "No");
assertThat(healthDetails).containsEntry("geode.disk-store.MockDiskStoreTwo.auto-compact", "Yes");
assertThat(healthDetails).containsEntry("geode.disk-store.MockDiskStoreTwo.compaction-threshold", 50);
assertThat(healthDetails).containsEntry("geode.disk-store.MockDiskStoreTwo.disk-directories", "[]");
assertThat(healthDetails).containsEntry("geode.disk-store.MockDiskStoreTwo.disk-directory-sizes", "[]");
assertThat(healthDetails).containsEntry("geode.disk-store.MockDiskStoreTwo.disk-usage-critical-percentage", 0.90f);
assertThat(healthDetails).containsEntry("geode.disk-store.MockDiskStoreTwo.disk-usage-warning-percentage", 0.80f);
assertThat(healthDetails).containsEntry("geode.disk-store.MockDiskStoreTwo.max-oplog-size", 2048000L);
assertThat(healthDetails).containsEntry("geode.disk-store.MockDiskStoreTwo.queue-size", 4096);
assertThat(healthDetails).containsEntry("geode.disk-store.MockDiskStoreTwo.time-interval", 15000L);
assertThat(healthDetails).containsKey("geode.disk-store.MockDiskStoreOne.uuid");
assertThat(healthDetails).containsEntry("geode.disk-store.MockDiskStoreTwo.write-buffer-size", 8192);
verify(this.mockApplicationContext, times(1)).getBeansOfType(eq(DiskStore.class));
}
@Test
public void healthCheckFailsWhenApplicationContextIsNotPresent() throws Exception {
GeodeDiskStoresHealthIndicator healthIndicator = new GeodeDiskStoresHealthIndicator();
Health.Builder builder = new Health.Builder();
healthIndicator.doHealthCheck(builder);
Health health = builder.build();
assertThat(health).isNotNull();
assertThat(health.getDetails()).isEmpty();
assertThat(health.getStatus()).isEqualTo(Status.UNKNOWN);
}
}

View File

@@ -0,0 +1,153 @@
/*
* Copyright 2017-present 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
*
* https://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.boot.actuate;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.springframework.data.gemfire.util.CollectionUtils.asSet;
import java.util.Comparator;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import org.apache.geode.cache.Cache;
import org.apache.geode.cache.GemFireCache;
import org.apache.geode.cache.client.ClientCache;
import org.apache.geode.cache.wan.GatewayReceiver;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.Status;
import org.springframework.data.gemfire.tests.mock.GatewayMockObjects;
/**
* Unit tests for {@link GeodeGatewayReceiversHealthIndicator}.
*
* @author John Blum
* @see org.junit.Test
* @see org.mockito.Mock
* @see org.mockito.Mockito
* @see org.mockito.junit.MockitoJUnitRunner
* @see org.apache.geode.cache.Cache
* @see org.apache.geode.cache.GemFireCache
* @see org.apache.geode.cache.client.ClientCache
* @see org.apache.geode.cache.wan.GatewayReceiver
* @see org.springframework.boot.actuate.health.Health
* @see org.springframework.boot.actuate.health.HealthIndicator
* @see org.springframework.data.gemfire.tests.mock.GatewayMockObjects
* @see org.springframework.geode.boot.actuate.GeodeGatewayReceiversHealthIndicator
* @since 1.0.0
*/
@RunWith(MockitoJUnitRunner.class)
public class GeodeGatewayReceiversHealthIndicatorUnitTests {
@Mock
private Cache mockCache;
private GeodeGatewayReceiversHealthIndicator gatewayReceiversHealthIndicator;
@Before
public void setup() {
this.gatewayReceiversHealthIndicator = new GeodeGatewayReceiversHealthIndicator(this.mockCache);
}
@Test
public void healthCheckCapturesDetails() throws Exception {
GatewayReceiver mockGatewayReceiverOne = GatewayMockObjects.mockGatewayReceiver("10.101.112.1",
8192, "CardboardBox", "Mailbox", false, 15000,
4096, true, null, 16384, 1024);
GatewayReceiver mockGatewayReceiverTwo = GatewayMockObjects.mockGatewayReceiver("10.101.112.4",
8192, "Skullbox", "PostOfficeBox", true, 5000,
8192, false, null, 65536, 1024);
Set<GatewayReceiver> mockGatewayReceivers =
new TreeSet<>(Comparator.comparing(GatewayReceiver::getBindAddress));
mockGatewayReceivers.addAll(asSet(mockGatewayReceiverOne, mockGatewayReceiverTwo));
when(this.mockCache.getGatewayReceivers()).thenReturn(mockGatewayReceivers);
Health.Builder builder = new Health.Builder();
this.gatewayReceiversHealthIndicator.doHealthCheck(builder);
Health health = builder.build();
assertThat(health).isNotNull();
assertThat(health.getStatus()).isEqualTo(Status.UP);
Map<String, Object> healthDetails = health.getDetails();
assertThat(healthDetails).isNotNull();
assertThat(healthDetails).isNotEmpty();
assertThat(healthDetails).containsEntry("geode.gateway-receiver.count", mockGatewayReceivers.size());
assertThat(healthDetails).containsEntry("geode.gateway-receiver.0.bind-address", "10.101.112.1");
assertThat(healthDetails).containsEntry("geode.gateway-receiver.0.end-port", 8192);
assertThat(healthDetails).containsEntry("geode.gateway-receiver.0.host", "CardboardBox");
assertThat(healthDetails).containsEntry("geode.gateway-receiver.0.max-time-between-pings", 15000);
assertThat(healthDetails).containsEntry("geode.gateway-receiver.0.port", 4096);
assertThat(healthDetails).containsEntry("geode.gateway-receiver.0.running", "Yes");
assertThat(healthDetails).containsEntry("geode.gateway-receiver.0.socket-buffer-size", 16384);
assertThat(healthDetails).containsEntry("geode.gateway-receiver.0.start-port", 1024);
assertThat(healthDetails).containsEntry("geode.gateway-receiver.1.bind-address", "10.101.112.4");
assertThat(healthDetails).containsEntry("geode.gateway-receiver.1.end-port", 8192);
assertThat(healthDetails).containsEntry("geode.gateway-receiver.1.host", "Skullbox");
assertThat(healthDetails).containsEntry("geode.gateway-receiver.1.max-time-between-pings",5000);
assertThat(healthDetails).containsEntry("geode.gateway-receiver.1.port", 8192);
assertThat(healthDetails).containsEntry("geode.gateway-receiver.1.running", "No");
assertThat(healthDetails).containsEntry("geode.gateway-receiver.1.socket-buffer-size", 65536);
assertThat(healthDetails).containsEntry("geode.gateway-receiver.1.start-port", 1024);
verify(this.mockCache, times(1)).getGatewayReceivers();
}
private void testHealthCheckFailsWhenGemFireCacheIsInvalid(GemFireCache gemfireCache) throws Exception {
GeodeGatewayReceiversHealthIndicator healthIndicator = gemfireCache != null
? new GeodeGatewayReceiversHealthIndicator(gemfireCache)
: new GeodeGatewayReceiversHealthIndicator();
Health.Builder builder = new Health.Builder();
healthIndicator.doHealthCheck(builder);
Health health = builder.build();
assertThat(health).isNotNull();
assertThat(health.getDetails()).isEmpty();
assertThat(health.getStatus()).isEqualTo(Status.UNKNOWN);
}
@Test
public void healthCheckFailsWhenGemFireCacheIsNotPeerCache() throws Exception {
testHealthCheckFailsWhenGemFireCacheIsInvalid(mock(ClientCache.class));
}
@Test
public void healthCheckFailsWhenGemFireCacheIsNotPresent() throws Exception {
testHealthCheckFailsWhenGemFireCacheIsInvalid(null);
}
}

View File

@@ -0,0 +1,173 @@
/*
* Copyright 2017-present 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
*
* https://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.boot.actuate;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.springframework.data.gemfire.util.CollectionUtils.asSet;
import java.util.Comparator;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import org.apache.geode.cache.Cache;
import org.apache.geode.cache.GemFireCache;
import org.apache.geode.cache.client.ClientCache;
import org.apache.geode.cache.wan.GatewaySender;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.Status;
import org.springframework.data.gemfire.tests.mock.GatewayMockObjects;
/**
* Unit tests for {@link GeodeGatewaySendersHealthIndicator}.
*
* @author John Blum
* @see org.junit.Test
* @see org.mockito.Mock
* @see org.mockito.Mockito
* @see org.mockito.junit.MockitoJUnitRunner
* @see org.apache.geode.cache.Cache
* @see org.apache.geode.cache.GemFireCache
* @see org.apache.geode.cache.client.ClientCache
* @see org.apache.geode.cache.wan.GatewaySender
* @see org.springframework.boot.actuate.health.Health
* @see org.springframework.boot.actuate.health.HealthIndicator
* @see org.springframework.data.gemfire.tests.mock.GatewayMockObjects
* @see org.springframework.geode.boot.actuate.GeodeGatewaySendersHealthIndicator
* @since 1.0.0
*/
@RunWith(MockitoJUnitRunner.class)
public class GeodeGatewaySendersHealthIndicatorUnitTests {
@Mock
private Cache mockCache;
private GeodeGatewaySendersHealthIndicator gatewaySendersHealthIndicator;
@Before
public void setup() {
this.gatewaySendersHealthIndicator = new GeodeGatewaySendersHealthIndicator(this.mockCache);
}
@Test
public void healthCheckCapturesDetails() throws Exception {
GatewaySender mockGatewaySenderOne = GatewayMockObjects.mockGatewaySender("MockGatewaySenderOne",
100, true, 250, 30000, "TestDiskStore",
true, 8, 16384, 24,
GatewaySender.OrderPolicy.THREAD, true, true, 123,
true, 32768, 15000);
GatewaySender mockGatewaySenderTwo = GatewayMockObjects.mockGatewaySender("MockGatewaySenderTwo",
99, false, 500, 20000, null,
false, 16, 8192, 32,
GatewaySender.OrderPolicy.KEY, false, false, 789,
false, 65536, 20000);
Set<GatewaySender> mockGatewaySenders =
new TreeSet<>(Comparator.comparing(GatewaySender::getId));
mockGatewaySenders.addAll(asSet(mockGatewaySenderOne, mockGatewaySenderTwo));
when(this.mockCache.getGatewaySenders()).thenReturn(mockGatewaySenders);
Health.Builder builder = new Health.Builder();
this.gatewaySendersHealthIndicator.doHealthCheck(builder);
Health health = builder.build();
assertThat(health).isNotNull();
assertThat(health.getStatus()).isEqualTo(Status.UP);
Map<String, Object> healthDetails = health.getDetails();
assertThat(healthDetails).isNotNull();
assertThat(healthDetails).isNotEmpty();
assertThat(healthDetails).containsEntry("geode.gateway-sender.count", mockGatewaySenders.size());
assertThat(healthDetails).containsEntry("geode.gateway-sender.MockGatewaySenderOne.alert-threshold", 100);
assertThat(healthDetails).containsEntry("geode.gateway-sender.MockGatewaySenderOne.batch-conflation-enabled", "Yes");
assertThat(healthDetails).containsEntry("geode.gateway-sender.MockGatewaySenderOne.batch-size", 250);
assertThat(healthDetails).containsEntry("geode.gateway-sender.MockGatewaySenderOne.batch-time-interval", 30000);
assertThat(healthDetails).containsEntry("geode.gateway-sender.MockGatewaySenderOne.disk-store-name", "TestDiskStore");
assertThat(healthDetails).containsEntry("geode.gateway-sender.MockGatewaySenderOne.disk-synchronous", "Yes");
assertThat(healthDetails).containsEntry("geode.gateway-sender.MockGatewaySenderOne.dispatcher-threads", 8);
assertThat(healthDetails).containsEntry("geode.gateway-sender.MockGatewaySenderOne.max-queue-memory", 16384);
assertThat(healthDetails).containsEntry("geode.gateway-sender.MockGatewaySenderOne.max-parallelism-for-replicated-region", 24);
assertThat(healthDetails).containsEntry("geode.gateway-sender.MockGatewaySenderOne.order-policy", GatewaySender.OrderPolicy.THREAD);
assertThat(healthDetails).containsEntry("geode.gateway-sender.MockGatewaySenderOne.parallel", "Yes");
assertThat(healthDetails).containsEntry("geode.gateway-sender.MockGatewaySenderOne.persistent", "Yes");
assertThat(healthDetails).containsEntry("geode.gateway-sender.MockGatewaySenderOne.remote-distributed-system-id", 123);
assertThat(healthDetails).containsEntry("geode.gateway-sender.MockGatewaySenderOne.running", "Yes");
assertThat(healthDetails).containsEntry("geode.gateway-sender.MockGatewaySenderOne.socket-buffer-size", 32768);
assertThat(healthDetails).containsEntry("geode.gateway-sender.MockGatewaySenderOne.socket-read-timeout", 15000);
assertThat(healthDetails).containsEntry("geode.gateway-sender.MockGatewaySenderTwo.alert-threshold", 99);
assertThat(healthDetails).containsEntry("geode.gateway-sender.MockGatewaySenderTwo.batch-conflation-enabled", "No");
assertThat(healthDetails).containsEntry("geode.gateway-sender.MockGatewaySenderTwo.batch-size", 500);
assertThat(healthDetails).containsEntry("geode.gateway-sender.MockGatewaySenderTwo.batch-time-interval", 20000);
assertThat(healthDetails).containsKey("geode.gateway-sender.MockGatewaySenderTwo.disk-store-name");
assertThat(healthDetails).containsEntry("geode.gateway-sender.MockGatewaySenderTwo.disk-synchronous", "No");
assertThat(healthDetails).containsEntry("geode.gateway-sender.MockGatewaySenderTwo.dispatcher-threads", 16);
assertThat(healthDetails).containsEntry("geode.gateway-sender.MockGatewaySenderTwo.max-queue-memory", 8192);
assertThat(healthDetails).containsEntry("geode.gateway-sender.MockGatewaySenderTwo.max-parallelism-for-replicated-region", 32);
assertThat(healthDetails).containsEntry("geode.gateway-sender.MockGatewaySenderTwo.order-policy", GatewaySender.OrderPolicy.KEY);
assertThat(healthDetails).containsEntry("geode.gateway-sender.MockGatewaySenderTwo.parallel", "No");
assertThat(healthDetails).containsEntry("geode.gateway-sender.MockGatewaySenderTwo.persistent", "No");
assertThat(healthDetails).containsEntry("geode.gateway-sender.MockGatewaySenderTwo.remote-distributed-system-id", 789);
assertThat(healthDetails).containsEntry("geode.gateway-sender.MockGatewaySenderTwo.running", "No");
assertThat(healthDetails).containsEntry("geode.gateway-sender.MockGatewaySenderTwo.socket-buffer-size", 65536);
assertThat(healthDetails).containsEntry("geode.gateway-sender.MockGatewaySenderTwo.socket-read-timeout", 20000);
verify(this.mockCache, times(1)).getGatewaySenders();
}
private void testHealthCheckFailsWithInvalidGemFireCache(GemFireCache gemfireCache) throws Exception {
GeodeGatewaySendersHealthIndicator healthIndicator = gemfireCache != null
? new GeodeGatewaySendersHealthIndicator(gemfireCache)
: new GeodeGatewaySendersHealthIndicator();
Health.Builder builder = new Health.Builder();
healthIndicator.doHealthCheck(builder);
Health health = builder.build();
assertThat(health).isNotNull();
assertThat(health.getStatus()).isEqualTo(Status.UNKNOWN);
assertThat(health.getDetails()).isEmpty();
}
@Test
public void healthCheckFailsWhenGemFireCacheIsNotPeerCache() throws Exception {
testHealthCheckFailsWithInvalidGemFireCache(mock(ClientCache.class));
}
@Test
public void healthCheckFailsWhenGemFireCacheIsNotPresent() throws Exception {
testHealthCheckFailsWithInvalidGemFireCache(null);
}
}

View File

@@ -0,0 +1,141 @@
/*
* Copyright 2017-present 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
*
* https://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.boot.actuate;
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 static org.mockito.Mockito.when;
import java.util.Collections;
import java.util.Map;
import org.apache.geode.cache.DataPolicy;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.query.Index;
import org.apache.geode.cache.query.IndexStatistics;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.Status;
import org.springframework.context.ApplicationContext;
import org.springframework.data.gemfire.IndexType;
import org.springframework.data.gemfire.tests.mock.CacheMockObjects;
import org.springframework.data.gemfire.tests.mock.IndexMockObjects;
/**
* Unit tests for {@link GeodeIndexesHealthIndicator}.
*
* @author John Blum
* @see org.junit.Test
* @see org.mockito.Mock
* @see org.mockito.Mockito
* @see org.mockito.junit.MockitoJUnitRunner
* @see org.apache.geode.cache.GemFireCache
* @see org.apache.geode.cache.Region
* @see org.apache.geode.cache.query.Index
* @see org.apache.geode.cache.query.IndexStatistics
* @see org.springframework.boot.actuate.health.Health
* @see org.springframework.boot.actuate.health.HealthIndicator
* @see org.springframework.context.ApplicationContext
* @see org.springframework.data.gemfire.tests.mock.CacheMockObjects
* @see org.springframework.data.gemfire.tests.mock.IndexMockObjects
* @see org.springframework.geode.boot.actuate.GeodeIndexesHealthIndicator
* @since 1.0.0
*/
@RunWith(MockitoJUnitRunner.class)
public class GeodeIndexesHealthIndicatorUnitTests {
@Mock
private ApplicationContext applicationContext;
private GeodeIndexesHealthIndicator indexesHealthIndicator;
@Before
public void setup() {
this.indexesHealthIndicator = new GeodeIndexesHealthIndicator(this.applicationContext);
}
@Test
public void healthCheckCapturesDetails() throws Exception {
Region mockRegion = CacheMockObjects.mockRegion("MockRegion", DataPolicy.PARTITION);
IndexStatistics mockIndexStatistics = IndexMockObjects.mockIndexStatistics(226,
100000, 6000, 1024000L, 51515L,
512, 2048L, 4096L);
Index mockIndex = IndexMockObjects.mockIndex("MockIndex", "/Example",
"id", "one, two", mockRegion, mockIndexStatistics,
IndexType.PRIMARY_KEY.getGemfireIndexType());
Map<String, Index> mockIndexes = Collections.singletonMap("MockIndex", mockIndex);
when(this.applicationContext.getBeansOfType(eq(Index.class))).thenReturn(mockIndexes);
Health.Builder builder = new Health.Builder();
this.indexesHealthIndicator.doHealthCheck(builder);
Health health = builder.build();
assertThat(health).isNotNull();
assertThat(health.getStatus()).isEqualTo(Status.UP);
Map<String, Object> healthDetails = health.getDetails();
assertThat(healthDetails).isNotNull();
assertThat(healthDetails).isNotEmpty();
assertThat(healthDetails).containsEntry("geode.index.count", mockIndexes.size());
assertThat(healthDetails).containsEntry("geode.index.MockIndex.from-clause", "/Example");
assertThat(healthDetails).containsEntry("geode.index.MockIndex.indexed-expression", "id");
assertThat(healthDetails).containsEntry("geode.index.MockIndex.projection-attributes", "one, two");
assertThat(healthDetails).containsEntry("geode.index.MockIndex.region", "/MockRegion");
assertThat(healthDetails).containsEntry("geode.index.MockIndex.type",
IndexType.PRIMARY_KEY.getGemfireIndexType().toString());
assertThat(healthDetails).containsEntry("geode.index.MockIndex.statistics.number-of-bucket-indexes", 226);
assertThat(healthDetails).containsEntry("geode.index.MockIndex.statistics.number-of-keys", 100000L);
assertThat(healthDetails).containsEntry("geode.index.MockIndex.statistics.number-of-map-index-keys", 6000L);
assertThat(healthDetails).containsEntry("geode.index.MockIndex.statistics.number-of-values", 1024000L);
assertThat(healthDetails).containsEntry("geode.index.MockIndex.statistics.number-of-updates", 51515L);
assertThat(healthDetails).containsEntry("geode.index.MockIndex.statistics.read-lock-count", 512);
assertThat(healthDetails).containsEntry("geode.index.MockIndex.statistics.total-update-time", 2048L);
assertThat(healthDetails).containsEntry("geode.index.MockIndex.statistics.total-uses", 4096L);
verify(this.applicationContext, times(1)).getBeansOfType(eq(Index.class));
}
@Test
public void healthCheckFailsWhenApplicationContextContainsIsNotPresent() throws Exception {
GeodeIndexesHealthIndicator healthIndicator = new GeodeIndexesHealthIndicator();
Health.Builder builder = new Health.Builder();
healthIndicator.doHealthCheck(builder);
Health health = builder.build();
assertThat(health).isNotNull();
assertThat(health.getDetails()).isEmpty();
assertThat(health.getStatus()).isEqualTo(Status.UNKNOWN);
}
}

View File

@@ -0,0 +1,184 @@
/*
* Copyright 2017-present 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
*
* https://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.boot.actuate;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.net.InetSocketAddress;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.apache.geode.cache.Cache;
import org.apache.geode.cache.GemFireCache;
import org.apache.geode.cache.client.ClientCache;
import org.apache.geode.cache.client.Pool;
import org.apache.geode.distributed.DistributedSystem;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.Status;
import org.springframework.data.gemfire.tests.mock.PoolMockObjects;
import org.springframework.data.gemfire.util.CacheUtils;
/**
* Unit tests for {@link GeodePoolsHealthIndicator}.
*
* @author John Blum
* @see org.junit.Test
* @see org.mockito.Mock
* @see org.mockito.Mockito
* @see org.mockito.junit.MockitoJUnitRunner
* @see org.apache.geode.cache.Cache
* @see org.apache.geode.cache.GemFireCache
* @see org.apache.geode.cache.client.ClientCache
* @see org.apache.geode.cache.client.Pool
* @see org.apache.geode.distributed.DistributedSystem
* @see org.springframework.boot.actuate.health.Health
* @see org.springframework.boot.actuate.health.HealthIndicator
* @see org.springframework.data.gemfire.tests.mock.PoolMockObjects
* @see org.springframework.geode.boot.actuate.GeodePoolsHealthIndicator
* @since 1.0.0
*/
@RunWith(MockitoJUnitRunner.class)
public class GeodePoolsHealthIndicatorUnitTests {
@Mock
private ClientCache mockClientCache;
private GeodePoolsHealthIndicator poolsHealthIndicator;
@Before
public void setup() {
this.poolsHealthIndicator = spy(new GeodePoolsHealthIndicator(mockDurableClient(this.mockClientCache)));
}
private ClientCache mockDurableClient(ClientCache mockClientCache) {
Properties gemfireProperties = new Properties();
gemfireProperties.setProperty(CacheUtils.DURABLE_CLIENT_ID_PROPERTY_NAME, "test-durable-client");
DistributedSystem mockDistributedSystem = mock(DistributedSystem.class);
when(mockDistributedSystem.isConnected()).thenReturn(true);
when(mockDistributedSystem.getProperties()).thenReturn(gemfireProperties);
when(mockClientCache.getDistributedSystem()).thenReturn(mockDistributedSystem);
return mockClientCache;
}
private InetSocketAddress testSocketAddress(String hostname, int port) {
return new InetSocketAddress(hostname, port);
}
@Test
public void healthCheckCapturesDetails() {
List<InetSocketAddress> mockLocators =
Arrays.asList(testSocketAddress("mailbox", 1234),
testSocketAddress("skullbox", 6789));
Pool mockPool = PoolMockObjects.mockPool("MockPool", false, 5000,
60000L, 1000, mockLocators, 500, 50,
true, mockLocators.subList(0, 1), 75, 15000L,
true, null, 10000, 2, "TestGroup",
Collections.emptyList(), 65536, 30000, 5000,
10000, true, 5000,
2, 8, false);
when(this.poolsHealthIndicator.findAllPools()).thenReturn(Collections.singletonMap("MockPool", mockPool));
Health.Builder builder = new Health.Builder();
this.poolsHealthIndicator.doHealthCheck(builder);
Health health = builder.build();
assertThat(health).isNotNull();
assertThat(health.getStatus()).isEqualTo(Status.UP);
Map<String, Object> healthDetails = health.getDetails();
assertThat(healthDetails).isNotNull();
assertThat(healthDetails).isNotEmpty();
assertThat(healthDetails).containsEntry("geode.pool.count", 1);
assertThat(healthDetails).containsEntry("geode.pool.MockPool.destroyed", "No");
assertThat(healthDetails).containsEntry("geode.pool.MockPool.free-connection-timeout", 5000);
assertThat(healthDetails).containsEntry("geode.pool.MockPool.idle-timeout", 60000L);
assertThat(healthDetails).containsEntry("geode.pool.MockPool.load-conditioning-interval", 1000);
assertThat(healthDetails).containsEntry("geode.pool.MockPool.locators", "mailbox:1234,skullbox:6789");
assertThat(healthDetails).containsEntry("geode.pool.MockPool.max-connections", 500);
assertThat(healthDetails).containsEntry("geode.pool.MockPool.min-connections", 50);
assertThat(healthDetails).containsEntry("geode.pool.MockPool.multi-user-authentication", "Yes");
assertThat(healthDetails).containsEntry("geode.pool.MockPool.online-locators", "mailbox:1234");
assertThat(healthDetails).containsEntry("geode.pool.MockPool.pending-event-count", 75);
assertThat(healthDetails).containsEntry("geode.pool.MockPool.ping-interval", 15000L);
assertThat(healthDetails).containsEntry("geode.pool.MockPool.pr-single-hop-enabled", "Yes");
assertThat(healthDetails).containsEntry("geode.pool.MockPool.read-timeout", 10000);
assertThat(healthDetails).containsEntry("geode.pool.MockPool.retry-attempts", 2);
assertThat(healthDetails).containsEntry("geode.pool.MockPool.server-group", "TestGroup");
assertThat(healthDetails).containsEntry("geode.pool.MockPool.servers", "");
assertThat(healthDetails).containsEntry("geode.pool.MockPool.socket-buffer-size", 65536);
assertThat(healthDetails).containsEntry("geode.pool.MockPool.statistic-interval", 5000);
assertThat(healthDetails).containsEntry("geode.pool.MockPool.subscription-ack-interval", 10000);
assertThat(healthDetails).containsEntry("geode.pool.MockPool.subscription-enabled", "Yes");
assertThat(healthDetails).containsEntry("geode.pool.MockPool.subscription-message-tracking-timeout", 5000);
assertThat(healthDetails).containsEntry("geode.pool.MockPool.subscription-redundancy", 2);
//assertThat(healthDetails).containsEntry("geode.pool.MockPool.thread-local-connections", "No");
verify(this.poolsHealthIndicator, times(1)).findAllPools();
}
public void testHealthCheckFailsWhenGemFireCacheIsInvalid(GemFireCache gemfireCache) {
GeodePoolsHealthIndicator healthIndicator = gemfireCache != null
? new GeodePoolsHealthIndicator(gemfireCache)
: new GeodePoolsHealthIndicator();
Health.Builder builder = new Health.Builder();
healthIndicator.doHealthCheck(builder);
Health health = builder.build();
assertThat(health).isNotNull();
assertThat(health.getDetails()).isEmpty();
assertThat(health.getStatus()).isEqualTo(Status.UNKNOWN);
}
@Test
public void healthCheckFailsWhenGemFireCacheIsNotClientCache() throws Exception {
testHealthCheckFailsWhenGemFireCacheIsInvalid(mock(Cache.class));
}
@Test
public void healthCheckFailsWhenGemFireCacheIsNotPresent() throws Exception {
testHealthCheckFailsWhenGemFireCacheIsInvalid(null);
}
}

View File

@@ -0,0 +1,224 @@
/*
* Copyright 2017-present 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
*
* https://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.boot.actuate;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.springframework.data.gemfire.util.CollectionUtils.asSet;
import java.util.Arrays;
import java.util.Currency;
import java.util.Map;
import java.util.Set;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.apache.geode.cache.CacheStatistics;
import org.apache.geode.cache.DataPolicy;
import org.apache.geode.cache.EvictionAction;
import org.apache.geode.cache.EvictionAlgorithm;
import org.apache.geode.cache.EvictionAttributes;
import org.apache.geode.cache.ExpirationAction;
import org.apache.geode.cache.ExpirationAttributes;
import org.apache.geode.cache.GemFireCache;
import org.apache.geode.cache.PartitionAttributes;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.Scope;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.Status;
import org.springframework.data.gemfire.tests.mock.CacheMockObjects;
/**
* Unit tests for {@link GeodeRegionsHealthIndicator}.
*
* @author John Blum
* @see org.junit.Test
* @see org.mockito.Mock
* @see org.mockito.Mockito
* @see org.mockito.junit.MockitoJUnitRunner
* @see org.apache.geode.cache.CacheStatistics
* @see org.apache.geode.cache.GemFireCache
* @see org.apache.geode.cache.Region
* @see org.springframework.boot.actuate.health.Health
* @see org.springframework.boot.actuate.health.HealthIndicator
* @see org.springframework.data.gemfire.tests.mock.CacheMockObjects
* @see org.springframework.geode.boot.actuate.GeodeRegionsHealthIndicator
* @since 1.0.0
*/
@RunWith(MockitoJUnitRunner.class)
public class GeodeRegionsHealthIndicatorUnitTests {
@Mock
private GemFireCache mockGemFireCache;
private GeodeRegionsHealthIndicator regionsHealthIndicator;
@Before
public void setup() {
this.regionsHealthIndicator = new GeodeRegionsHealthIndicator(this.mockGemFireCache);
}
@Test
@SuppressWarnings({ "rawtypes", "unchecked" })
public void healthCheckCapturesDetails() {
Region<?, ?> mockRegionOne = CacheMockObjects.mockRegion("MockRegionOne", DataPolicy.PARTITION);
when(mockRegionOne.getAttributes().getCloningEnabled()).thenReturn(true);
when(mockRegionOne.getAttributes().getInitialCapacity()).thenReturn(101);
when(mockRegionOne.getAttributes().getLoadFactor()).thenReturn(0.75f);
when(mockRegionOne.getAttributes().getKeyConstraint()).thenReturn((Class) Long.class);
when(mockRegionOne.getAttributes().getOffHeap()).thenReturn(true);
when(mockRegionOne.getAttributes().getPoolName()).thenReturn("");
when(mockRegionOne.getAttributes().getScope()).thenReturn(Scope.DISTRIBUTED_ACK);
when(mockRegionOne.getAttributes().getStatisticsEnabled()).thenReturn(false);
when(mockRegionOne.getAttributes().getValueConstraint()).thenReturn((Class) Currency.class);
PartitionAttributes<?, ?> mockPartitionAttributes = mock(PartitionAttributes.class);
when(mockPartitionAttributes.getColocatedWith()).thenReturn("CollocatedRegion");
when(mockPartitionAttributes.getLocalMaxMemory()).thenReturn(10240);
when(mockPartitionAttributes.getRedundantCopies()).thenReturn(2);
//when(mockPartitionAttributes.getTotalMaxMemory()).thenReturn(4096000L);
when(mockPartitionAttributes.getTotalNumBuckets()).thenReturn(226);
when(mockRegionOne.getAttributes().getPartitionAttributes()).thenReturn(mockPartitionAttributes);
EvictionAttributes mockEvictionAttributes = mock(EvictionAttributes.class);
when(mockEvictionAttributes.getAction()).thenReturn(EvictionAction.LOCAL_DESTROY);
when(mockEvictionAttributes.getAlgorithm()).thenReturn(EvictionAlgorithm.LRU_ENTRY);
when(mockEvictionAttributes.getMaximum()).thenReturn(10000);
when(mockRegionOne.getAttributes().getEvictionAttributes()).thenReturn(mockEvictionAttributes);
Region<?, ?> mockRegionTwo = CacheMockObjects.mockRegion("MockRegionTwo", DataPolicy.EMPTY);
when(mockRegionTwo.getAttributes().getCloningEnabled()).thenReturn(false);
when(mockRegionTwo.getAttributes().getInitialCapacity()).thenReturn(0);
when(mockRegionTwo.getAttributes().getLoadFactor()).thenReturn(0.0f);
when(mockRegionTwo.getAttributes().getKeyConstraint()).thenReturn((Class) Integer.class);
when(mockRegionTwo.getAttributes().getOffHeap()).thenReturn(false);
when(mockRegionTwo.getAttributes().getPoolName()).thenReturn("TestPool");
when(mockRegionTwo.getAttributes().getScope()).thenReturn(Scope.DISTRIBUTED_NO_ACK);
when(mockRegionTwo.getAttributes().getStatisticsEnabled()).thenReturn(true);
when(mockRegionTwo.getAttributes().getValueConstraint()).thenReturn((Class) String.class);
ExpirationAttributes mockIdleTimeoutEntryExpirationAttributes =
mock(ExpirationAttributes.class, "Entry-TTI");
when(mockIdleTimeoutEntryExpirationAttributes.getAction()).thenReturn(ExpirationAction.INVALIDATE);
when(mockIdleTimeoutEntryExpirationAttributes.getTimeout()).thenReturn(600);
when(mockRegionTwo.getAttributes().getEntryIdleTimeout()).thenReturn(mockIdleTimeoutEntryExpirationAttributes);
ExpirationAttributes mockTimeToLiveEntryExpirationAttributes =
mock(ExpirationAttributes.class, "Entry-TTL");
when(mockTimeToLiveEntryExpirationAttributes.getAction()).thenReturn(ExpirationAction.DESTROY);
when(mockTimeToLiveEntryExpirationAttributes.getTimeout()).thenReturn(900);
when(mockRegionTwo.getAttributes().getEntryTimeToLive()).thenReturn(mockTimeToLiveEntryExpirationAttributes);
CacheStatistics mockCacheStatistics = mock(CacheStatistics.class);
when(mockCacheStatistics.getHitCount()).thenReturn(202408L);
when(mockCacheStatistics.getHitRatio()).thenReturn(0.82f);
when(mockCacheStatistics.getLastAccessedTime()).thenReturn(1L);
when(mockCacheStatistics.getLastModifiedTime()).thenReturn(2L);
when(mockCacheStatistics.getMissCount()).thenReturn(767L);
when(mockRegionTwo.getStatistics()).thenReturn(mockCacheStatistics);
Set<Region<?, ?>> mockRegions = asSet(mockRegionOne, mockRegionTwo);
when(this.mockGemFireCache.rootRegions()).thenReturn(mockRegions);
Health.Builder builder = new Health.Builder();
this.regionsHealthIndicator.doHealthCheck(builder);
Health health = builder.build();
assertThat(health).isNotNull();
assertThat(health.getStatus()).isEqualTo(Status.UP);
Map<String, Object> healthDetails = health.getDetails();
assertThat(healthDetails).isNotNull();
assertThat(healthDetails).isNotEmpty();
assertThat(healthDetails).containsEntry("geode.cache.regions", Arrays.asList("/MockRegionOne", "/MockRegionTwo"));
assertThat(healthDetails).containsEntry("geode.cache.regions.count", (long) mockRegions.size());
assertThat(healthDetails).containsEntry("geode.cache.regions.MockRegionOne.cloning-enabled", "Yes");
assertThat(healthDetails).containsEntry("geode.cache.regions.MockRegionOne.data-policy", DataPolicy.PARTITION.toString());
assertThat(healthDetails).containsEntry("geode.cache.regions.MockRegionOne.initial-capacity", 101);
assertThat(healthDetails).containsEntry("geode.cache.regions.MockRegionOne.load-factor", 0.75f);
assertThat(healthDetails).containsEntry("geode.cache.regions.MockRegionOne.key-constraint", Long.class.getName());
assertThat(healthDetails).containsEntry("geode.cache.regions.MockRegionOne.off-heap", "Yes");
assertThat(healthDetails).containsEntry("geode.cache.regions.MockRegionOne.eviction.action", EvictionAction.LOCAL_DESTROY.toString());
assertThat(healthDetails).containsEntry("geode.cache.regions.MockRegionOne.eviction.algorithm", EvictionAlgorithm.LRU_ENTRY.toString());
assertThat(healthDetails).containsEntry("geode.cache.regions.MockRegionOne.eviction.maximum", 10000);
assertThat(healthDetails).containsEntry("geode.cache.regions.MockRegionOne.partition.collocated-with", "CollocatedRegion");
assertThat(healthDetails).containsEntry("geode.cache.regions.MockRegionOne.partition.local-max-memory", 10240);
assertThat(healthDetails).containsEntry("geode.cache.regions.MockRegionOne.partition.redundant-copies", 2);
//assertThat(healthDetails).containsEntry("geode.cache.regions.MockRegionOne.partition.total-max-memory", 4096000L);
assertThat(healthDetails).containsEntry("geode.cache.regions.MockRegionOne.partition.total-number-of-buckets", 226);
assertThat(healthDetails).containsEntry("geode.cache.regions.MockRegionOne.pool-name", "");
assertThat(healthDetails).containsEntry("geode.cache.regions.MockRegionOne.scope", Scope.DISTRIBUTED_ACK.toString());
assertThat(healthDetails).containsEntry("geode.cache.regions.MockRegionOne.statistics-enabled", "No");
assertThat(healthDetails).containsEntry("geode.cache.regions.MockRegionOne.value-constraint", Currency.class.getName());
assertThat(healthDetails).containsEntry("geode.cache.regions.MockRegionTwo.cloning-enabled", "No");
assertThat(healthDetails).containsEntry("geode.cache.regions.MockRegionTwo.data-policy", DataPolicy.EMPTY.toString());
assertThat(healthDetails).containsEntry("geode.cache.regions.MockRegionTwo.initial-capacity", 0);
assertThat(healthDetails).containsEntry("geode.cache.regions.MockRegionTwo.load-factor", 0.0f);
assertThat(healthDetails).containsEntry("geode.cache.regions.MockRegionTwo.key-constraint", Integer.class.getName());
assertThat(healthDetails).containsEntry("geode.cache.regions.MockRegionTwo.off-heap", "No");
assertThat(healthDetails).containsEntry("geode.cache.regions.MockRegionTwo.pool-name", "TestPool");
assertThat(healthDetails).containsEntry("geode.cache.regions.MockRegionTwo.scope", Scope.DISTRIBUTED_NO_ACK.toString());
assertThat(healthDetails).containsEntry("geode.cache.regions.MockRegionTwo.statistics-enabled", "Yes");
assertThat(healthDetails).containsEntry("geode.cache.regions.MockRegionTwo.expiration.entry.tti.action", ExpirationAction.INVALIDATE.toString());
assertThat(healthDetails).containsEntry("geode.cache.regions.MockRegionTwo.expiration.entry.tti.timeout", 600);
assertThat(healthDetails).containsEntry("geode.cache.regions.MockRegionTwo.expiration.entry.ttl.action", ExpirationAction.DESTROY.toString());
assertThat(healthDetails).containsEntry("geode.cache.regions.MockRegionTwo.expiration.entry.ttl.timeout", 900);
assertThat(healthDetails).containsEntry("geode.cache.regions.MockRegionTwo.statistics.hit-count", 202408L);
assertThat(healthDetails).containsEntry("geode.cache.regions.MockRegionTwo.statistics.hit-ratio", 0.82f);
assertThat(healthDetails).containsEntry("geode.cache.regions.MockRegionTwo.statistics.last-accessed-time", 1L);
assertThat(healthDetails).containsEntry("geode.cache.regions.MockRegionTwo.statistics.last-modified-time", 2L);
assertThat(healthDetails).containsEntry("geode.cache.regions.MockRegionTwo.statistics.miss-count", 767L);
assertThat(healthDetails).containsEntry("geode.cache.regions.MockRegionTwo.value-constraint", String.class.getName());
verify(this.mockGemFireCache, times(1)).rootRegions();
}
@Test
public void healthCheckFailsWhenGemFireCacheIsNotPresent() {
GeodeRegionsHealthIndicator healthIndicator = new GeodeRegionsHealthIndicator();
Health.Builder builder = new Health.Builder();
healthIndicator.doHealthCheck(builder);
Health health = builder.build();
assertThat(health).isNotNull();
assertThat(health.getDetails()).isEmpty();
assertThat(health.getStatus()).isEqualTo(Status.UNKNOWN);
}
}

View File

@@ -0,0 +1,3 @@
# This file is generated by the 'io.freefair.lombok' Gradle plugin
config.stopBubbling = true
lombok.addLombokGeneratedAnnotation = true

View File

@@ -0,0 +1,43 @@
plugins {
id "io.freefair.lombok" version "6.3.0"
}
apply plugin: 'io.spring.convention.spring-module'
description = "Spring Boot Auto-Configuration for Apache Geode"
dependencies {
api project(":spring-geode")
implementation "jakarta.annotation:jakarta.annotation-api"
compileOnly "com.google.code.findbugs:jsr305:$findbugsVersion"
optional project(':apache-geode-extensions')
optional "org.springframework.boot:spring-boot-autoconfigure-processor"
optional "org.springframework.boot:spring-boot-configuration-processor"
optional "org.springframework.session:spring-session-data-geode"
// See additional testImplementation dependencies declared in the testDependencies project extension
// defined in the DependencySetPlugin.
testImplementation "jakarta.servlet:jakarta.servlet-api"
testImplementation "org.springframework.boot:spring-boot-starter-test"
testImplementation "org.springframework.boot:spring-boot-starter-web"
testCompileOnly "com.google.code.findbugs:jsr305:$findbugsVersion"
testRuntimeOnly "javax.cache:cache-api"
testRuntimeOnly "org.apache.geode:geode-http-service:$apacheGeodeVersion"
testRuntimeOnly "org.apache.geode:geode-web:$apacheGeodeVersion"
testRuntimeOnly "org.springframework.boot:spring-boot-starter-jetty"
testRuntimeOnly "org.springframework.boot:spring-boot-starter-json"
testRuntimeOnly "org.springframework.shell:spring-shell:$springShellVersion"
// Runtime Test dependency on Spring Cloud Services (SCS) to verify workaround to SCS problem!
//testRuntimeOnly("io.pivotal.spring.cloud:spring-cloud-services-starter-service-registry:2.0.3.RELEASE") {
// exclude group: "org.apache.logging.log4j", module: "log4j-core"
//}
}

View File

@@ -0,0 +1,112 @@
/*
* Copyright 2017-present 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
*
* https://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.boot.autoconfigure;
import java.util.Optional;
import org.apache.geode.cache.Cache;
import org.apache.geode.cache.GemFireCache;
import org.apache.geode.cache.client.ClientCache;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Bean;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.Environment;
import org.springframework.data.gemfire.CacheFactoryBean;
import org.springframework.data.gemfire.config.annotation.ClientCacheConfigurer;
import org.springframework.data.gemfire.config.annotation.PeerCacheConfigurer;
import org.springframework.geode.boot.autoconfigure.condition.ConditionalOnMissingProperty;
import org.springframework.util.StringUtils;
/**
* Spring Boot {@link EnableAutoConfiguration auto-configuration} class used to configure the Apache Geode
* {@link ClientCache} application or peer {@link Cache} member node name (i.e. {@literal gemfire.name})
* with the Spring Boot {@literal spring.application.name} property.
*
* @author John Blum
* @see org.apache.geode.cache.Cache
* @see org.apache.geode.cache.GemFireCache
* @see org.apache.geode.cache.client.ClientCache
* @see org.springframework.boot.SpringBootConfiguration
* @see org.springframework.boot.autoconfigure.EnableAutoConfiguration
* @see org.springframework.context.annotation.Bean
* @see org.springframework.core.env.Environment
* @see org.springframework.data.gemfire.CacheFactoryBean
* @see org.springframework.data.gemfire.config.annotation.ClientCacheConfigurer
* @see org.springframework.data.gemfire.config.annotation.PeerCacheConfigurer
* @since 1.0.0
*/
@SpringBootConfiguration
@ConditionalOnClass({ CacheFactoryBean.class, GemFireCache.class })
@SuppressWarnings("unused")
public class CacheNameAutoConfiguration {
private static final String GEMFIRE_NAME_PROPERTY = "name";
private static final String SPRING_APPLICATION_NAME_PROPERTY = "spring.application.name";
private static final String SPRING_DATA_GEMFIRE_CACHE_NAME_PROPERTY = "spring.data.gemfire.cache.name";
private static final String SPRING_DATA_GEMFIRE_NAME_PROPERTY = "spring.data.gemfire.name";
private static final String SPRING_DATA_GEODE_CACHE_NAME_PROPERTY = "spring.data.geode.cache.name";
private static final String SPRING_DATA_GEODE_NAME_PROPERTY = "spring.data.geode.name";
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE + 1) // apply next (e.g. after @UseMemberName)
@ConditionalOnMissingProperty({
SPRING_DATA_GEMFIRE_CACHE_NAME_PROPERTY,
SPRING_DATA_GEMFIRE_NAME_PROPERTY,
SPRING_DATA_GEODE_CACHE_NAME_PROPERTY,
SPRING_DATA_GEODE_NAME_PROPERTY,
})
ClientCacheConfigurer clientCacheNameConfigurer(Environment environment) {
return (beanName, clientCacheFactoryBean) -> configureCacheName(environment, clientCacheFactoryBean);
}
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE + 1) // apply next (e.g. after @UseMemberName)
@ConditionalOnMissingProperty({
SPRING_DATA_GEMFIRE_CACHE_NAME_PROPERTY,
SPRING_DATA_GEMFIRE_NAME_PROPERTY,
SPRING_DATA_GEODE_CACHE_NAME_PROPERTY,
SPRING_DATA_GEODE_NAME_PROPERTY,
})
PeerCacheConfigurer peerCacheNameConfigurer(Environment environment) {
return (beanName, peerCacheFactoryBean) -> configureCacheName(environment, peerCacheFactoryBean);
}
private void configureCacheName(Environment environment, CacheFactoryBean cacheFactoryBean) {
String springApplicationName = resolveSpringApplicationName(environment);
if (StringUtils.hasText(springApplicationName)) {
setGemFireName(cacheFactoryBean, springApplicationName);
}
}
private String resolveSpringApplicationName(Environment environment) {
return Optional.ofNullable(environment)
.filter(it -> it.containsProperty(SPRING_APPLICATION_NAME_PROPERTY))
.map(it -> it.getProperty(SPRING_APPLICATION_NAME_PROPERTY))
.filter(StringUtils::hasText)
.orElse(null);
}
private void setGemFireName(CacheFactoryBean cacheFactoryBean, String gemfireName) {
cacheFactoryBean.getProperties().setProperty(GEMFIRE_NAME_PROPERTY, gemfireName);
}
}

View File

@@ -0,0 +1,123 @@
/*
* Copyright 2017-present 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
*
* https://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.boot.autoconfigure;
import static org.springframework.data.gemfire.util.CollectionUtils.asSet;
import java.util.Optional;
import java.util.Set;
import jakarta.annotation.PostConstruct;
import org.apache.geode.cache.GemFireCache;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.cache.CacheManagerCustomizers;
import org.springframework.boot.autoconfigure.cache.CacheProperties;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.cache.CacheManager;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.context.annotation.Conditional;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.data.gemfire.cache.GemfireCacheManager;
import org.springframework.data.gemfire.cache.config.EnableGemfireCaching;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* Spring Boot {@link EnableAutoConfiguration auto-configuration} for Spring's Cache Abstraction
* using Apache Geode as the caching provider.
*
* @author John Blum
* @see jakarta.annotation.PostConstruct
* @see org.apache.geode.cache.GemFireCache
* @see org.springframework.boot.SpringBootConfiguration
* @see org.springframework.boot.autoconfigure.EnableAutoConfiguration
* @see org.springframework.boot.autoconfigure.cache.CacheManagerCustomizers
* @see org.springframework.boot.autoconfigure.cache.CacheProperties
* @see org.springframework.cache.CacheManager
* @see org.springframework.data.gemfire.cache.GemfireCacheManager
* @see org.springframework.data.gemfire.cache.config.EnableGemfireCaching
* @see org.springframework.geode.boot.autoconfigure.ClientCacheAutoConfiguration
* @since 1.0.0
*/
@SpringBootConfiguration
@AutoConfigureAfter(ClientCacheAutoConfiguration.class)
@Conditional(CachingProviderAutoConfiguration.SpringCacheTypeCondition.class)
@ConditionalOnBean(GemFireCache.class)
@ConditionalOnClass({ GemfireCacheManager.class, GemFireCache.class })
@ConditionalOnMissingBean(CacheManager.class)
@EnableGemfireCaching
@SuppressWarnings("unused")
public class CachingProviderAutoConfiguration {
protected static final Set<String> SPRING_CACHE_TYPES = asSet("gemfire", "geode");
protected static final String SPRING_CACHE_TYPE_PROPERTY = "spring.cache.type";
private final CacheManagerCustomizers cacheManagerCustomizers;
private final CacheProperties cacheProperties;
@Autowired
private GemfireCacheManager cacheManager;
CachingProviderAutoConfiguration(
@Autowired(required = false) CacheProperties cacheProperties,
@Autowired(required = false) CacheManagerCustomizers cacheManagerCustomizers) {
this.cacheProperties = cacheProperties;
this.cacheManagerCustomizers = cacheManagerCustomizers;
}
GemfireCacheManager getCacheManager() {
Assert.state(this.cacheManager != null, "GemfireCacheManager was not properly configured");
return this.cacheManager;
}
Optional<CacheManagerCustomizers> getCacheManagerCustomizers() {
return Optional.ofNullable(this.cacheManagerCustomizers);
}
Optional<CacheProperties> getCacheProperties() {
return Optional.ofNullable(this.cacheProperties);
}
@PostConstruct
public void onGeodeCachingInitialization() {
getCacheManagerCustomizers()
.ifPresent(cacheManagerCustomizers -> cacheManagerCustomizers.customize(getCacheManager()));
}
public static class SpringCacheTypeCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
String springCacheType = context.getEnvironment().getProperty(SPRING_CACHE_TYPE_PROPERTY);
return !StringUtils.hasText(springCacheType) || SPRING_CACHE_TYPES.contains(springCacheType);
}
}
}

View File

@@ -0,0 +1,49 @@
/*
* Copyright 2017-present 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
*
* https://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.boot.autoconfigure;
import org.apache.geode.cache.GemFireCache;
import org.apache.geode.cache.client.ClientCache;
import org.apache.geode.distributed.Locator;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.data.gemfire.client.ClientCacheFactoryBean;
import org.springframework.data.gemfire.config.annotation.ClientCacheApplication;
/**
* Spring Boot {@link EnableAutoConfiguration auto-configuration} for bootstrapping an Apache Geode {@link ClientCache}
* instance constructed, configured and initialized with Spring Data for Apache Geode.
*
* @author John Blum
* @see org.apache.geode.cache.GemFireCache
* @see org.apache.geode.cache.client.ClientCache
* @see org.apache.geode.distributed.Locator
* @see org.springframework.boot.SpringBootConfiguration
* @see org.springframework.boot.autoconfigure.EnableAutoConfiguration
* @see org.springframework.data.gemfire.client.ClientCacheFactoryBean
* @see org.springframework.data.gemfire.config.annotation.ClientCacheApplication
* @since 1.0.0
*/
@SpringBootConfiguration
@ConditionalOnClass({ ClientCacheFactoryBean.class, ClientCache.class })
@ConditionalOnMissingBean({ GemFireCache.class, Locator.class })
@ClientCacheApplication
public class ClientCacheAutoConfiguration {
}

View File

@@ -0,0 +1,338 @@
/*
* Copyright 2017-present 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
*
* https://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.boot.autoconfigure;
import static org.springframework.data.gemfire.util.RuntimeExceptionFactory.newIllegalStateException;
import java.util.Optional;
import java.util.Properties;
import org.apache.geode.cache.GemFireCache;
import org.apache.geode.cache.client.ClientCache;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.AllNestedConditions;
import org.springframework.boot.autoconfigure.condition.AnyNestedCondition;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnCloudPlatform;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.cloud.CloudPlatform;
import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.context.annotation.Conditional;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.core.env.PropertySource;
import org.springframework.data.gemfire.client.ClientCacheFactoryBean;
import org.springframework.data.gemfire.config.annotation.EnableSecurity;
import org.springframework.data.gemfire.config.annotation.support.AutoConfiguredAuthenticationInitializer;
import org.springframework.geode.core.env.VcapPropertySource;
import org.springframework.geode.core.env.support.CloudCacheService;
import org.springframework.geode.core.env.support.User;
import org.springframework.lang.Nullable;
import org.springframework.util.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Spring Boot {@link EnableAutoConfiguration auto-configuration} enabling Apache Geode's Security functionality,
* and specifically Authentication between a client and server using Spring Data Geode Security annotations.
*
* @author John Blum
* @see java.util.Properties
* @see org.apache.geode.cache.GemFireCache
* @see org.apache.geode.cache.client.ClientCache
* @see org.springframework.boot.SpringApplication
* @see org.springframework.boot.SpringBootConfiguration
* @see org.springframework.boot.autoconfigure.AutoConfigureBefore
* @see org.springframework.boot.autoconfigure.EnableAutoConfiguration
* @see org.springframework.boot.autoconfigure.condition.AllNestedConditions
* @see org.springframework.boot.autoconfigure.condition.AnyNestedCondition
* @see org.springframework.boot.autoconfigure.condition.ConditionalOnClass
* @see org.springframework.boot.autoconfigure.condition.ConditionalOnCloudPlatform
* @see org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
* @see org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
* @see org.springframework.boot.cloud.CloudPlatform
* @see org.springframework.boot.env.EnvironmentPostProcessor
* @see org.springframework.context.annotation.Conditional
* @see org.springframework.core.env.ConfigurableEnvironment
* @see org.springframework.core.env.Environment
* @see org.springframework.core.env.PropertySource
* @see org.springframework.data.gemfire.client.ClientCacheFactoryBean
* @see org.springframework.data.gemfire.config.annotation.EnableSecurity
* @see org.springframework.data.gemfire.config.annotation.support.AutoConfiguredAuthenticationInitializer
* @see org.springframework.geode.boot.autoconfigure.ClientCacheAutoConfiguration
* @see org.springframework.geode.core.env.VcapPropertySource
* @see org.springframework.geode.core.env.support.CloudCacheService
* @see org.springframework.geode.core.env.support.Service
* @see org.springframework.geode.core.env.support.User
* @since 1.0.0
*/
@SpringBootConfiguration
@AutoConfigureBefore(ClientCacheAutoConfiguration.class)
@Conditional(ClientSecurityAutoConfiguration.EnableSecurityCondition.class)
@ConditionalOnClass({ ClientCacheFactoryBean.class, ClientCache.class })
@ConditionalOnMissingBean(GemFireCache.class)
@EnableSecurity
//@Import(HttpBasicAuthenticationSecurityConfiguration.class)
@SuppressWarnings("unused")
public class ClientSecurityAutoConfiguration {
public static final String CLOUD_CACHE_SERVICE_INSTANCE_NAME_PROPERTY =
"spring.boot.data.gemfire.cloud.cloudfoundry.service.cloudcache.name";
public static final String CLOUD_SECURITY_ENVIRONMENT_POST_PROCESSOR_ENABLED_PROPERTY =
"spring.boot.data.gemfire.security.auth.environment.post-processor.enabled";
private static final Logger logger = LoggerFactory.getLogger(ClientSecurityAutoConfiguration.class);
private static final String CLOUD_CACHE_PROPERTY_SOURCE_NAME = "boot.data.gemfire.cloudcache";
private static final String MANAGEMENT_HTTP_HOST_PROPERTY = "spring.data.gemfire.management.http.host";
private static final String MANAGEMENT_HTTP_PORT_PROPERTY = "spring.data.gemfire.management.http.port";
private static final String MANAGEMENT_REQUIRE_HTTPS_PROPERTY = "spring.data.gemfire.management.require-https";
private static final String MANAGEMENT_USE_HTTP_PROPERTY = "spring.data.gemfire.management.use-http";
private static final String POOL_LOCATORS_PROPERTY = "spring.data.gemfire.pool.locators";
private static final String SECURITY_USERNAME_PROPERTY =
AutoConfiguredAuthenticationInitializer.SDG_SECURITY_USERNAME_PROPERTY;
private static final String SECURITY_PASSWORD_PROPERTY =
AutoConfiguredAuthenticationInitializer.SDG_SECURITY_PASSWORD_PROPERTY;
private static final String SSL_USE_DEFAULT_CONTEXT_PROPERTY =
"spring.data.gemfire.security.ssl.use-default-context";
private static final String VCAP_PROPERTY_SOURCE_NAME = "vcap";
public static class AutoConfiguredCloudSecurityEnvironmentPostProcessor implements EnvironmentPostProcessor {
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
Optional.of(environment)
.filter(this::isEnabled)
.filter(this::isCloudFoundryEnvironment)
.ifPresent(this::configureSecurityContext);
}
private boolean isCloudFoundryEnvironment(Environment environment) {
return CloudPlatform.CLOUD_FOUNDRY.isActive(environment);
}
private boolean isEnabled(Environment environment) {
boolean clientSecurityAutoConfigurationEnabled =
environment.getProperty(CLOUD_SECURITY_ENVIRONMENT_POST_PROCESSOR_ENABLED_PROPERTY,
Boolean.class, true);
logger.debug("{} enabled? [{}]", ClientSecurityAutoConfiguration.class.getSimpleName(),
clientSecurityAutoConfigurationEnabled);
return clientSecurityAutoConfigurationEnabled;
}
private boolean isSecurityPropertiesSet(Environment environment) {
boolean securityPropertiesSet = environment.containsProperty(SECURITY_USERNAME_PROPERTY)
&& environment.containsProperty(SECURITY_PASSWORD_PROPERTY);
logger.debug("Security Properties set? [{}]", securityPropertiesSet);
return securityPropertiesSet;
}
private boolean isSecurityPropertiesNotSet(Environment environment) {
return !isSecurityPropertiesSet(environment);
}
private void configureAuthentication(Environment environment, VcapPropertySource vcapPropertySource,
CloudCacheService cloudCacheService, Properties cloudCacheProperties) {
if (isSecurityPropertiesNotSet(environment)) {
if (environment.containsProperty(SECURITY_USERNAME_PROPERTY)) {
String targetUsername = environment.getProperty(SECURITY_USERNAME_PROPERTY);
vcapPropertySource.findUserByName(cloudCacheService, targetUsername)
.flatMap(User::getPassword)
.map(password -> {
cloudCacheProperties.setProperty(SECURITY_USERNAME_PROPERTY, targetUsername);
cloudCacheProperties.setProperty(SECURITY_PASSWORD_PROPERTY, password);
return password;
})
.orElseThrow(() -> newIllegalStateException(
"No User with name [%s] was configured for Cloud Cache service [%s]",
targetUsername, cloudCacheService.getName()));
}
else {
vcapPropertySource.findFirstUserByRoleClusterOperator(cloudCacheService)
.ifPresent(user -> {
cloudCacheProperties.setProperty(SECURITY_USERNAME_PROPERTY, user.getName());
user.getPassword().ifPresent(password ->
cloudCacheProperties.setProperty(SECURITY_PASSWORD_PROPERTY, password));
});
}
}
}
private void configureLocators(Environment environment, VcapPropertySource vcapPropertySource,
CloudCacheService cloudCacheService, Properties cloudCacheProperties) {
cloudCacheService.getLocators().ifPresent(locators ->
cloudCacheProperties.setProperty(POOL_LOCATORS_PROPERTY, locators));
}
private void configureManagementRestApiAccess(Environment environment, VcapPropertySource vcapPropertySource,
CloudCacheService cloudCacheService, Properties cloudCacheProperties) {
cloudCacheService.getGfshUrl().ifPresent(url -> {
cloudCacheProperties.setProperty(MANAGEMENT_HTTP_HOST_PROPERTY, url.getHost());
cloudCacheProperties.setProperty(MANAGEMENT_HTTP_PORT_PROPERTY, String.valueOf(url.getPort()));
cloudCacheProperties.setProperty(MANAGEMENT_REQUIRE_HTTPS_PROPERTY, Boolean.TRUE.toString());
cloudCacheProperties.setProperty(MANAGEMENT_USE_HTTP_PROPERTY, Boolean.TRUE.toString());
});
}
private void configureSsl(Environment environment, VcapPropertySource vcapPropertySource,
CloudCacheService cloudCacheService, Properties cloudCacheProperties) {
if (cloudCacheService.isTlsEnabled()) {
cloudCacheProperties.setProperty(SSL_USE_DEFAULT_CONTEXT_PROPERTY, Boolean.TRUE.toString());
}
}
public void configureSecurityContext(ConfigurableEnvironment environment) {
String cloudcacheServiceInstanceName = environment.getProperty(CLOUD_CACHE_SERVICE_INSTANCE_NAME_PROPERTY);
VcapPropertySource vcapPropertySource = StringUtils.hasText(cloudcacheServiceInstanceName)
? toVcapPropertySource(environment).withVcapServiceName(cloudcacheServiceInstanceName)
: toVcapPropertySource(environment);
vcapPropertySource.findFirstCloudCacheService()
.map(cloudCacheService -> {
Properties cloudCacheProperties = new Properties();
configureAuthentication(environment, vcapPropertySource, cloudCacheService, cloudCacheProperties);
configureLocators(environment, vcapPropertySource, cloudCacheService, cloudCacheProperties);
configureManagementRestApiAccess(environment, vcapPropertySource, cloudCacheService, cloudCacheProperties);
configureSsl(environment, vcapPropertySource, cloudCacheService, cloudCacheProperties);
environment.getPropertySources()
.addLast(newPropertySource(CLOUD_CACHE_PROPERTY_SOURCE_NAME, cloudCacheProperties));
return cloudCacheService;
})
.orElseGet(() -> {
if (StringUtils.hasText(cloudcacheServiceInstanceName)) {
throw newIllegalStateException("No Cloud Cache service instance with name [%s] was found",
cloudcacheServiceInstanceName);
}
else {
logger.warn("No Cloud Cache service instance was found");
}
return null;
});
}
private PropertySource<?> newPropertySource(String name, Properties properties) {
return new SpringDataGemFirePropertiesPropertySource(name, properties);
}
private VcapPropertySource toVcapPropertySource(Environment environment) {
return VcapPropertySource.from(environment);
}
}
static class EnableSecurityCondition extends AllNestedConditions {
EnableSecurityCondition() {
super(ConfigurationPhase.PARSE_CONFIGURATION);
}
@ConditionalOnProperty(name = CLOUD_SECURITY_ENVIRONMENT_POST_PROCESSOR_ENABLED_PROPERTY,
havingValue = "true", matchIfMissing = true)
static class SpringBootDataGemFireSecurityAuthEnvironmentPostProcessorEnabled { }
@Conditional(SecurityTriggersCondition.class)
static class AnySecurityTriggerCondition { }
}
static class SecurityTriggersCondition extends AnyNestedCondition {
SecurityTriggersCondition() {
super(ConfigurationPhase.PARSE_CONFIGURATION);
}
@ConditionalOnCloudPlatform(CloudPlatform.CLOUD_FOUNDRY)
static class CloudPlatformSecurityContextCondition { }
@ConditionalOnProperty({
"spring.data.gemfire.security.username",
"spring.data.gemfire.security.password",
})
static class SpringDataGeodeSecurityContextCondition { }
@ConditionalOnProperty({
"gemfire.security-username",
"gemfire.security-password",
})
static class UsingApacheGeodeSecurityContextCondition { }
}
// This custom PropertySource is required to prevent Pivotal Spring Cloud Services
// (spring-cloud-services-starter-service-registry) from losing the Apache Geode or Cloud Cache Security Context
// credentials stored in the Environment.
static class SpringDataGemFirePropertiesPropertySource extends PropertySource<Properties> {
private static final String SPRING_DATA_GEMFIRE_PROPERTIES_PROPERTY_SOURCE_NAME =
"spring.data.gemfire.properties";
SpringDataGemFirePropertiesPropertySource(Properties springDataGemFireProperties) {
this(SPRING_DATA_GEMFIRE_PROPERTIES_PROPERTY_SOURCE_NAME, springDataGemFireProperties);
}
SpringDataGemFirePropertiesPropertySource(String name, Properties springDataGemFireProperties) {
super(name, springDataGemFireProperties);
}
@Nullable @Override
public Object getProperty(String name) {
return getSource().getProperty(name);
}
@Override
public boolean containsProperty(String name) {
return getSource().containsKey(name);
}
}
}

View File

@@ -0,0 +1,59 @@
/*
* Copyright 2017-present 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
*
* https://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.boot.autoconfigure;
import org.apache.geode.cache.client.ClientCache;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Import;
import org.springframework.data.gemfire.client.ClientCacheFactoryBean;
import org.springframework.data.gemfire.config.annotation.EnableContinuousQueries;
import org.springframework.geode.boot.autoconfigure.support.EnableSubscriptionConfiguration;
import org.springframework.geode.config.annotation.ClusterAvailableConfiguration;
/**
* Spring Boot {@link EnableAutoConfiguration auto-configuration} enabling Apache Geode's Continuous Query (CQ)
* functionality in a {@link ClientCache} application.
*
* @author John Blum
* @see org.apache.geode.cache.client.ClientCache
* @see org.springframework.boot.SpringBootConfiguration
* @see org.springframework.boot.autoconfigure.EnableAutoConfiguration
* @see org.springframework.context.annotation.Bean
* @see org.springframework.context.annotation.Conditional
* @see org.springframework.context.annotation.Import
* @see org.springframework.data.gemfire.client.ClientCacheFactoryBean
* @see org.springframework.data.gemfire.config.annotation.ClientCacheConfigurer
* @see org.springframework.data.gemfire.config.annotation.EnableContinuousQueries
* @see org.springframework.geode.boot.autoconfigure.support.EnableSubscriptionConfiguration
* @see org.springframework.geode.config.annotation.ClusterAvailableConfiguration.AnyClusterAvailableCondition
* @since 1.0.0
*/
@SpringBootConfiguration
@Conditional(ClusterAvailableConfiguration.AnyClusterAvailableCondition.class)
@ConditionalOnBean(ClientCacheFactoryBean.class)
@ConditionalOnMissingBean(name = "continuousQueryBeanPostProcessor",
type = "org.springframework.data.gemfire.listener.ContinuousQueryListenerContainer")
@EnableContinuousQueries
@Import(EnableSubscriptionConfiguration.class)
@SuppressWarnings("unused")
public class ContinuousQueryAutoConfiguration {
}

View File

@@ -0,0 +1,184 @@
/*
* Copyright 2017-present 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
*
* https://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.boot.autoconfigure;
import java.util.Optional;
import java.util.function.Predicate;
import org.apache.geode.cache.GemFireCache;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.AnyNestedCondition;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.context.annotation.Conditional;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.data.gemfire.CacheFactoryBean;
import org.springframework.geode.boot.autoconfigure.support.PdxInstanceWrapperRegionAspect;
import org.springframework.geode.cache.SimpleCacheResolver;
import org.springframework.geode.data.AbstractCacheDataImporterExporter;
import org.springframework.geode.data.CacheDataImporterExporter;
import org.springframework.geode.data.json.JsonCacheDataImporterExporter;
import org.springframework.geode.data.support.LifecycleAwareCacheDataImporterExporter;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
/**
* Spring Boot {@link EnableAutoConfiguration auto-configuration} for cache data import/export.
*
* @author John Blum
* @see org.apache.geode.cache.GemFireCache
* @see org.apache.geode.cache.Region
* @see org.springframework.boot.SpringBootConfiguration
* @see org.springframework.boot.autoconfigure.EnableAutoConfiguration
* @see org.springframework.boot.autoconfigure.condition.AnyNestedCondition
* @see org.springframework.boot.autoconfigure.condition.ConditionalOnBean
* @see org.springframework.boot.autoconfigure.condition.ConditionalOnClass
* @see org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
* @see org.springframework.context.annotation.Bean
* @see org.springframework.context.annotation.Condition
* @see org.springframework.context.annotation.Conditional
* @see org.springframework.core.env.ConfigurableEnvironment
* @see org.springframework.core.env.Environment
* @see org.springframework.data.gemfire.CacheFactoryBean
* @see org.springframework.geode.boot.autoconfigure.support.PdxInstanceWrapperRegionAspect
* @see org.springframework.geode.data.CacheDataImporterExporter
* @see org.springframework.geode.data.json.JsonCacheDataImporterExporter
* @see org.springframework.geode.data.support.LifecycleAwareCacheDataImporterExporter
* @since 1.3.0
*/
@SpringBootConfiguration
@ConditionalOnBean(GemFireCache.class)
@ConditionalOnClass({ CacheFactoryBean.class, GemFireCache.class })
@SuppressWarnings("unused")
public class DataImportExportAutoConfiguration {
protected static final String GEMFIRE_DISABLE_SHUTDOWN_HOOK = "gemfire.disableShutdownHook";
protected static final String PDX_READ_SERIALIZED_PROPERTY = "spring.data.gemfire.pdx.read-serialized";
protected static final String REGION_ADVICE_ENABLED_PROPERTY =
"spring.boot.data.gemfire.cache.region.advice.enabled";
@Bean
CacheDataImporterExporter jsonCacheDataImporterExporter() {
return new LifecycleAwareCacheDataImporterExporter(newCacheDataImporterExporter());
}
protected CacheDataImporterExporter newCacheDataImporterExporter() {
return new JsonCacheDataImporterExporter();
}
@Bean
@Conditional(RegionAdviceConditions.class)
PdxInstanceWrapperRegionAspect pdxInstanceWrapperAspect() {
return new PdxInstanceWrapperRegionAspect();
}
static class RegionAdviceConditions extends AnyNestedCondition {
RegionAdviceConditions() {
super(ConfigurationPhase.REGISTER_BEAN);
}
@ConditionalOnProperty(name = REGION_ADVICE_ENABLED_PROPERTY, havingValue = "true")
static class AdviseRegionOnRegionAdviceEnabledProperty { }
@Conditional(PdxReadSerializedCondition.class)
static class AdviseRegionOnPdxReadSerializedCondition { }
}
static class PdxReadSerializedCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return isPdxReadSerializedEnabled(context.getEnvironment()) || isCachePdxReadSerializedEnabled();
}
private boolean isCachePdxReadSerializedEnabled() {
return SimpleCacheResolver.getInstance().resolve()
.filter(GemFireCache::getPdxReadSerialized)
.isPresent();
}
private boolean isPdxReadSerializedEnabled(@NonNull Environment environment) {
return Optional.ofNullable(environment)
.filter(env -> env.getProperty(PDX_READ_SERIALIZED_PROPERTY, Boolean.class, false))
.isPresent();
}
}
private static final boolean DEFAULT_EXPORT_ENABLED = false;
private static final Predicate<Environment> disableGemFireShutdownHookPredicate = environment ->
Optional.ofNullable(environment)
.filter(env -> env.getProperty(CacheDataImporterExporterReference.EXPORT_ENABLED_PROPERTY_NAME,
Boolean.class, DEFAULT_EXPORT_ENABLED))
.isPresent();
static abstract class AbstractDisableGemFireShutdownHookSupport {
boolean shouldDisableGemFireShutdownHook(@Nullable Environment environment) {
return disableGemFireShutdownHookPredicate.test(environment);
}
/**
* If we do not disable Apache Geode's {@link org.apache.geode.distributed.DistributedSystem} JRE/JVM runtime
* shutdown hook then the {@link org.apache.geode.cache.Region} is prematurely closed by the JRE/JVM shutdown hook
* before Spring's {@link org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor}s can do
* their work of exporting data from the {@link org.apache.geode.cache.Region} as JSON.
*/
void disableGemFireShutdownHook(@Nullable Environment environment) {
System.setProperty(GEMFIRE_DISABLE_SHUTDOWN_HOOK, Boolean.TRUE.toString());
}
}
static abstract class CacheDataImporterExporterReference extends AbstractCacheDataImporterExporter {
static final String EXPORT_ENABLED_PROPERTY_NAME =
AbstractCacheDataImporterExporter.CACHE_DATA_EXPORT_ENABLED_PROPERTY_NAME;
}
static class DisableGemFireShutdownHookCondition extends AbstractDisableGemFireShutdownHookSupport
implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return shouldDisableGemFireShutdownHook(context.getEnvironment());
}
}
public static class DisableGemFireShutdownHookEnvironmentPostProcessor
extends AbstractDisableGemFireShutdownHookSupport implements EnvironmentPostProcessor {
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
if (shouldDisableGemFireShutdownHook(environment)) {
disableGemFireShutdownHook(environment);
}
}
}
}

View File

@@ -0,0 +1,166 @@
/*
* Copyright 2017-present 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
*
* https://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.boot.autoconfigure;
import java.util.Arrays;
import java.util.Properties;
import java.util.Set;
import java.util.stream.Collectors;
import org.apache.geode.cache.GemFireCache;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Bean;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.EnumerablePropertySource;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.data.gemfire.CacheFactoryBean;
import org.springframework.data.gemfire.GemFireProperties;
import org.springframework.data.gemfire.config.annotation.ClientCacheConfigurer;
import org.springframework.data.gemfire.config.annotation.PeerCacheConfigurer;
import org.springframework.data.gemfire.util.ArrayUtils;
import org.springframework.lang.NonNull;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Spring Boot {@link EnableAutoConfiguration auto-configuration} enabling the processing of
* {@literal gemfire.properties}, or Geode {@link Properties} declared in Spring Boot {@literal application.properties}.
*
* @author John Blum
* @see java.util.Properties
* @see org.apache.geode.cache.GemFireCache
* @see org.springframework.boot.SpringBootConfiguration
* @see org.springframework.boot.autoconfigure.EnableAutoConfiguration
* @see org.springframework.context.annotation.Bean
* @see org.springframework.core.Ordered
* @see org.springframework.core.annotation.Order
* @see org.springframework.core.env.ConfigurableEnvironment
* @see org.springframework.core.env.EnumerablePropertySource
* @see org.springframework.core.env.MutablePropertySources
* @see org.springframework.data.gemfire.CacheFactoryBean
* @see org.springframework.data.gemfire.GemFireProperties
* @see org.springframework.data.gemfire.config.annotation.ClientCacheConfigurer
* @see org.springframework.data.gemfire.config.annotation.PeerCacheConfigurer
* @see <a href="https://geode.apache.org/docs/guide/112/reference/topics/gemfire_properties.html">Geode Properties</a>
* @since 1.3.0
*/
@SpringBootConfiguration
@ConditionalOnClass({ GemFireCache.class, CacheFactoryBean.class })
@AutoConfigureBefore({ ClientCacheAutoConfiguration.class })
@SuppressWarnings("unused")
public class EnvironmentSourcedGemFirePropertiesAutoConfiguration {
private static final String GEMFIRE_PROPERTY_PREFIX = GemFireProperties.PROPERTY_NAME_PREFIX;
private final Logger logger = LoggerFactory.getLogger(EnvironmentSourcedGemFirePropertiesAutoConfiguration.class);
@Bean
@Order(Ordered.LOWEST_PRECEDENCE)
@SuppressWarnings("all")
public ClientCacheConfigurer clientCacheGemFirePropertiesConfigurer(ConfigurableEnvironment environment) {
return (beanName, bean) -> configureGemFireProperties(environment, bean);
}
@Bean
@Order(Ordered.LOWEST_PRECEDENCE)
@SuppressWarnings("all")
public PeerCacheConfigurer peerCacheGemFirePropertiesConfigurer(ConfigurableEnvironment environment) {
return (beanName, bean) -> configureGemFireProperties(environment, bean);
}
protected void configureGemFireProperties(@NonNull ConfigurableEnvironment environment,
@NonNull CacheFactoryBean cache) {
Assert.notNull(environment, "Environment must not be null");
Assert.notNull(cache, "CacheFactoryBean must not be null");
MutablePropertySources propertySources = environment.getPropertySources();
if (propertySources != null) {
Set<String> gemfirePropertyNames = propertySources.stream()
.filter(EnumerablePropertySource.class::isInstance)
.map(EnumerablePropertySource.class::cast)
.map(EnumerablePropertySource::getPropertyNames)
.map(propertyNamesArray -> ArrayUtils.nullSafeArray(propertyNamesArray, String.class))
.flatMap(Arrays::stream)
.filter(this::isGemFireDotPrefixedProperty)
.filter(this::isValidGemFireProperty)
.collect(Collectors.toSet());
Properties gemfireProperties = cache.getProperties();
gemfirePropertyNames.stream()
.filter(gemfirePropertyName -> isNotSet(gemfireProperties, gemfirePropertyName))
.filter(this::isValidGemFireProperty)
.forEach(gemfirePropertyName -> {
String propertyName = normalizeGemFirePropertyName(gemfirePropertyName);
String propertyValue = environment.getProperty(gemfirePropertyName);
if (StringUtils.hasText(propertyValue)) {
gemfireProperties.setProperty(propertyName, propertyValue);
}
else {
getLogger().warn("Apache Geode Property [{}] was not set", propertyName);
}
});
cache.setProperties(gemfireProperties);
}
}
protected Logger getLogger() {
return this.logger;
}
private boolean isGemFireDotPrefixedProperty(@NonNull String propertyName) {
return StringUtils.hasText(propertyName) && propertyName.startsWith(GEMFIRE_PROPERTY_PREFIX);
}
private boolean isNotSet(Properties gemfireProperties, String propertyName) {
return !gemfireProperties.containsKey(normalizeGemFirePropertyName(propertyName));
}
private boolean isValidGemFireProperty(String propertyName) {
try {
GemFireProperties.from(normalizeGemFirePropertyName(propertyName));
return true;
}
catch (IllegalArgumentException cause) {
getLogger().warn(String.format("[%s] is not a valid Apache Geode property", propertyName));
// TODO: uncomment line below and replace line above when SBDG is rebased on SDG 2.3.0.RC2 or later.
//getLogger().warn(cause.getMessage());
return false;
}
}
private String normalizeGemFirePropertyName(@NonNull String propertyName) {
int index = propertyName.lastIndexOf(".");
return index > -1 ? propertyName.substring(index + 1) : propertyName;
}
}

View File

@@ -0,0 +1,55 @@
/*
* Copyright 2017-present 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
*
* https://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.boot.autoconfigure;
import org.apache.geode.cache.GemFireCache;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Import;
import org.springframework.data.gemfire.function.config.EnableGemfireFunctions;
import org.springframework.data.gemfire.function.execution.GemfireFunctionOperations;
import org.springframework.geode.function.config.GemFireFunctionExecutionAutoConfigurationRegistrar;
/**
* Spring Boot {@link EnableAutoConfiguration auto-configuration} enabling Apache Geode's Function Execution
* functionality in a {@link GemFireCache} application.
*
* @author John Blum
* @see org.apache.geode.cache.GemFireCache
* @see org.springframework.boot.SpringBootConfiguration
* @see org.springframework.boot.autoconfigure.EnableAutoConfiguration
* @see org.springframework.context.annotation.Import
* @see org.springframework.data.gemfire.function.config.EnableGemfireFunctions
* @see org.springframework.data.gemfire.function.config.EnableGemfireFunctionExecutions
* @see org.springframework.geode.function.config.GemFireFunctionExecutionAutoConfigurationRegistrar
* @see org.springframework.data.gemfire.function.execution.GemfireFunctionOperations
* @see org.springframework.geode.boot.autoconfigure.ClientCacheAutoConfiguration
* @since 1.0.0
*/
@SpringBootConfiguration
@AutoConfigureAfter(ClientCacheAutoConfiguration.class)
@ConditionalOnBean(GemFireCache.class)
@ConditionalOnClass({ GemfireFunctionOperations.class, GemFireCache.class })
@EnableGemfireFunctions
@Import(GemFireFunctionExecutionAutoConfigurationRegistrar.class)
@SuppressWarnings("unused")
public class FunctionExecutionAutoConfiguration {
}

View File

@@ -0,0 +1,53 @@
/*
* Copyright 2017-present 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
*
* https://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.boot.autoconfigure;
import org.apache.geode.cache.GemFireCache;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.core.env.Environment;
import org.springframework.data.gemfire.CacheFactoryBean;
import org.springframework.geode.boot.autoconfigure.configuration.GemFireProperties;
/**
* Spring Boot {@link EnableAutoConfiguration auto-configuration} class used to configure Spring Boot
* {@link ConfigurationProperties} classes and beans from the Spring {@link Environment} containing Apache Geode
* configuration properties.
*
* @author John Blum
* @see org.apache.geode.cache.GemFireCache
* @see org.springframework.boot.SpringBootConfiguration
* @see org.springframework.boot.autoconfigure.EnableAutoConfiguration
* @see org.springframework.boot.context.properties.ConfigurationProperties
* @see org.springframework.boot.context.properties.EnableConfigurationProperties
* @see org.springframework.core.env.Environment
* @see org.springframework.data.gemfire.CacheFactoryBean
* @see org.springframework.geode.boot.autoconfigure.configuration.GemFireProperties
* @since 1.0.0
*/
@SpringBootConfiguration
@ConditionalOnBean(GemFireCache.class)
@ConditionalOnClass({ GemFireCache.class, CacheFactoryBean.class })
@EnableConfigurationProperties({ GemFireProperties.class })
@SuppressWarnings("unused")
public class GemFirePropertiesAutoConfiguration {
}

View File

@@ -0,0 +1,56 @@
/*
* Copyright 2017-present 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
*
* https://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.boot.autoconfigure;
import java.lang.annotation.Annotation;
import org.springframework.boot.autoconfigure.data.AbstractRepositoryConfigurationSourceSupport;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.data.gemfire.repository.config.EnableGemfireRepositories;
import org.springframework.data.gemfire.repository.config.GemfireRepositoryConfigurationExtension;
import org.springframework.data.repository.config.RepositoryConfigurationExtension;
/**
* Spring {@link ImportBeanDefinitionRegistrar} used to auto-configure Spring Data Geode Repositories.
*
* @author John Blum
* @see org.springframework.boot.autoconfigure.data.AbstractRepositoryConfigurationSourceSupport
* @see org.springframework.data.gemfire.repository.config.EnableGemfireRepositories
* @see org.springframework.data.gemfire.repository.config.GemfireRepositoryConfigurationExtension
* @see org.springframework.geode.boot.autoconfigure.RepositoriesAutoConfiguration
* @since 1.0.0
*/
public class GemFireRepositoriesAutoConfigurationRegistrar extends AbstractRepositoryConfigurationSourceSupport {
@Override
protected Class<? extends Annotation> getAnnotation() {
return EnableGemfireRepositories.class;
}
@Override
protected Class<?> getConfiguration() {
return EnableGemFireRepositoriesConfiguration.class;
}
@Override
protected RepositoryConfigurationExtension getRepositoryConfigurationExtension() {
return new GemfireRepositoryConfigurationExtension();
}
@EnableGemfireRepositories
private static class EnableGemFireRepositoriesConfiguration { }
}

View File

@@ -0,0 +1,54 @@
/*
* Copyright 2017-present 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
*
* https://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.boot.autoconfigure;
import org.apache.geode.cache.GemFireCache;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.data.gemfire.CacheFactoryBean;
import org.springframework.data.gemfire.config.annotation.EnableLogging;
/**
* Spring Boot {@link EnableAutoConfiguration auto-Configuration} for Apache Geode logging.
*
* @author John Blum
* @see org.springframework.boot.SpringBootConfiguration
* @see org.springframework.boot.autoconfigure.EnableAutoConfiguration
* @see org.springframework.boot.autoconfigure.condition.ConditionalOnBean
* @see org.springframework.boot.autoconfigure.condition.ConditionalOnClass
* @see org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
* @see org.springframework.data.gemfire.CacheFactoryBean
* @see org.springframework.data.gemfire.config.annotation.EnableLogging
* @since 1.1.0
*/
@SpringBootConfiguration
@ConditionalOnBean(GemFireCache.class)
@ConditionalOnClass(CacheFactoryBean.class)
@ConditionalOnMissingBean(name = {
"org.springframework.data.gemfire.config.annotation.LoggingConfiguration.ClientGemFirePropertiesConfigurer",
"org.springframework.data.gemfire.config.annotation.LoggingConfiguration.LocatorGemFirePropertiesConfigurer",
"org.springframework.data.gemfire.config.annotation.LoggingConfiguration.PeerGemFirePropertiesConfigurer",
})
@EnableLogging
@SuppressWarnings("unused")
// TODO Find a more reliable way to refer to the LoggingConfiguration Configurer beans defined above other than by name!
public class LoggingAutoConfiguration {
}

View File

@@ -0,0 +1,50 @@
/*
* Copyright 2017-present 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
*
* https://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.boot.autoconfigure;
import org.apache.geode.cache.Cache;
import org.apache.geode.cache.GemFireCache;
import org.apache.geode.cache.client.ClientCache;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.data.gemfire.config.annotation.EnablePdx;
/**
* Spring Boot {@link EnableAutoConfiguration auto-configuration} enabling Apache Geode's PDX Serialization
* functionality in a either a {@link Cache peer cache} or {@link ClientCache} application.
*
* @author John Blum
* @see org.apache.geode.cache.Cache
* @see org.apache.geode.cache.GemFireCache
* @see org.apache.geode.cache.client.ClientCache
* @see org.springframework.boot.SpringBootConfiguration
* @see org.springframework.boot.autoconfigure.EnableAutoConfiguration
* @see org.springframework.data.gemfire.config.annotation.EnablePdx
* @since 1.0.0
*/
@SpringBootConfiguration
@ConditionalOnBean(GemFireCache.class)
@ConditionalOnMissingBean(
name = { "clientCachePdxConfigurer", "peerCachePdxConfigurer" },
type = "org.springframework.data.gemfire.config.support.PdxDiskStoreAwareBeanFactoryPostProcessor"
)
@EnablePdx
public class PdxSerializationAutoConfiguration {
}

View File

@@ -0,0 +1,55 @@
/*
* Copyright 2017-present 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
*
* https://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.boot.autoconfigure;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.data.gemfire.client.ClientCacheFactoryBean;
import org.springframework.data.gemfire.config.annotation.ApacheShiroSecurityConfiguration;
import org.springframework.data.gemfire.config.annotation.GeodeIntegratedSecurityConfiguration;
import org.springframework.geode.config.annotation.EnableSecurityManager;
/**
* Spring Boot {@link EnableAutoConfiguration auto-configuration} enabling Apache Geode's Security functionality,
* and specifically Authentication between a client and server using Spring Data Geode Security annotations.
*
* @author John Blum
* @see org.apache.geode.security.SecurityManager
* @see org.springframework.boot.SpringBootConfiguration
* @see org.springframework.boot.autoconfigure.EnableAutoConfiguration
* @see org.springframework.context.annotation.Import
* @see org.springframework.data.gemfire.CacheFactoryBean
* @see org.springframework.data.gemfire.client.ClientCacheFactoryBean
* @see org.springframework.data.gemfire.config.annotation.ApacheShiroSecurityConfiguration
* @see org.springframework.data.gemfire.config.annotation.GeodeIntegratedSecurityConfiguration
* @see org.springframework.geode.config.annotation.EnableSecurityManager
* @since 1.0.0
*/
@SpringBootConfiguration
@ConditionalOnBean(org.apache.geode.security.SecurityManager.class)
@ConditionalOnMissingBean({
ClientCacheFactoryBean.class,
ApacheShiroSecurityConfiguration.class,
GeodeIntegratedSecurityConfiguration.class
})
@EnableSecurityManager
//@Import(HttpBasicAuthenticationSecurityConfiguration.class)
@SuppressWarnings("unused")
public class PeerSecurityAutoConfiguration {
}

View File

@@ -0,0 +1,331 @@
/*
* Copyright 2017-present 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
*
* https://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.boot.autoconfigure;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.Predicate;
import org.apache.geode.cache.GemFireCache;
import org.apache.geode.cache.Region;
import org.springframework.beans.BeansException;
import org.springframework.beans.PropertyValue;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.beans.factory.config.ConstructorArgumentValues;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.EventListener;
import org.springframework.core.type.StandardMethodMetadata;
import org.springframework.data.gemfire.GemfireOperations;
import org.springframework.data.gemfire.GemfireTemplate;
import org.springframework.data.gemfire.GemfireUtils;
import org.springframework.data.gemfire.ResolvableRegionFactoryBean;
import org.springframework.data.gemfire.config.xml.GemfireConstants;
import org.springframework.data.gemfire.util.ArrayUtils;
import org.springframework.data.gemfire.util.CollectionUtils;
import org.springframework.data.gemfire.util.SpringUtils;
import org.springframework.geode.config.annotation.support.TypelessAnnotationConfigSupport;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import org.springframework.util.StringUtils;
/**
* Spring Boot {@link EnableAutoConfiguration auto-configuration} class used to configure a {@link GemfireTemplate}
* for each Apache Geode cache {@link Region} declared/defined in the Spring {@link ConfigurableApplicationContext}
* in order to perform {@link Region} data access operations.
*
* @author John Blum
* @see org.apache.geode.cache.GemFireCache
* @see org.apache.geode.cache.Region
* @see org.springframework.beans.factory.BeanFactory
* @see org.springframework.beans.factory.config.BeanDefinition
* @see org.springframework.beans.factory.config.BeanFactoryPostProcessor
* @see org.springframework.beans.factory.config.BeanPostProcessor
* @see org.springframework.beans.factory.config.ConfigurableBeanFactory
* @see org.springframework.beans.factory.support.BeanDefinitionBuilder
* @see org.springframework.beans.factory.support.BeanDefinitionRegistry
* @see org.springframework.boot.SpringBootConfiguration
* @see org.springframework.boot.autoconfigure.AutoConfigureAfter
* @see org.springframework.boot.autoconfigure.condition.ConditionalOnBean
* @see org.springframework.boot.autoconfigure.condition.ConditionalOnClass
* @see org.springframework.context.ApplicationContext
* @see org.springframework.context.ConfigurableApplicationContext
* @see org.springframework.context.annotation.Bean
* @see org.springframework.context.event.EventListener
* @see org.springframework.data.gemfire.GemfireTemplate
* @see org.springframework.data.gemfire.ResolvableRegionFactoryBean
* @see org.springframework.geode.config.annotation.support.TypelessAnnotationConfigSupport
* @since 1.0.0
*/
@SpringBootConfiguration
@AutoConfigureAfter(ClientCacheAutoConfiguration.class)
@ConditionalOnBean(GemFireCache.class)
@ConditionalOnClass(GemfireTemplate.class)
@SuppressWarnings("unused")
public class RegionTemplateAutoConfiguration extends TypelessAnnotationConfigSupport {
private static final Object NON_BEAN = new Object();
private static final String TEMPLATE = "Template";
private final Set<String> autoConfiguredRegionTemplateBeanNames = Collections.synchronizedSet(new HashSet<>());
private final Set<String> regionNamesWithTemplates = Collections.synchronizedSet(new HashSet<>());
@Bean
BeanFactoryPostProcessor regionTemplateBeanFactoryPostProcessor() {
return beanFactory -> {
if (beanFactory instanceof BeanDefinitionRegistry) {
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
List<String> beanDefinitionNames =
Arrays.asList(ArrayUtils.nullSafeArray(registry.getBeanDefinitionNames(), String.class));
Set<String> userRegionTemplateNames = new HashSet<>();
for (String beanName : beanDefinitionNames) {
String regionTemplateBeanName = toRegionTemplateBeanName(beanName);
if (!beanDefinitionNames.contains(regionTemplateBeanName)) {
BeanDefinition beanDefinition = registry.getBeanDefinition(beanName);
Class<?> resolvedBeanType = resolveBeanClass(beanDefinition, registry).orElse(null);
if (isRegionBeanDefinition(resolvedBeanType)) {
register(newGemfireTemplateBeanDefinition(beanName), regionTemplateBeanName, registry);
}
else if (isGemfireTemplateBeanDefinition(resolvedBeanType)) {
userRegionTemplateNames.add(beanName);
}
else if (isBeanWithGemfireTemplateDependency(beanFactory, beanDefinition)) {
SpringUtils.addDependsOn(beanDefinition, GemfireConstants.DEFAULT_GEMFIRE_CACHE_NAME);
}
}
}
setAutoConfiguredRegionTemplateDependencies(registry, userRegionTemplateNames);
}
};
}
private boolean isBeanWithGemfireTemplateDependency(@NonNull BeanFactory beanFactory,
@NonNull BeanDefinition beanDefinition) {
Predicate<Object> isGemfireTemplate = value -> value instanceof RuntimeBeanReference
? beanFactory.isTypeMatch(((RuntimeBeanReference) value).getBeanName(), GemfireOperations.class)
: value instanceof GemfireOperations;
boolean match = beanDefinition.getConstructorArgumentValues().getGenericArgumentValues().stream()
.map(ConstructorArgumentValues.ValueHolder::getValue)
.anyMatch(isGemfireTemplate);
match |= match || beanDefinition.getPropertyValues().getPropertyValueList().stream()
.map(PropertyValue::getValue)
.anyMatch(isGemfireTemplate);
match |= match || Optional.of(beanDefinition)
.filter(AnnotatedBeanDefinition.class::isInstance)
.map(AnnotatedBeanDefinition.class::cast)
.map(AnnotatedBeanDefinition::getFactoryMethodMetadata)
.filter(StandardMethodMetadata.class::isInstance)
.map(StandardMethodMetadata.class::cast)
.map(StandardMethodMetadata::getIntrospectedMethod)
.map(method -> Arrays.stream(ArrayUtils.nullSafeArray(method.getParameterTypes(), Class.class))
.filter(Objects::nonNull)
.anyMatch(GemfireOperations.class::isAssignableFrom)
).orElse(false);
return match;
}
private boolean isGemfireTemplateBeanDefinition(@Nullable Class<?> beanType) {
return beanType != null && GemfireOperations.class.isAssignableFrom(beanType);
}
private boolean isRegionBeanDefinition(@Nullable Class<?> beanType) {
return beanType != null && ResolvableRegionFactoryBean.class.isAssignableFrom(beanType);
}
private BeanDefinition newGemfireTemplateBeanDefinition(String regionBeanName) {
BeanDefinitionBuilder builder =
BeanDefinitionBuilder.genericBeanDefinition(GemfireTemplate.class);
builder.addConstructorArgReference(regionBeanName);
return builder.getBeanDefinition();
}
// Register BeanDefinition with bean name in BeanDefinitionRegistry
private boolean register(BeanDefinition beanDefinition, String beanName, BeanDefinitionRegistry registry) {
if (this.autoConfiguredRegionTemplateBeanNames.add(beanName)) {
registry.registerBeanDefinition(beanName, beanDefinition);
return true;
}
return false;
}
private void setAutoConfiguredRegionTemplateDependencies(BeanDefinitionRegistry registry,
Set<String> dependencyBeanNames) {
String[] dependencyBeanNamesArray = dependencyBeanNames.toArray(new String[0]);
this.autoConfiguredRegionTemplateBeanNames.stream()
.map(registry::getBeanDefinition)
.forEach(beanDefinition -> SpringUtils.addDependsOn(beanDefinition, dependencyBeanNamesArray));
}
// Required by @EnableClusterDefinedRegions & Native-Defined Regions (e.g. Regions defined in "cache.xml").
@Bean
BeanPostProcessor regionTemplateBeanPostProcessor(ConfigurableApplicationContext applicationContext) {
handlePrematureCacheCreation(applicationContext);
return new BeanPostProcessor() {
/**
* User-defined {@link GemfireTemplate} beans should be post processed before
* auto-configured {@link GemfireTemplate} beans!
*
* @see RegionTemplateAutoConfiguration#setAutoConfiguredRegionTemplateDependencies(BeanDefinitionRegistry, Set)
*/
@Nullable @Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof GemfireTemplate) {
if (autoConfiguredRegionTemplateBeanNames.contains(beanName)) {
if (regionNamesWithTemplates.contains(((GemfireTemplate) bean).getRegion().getName())) {
// Returning NO_BEAN means an existing, user-defined GemfireTemplate bean already exists
// for the target Region and the auto-configured GemfireTemplate bean is not required.
bean = NON_BEAN;
}
}
else {
regionNamesWithTemplates.add(((GemfireTemplate) bean).getRegion().getName());
}
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof GemFireCache) {
GemFireCache cache = (GemFireCache) bean;
registerRegionTemplatesForCacheRegions(applicationContext, cache);
}
return bean;
}
};
}
// TODO: Remove this logic when DATAGEODE-231 is resolved!
private void handlePrematureCacheCreation(ConfigurableApplicationContext applicationContext) {
Optional.ofNullable(GemfireUtils.resolveGemFireCache())
.ifPresent(cache -> registerRegionTemplatesForCacheRegions(applicationContext, cache));
}
// Required by @EnableCachingDefinedRegions
@EventListener({ ContextRefreshedEvent.class })
public void regionTemplateContextRefreshedEventListener(ContextRefreshedEvent event) {
this.regionNamesWithTemplates.clear();
ApplicationContext applicationContext = event.getApplicationContext();
if (applicationContext instanceof ConfigurableApplicationContext) {
ConfigurableApplicationContext configurableApplicationContext =
(ConfigurableApplicationContext) applicationContext;
GemFireCache cache = configurableApplicationContext.getBean(GemFireCache.class);
registerRegionTemplatesForCacheRegions(configurableApplicationContext, cache);
}
}
private void registerRegionTemplatesForCacheRegions(@NonNull ConfigurableApplicationContext applicationContext,
@NonNull GemFireCache cache) {
for (Region<?, ?> region : CollectionUtils.nullSafeSet(cache.rootRegions())) {
String regionTemplateBeanName = toRegionTemplateBeanName(region.getName());
registerRegionTemplateBean(applicationContext, region, regionTemplateBeanName);
}
}
private void registerRegionTemplateBean(@NonNull ConfigurableApplicationContext applicationContext,
@NonNull Region<?, ?> region, String regionTemplateBeanName) {
Optional.of(applicationContext)
.filter(it -> isNotBean(it, regionTemplateBeanName))
.map(ConfigurableApplicationContext::getBeanFactory)
.ifPresent(beanFactory -> register(newGemfireTemplate(region), regionTemplateBeanName, beanFactory));
}
private boolean isNotBean(@NonNull ApplicationContext applicationContext, @Nullable String beanName) {
return !(StringUtils.hasText(beanName) && applicationContext.containsBean(beanName));
}
private GemfireTemplate newGemfireTemplate(@NonNull Region<?, ?> region) {
return new GemfireTemplate(region);
}
// Register Singleton Object with bean name in BeanDefinitionRegistry
private void register(Object singletonObject, String beanName, ConfigurableBeanFactory beanFactory) {
if (this.autoConfiguredRegionTemplateBeanNames.add(beanName)) {
beanFactory.registerSingleton(beanName, singletonObject);
}
}
private String toRegionTemplateBeanName(@NonNull String regionName) {
return StringUtils.uncapitalize(regionName) + TEMPLATE;
}
}

View File

@@ -0,0 +1,70 @@
/*
* Copyright 2017-present 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
*
* https://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.boot.autoconfigure;
import org.apache.geode.cache.Cache;
import org.apache.geode.cache.GemFireCache;
import org.apache.geode.cache.client.ClientCache;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Import;
import org.springframework.data.gemfire.repository.GemfireRepository;
import org.springframework.data.gemfire.repository.config.EnableGemfireRepositories;
import org.springframework.data.gemfire.repository.config.GemfireRepositoryConfigurationExtension;
import org.springframework.data.gemfire.repository.support.GemfireRepositoryFactoryBean;
/**
* Spring Boot {@link EnableAutoConfiguration auto-configuration} for Spring Data for Apache Geode (SDG) Repositories.
*
* Activates when there is a bean of type {@link Cache} or {@link ClientCache} configured in the Spring context,
* the Spring Data Geode {@link GemfireRepository} type is on the classpath, and no other existing
* {@link GemfireRepository GemfireRepositories} are configured.
*
* Once in effect, the auto-configuration is the equivalent of enabling Geode Repositories using the
* {@link EnableGemfireRepositories} annotation.
*
* @author John Blum
* @see org.apache.geode.cache.Cache
* @see org.apache.geode.cache.GemFireCache
* @see org.apache.geode.cache.client.ClientCache
* @see org.springframework.boot.SpringBootConfiguration
* @see org.springframework.boot.autoconfigure.EnableAutoConfiguration
* @see org.springframework.context.annotation.Import
* @see org.springframework.data.gemfire.repository.GemfireRepository
* @see org.springframework.data.gemfire.repository.config.EnableGemfireRepositories
* @see org.springframework.data.gemfire.repository.config.GemfireRepositoryConfigurationExtension
* @see org.springframework.data.gemfire.repository.support.GemfireRepositoryFactoryBean
* @see org.springframework.geode.boot.autoconfigure.ClientCacheAutoConfiguration
* @see org.springframework.geode.boot.autoconfigure.GemFireRepositoriesAutoConfigurationRegistrar
* @since 1.0.0
*/
@SpringBootConfiguration
@AutoConfigureAfter(ClientCacheAutoConfiguration.class)
@ConditionalOnBean(GemFireCache.class)
@ConditionalOnClass(GemfireRepository.class)
@ConditionalOnMissingBean({ GemfireRepositoryConfigurationExtension.class, GemfireRepositoryFactoryBean.class })
@ConditionalOnProperty(prefix = "spring.data.gemfire.repositories", name = "enabled", havingValue = "true",
matchIfMissing = true)
@Import(GemFireRepositoriesAutoConfigurationRegistrar.class)
public class RepositoriesAutoConfiguration {
}

View File

@@ -0,0 +1,152 @@
/*
* Copyright 2017-present 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
*
* https://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.boot.autoconfigure;
import static org.springframework.data.gemfire.util.CollectionUtils.asSet;
import java.util.Properties;
import java.util.Set;
import org.apache.geode.cache.GemFireCache;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Import;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.PropertiesPropertySource;
import org.springframework.core.env.PropertySource;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.geode.boot.autoconfigure.support.EnableSubscriptionConfiguration;
import org.springframework.session.Session;
import org.springframework.session.data.gemfire.config.annotation.web.http.EnableGemFireHttpSession;
import org.springframework.session.data.gemfire.config.annotation.web.http.GemFireHttpSessionConfiguration;
import org.springframework.session.web.http.SessionRepositoryFilter;
import org.springframework.util.StringUtils;
/**
* Spring Boot {@link EnableAutoConfiguration auto-configuration} for configuring Apache Geode
* as an (HTTP) {@link Session} state management provider in Spring Session.
*
* @author John Blum
* @see java.util.Properties
* @see org.apache.geode.cache.GemFireCache
* @see org.springframework.boot.SpringApplication
* @see org.springframework.boot.SpringBootConfiguration
* @see org.springframework.boot.autoconfigure.AutoConfigureAfter
* @see org.springframework.boot.autoconfigure.EnableAutoConfiguration
* @see org.springframework.boot.autoconfigure.condition.ConditionalOnBean
* @see org.springframework.boot.autoconfigure.condition.ConditionalOnClass
* @see org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
* @see org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication
* @see org.springframework.context.annotation.Condition
* @see org.springframework.context.annotation.ConditionContext
* @see org.springframework.context.annotation.Conditional
* @see org.springframework.context.annotation.Import
* @see org.springframework.core.env.ConfigurableEnvironment
* @see org.springframework.core.env.PropertiesPropertySource
* @see org.springframework.core.env.PropertySource
* @see org.springframework.core.type.AnnotatedTypeMetadata
* @see org.springframework.geode.boot.autoconfigure.support.EnableSubscriptionConfiguration
* @see org.springframework.session.Session
* @see org.springframework.session.data.gemfire.config.annotation.web.http.EnableGemFireHttpSession
* @see org.springframework.session.data.gemfire.config.annotation.web.http.GemFireHttpSessionConfiguration
* @see org.springframework.session.web.http.SessionRepositoryFilter
* @since 1.0.0
*/
@SpringBootConfiguration
@AutoConfigureAfter(ClientCacheAutoConfiguration.class)
@Conditional(SpringSessionAutoConfiguration.SpringSessionStoreTypeCondition.class)
@ConditionalOnBean(GemFireCache.class)
@ConditionalOnClass({ GemFireCache.class, GemFireHttpSessionConfiguration.class })
@ConditionalOnMissingBean(SessionRepositoryFilter.class)
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@EnableGemFireHttpSession(poolName = "DEFAULT")
@Import(EnableSubscriptionConfiguration.class)
@SuppressWarnings("unused")
public class SpringSessionAutoConfiguration {
protected static final Set<String> SPRING_SESSION_STORE_TYPES = asSet("gemfire", "geode");
protected static final String SERVER_SERVLET_SESSION_TIMEOUT_PROPERTY = "server.servlet.session.timeout";
protected static final String SPRING_SESSION_DATA_GEMFIRE_SESSION_EXPIRATION_TIMEOUT =
"spring.session.data.gemfire.session.expiration.max-inactive-interval-seconds";
protected static final String SPRING_SESSION_PROPERTY_SOURCE_NAME = "SpringSessionProperties";
protected static final String SPRING_SESSION_STORE_TYPE_PROPERTY = "spring.session.store-type";
protected static final String SPRING_SESSION_TIMEOUT_PROPERTY = "spring.session.timeout";
public static class SpringSessionPropertiesEnvironmentPostProcessor implements EnvironmentPostProcessor {
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
if (isNotSet(environment, SPRING_SESSION_DATA_GEMFIRE_SESSION_EXPIRATION_TIMEOUT)) {
Properties springSessionProperties = new Properties();
if (isSet(environment, SPRING_SESSION_TIMEOUT_PROPERTY)) {
springSessionProperties.setProperty(SPRING_SESSION_DATA_GEMFIRE_SESSION_EXPIRATION_TIMEOUT,
environment.getProperty(SPRING_SESSION_TIMEOUT_PROPERTY));
}
else if (isSet(environment, SERVER_SERVLET_SESSION_TIMEOUT_PROPERTY)) {
springSessionProperties.setProperty(SPRING_SESSION_DATA_GEMFIRE_SESSION_EXPIRATION_TIMEOUT,
environment.getProperty(SERVER_SERVLET_SESSION_TIMEOUT_PROPERTY));
}
if (!springSessionProperties.isEmpty()) {
environment.getPropertySources()
.addFirst(newPropertySource(SPRING_SESSION_PROPERTY_SOURCE_NAME, springSessionProperties));
}
}
}
private PropertySource<?> newPropertySource(String name, Properties properties) {
return new PropertiesPropertySource(name, properties);
}
}
protected static boolean isNotSet(ConfigurableEnvironment environment, String propertyName) {
return !isSet(environment, propertyName);
}
protected static boolean isSet(ConfigurableEnvironment environment, String propertyName) {
return environment.containsProperty(propertyName)
&& StringUtils.hasText(environment.getProperty(propertyName));
}
protected static class SpringSessionStoreTypeCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
String springSessionStoreTypeValue =
context.getEnvironment().getProperty(SPRING_SESSION_STORE_TYPE_PROPERTY);
return !StringUtils.hasText(springSessionStoreTypeValue)
|| SPRING_SESSION_STORE_TYPES.contains(springSessionStoreTypeValue.trim().toLowerCase());
}
}
}

View File

@@ -0,0 +1,55 @@
/*
* Copyright 2017-present 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
*
* https://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.boot.autoconfigure;
import org.apache.geode.cache.GemFireCache;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.core.env.Environment;
import org.springframework.data.gemfire.CacheFactoryBean;
import org.springframework.geode.boot.autoconfigure.configuration.SpringSessionProperties;
import org.springframework.session.SessionRepository;
import org.springframework.session.data.gemfire.config.annotation.web.http.GemFireHttpSessionConfiguration;
/**
* Spring Boot {@link EnableAutoConfiguration auto-configuration} class used to configure Spring Boot
* {@link ConfigurationProperties} classes and beans from the Spring {@link Environment} containing Spring Session
* configuration properties used to configure either Apache Geode to manage (HTTP) Session state.
*
* @author John Blum
* @see org.apache.geode.cache.GemFireCache
* @see org.springframework.boot.SpringBootConfiguration
* @see org.springframework.boot.autoconfigure.EnableAutoConfiguration
* @see org.springframework.boot.context.properties.ConfigurationProperties
* @see org.springframework.boot.context.properties.EnableConfigurationProperties
* @see org.springframework.core.env.Environment
* @see org.springframework.geode.boot.autoconfigure.configuration.SpringSessionProperties
* @see org.springframework.session.SessionRepository
* @since 1.0.0
*/
@SpringBootConfiguration
@ConditionalOnBean({ GemFireCache.class, SessionRepository.class })
@ConditionalOnClass({ GemFireCache.class, CacheFactoryBean.class, GemFireHttpSessionConfiguration.class })
@EnableConfigurationProperties({ SpringSessionProperties.class })
@SuppressWarnings("unused")
public class SpringSessionPropertiesAutoConfiguration {
}

View File

@@ -0,0 +1,340 @@
/*
* Copyright 2017-present 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
*
* https://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.boot.autoconfigure;
import static org.springframework.data.gemfire.util.ArrayUtils.nullSafeArray;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Optional;
import java.util.Properties;
import org.apache.geode.cache.GemFireCache;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.AllNestedConditions;
import org.springframework.boot.autoconfigure.condition.AnyNestedCondition;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.context.annotation.Conditional;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.core.env.PropertiesPropertySource;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.data.gemfire.CacheFactoryBean;
import org.springframework.data.gemfire.config.annotation.EnableSsl;
import org.springframework.util.Assert;
import org.springframework.util.FileCopyUtils;
import org.springframework.util.ResourceUtils;
import org.springframework.util.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Spring Boot {@link EnableAutoConfiguration auto-configuration} enabling Apache Geode's SSL transport
* between client and servers when using the client/server topology.
*
* @author John Blum
* @see java.io.File
* @see java.net.URL
* @see java.util.Properties
* @see org.apache.geode.cache.GemFireCache
* @see org.springframework.boot.SpringApplication
* @see org.springframework.boot.SpringBootConfiguration
* @see org.springframework.boot.autoconfigure.AutoConfigureBefore
* @see org.springframework.boot.autoconfigure.EnableAutoConfiguration
* @see org.springframework.boot.autoconfigure.condition.ConditionalOnClass
* @see org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
* @see org.springframework.boot.env.EnvironmentPostProcessor
* @see org.springframework.context.annotation.Condition
* @see org.springframework.context.annotation.ConditionContext
* @see org.springframework.context.annotation.Conditional
* @see org.springframework.core.env.ConfigurableEnvironment
* @see org.springframework.core.env.Environment
* @see org.springframework.core.env.PropertiesPropertySource
* @see org.springframework.core.env.PropertySource
* @see org.springframework.core.io.ClassPathResource
* @see org.springframework.core.io.Resource
* @see org.springframework.core.type.AnnotatedTypeMetadata
* @see org.springframework.data.gemfire.CacheFactoryBean
* @see org.springframework.data.gemfire.config.annotation.EnableSsl
* @see org.springframework.geode.boot.autoconfigure.ClientCacheAutoConfiguration
* @since 1.0.0
*/
@SpringBootConfiguration
@AutoConfigureBefore(ClientCacheAutoConfiguration.class)
@Conditional(SslAutoConfiguration.EnableSslCondition.class)
@ConditionalOnClass({ CacheFactoryBean.class, GemFireCache.class })
@EnableSsl
@SuppressWarnings("unused")
public class SslAutoConfiguration {
public static final String SECURITY_SSL_ENVIRONMENT_POST_PROCESSOR_ENABLED_PROPERTY =
"spring.boot.data.gemfire.security.ssl.environment.post-processor.enabled";
private static final String CURRENT_WORKING_DIRECTORY = System.getProperty("user.dir");
private static final String GEMFIRE_SSL_KEYSTORE_PROPERTY = "gemfire.ssl-keystore";
private static final String GEMFIRE_SSL_PROPERTY_SOURCE_NAME = "gemfire-ssl";
private static final String GEMFIRE_SSL_TRUSTSTORE_PROPERTY = "gemfire.ssl-truststore";
private static final String SECURITY_SSL_PROPERTY_PREFIX = "spring.data.gemfire.security.ssl";
private static final String SECURITY_SSL_KEYSTORE_PROPERTY = SECURITY_SSL_PROPERTY_PREFIX + ".keystore";
private static final String SECURITY_SSL_TRUSTSTORE_PROPERTY = SECURITY_SSL_PROPERTY_PREFIX + ".truststore";
private static final String SECURITY_SSL_USE_DEFAULT_CONTEXT = SECURITY_SSL_PROPERTY_PREFIX + ".use-default-context";
private static final String TRUSTED_KEYSTORE_FILENAME = "trusted.keystore";
private static final String TRUSTED_KEYSTORE_FILENAME_PROPERTY = "spring.boot.data.gemfire.security.ssl.keystore.name";
private static final String USER_HOME_DIRECTORY = System.getProperty("user.home");
private static final Logger logger = LoggerFactory.getLogger(SslAutoConfiguration.class);
private static boolean isSslConfigured(Environment environment) {
return (environment.containsProperty(SECURITY_SSL_KEYSTORE_PROPERTY)
&& environment.containsProperty(SECURITY_SSL_TRUSTSTORE_PROPERTY))
|| (environment.containsProperty(GEMFIRE_SSL_KEYSTORE_PROPERTY)
&& environment.containsProperty(GEMFIRE_SSL_TRUSTSTORE_PROPERTY));
}
private static boolean isSslNotConfigured(Environment environment) {
return !isSslConfigured(environment);
}
private static String resolveTrustedKeyStore(Environment environment) {
return locateKeyStoreInFileSystem(environment)
.map(File::getAbsolutePath)
.orElseGet(() -> locateKeyStoreInUserHome(environment)
.map(File::getAbsolutePath)
.orElseGet(() -> resolveKeyStoreFromClassPathAsPathname(environment)
.orElse(null)));
}
private static String resolveTrustedKeystoreName(Environment environment) {
return environment != null && environment.containsProperty(TRUSTED_KEYSTORE_FILENAME_PROPERTY)
? environment.getProperty(TRUSTED_KEYSTORE_FILENAME_PROPERTY)
: TRUSTED_KEYSTORE_FILENAME;
}
private static Optional<String> resolveKeyStoreFromClassPathAsPathname(Environment environment) {
return resolveKeyStoreFromClassPath(environment)
.filter(File::isFile)
.map(File::getAbsolutePath)
.filter(StringUtils::hasText);
}
private static Optional<File> resolveKeyStoreFromClassPath(Environment environment) {
return locateKeyStoreInClassPath(environment)
.map(resource -> {
File trustedKeyStore = null;
try {
URL url = resource.getURL();
if (ResourceUtils.isFileURL(url)) {
trustedKeyStore = new File(url.toURI());
}
else if (ResourceUtils.isJarURL(url)) {
trustedKeyStore = new File(CURRENT_WORKING_DIRECTORY, resolveTrustedKeystoreName(environment));
FileCopyUtils.copy(url.openStream(), new FileOutputStream(trustedKeyStore));
}
}
catch (IOException | URISyntaxException cause) {
if (logger.isWarnEnabled()) {
logger.warn("Trusted KeyStore {} found in Class Path but is not resolvable as a File: {}",
resource, cause.getMessage());
if (logger.isTraceEnabled()) {
logger.trace("Caused by:", cause);
}
}
}
return trustedKeyStore;
});
}
private static Optional<ClassPathResource> locateKeyStoreInClassPath(Environment environment) {
return locateKeyStoreInClassPath(resolveTrustedKeystoreName(environment));
}
private static Optional<ClassPathResource> locateKeyStoreInClassPath(String keystoreName) {
return Optional.of(new ClassPathResource(keystoreName))
.filter(Resource::exists);
}
private static Optional<File> locateKeyStoreInFileSystem(Environment environment) {
return locateKeyStoreInFileSystem(environment, new File(CURRENT_WORKING_DIRECTORY));
}
private static Optional<File> locateKeyStoreInFileSystem(Environment environment, File directory) {
return locateKeyStoreInFileSystem(directory, resolveTrustedKeystoreName(environment));
}
private static Optional<File> locateKeyStoreInFileSystem(String keystoreName) {
return locateKeyStoreInFileSystem(new File(CURRENT_WORKING_DIRECTORY), keystoreName);
}
@SuppressWarnings("all")
private static Optional<File> locateKeyStoreInFileSystem(File directory, String keystoreFilename) {
assertDirectory(directory);
for (File file : nullSafeListFiles(directory)) {
if (isDirectory(file)) {
Optional<File> theFile = locateKeyStoreInFileSystem(file, keystoreFilename);
if (theFile.isPresent()) {
return theFile;
}
else {
continue;
}
}
if (file.getName().equals(keystoreFilename)) {
return Optional.of(file);
}
}
return Optional.empty();
}
private static Optional<File> locateKeyStoreInUserHome(Environment environment) {
return locateKeyStoreInUserHome(resolveTrustedKeystoreName(environment));
}
private static Optional<File> locateKeyStoreInUserHome(String keystoreFilename) {
return Optional.of(new File(USER_HOME_DIRECTORY, keystoreFilename))
.filter(File::isFile);
}
private static void assertDirectory(File path) {
Assert.isTrue(isDirectory(path), String.format("[%s] is not a valid directory", path));
}
private static boolean isDirectory(File path) {
return path != null && path.isDirectory();
}
private static File[] nullSafeListFiles(File directory) {
return nullSafeArray(directory.listFiles(), File.class);
}
public static class SslEnvironmentPostProcessor implements EnvironmentPostProcessor {
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
Optional.of(environment)
.filter(this::isEnabled)
.filter(SslAutoConfiguration::isSslNotConfigured)
.map(SslAutoConfiguration::resolveTrustedKeyStore)
.filter(StringUtils::hasText)
.ifPresent(trustedKeyStore -> configureSsl(environment, trustedKeyStore));
}
private PropertySource<?> newPropertySource(String name, Properties properties) {
return new PropertiesPropertySource(name, properties);
}
private boolean isEnabled(Environment environment) {
return environment.getProperty(SECURITY_SSL_ENVIRONMENT_POST_PROCESSOR_ENABLED_PROPERTY,
Boolean.class, true);
}
private void configureSsl(ConfigurableEnvironment environment, String trustedKeyStore) {
Properties gemfireSslProperties = new Properties();
gemfireSslProperties.setProperty(SECURITY_SSL_KEYSTORE_PROPERTY, trustedKeyStore);
gemfireSslProperties.setProperty(SECURITY_SSL_TRUSTSTORE_PROPERTY, trustedKeyStore);
environment.getPropertySources()
.addFirst(newPropertySource(GEMFIRE_SSL_PROPERTY_SOURCE_NAME, gemfireSslProperties));
}
}
static class EnableSslCondition extends AllNestedConditions {
public EnableSslCondition() {
super(ConfigurationPhase.PARSE_CONFIGURATION);
}
@ConditionalOnProperty(name = SECURITY_SSL_ENVIRONMENT_POST_PROCESSOR_ENABLED_PROPERTY,
havingValue = "true", matchIfMissing = true)
static class SpringBootDataGemFireSecuritySslEnvironmentPostProcessorEnabled { }
@Conditional(SslTriggersCondition.class)
static class AnySslTriggerCondition { }
}
static class SslTriggersCondition extends AnyNestedCondition {
public SslTriggersCondition() {
super(ConfigurationPhase.PARSE_CONFIGURATION);
}
@Conditional(TrustedKeyStoreIsPresentCondition.class)
static class TrustedKeyStoreCondition { }
@ConditionalOnProperty(prefix = SECURITY_SSL_PROPERTY_PREFIX, name = { "keystore", "truststore" })
static class SpringDataGemFireSecuritySslKeyStoreAndTruststorePropertiesSet { }
@ConditionalOnProperty(SECURITY_SSL_USE_DEFAULT_CONTEXT)
static class SpringDataGeodeSslUseDefaultContextPropertySet { }
@ConditionalOnProperty({ GEMFIRE_SSL_KEYSTORE_PROPERTY, GEMFIRE_SSL_TRUSTSTORE_PROPERTY })
static class ApacheGeodeSslKeyStoreAndTruststorePropertiesSet { }
}
static class TrustedKeyStoreIsPresentCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Environment environment = context.getEnvironment();
return locateKeyStoreInClassPath(environment).isPresent()
|| locateKeyStoreInFileSystem(environment).isPresent()
|| locateKeyStoreInUserHome(environment).isPresent();
}
}
}

View File

@@ -0,0 +1,80 @@
/*
* Copyright 2017-present 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
*
* https://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.boot.autoconfigure.condition;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Conditional;
import org.springframework.core.annotation.AliasFor;
/**
* The {@link ConditionalOnMissingProperty} annotation is a Spring {@link Conditional} used to conditionally enable
* or disable functionality based on the absence of any declared properties.
*
* @author John Blum
* @see org.springframework.context.annotation.Conditional
* @since 1.0.0
*/
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnMissingPropertyCondition.class)
@SuppressWarnings("unused")
public @interface ConditionalOnMissingProperty {
/**
* The {@link String names} of the properties to test.
*
* If a {@link String prefix} has been defined, it is applied to compute the full key of each property.
*
* For instance, if the {@link String prefix} is {@code app.config} and one value is {@code my-value},
* the full key would be {@code app.config.my-value}.
*
* Use dashed notation to specify each property, that is all lower case with a "-" to separate words
* (e.g. {@code my-long-property}).
*
* @return the {@link String names} of the properties to test.
* @see #prefix()
*/
@AliasFor("value")
String[] name() default {};
/**
* A {@link String prefix} that should be applied to each property.
*
* The {@link String prefix} automatically ends with a dot if not specified. A valid {@link String prefix}
* is defined by one or more words separated with dots (e.g. {@code "acme.system.feature"}).
*
* @return the property {@link String prefix}.
* @see #name()
*/
String prefix() default "";
/**
* Alias for {@link #name()}.
*
* @return the {@link String names} of the properties to test.
* @see #name()
*/
@AliasFor("name")
String[] value() default {};
}

View File

@@ -0,0 +1,162 @@
/*
* Copyright 2017-present 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
*
* https://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.boot.autoconfigure.condition;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
import org.springframework.boot.autoconfigure.condition.ConditionMessage;
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.env.PropertyResolver;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.util.Assert;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
/**
* The {@link OnMissingPropertyCondition} class is a {@link SpringBootCondition}, Spring {@link Condition} type
* asserting whether the specified, declared properties are missing.
*
* @author John Blum
* @see org.springframework.boot.autoconfigure.condition.ConditionOutcome
* @see org.springframework.boot.autoconfigure.condition.SpringBootCondition
* @see org.springframework.context.annotation.Condition
* @see org.springframework.context.annotation.ConditionContext
* @see org.springframework.core.annotation.AnnotationAttributes
* @see org.springframework.core.env.PropertyResolver
* @see org.springframework.core.type.AnnotatedTypeMetadata
* @see org.springframework.geode.boot.autoconfigure.condition.ConditionalOnMissingProperty
* @since 1.0.0
*/
public class OnMissingPropertyCondition extends SpringBootCondition {
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
String annotationName = ConditionalOnMissingProperty.class.getName();
Collection<AnnotationAttributes> annotationAttributesCollection =
toAnnotationAttributesFromMultiValueMap(metadata.getAllAnnotationAttributes(annotationName));
PropertyResolver propertyResolver = getPropertyResolver(context);
Collection<String> allMatchingProperties = new ArrayList<>();
annotationAttributesCollection.forEach(annotationAttributes -> {
List<String> propertyNames = collectPropertyNames(annotationAttributes);
allMatchingProperties.addAll(findMatchingProperties(propertyResolver, propertyNames));
});
return determineConditionOutcome(allMatchingProperties);
}
@SuppressWarnings("unchecked")
private <T extends Collection<AnnotationAttributes>> T toAnnotationAttributesFromMultiValueMap(
MultiValueMap<String, Object> map) {
List<AnnotationAttributes> annotationAttributesList = new ArrayList<>();
map.forEach((key, value) -> {
for (int index = 0, size = value.size(); index < size; index++) {
AnnotationAttributes annotationAttributes =
resolveAnnotationAttributes(annotationAttributesList, index);
annotationAttributes.put(key, value.get(index));
}
});
return (T) annotationAttributesList;
}
private AnnotationAttributes resolveAnnotationAttributes(List<AnnotationAttributes> annotationAttributesList,
int index) {
if (index < annotationAttributesList.size()) {
return annotationAttributesList.get(index);
}
else {
AnnotationAttributes newAnnotationAttributes = new AnnotationAttributes();
annotationAttributesList.add(newAnnotationAttributes);
return newAnnotationAttributes;
}
}
private PropertyResolver getPropertyResolver(ConditionContext context) {
return context.getEnvironment();
}
private List<String> collectPropertyNames(AnnotationAttributes annotationAttributes) {
String prefix = getPrefix(annotationAttributes);
String[] names = getNames(annotationAttributes);
return Arrays.stream(names).map(name -> prefix + name).collect(Collectors.toList());
}
private String[] getNames(AnnotationAttributes annotationAttributes) {
String[] names = annotationAttributes.getStringArray("name");
String[] values = annotationAttributes.getStringArray("value");
Assert.isTrue(names.length > 0 || values.length > 0,
String.format("The name or value attribute of @%s is required",
ConditionalOnMissingProperty.class.getSimpleName()));
// TODO remove; not needed when using @AliasFor.
/*
Assert.isTrue(names.length * values.length == 0,
String.format("The name and value attributes of @%s are exclusive",
ConditionalOnMissingProperty.class.getSimpleName()));
*/
return names.length > 0 ? names : values;
}
private String getPrefix(AnnotationAttributes annotationAttributes) {
String prefix = annotationAttributes.getString("prefix");
return StringUtils.hasText(prefix) ? prefix.trim().endsWith(".") ? prefix.trim() : prefix.trim() + "." : "";
}
private Collection<String> findMatchingProperties(PropertyResolver propertyResolver, List<String> propertyNames) {
return propertyNames.stream().filter(propertyResolver::containsProperty).collect(Collectors.toSet());
}
private ConditionOutcome determineConditionOutcome(Collection<String> matchingProperties) {
if (!matchingProperties.isEmpty()) {
return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnMissingProperty.class)
.found("property already defined", "properties already defined")
.items(matchingProperties));
}
return ConditionOutcome.match();
}
}

View File

@@ -0,0 +1,179 @@
/*
* Copyright 2017-present 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
*
* https://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.boot.autoconfigure.configuration;
import java.util.Properties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.NestedConfigurationProperty;
import org.springframework.geode.boot.autoconfigure.configuration.support.CacheProperties;
import org.springframework.geode.boot.autoconfigure.configuration.support.ClusterProperties;
import org.springframework.geode.boot.autoconfigure.configuration.support.DiskStoreProperties;
import org.springframework.geode.boot.autoconfigure.configuration.support.EntityProperties;
import org.springframework.geode.boot.autoconfigure.configuration.support.LocatorProperties;
import org.springframework.geode.boot.autoconfigure.configuration.support.LoggingProperties;
import org.springframework.geode.boot.autoconfigure.configuration.support.ManagementProperties;
import org.springframework.geode.boot.autoconfigure.configuration.support.ManagerProperties;
import org.springframework.geode.boot.autoconfigure.configuration.support.PdxProperties;
import org.springframework.geode.boot.autoconfigure.configuration.support.PoolProperties;
import org.springframework.geode.boot.autoconfigure.configuration.support.SecurityProperties;
import org.springframework.geode.boot.autoconfigure.configuration.support.ServiceProperties;
/**
* Spring Boot {@link ConfigurationProperties} for well-known, documented Spring Data for Apache Geode (SDG)
* {@link Properties}.
*
* This class assists the application developer in the auto-completion / content-assist of the well-known, documented
* SDG {@link Properties}.
*
* @author John Blum
* @see java.util.Properties
* @see org.springframework.boot.context.properties.ConfigurationProperties
* @see org.springframework.boot.context.properties.NestedConfigurationProperty
* @see org.springframework.geode.boot.autoconfigure.configuration.support.CacheProperties
* @see org.springframework.geode.boot.autoconfigure.configuration.support.ClusterProperties
* @see org.springframework.geode.boot.autoconfigure.configuration.support.DiskStoreProperties
* @see org.springframework.geode.boot.autoconfigure.configuration.support.EntityProperties
* @see org.springframework.geode.boot.autoconfigure.configuration.support.LocatorProperties
* @see org.springframework.geode.boot.autoconfigure.configuration.support.LoggingProperties
* @see org.springframework.geode.boot.autoconfigure.configuration.support.ManagementProperties
* @see org.springframework.geode.boot.autoconfigure.configuration.support.ManagerProperties
* @see org.springframework.geode.boot.autoconfigure.configuration.support.PdxProperties
* @see org.springframework.geode.boot.autoconfigure.configuration.support.PoolProperties
* @see org.springframework.geode.boot.autoconfigure.configuration.support.SecurityProperties
* @see org.springframework.geode.boot.autoconfigure.configuration.support.ServiceProperties
* @since 1.0.0
*/
@SuppressWarnings("unused")
@ConfigurationProperties(prefix = "spring.data.gemfire")
public class GemFireProperties {
private static final boolean DEFAULT_USE_BEAN_FACTORY_LOCATOR = false;
private boolean useBeanFactoryLocator = DEFAULT_USE_BEAN_FACTORY_LOCATOR;
@NestedConfigurationProperty
private final CacheProperties cache = new CacheProperties();
@NestedConfigurationProperty
private final ClusterProperties cluster = new ClusterProperties();
@NestedConfigurationProperty
private final DiskStoreProperties disk = new DiskStoreProperties();
@NestedConfigurationProperty
private final EntityProperties entities = new EntityProperties();
@NestedConfigurationProperty
private final LocatorProperties locator = new LocatorProperties();
@NestedConfigurationProperty
private final LoggingProperties logging = new LoggingProperties();
@NestedConfigurationProperty
private final ManagementProperties management = new ManagementProperties();
@NestedConfigurationProperty
private final ManagerProperties manager = new ManagerProperties();
@NestedConfigurationProperty
private final PdxProperties pdx = new PdxProperties();
@NestedConfigurationProperty
private final PoolProperties pool = new PoolProperties();
@NestedConfigurationProperty
private final SecurityProperties security = new SecurityProperties();
@NestedConfigurationProperty
private final ServiceProperties service = new ServiceProperties();
private String name;
private String[] locators;
public CacheProperties getCache() {
return this.cache;
}
public ClusterProperties getCluster() {
return this.cluster;
}
public DiskStoreProperties getDisk() {
return this.disk;
}
public EntityProperties getEntities() {
return this.entities;
}
public LocatorProperties getLocator() {
return this.locator;
}
public String[] getLocators() {
return this.locators;
}
public void setLocators(String[] locators) {
this.locators = locators;
}
public LoggingProperties getLogging() {
return this.logging;
}
public ManagementProperties getManagement() {
return this.management;
}
public ManagerProperties getManager() {
return this.manager;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public PdxProperties getPdx() {
return this.pdx;
}
public PoolProperties getPool() {
return this.pool;
}
public SecurityProperties getSecurity() {
return this.security;
}
public ServiceProperties getService() {
return this.service;
}
public boolean isUseBeanFactoryLocator() {
return this.useBeanFactoryLocator;
}
public void setUseBeanFactoryLocator(boolean useBeanFactoryLocator) {
this.useBeanFactoryLocator = useBeanFactoryLocator;
}
}

View File

@@ -0,0 +1,220 @@
/*
* Copyright 2017-present 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
*
* https://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.boot.autoconfigure.configuration;
import org.apache.geode.cache.RegionShortcut;
import org.apache.geode.cache.client.ClientRegionShortcut;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.NestedConfigurationProperty;
/**
* Spring Boot {@link ConfigurationProperties} used to configure Spring Session for Apache Geode (SSDG) in order to
* manage (HTTP) Session state with Spring Session backed by Apache Geode.
*
* @author John Blum
* @see org.springframework.boot.context.properties.ConfigurationProperties
* @see org.springframework.boot.context.properties.NestedConfigurationProperty
* @since 1.0.0
*/
@SuppressWarnings("unused")
@ConfigurationProperties(prefix = "spring.session.data.gemfire")
public class SpringSessionProperties {
@NestedConfigurationProperty
private final CacheProperties cache = new CacheProperties();
@NestedConfigurationProperty
private final SessionProperties session = new SessionProperties();
public CacheProperties getCache() {
return this.cache;
}
public SessionProperties getSession() {
return this.session;
}
public static class CacheProperties {
@NestedConfigurationProperty
private final CacheServerProperties server = new CacheServerProperties();
@NestedConfigurationProperty
private final ClientCacheProperties client = new ClientCacheProperties();
public ClientCacheProperties getClient() {
return this.client;
}
public CacheServerProperties getServer() {
return this.server;
}
}
public static class CacheServerProperties {
@NestedConfigurationProperty
private final ServerRegionProperties region = new ServerRegionProperties();
public ServerRegionProperties getRegion() {
return region;
}
}
public static class ClientCacheProperties {
@NestedConfigurationProperty
private final ClientRegionProperties region = new ClientRegionProperties();
@NestedConfigurationProperty
private final PoolProperties pool = new PoolProperties();
public PoolProperties getPool() {
return this.pool;
}
public ClientRegionProperties getRegion() {
return this.region;
}
}
public static class ClientRegionProperties {
public static final ClientRegionShortcut DEFAULT_CLIENT_REGION_SHORTCUT = ClientRegionShortcut.PROXY;
private ClientRegionShortcut shortcut = ClientRegionShortcut.PROXY;
public ClientRegionShortcut getShortcut() {
return this.shortcut != null ? this.shortcut : DEFAULT_CLIENT_REGION_SHORTCUT;
}
public void setShortcut(ClientRegionShortcut shortcut) {
this.shortcut = shortcut;
}
}
public static class PoolProperties {
private String name;
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
public static class ServerRegionProperties {
public static final RegionShortcut DEFAULT_SERVER_REGION_SHORTCUT = RegionShortcut.PARTITION;
private RegionShortcut shortcut;
public RegionShortcut getShortcut() {
return this.shortcut != null ? this.shortcut : DEFAULT_SERVER_REGION_SHORTCUT;
}
public void setShortcut(RegionShortcut shortcut) {
this.shortcut = shortcut;
}
}
public static class SessionProperties {
@NestedConfigurationProperty
private final SessionAttributesProperties attributes = new SessionAttributesProperties();
@NestedConfigurationProperty
private final SessionExpirationProperties expiration = new SessionExpirationProperties();
@NestedConfigurationProperty
private final SessionRegionProperties region = new SessionRegionProperties();
@NestedConfigurationProperty
private final SessionSerializerProperties serializer = new SessionSerializerProperties();
public SessionAttributesProperties getAttributes() {
return this.attributes;
}
public SessionExpirationProperties getExpiration() {
return this.expiration;
}
public SessionRegionProperties getRegion() {
return this.region;
}
public SessionSerializerProperties getSerializer() {
return this.serializer;
}
}
public static class SessionAttributesProperties {
private String[] indexable;
public String[] getIndexable() {
return this.indexable;
}
public void setIndexable(String[] indexable) {
this.indexable = indexable;
}
}
public static class SessionExpirationProperties {
private int maxInactiveIntervalSeconds;
public int getMaxInactiveIntervalSeconds() {
return this.maxInactiveIntervalSeconds;
}
public void setMaxInactiveIntervalSeconds(int maxInactiveIntervalSeconds) {
this.maxInactiveIntervalSeconds = maxInactiveIntervalSeconds;
}
}
public static class SessionRegionProperties {
private String name;
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
public static class SessionSerializerProperties {
private String beanName;
public String getBeanName() {
return this.beanName;
}
public void setBeanName(String beanName) {
this.beanName = beanName;
}
}
}

View File

@@ -0,0 +1,211 @@
/*
* Copyright 2017-present 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
*
* https://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.boot.autoconfigure.configuration.support;
import java.util.Properties;
import org.apache.geode.cache.Cache;
import org.apache.geode.cache.client.ClientCache;
import org.apache.geode.cache.control.ResourceManager;
import org.apache.geode.cache.server.CacheServer;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.NestedConfigurationProperty;
/**
* Spring Boot {@link ConfigurationProperties} used to configure Apache Geode peer {@link Cache}, {@link ClientCache}
* and {@link CacheServer} objects.
*
* The configuration {@link Properties} are based on well-known, documented Spring Data for Apache Geode (SDG)
* {@link Properties}.
*
* @author John Blum
* @see java.util.Properties
* @see org.apache.geode.cache.Cache
* @see org.apache.geode.cache.client.ClientCache
* @see org.apache.geode.cache.control.ResourceManager
* @see org.apache.geode.cache.server.CacheServer
* @see org.springframework.boot.context.properties.ConfigurationProperties
* @see org.springframework.boot.context.properties.NestedConfigurationProperty
* @since 1.0.0
*/
@SuppressWarnings("unused")
public class CacheProperties {
private static final boolean DEFAULT_COPY_ON_READ = false;
private static final boolean DEFAULT_AUTO_REGION_LOOKUP = true;
private static final float DEFAULT_CRITICAL_OFF_HEAP_PERCENTAGE = 0.0f;
private static final float DEFAULT_EVICTION_OFF_HEAP_PERCENTAGE = 0.0f;
private static final String DEFAULT_LOG_LEVEL = "config";
private boolean copyOnRead = DEFAULT_COPY_ON_READ;
private boolean enableAutoRegionLookup = DEFAULT_AUTO_REGION_LOOKUP;
private float criticalHeapPercentage = ResourceManager.DEFAULT_CRITICAL_PERCENTAGE;
private float criticalOffHeapPercentage = DEFAULT_CRITICAL_OFF_HEAP_PERCENTAGE;
private float evictionHeapPercentage = ResourceManager.DEFAULT_EVICTION_PERCENTAGE;
private float evictionOffHeapPercentage = DEFAULT_EVICTION_OFF_HEAP_PERCENTAGE;
@NestedConfigurationProperty
private final CacheServerProperties server = new CacheServerProperties();
@NestedConfigurationProperty
private final ClientCacheProperties client = new ClientCacheProperties();
@NestedConfigurationProperty
private final CompressionProperties compression = new CompressionProperties();
@NestedConfigurationProperty
private final OffHeapProperties offHeap = new OffHeapProperties();
@NestedConfigurationProperty
private final PeerCacheProperties peer = new PeerCacheProperties();
private String logLevel = DEFAULT_LOG_LEVEL;
private String name;
public ClientCacheProperties getClient() {
return this.client;
}
public CompressionProperties getCompression() {
return this.compression;
}
public boolean isCopyOnRead() {
return copyOnRead;
}
public void setCopyOnRead(boolean copyOnRead) {
this.copyOnRead = copyOnRead;
}
public float getCriticalHeapPercentage() {
return this.criticalHeapPercentage;
}
public void setCriticalHeapPercentage(float criticalHeapPercentage) {
this.criticalHeapPercentage = criticalHeapPercentage;
}
public float getCriticalOffHeapPercentage() {
return this.criticalOffHeapPercentage;
}
public void setCriticalOffHeapPercentage(float criticalOffHeapPercentage) {
this.criticalOffHeapPercentage = criticalOffHeapPercentage;
}
public boolean isEnableAutoRegionLookup() {
return this.enableAutoRegionLookup;
}
public void setEnableAutoRegionLookup(boolean enableAutoRegionLookup) {
this.enableAutoRegionLookup = enableAutoRegionLookup;
}
public float getEvictionHeapPercentage() {
return this.evictionHeapPercentage;
}
public void setEvictionHeapPercentage(float evictionHeapPercentage) {
this.evictionHeapPercentage = evictionHeapPercentage;
}
public float getEvictionOffHeapPercentage() {
return this.evictionOffHeapPercentage;
}
public void setEvictionOffHeapPercentage(float evictionOffHeapPercentage) {
this.evictionOffHeapPercentage = evictionOffHeapPercentage;
}
public String getLogLevel() {
return this.logLevel;
}
public void setLogLevel(String logLevel) {
this.logLevel = logLevel;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public OffHeapProperties getOffHeap() {
return this.offHeap;
}
public PeerCacheProperties getPeer() {
return this.peer;
}
public CacheServerProperties getServer() {
return this.server;
}
public static class CompressionProperties {
private String compressorBeanName;
private String[] regionNames = {};
public String getCompressorBeanName() {
return this.compressorBeanName;
}
public void setCompressorBeanName(String compressorBeanName) {
this.compressorBeanName = compressorBeanName;
}
public String[] getRegionNames() {
return this.regionNames;
}
public void setRegionNames(String[] regionNames) {
this.regionNames = regionNames;
}
}
public static class OffHeapProperties {
private String memorySize;
private String[] regionNames = {};
public String getMemorySize() {
return this.memorySize;
}
public void setMemorySize(String memorySize) {
this.memorySize = memorySize;
}
public String[] getRegionNames() {
return this.regionNames;
}
public void setRegionNames(String[] regionNames) {
this.regionNames = regionNames;
}
}
}

View File

@@ -0,0 +1,182 @@
/*
* Copyright 2017-present 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
*
* https://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.boot.autoconfigure.configuration.support;
import java.util.Properties;
import org.apache.geode.cache.server.CacheServer;
import org.apache.geode.cache.server.ClientSubscriptionConfig;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.data.gemfire.server.SubscriptionEvictionPolicy;
/**
* Spring Boot {@link ConfigurationProperties} used to configure an Apache Geode {@link CacheServer}.
*
* The configuration {@link Properties} are based on well-known, documented Spring Data for Apache Geode (SDG)
* {@link Properties}.
*
* @author John Blum
* @see java.util.Properties
* @see org.apache.geode.cache.server.CacheServer
* @see org.springframework.boot.context.properties.ConfigurationProperties
* @since 1.0.0
*/
@SuppressWarnings("unused")
public class CacheServerProperties {
private static final boolean DEFAULT_AUTO_STARTUP = true;
private boolean autoStartup = DEFAULT_AUTO_STARTUP;
private boolean tcpNoDelay = CacheServer.DEFAULT_TCP_NO_DELAY;
private int maxConnections = CacheServer.DEFAULT_MAX_CONNECTIONS;
private int maxMessageCount = CacheServer.DEFAULT_MAXIMUM_MESSAGE_COUNT;
private int maxThreads = CacheServer.DEFAULT_MAX_THREADS;
private int maxTimeBetweenPings = CacheServer.DEFAULT_MAXIMUM_TIME_BETWEEN_PINGS;
private int messageTimeToLive = CacheServer.DEFAULT_MESSAGE_TIME_TO_LIVE;
private int port = CacheServer.DEFAULT_PORT;
private int socketBufferSize = CacheServer.DEFAULT_SOCKET_BUFFER_SIZE;
private int subscriptionCapacity = ClientSubscriptionConfig.DEFAULT_CAPACITY;
private long loadPollInterval = CacheServer.DEFAULT_LOAD_POLL_INTERVAL;
private String bindAddress = CacheServer.DEFAULT_BIND_ADDRESS;
private String hostnameForClients = CacheServer.DEFAULT_HOSTNAME_FOR_CLIENTS;
private String subscriptionDiskStoreName;
private SubscriptionEvictionPolicy subscriptionEvictionPolicy = SubscriptionEvictionPolicy.NONE;
public boolean isAutoStartup() {
return this.autoStartup;
}
public void setAutoStartup(boolean autoStartup) {
this.autoStartup = autoStartup;
}
public String getBindAddress() {
return this.bindAddress;
}
public void setBindAddress(String bindAddress) {
this.bindAddress = bindAddress;
}
public String getHostnameForClients() {
return this.hostnameForClients;
}
public void setHostnameForClients(String hostnameForClients) {
this.hostnameForClients = hostnameForClients;
}
public long getLoadPollInterval() {
return this.loadPollInterval;
}
public void setLoadPollInterval(long loadPollInterval) {
this.loadPollInterval = loadPollInterval;
}
public int getMaxConnections() {
return this.maxConnections;
}
public void setMaxConnections(int maxConnections) {
this.maxConnections = maxConnections;
}
public int getMaxMessageCount() {
return this.maxMessageCount;
}
public void setMaxMessageCount(int maxMessageCount) {
this.maxMessageCount = maxMessageCount;
}
public int getMaxThreads() {
return this.maxThreads;
}
public void setMaxThreads(int maxThreads) {
this.maxThreads = maxThreads;
}
public int getMaxTimeBetweenPings() {
return this.maxTimeBetweenPings;
}
public void setMaxTimeBetweenPings(int maxTimeBetweenPings) {
this.maxTimeBetweenPings = maxTimeBetweenPings;
}
public int getMessageTimeToLive() {
return this.messageTimeToLive;
}
public void setMessageTimeToLive(int messageTimeToLive) {
this.messageTimeToLive = messageTimeToLive;
}
public int getPort() {
return this.port;
}
public void setPort(int port) {
this.port = port;
}
public int getSocketBufferSize() {
return this.socketBufferSize;
}
public void setSocketBufferSize(int socketBufferSize) {
this.socketBufferSize = socketBufferSize;
}
public int getSubscriptionCapacity() {
return this.subscriptionCapacity;
}
public void setSubscriptionCapacity(int subscriptionCapacity) {
this.subscriptionCapacity = subscriptionCapacity;
}
public String getSubscriptionDiskStoreName() {
return this.subscriptionDiskStoreName;
}
public void setSubscriptionDiskStoreName(String subscriptionDiskStoreName) {
this.subscriptionDiskStoreName = subscriptionDiskStoreName;
}
public SubscriptionEvictionPolicy getSubscriptionEvictionPolicy() {
return this.subscriptionEvictionPolicy;
}
public void setSubscriptionEvictionPolicy(SubscriptionEvictionPolicy subscriptionEvictionPolicy) {
this.subscriptionEvictionPolicy = subscriptionEvictionPolicy;
}
public boolean isTcpNoDelay() {
return this.tcpNoDelay;
}
public void setTcpNoDelay(boolean tcpNoDelay) {
this.tcpNoDelay = tcpNoDelay;
}
}

View File

@@ -0,0 +1,72 @@
/*
* Copyright 2017-present 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
*
* https://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.boot.autoconfigure.configuration.support;
import java.util.Properties;
import org.apache.geode.cache.client.ClientCache;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* Spring Boot {@link ConfigurationProperties} used to configure an Apache Geode {@link ClientCache}.
*
* The configuration {@link Properties} are based on well-known, documented Spring Data for Apache Geode (SDG)
* {@link Properties}.
*
* @author John Blum
* @see java.util.Properties
* @see org.apache.geode.cache.client.ClientCache
* @see org.springframework.boot.context.properties.ConfigurationProperties
* @since 1.0.0
*/
@SuppressWarnings("unused")
public class ClientCacheProperties {
private static final boolean DEFAULT_KEEP_ALIVE = false;
private static final int DEFAULT_DURABLE_CLIENT_TIMEOUT_IN_SECONDS = 300;
private boolean keepAlive = DEFAULT_KEEP_ALIVE;
private int durableClientTimeout = DEFAULT_DURABLE_CLIENT_TIMEOUT_IN_SECONDS;
private String durableClientId;
public String getDurableClientId() {
return this.durableClientId;
}
public void setDurableClientId(String durableClientId) {
this.durableClientId = durableClientId;
}
public int getDurableClientTimeout() {
return this.durableClientTimeout;
}
public void setDurableClientTimeout(int durableClientTimeout) {
this.durableClientTimeout = durableClientTimeout;
}
public boolean isKeepAlive() {
return this.keepAlive;
}
public void setKeepAlive(boolean keepAlive) {
this.keepAlive = keepAlive;
}
}

View File

@@ -0,0 +1,85 @@
/*
* Copyright 2017-present 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
*
* https://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.boot.autoconfigure.configuration.support;
import java.util.Properties;
import org.apache.geode.cache.client.ClientCache;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* Spring Boot {@link ConfigurationProperties} used to configure an Apache Geode {@link ClientCache} Security
* (authentication &amp; authorization).
*
* The configuration {@link Properties} are based on well-known, documented Spring Data for Apache Geode (SDG)
* {@link Properties}.
*
* @author John Blum
* @see java.util.Properties
* @see org.apache.geode.cache.client.ClientCache
* @see org.springframework.boot.context.properties.ConfigurationProperties
* @since 1.0.0
*/
@SuppressWarnings("unused")
public class ClientSecurityProperties {
private String accessor;
private String accessorPostProcessor;
private String authenticationInitializer;
private String authenticator;
private String diffieHellmanAlgorithm;
public String getAccessor() {
return this.accessor;
}
public void setAccessor(String accessor) {
this.accessor = accessor;
}
public String getAccessorPostProcessor() {
return this.accessorPostProcessor;
}
public void setAccessorPostProcessor(String accessorPostProcessor) {
this.accessorPostProcessor = accessorPostProcessor;
}
public String getAuthenticationInitializer() {
return this.authenticationInitializer;
}
public void setAuthenticationInitializer(String authenticationInitializer) {
this.authenticationInitializer = authenticationInitializer;
}
public String getAuthenticator() {
return this.authenticator;
}
public void setAuthenticator(String authenticator) {
this.authenticator = authenticator;
}
public String getDiffieHellmanAlgorithm() {
return this.diffieHellmanAlgorithm;
}
public void setDiffieHellmanAlgorithm(String diffieHellmanAlgorithm) {
this.diffieHellmanAlgorithm = diffieHellmanAlgorithm;
}
}

View File

@@ -0,0 +1,60 @@
/*
* Copyright 2017-present 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
*
* https://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.boot.autoconfigure.configuration.support;
import java.util.Properties;
import org.apache.geode.cache.DataPolicy;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.RegionShortcut;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* Spring Boot {@link ConfigurationProperties} used to configure the {@link DataPolicy} of all {@link Region Regions}
* in an Apache Geode cluster.
*
* The configuration {@link Properties} are based on well-known, documented Spring Data for Apache Geode (SDG)
* {@link Properties}.
*
* @author John Blum
* @see java.util.Properties
* @see org.apache.geode.cache.Region
* @see org.springframework.boot.context.properties.ConfigurationProperties
* @since 1.0.0
*/
@SuppressWarnings("unused")
public class ClusterProperties {
private final RegionProperties regionProperties = new RegionProperties();
public RegionProperties getRegion() {
return this.regionProperties;
}
public static class RegionProperties {
private RegionShortcut peerRegionType;
public RegionShortcut getType() {
return this.peerRegionType;
}
public void setType(RegionShortcut peerRegionType) {
this.peerRegionType = peerRegionType;
}
}
}

View File

@@ -0,0 +1,167 @@
/*
* Copyright 2017-present 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
*
* https://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.boot.autoconfigure.configuration.support;
import java.util.Properties;
import org.apache.geode.cache.DiskStore;
import org.apache.geode.cache.DiskStoreFactory;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* Spring Boot {@link ConfigurationProperties} used to configure Apache Geode {@link DiskStore DiskStores}.
*
* The configuration {@link Properties} are based on well-known, documented Spring Data for Apache Geode (SDG)
* {@link Properties}.
*
* @author John Blum
* @see java.util.Properties
* @see org.apache.geode.cache.DiskStore
* @see org.apache.geode.cache.DiskStoreFactory
* @see org.springframework.boot.context.properties.ConfigurationProperties
* @since 1.0.0
*/
@SuppressWarnings("unused")
public class DiskStoreProperties {
private final StoreProperties storeProperties = new StoreProperties();
public StoreProperties getStore() {
return this.storeProperties;
}
public static class DirectoryProperties {
private int size = DiskStoreFactory.DEFAULT_DISK_DIR_SIZE;
private String location;
public String getLocation() {
return this.location;
}
public void setLocation(String location) {
this.location = location;
}
public int getSize() {
return this.size;
}
public void setSize(int size) {
this.size = size;
}
}
public static class StoreProperties {
private boolean allowForceCompaction = DiskStoreFactory.DEFAULT_ALLOW_FORCE_COMPACTION;
private boolean autoCompact = DiskStoreFactory.DEFAULT_AUTO_COMPACT;
private float diskUsageCriticalPercentage = DiskStoreFactory.DEFAULT_DISK_USAGE_CRITICAL_PERCENTAGE;
private float diskUsageWarningPercentage = DiskStoreFactory.DEFAULT_DISK_USAGE_WARNING_PERCENTAGE;
private int compactionThreshold = DiskStoreFactory.DEFAULT_COMPACTION_THRESHOLD;
private int queueSize = DiskStoreFactory.DEFAULT_QUEUE_SIZE;
private int writeBufferSize = DiskStoreFactory.DEFAULT_WRITE_BUFFER_SIZE;
private long maxOplogSize = DiskStoreFactory.DEFAULT_MAX_OPLOG_SIZE;
private long timeInterval = DiskStoreFactory.DEFAULT_TIME_INTERVAL;
private DirectoryProperties[] directoryProperties = {};
public boolean isAllowForceCompaction() {
return this.allowForceCompaction;
}
public void setAllowForceCompaction(boolean allowForceCompaction) {
this.allowForceCompaction = allowForceCompaction;
}
public boolean isAutoCompact() {
return this.autoCompact;
}
public void setAutoCompact(boolean autoCompact) {
this.autoCompact = autoCompact;
}
public int getCompactionThreshold() {
return this.compactionThreshold;
}
public void setCompactionThreshold(int compactionThreshold) {
this.compactionThreshold = compactionThreshold;
}
public DirectoryProperties[] getDirectory() {
return this.directoryProperties;
}
public void setDirectory(DirectoryProperties[] directoryProperties) {
this.directoryProperties = directoryProperties;
}
public float getDiskUsageCriticalPercentage() {
return this.diskUsageCriticalPercentage;
}
public void setDiskUsageCriticalPercentage(float diskUsageCriticalPercentage) {
this.diskUsageCriticalPercentage = diskUsageCriticalPercentage;
}
public float getDiskUsageWarningPercentage() {
return this.diskUsageWarningPercentage;
}
public void setDiskUsageWarningPercentage(float diskUsageWarningPercentage) {
this.diskUsageWarningPercentage = diskUsageWarningPercentage;
}
public long getMaxOplogSize() {
return this.maxOplogSize;
}
public void setMaxOplogSize(long maxOplogSize) {
this.maxOplogSize = maxOplogSize;
}
public int getQueueSize() {
return this.queueSize;
}
public void setQueueSize(int queueSize) {
this.queueSize = queueSize;
}
public long getTimeInterval() {
return this.timeInterval;
}
public void setTimeInterval(long timeInterval) {
this.timeInterval = timeInterval;
}
public int getWriteBufferSize() {
return this.writeBufferSize;
}
public void setWriteBufferSize(int writeBufferSize) {
this.writeBufferSize = writeBufferSize;
}
}
}

Some files were not shown because too many files have changed in this diff Show More