/* * Copyright 2010-2013 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.data.gemfire.client; import java.util.List; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.geode.cache.InterestResultPolicy; import org.springframework.beans.factory.InitializingBean; import org.springframework.core.Constants; import org.springframework.util.Assert; /** * The Interest class holds details for registering a client interest. * * @author Costin Leau * @author John Blum * @see java.util.regex.Pattern * @see org.apache.geode.cache.InterestResultPolicy * @see org.springframework.beans.factory.InitializingBean * @see org.springframework.core.Constants * @since 1.0.0 */ @SuppressWarnings("unused") public class Interest<K> implements InitializingBean { public static final String ALL_KEYS = "ALL_KEYS"; protected static final boolean DEFAULT_DURABLE = false; protected static final boolean DEFAULT_RECEIVE_VALUES = true; private static final Constants constants = new Constants(InterestResultPolicy.class); protected final Log logger = LogFactory.getLog(getClass()); private boolean durable = false; private boolean receiveValues = true; private InterestResultPolicy policy = InterestResultPolicy.DEFAULT; private K key; private Type type; /** * Factory method to construct a new instance of {@link Interest} initialized with the given key. * * @param <K> {@link Class} type of the key. * @param key key of interest. * @return a new instance of {@link Interest} initialized with the given key. * @see #Interest(Object) */ public static <K> Interest newInterest(K key) { return new Interest<>(key); } /** * Constructs an instance of non-durable {@link Interest} initialized with the given key to register interest in, * using the {@link InterestResultPolicy#DEFAULT} to initialize the client cache and receiving values by default. * * @param key key(s) of interest. * @see #Interest(Object, InterestResultPolicy, boolean, boolean) */ public Interest(K key) { this(key, InterestResultPolicy.DEFAULT, DEFAULT_DURABLE, DEFAULT_RECEIVE_VALUES); } /** * Constructs an instance of non-durable {@link Interest} initialized with the given key to register interest in, * the given {@link InterestResultPolicy} used to initialize the client cache, receiving values by default. * * @param key key(s) of interest. * @param policy initial {@link InterestResultPolicy} used to initialize the client cache. * @see #Interest(Object, InterestResultPolicy, boolean, boolean) */ public Interest(K key, InterestResultPolicy policy) { this(key, policy, DEFAULT_DURABLE, DEFAULT_RECEIVE_VALUES); } /** * Constructs an instance of {@link Interest} initialized with the given key to register interest in, * the given {@link InterestResultPolicy} used to initialize the client cache, the given boolean value * to indicate whether interest registration should be durable, receiving values by default. * * @param key key(s) of interest. * @param policy initial {@link InterestResultPolicy} used to initialize the client cache. * @param durable boolean value to indicate whether the interest registration should be durable. * @see #Interest(Object, InterestResultPolicy, boolean, boolean) */ public Interest(K key, InterestResultPolicy policy, boolean durable) { this(key, policy, durable, DEFAULT_RECEIVE_VALUES); } /** * Constructs an instance of {@link Interest} initialized with the given key to register interest in, * the given {@link InterestResultPolicy} used to initialize the client cache and the given boolean values * indicating whether interest registration should be durable and whether to receive values during notifications. * * @param key key(s) of interest. * @param policy initial {@link InterestResultPolicy} used to initialize the client cache. * @param durable boolean value to indicate whether the interest registration should be durable. * @param receiveValues boolean value to indicate whether to receive value in notifications. * @see #Interest(Object, InterestResultPolicy, boolean, boolean) * @see #afterPropertiesSet() */ public Interest(K key, InterestResultPolicy policy, boolean durable, boolean receiveValues) { this.key = key; this.policy = policy; this.durable = durable; this.receiveValues = receiveValues; afterPropertiesSet(); } /** * @deprecated * @see #Interest(Object, InterestResultPolicy) */ @Deprecated public Interest(K key, String policy) { this(key, policy, DEFAULT_DURABLE, DEFAULT_RECEIVE_VALUES); } /** * @deprecated * @see #Interest(Object, InterestResultPolicy, boolean) */ @Deprecated public Interest(K key, String policy, boolean durable) { this(key, policy, durable, DEFAULT_RECEIVE_VALUES); } /** * @deprecated * @see #Interest(Object, InterestResultPolicy, boolean, boolean) */ @Deprecated public Interest(K key, String policy, boolean durable, boolean receiveValues) { this(key, (InterestResultPolicy) constants.asObject(policy), durable, receiveValues); } /** * @inheritDoc */ public void afterPropertiesSet() { Assert.notNull(key, "Key is required"); setType(resolveType(getType())); } /** * Attempts to resolve the {@link Interest.Type} based on the configured {@link #getKey()}. * * @param type provided {@link Interest.Type} used if {@literal non-null}. * @return the resolved {@link Interest.Type}. * @see #isRegularExpression(Object) */ protected Type resolveType(Type type) { return (type != null ? type : (isRegularExpression(getKey()) ? Type.REGEX : Type.KEY)); } /** * Determines whether the given {@code key} is a Regular Expression (Regex). * * If the given {@code key} is {@literal "ALL_KEYS"}, a {@link List} or only contains letters, numbers and spaces, * then the {@code key} is not considered a Regular Expression by GemFire, and can be handled with normal * interest registration using {@link org.apache.geode.cache.Region#registerInterest(Object)}. * * @param key {@link Object} to evaluate. * @return a boolean value indicating whether the given {@link Object} {@code key} is a Regular Expression. * @see #isRegularExpression(String) */ protected boolean isRegularExpression(Object key) { return (!(ALL_KEYS.equals(key) || key instanceof List) && isRegularExpression(String.valueOf(key))); } /** * Determines whether the given {@code key} is a Regular Expression (Regex). * * If the given {@code value} contains at least 1 special character (e.g. *) and can be compiled * using {@link Pattern#compile(String)}, then the {@code key} is considered a Regular Expression * and interest will be registered using {@link org.apache.geode.cache.Region#registerInterestRegex(String)}. * * @param value {@link String} to evaluate. * @return a boolean value indicating whether the given {@link String} {@code value} is a Regular Expression. * @see #containsNonAlphaNumericWhitespace(String) * @see java.util.regex.Pattern#compile(String) */ @SuppressWarnings("all") protected boolean isRegularExpression(String value) { try { return (containsNonAlphaNumericWhitespace(value) && Pattern.compile(value) != null); } catch (PatternSyntaxException ignore) { return false; } } /** * Determines whether the given {@link String} value contains at least 1 special character. * * @param value {@link String} to evaluate. * @return a boolean value indicating whether the given {@link String} contains at least 1 special character, * a non-alphanumeric, non-whitespace character. * @see #isAlphaNumericWhitespace(char) * @see #isNotAlphaNumericWhitespace(char) */ protected boolean containsNonAlphaNumericWhitespace(String value) { for (char character : String.valueOf(value).toCharArray()) { if (isNotAlphaNumericWhitespace(character)) { return true; } } return false; } /** * Determines whether the given {@code character} is a special character (non-alphanumeric, non-whitespace). * * @param character {@link Character} to evaluate. * @return a boolean value indicating whether the given {@code character} is a special character. * @see #isAlphaNumericWhitespace(char) */ protected boolean isNotAlphaNumericWhitespace(char character) { return !isAlphaNumericWhitespace(character); } /** * Determines whether the given {@code character} is an alphanumeric or whitespace character. * * @param character {@link Character} to evaluate. * @return a boolean value indicating whether the given {@code character} is an alphanumeric * or whitespace character. * @see java.lang.Character#isDigit(char) * @see java.lang.Character#isLetter(char) * @see java.lang.Character#isWhitespace(char) */ protected boolean isAlphaNumericWhitespace(char character) { return (Character.isDigit(character) || Character.isLetter(character) || Character.isWhitespace(character)); } /** * Determines whether the interest registration is durable and persists between cache client sessions. * * @return a boolean value indicating whether this interest registration is durable. */ public boolean isDurable() { return this.durable; } /** * Sets whether interest registration is durable and persists between cache client sessions. * * @param durable boolean value to indicate whether this interest registration is durable. */ public void setDurable(boolean durable) { this.durable = durable; } /** * Returns the key on which interest is registered. * * @return the key of interest. */ public K getKey() { return this.key; } /** * Sets the key on which interest is registered. * * @param key the key of interest. */ public void setKey(K key) { this.key = key; } /** * Returns the {@link InterestResultPolicy} used when interest is registered and determines whether KEYS, * KEYS_VALUES or nothing (NONE) is initially fetched on initial registration. * * @return the policy */ public InterestResultPolicy getPolicy() { return this.policy; } /** * Sets the initial {@link InterestResultPolicy} used when interest is first registered and determines whether KEYS, * KEYS_VALUE or nothing (NONE) is initially fetched. * * The argument is set as an {@link Object} to be able to accept both {@link InterestResultPolicy} * and {@link String Strings}, used in XML configuration meta-data. * * @param policy initial {@link InterestResultPolicy} to set. * @throws IllegalArgumentException if the given {@code policy} is not a valid type. * @see org.apache.geode.cache.InterestResultPolicy */ public void setPolicy(Object policy) { if (policy instanceof InterestResultPolicy) { this.policy = (InterestResultPolicy) policy; } else if (policy instanceof String) { this.policy = (InterestResultPolicy) constants.asObject(String.valueOf(policy)); } else { throw new IllegalArgumentException(String.format( "Unknown argument type [%s] for property 'policy'", policy)); } } /** * Returns the type of values received by the listener. * * @return the receiveValues */ public boolean isReceiveValues() { return this.receiveValues; } /** * Switches between the different entities received by the listener. * * @param receiveValues the receiveValues to set */ public void setReceiveValues(boolean receiveValues) { this.receiveValues = receiveValues; } /** * Returns the type of interest registration (e.g. based on KEY or Regex). * * @return a {@link Interest.Type} determining the type of interest. * @see Interest.Type */ public Type getType() { return this.type; } /** * Set the type of interest registration (e.g. based on KEY or Regex). * * @param type {@link Interest.Type} qualifying the type of interest. * @see Interest.Type */ public void setType(Type type) { this.type = type; } /** * Determines whether this {@link Interest} is a KEY interest registration. * * @return a boolean value indicating whether this is KEY interest. * @see Interest.Type#KEY * @see #getType() */ public boolean isKeyType() { return Type.KEY.equals(getType()); } /** * Determines whether this {@link Interest} is a REGEX interest registration. * * @return a boolean value indicating whether this is REGEX interest. * @see Interest.Type#REGEX * @see #getType() */ public boolean isRegexType() { return Type.REGEX.equals(getType()); } /** * @inheritDoc */ @Override public String toString() { return String.format("{ @type = %1$s, key = %2$s, durable = %3$s, policy = %4$s, receiveValues = %5$s, type = %6$s }", getClass().getName(), getKey(), isDurable(), getPolicy(), isReceiveValues(), getType()); } /** * Builder method to specify the type of interest registration. * * @param type {@link Interest.Type} of interest registration. * @return this {@link Interest}. * @see Interest.Type * @see #resolveType(Type) * @see #setType(Type) */ public Interest asType(Type type) { setType(resolveType(type)); return this; } /** * Builder method to mark this {@link Interest} as durable. * * @return this {@link Interest}. * @see #setDurable(boolean) */ public Interest makeDurable() { setDurable(true); return this; } /** * Builder method to set whether the interest event notifications will receive values along with keys. * * @param receiveValues boolean to indicate that value should be sent along with keys * on interest event notifications. * @return this {@link Interest}. * @see #setReceiveValues(boolean) */ public Interest receivesValues(boolean receiveValues) { setReceiveValues(receiveValues); return this; } /** * Builder method to set the {@link InterestResultPolicy} used to initialize the cache. * * @param policy {@link InterestResultPolicy}. * @return this {@link Interest}. * @see org.apache.geode.cache.InterestResultPolicy * @see #setPolicy(Object) */ public Interest usingPolicy(InterestResultPolicy policy) { setPolicy(policy); return this; } /** * Builder method to express the key of interest. * * @param key key of interests. * @return this {@link Interest}. * @see #setKey(Object) * @see #getType() * @see #resolveType(Type) * @see #setType(Type) */ public Interest withKey(K key) { setKey(key); setType(resolveType(getType())); return this; } /** * Type of interest registration. */ public enum Type { KEY, REGEX } }