SGF-512 - Configure GemFire System Properties with annotations.

This commit is contained in:
John Blum
2016-08-29 15:25:44 -07:00
parent 64ccca1551
commit a1b79a308a
7 changed files with 838 additions and 205 deletions

View File

@@ -28,11 +28,13 @@ import org.springframework.context.annotation.Import;
/**
* The EnableManager annotation marks a Spring {@link org.springframework.context.annotation.Configuration @Configuration}
* annotated class to embed and start a GemFire Manager service in the GemFire server/data node.
* annotated class to configure, embed and start a GemFire/Geode Manager service in the GemFire/Geode Server.
*
* Automatically sets {@literal jmx-manager} to {@literal true}.
*
* @author John Blum
* @see org.springframework.context.annotation.Import
* @see ManagerConfiguration
* @see org.springframework.data.gemfire.config.annotation.ManagerConfiguration
* @since 1.9.0
*/
@Target(ElementType.TYPE)
@@ -43,15 +45,6 @@ import org.springframework.context.annotation.Import;
@SuppressWarnings("unused")
public @interface EnableManager {
/**
* If {@literal true} then this member is willing to be a JMX Manager. All the other JMX Manager properties will be
* used when it does become a manager. If this property is {@literal false} then all other jmx-manager-* properties
* are ignored.
*
* Defaults to {@literal true}.
*/
boolean jmxManager() default true;
/**
* By default, the JMX Manager will allow full access to all mbeans by any client. If this property is set to
* the name of a file then it can restrict clients to only being able to read MBeans; they will not be able
@@ -59,17 +52,19 @@ public @interface EnableManager {
* in the password file. For more information about the format of this file see Oracle's documentation
* of the {@code com.sun.management.jmxremote.access.file} System property. Ignored if {@literal jmx-manager}
* is false or if {@literal jmx-manager-port} is zero.
*
* Defaults to unset.
*/
String jmxManagerAccessFile() default "";
String accessFile() default "";
/**
* By default, the JMX Manager (when configured with a port) will listen on all the local host's addresses.
* You can use this property to configure what IP address or host name the JMX Manager will listen on for
* non-HTTP connections. Ignored if JMX Manager is {@literal false} or {@literal jmx-manager-port} is zero.
*
* Defaults to {@literal localhost}.
* Defaults to unset.
*/
String jmxManagerBindAddress() default "";
String bindAddress() default "";
/**
* Lets you control what hostname will be given to clients that ask the Locator for the location of a JMX Manager.
@@ -77,9 +72,9 @@ public @interface EnableManager {
* this property allows you to configure a different hostname that will be given to clients. Ignored if
* {@literal jmx-manager} is {@literal false} or {@literal jmx-manager-port} is zero.
*
* Defaults to {@literal localhost}.
* Defaults to unset.
*/
String jmxManagerHostnameForClients() default "";
String hostnameForClients() default "";
/**
* By default, the JMX Manager will allow clients without credentials to connect. If this property is set to
@@ -87,8 +82,10 @@ public @interface EnableManager {
* be allowed. Most JVMs require that the file is only readable by the owner. For more information about the
* format of this file see Oracle's documentation of the {@literal com.sun.management.jmxremote.password.file}
* System property. Ignored if {@literal jmx-manager} is {@literal false} or {@literal jmx-manager-port} is zero.
*
* Defaults to unset.
*/
String jmxManagerPasswordFile() default "";
String passwordFile() default "";
/**
* The port this JMX Manager will listen to for client connections. If this property is set to zero then GemFire
@@ -97,61 +94,25 @@ public @interface EnableManager {
*
* Defaults to {@literal 1099}.
*/
int jmxManagerPort() default ManagerConfiguration.DEFAULT_JMX_MANAGER_PORT;
int port() default ManagerConfiguration.DEFAULT_JMX_MANAGER_PORT;
/**
* Enables or disables SSL for connections to the JMX Manager. If {@literal true} and {@literal jmx-manager-port}
* is not zero, then the JMX Manager will only accept SSL connections. If this property is not set, then GemFire
* uses the value of {@literal cluster-ssl-enabled} to determine whether JMX connections should use SSL.
* If {@literal true} then this member will start a JMX Manager when it creates a cache. Management tools
* like Gfsh can be configured to connect to the JMX Manager. In most cases you should not set this
* because a JMX Manager will automatically be started when needed on a member that sets {@literal jmx-manager}
* to {@literal true}. Ignored if {@literal jmx-manager} is {@literal false}.
*
* Defaults to {@literal false}.
*/
boolean jmxManagerSslEnabled() default false;
/**
* A space-separated list of the valid SSL ciphers for JMX Manager connections. A setting of 'any' uses any ciphers
* that are enabled by default in the configured JSSE provider. If this property is not set, then GemFire uses
* the value of {@literal cluster-ssl-ciphers} to determine which SSL ciphers are used for JMX connections.
*
* Defaults to {@literal any}.
*/
String jmxManagerSslCiphers() default "any";
/**
* A space-separated list of the valid SSL protocols for JMX Manager connections. A setting of 'any' uses any
* protocol that is enabled by default in the configured JSSE provider. If this property is not set, then GemFire
* uses the value of {@literal cluster-ssl-protocols} to determine which SSL protocols are used for JMX connections.
*
* Defaults to {@literal any}.
*/
String jmxManagerSslProtocols() default "any";
/**
* Boolean indicating whether to require authentication for JMX Manager connections. If this property is not set,
* then GemFire uses the value of {@literal cluster-ssl-require-authentication} to determine whether JMX connections
* require authentication.
*
* Defaults to {@literal true}.
*/
boolean jmxManagerSslRequireAuthentication() default true;
/**
* If true then this member will start a JMX Manager when it creates a cache. Management tools like Gfsh can be
* configured to connect to the JMX Manager. In most cases you should not set this because a JMX Manager will
* automatically be started when needed on a member that sets {@literal jmx-manager} to {@literal true}. Ignored if
* {@literal jmx-manager} is {@literal false}.
*
* Defaults to {@literal false}.
*/
boolean jmxManagerStart() default false;
boolean start() default false;
/**
* The rate, in milliseconds, at which this member will push updates to any JMX Managers. Currently this value
* should be greater than or equal to the {@literal statistic-sample-rate}. Setting this value too high will
* cause stale values to be seen by Gfsh and GemFire Pulse.
*
* Defaults to {@literal 2000}.
* Defaults to {@literal 2000} milliseconds.
*/
int jmxManagerUpdateRate() default 2000;
int updateRate() default 2000;
}

View File

@@ -0,0 +1,178 @@
/*
* Copyright 2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.springframework.data.gemfire.config.annotation;
import java.util.Map;
import java.util.Properties;
import org.springframework.data.gemfire.config.annotation.support.EmbeddedServiceConfigurationSupport;
import org.springframework.data.gemfire.util.PropertiesBuilder;
/**
* The GemFirePropertiesConfiguration class is a Spring {@link org.springframework.context.annotation.ImportBeanDefinitionRegistrar}
* capable of configuring additional GemFire Properties on the (Spring Boot) application class at runtime
* during startup.
*
* @author John Blum
* @see org.springframework.data.gemfire.config.annotation.WithGemFireProperties
* @see org.springframework.data.gemfire.config.annotation.support.EmbeddedServiceConfigurationSupport
* @since 1.9.0
*/
public class GemFirePropertiesConfiguration extends EmbeddedServiceConfigurationSupport {
public static final boolean DEFAULT_CONSERVE_SOCKETS = true;
public static final boolean DEFAULT_DELTA_PROPAGATION = true;
public static final boolean DEFAULT_DISABLE_TCP = false;
public static final boolean DEFAULT_ENABLE_NETWORK_PARTITION_DETECTION = false;
public static final boolean DEFAULT_ENFORCE_UNIQUE_HOST = false;
public static final boolean DEFAULT_LOAD_CLUSTER_CONFIGURATION_FROM_DIRECTORY = false;
public static final boolean DEFAULT_LOCK_MEMORY = false;
public static final boolean DEFAULT_REMOVE_UNRESPONSIVE_CLIENT = false;
public static final int DEFAULT_ACK_SEVERE_ALERT_THRESHOLD = 0;
public static final int DEFAULT_ACK_WAIT_THRESHOLD = 15;
public static final int DEFAULT_ASYNC_MAX_QUEUE_SIZE = 8;
public static final int DEFAULT_DISTRIBUTED_SYSTEM_ID = -1;
public static final int DEFAULT_SOCKET_BUFFER_SIZE = 32768;
public static final int DEFAULT_TCP_PORT = 0;
public static final int DEFAULT_TOMBSTONE_THRESHOLD = 100000;
public static final int DEFAULT_UDP_FRAGMENT_SIZE = 60000;
public static final int DEFAULT_UDP_RECEIVE_BUFFER_SIZE = 1048576;
public static final int DEFAULT_UDP_SEND_BUFFER_SIZE = 65535;
public static final long DEFAULT_ASYNC_DISTRIBUTION_TIMEOUT = 0L;
public static final long DEFAULT_ASYNC_QUEUE_TIMEOUT = 60000L;
public static final long DEFAULT_LOCATOR_WAIT_TIME = 0L;
public static final long DEFAULT_MAX_WAIT_TIME_RECONNECT = 60000L;
public static final long DEFAULT_MEMBER_TIMEOUT = 5000L;
public static final long DEFAULT_SOCKET_LEASE_TIME = 60000L;
public static final String DEFAULT_CONFLATE_EVENTS = "server";
public static final String DEFAULT_DEPLOY_WORKING_DIRECTORY = ".";
public static final String DEFAULT_MEMBERSHIP_PORT_RANGE = "1024-65535";
/* (non-Javadoc) */
@Override
protected Class getAnnotationType() {
return WithGemFireProperties.class;
}
/* (non-Javadoc) */
@Override
protected Properties toGemFireProperties(Map<String, Object> annotationAttributes) {
PropertiesBuilder gemfireProperties = new PropertiesBuilder();
gemfireProperties.setPropertyIfNotDefault("ack-severe-alert-threshold",
annotationAttributes.get("ackSevereAlertThreshold"), DEFAULT_ACK_SEVERE_ALERT_THRESHOLD);
gemfireProperties.setPropertyIfNotDefault("ack-wait-threshold",
annotationAttributes.get("ackWaitThreshold"), DEFAULT_ACK_WAIT_THRESHOLD);
gemfireProperties.setPropertyIfNotDefault("async-distribution-timeout",
annotationAttributes.get("asyncDistributionTimeout"), DEFAULT_ASYNC_DISTRIBUTION_TIMEOUT);
gemfireProperties.setPropertyIfNotDefault("async-max-queue-size",
annotationAttributes.get("asyncMaxQueueSize"), DEFAULT_ASYNC_MAX_QUEUE_SIZE);
gemfireProperties.setPropertyIfNotDefault("async-queue-timeout",
annotationAttributes.get("asyncQueueTimeout"), DEFAULT_ASYNC_QUEUE_TIMEOUT);
gemfireProperties.setProperty("bind-address", annotationAttributes.get("bindAddress"));
gemfireProperties.setProperty("cache-xml-file", annotationAttributes.get("cacheXmlFile"));
gemfireProperties.setProperty("cluster-configuration-dir",
annotationAttributes.get("clusterConfigurationDirectory"));
gemfireProperties.setPropertyIfNotDefault("conflate-events",
annotationAttributes.get("conflateEvents"), DEFAULT_CONFLATE_EVENTS);
gemfireProperties.setPropertyIfNotDefault("conserve-sockets",
annotationAttributes.get("conserveSockets"), DEFAULT_CONSERVE_SOCKETS);
gemfireProperties.setPropertyIfNotDefault("delta-propagation",
annotationAttributes.get("deltaPropagation"), DEFAULT_DELTA_PROPAGATION);
gemfireProperties.setPropertyIfNotDefault("deploy-working-dir",
annotationAttributes.get("deployWorkingDirectory"), DEFAULT_DEPLOY_WORKING_DIRECTORY);
gemfireProperties.setPropertyIfNotDefault("disable-tcp",
annotationAttributes.get("disableTcp"), DEFAULT_DISABLE_TCP);
gemfireProperties.setPropertyIfNotDefault("distributed-system-id",
annotationAttributes.get("distributedSystemId"), DEFAULT_DISTRIBUTED_SYSTEM_ID);
gemfireProperties.setPropertyIfNotDefault("enable-network-partition-detection",
annotationAttributes.get("enableNetworkPartitionDetection"), DEFAULT_ENABLE_NETWORK_PARTITION_DETECTION);
gemfireProperties.setPropertyIfNotDefault("enforce-unique-host",
annotationAttributes.get("enforceUniqueHost"), DEFAULT_ENFORCE_UNIQUE_HOST);
gemfireProperties.setProperty("groups", (String[]) annotationAttributes.get("groups"));
gemfireProperties.setPropertyIfNotDefault("load-cluster-configuration-from-dir",
annotationAttributes.get("loadClusterConfigurationFromDirectory"),
DEFAULT_LOAD_CLUSTER_CONFIGURATION_FROM_DIRECTORY);
gemfireProperties.setPropertyIfNotDefault("locator-wait-time",
annotationAttributes.get("locatorWaitTimeout"), DEFAULT_LOCATOR_WAIT_TIME);
gemfireProperties.setPropertyIfNotDefault("lock-memory",
annotationAttributes.get("lockMemory"), DEFAULT_LOCK_MEMORY);
gemfireProperties.setPropertyIfNotDefault("max-wait-time-reconnect",
annotationAttributes.get("maxWaitTimeReconnect"), DEFAULT_MAX_WAIT_TIME_RECONNECT);
gemfireProperties.setPropertyIfNotDefault("member-timeout",
annotationAttributes.get("memberTimeout"), DEFAULT_MEMBER_TIMEOUT);
gemfireProperties.setPropertyIfNotDefault("membership-port-range",
annotationAttributes.get("membershipPortRange"), DEFAULT_MEMBERSHIP_PORT_RANGE);
gemfireProperties.setProperty("redundancy-zone", annotationAttributes.get("redundancyZone"));
gemfireProperties.setProperty("remote-locators", annotationAttributes.get("remoteLocators"));
gemfireProperties.setPropertyIfNotDefault("remove-unresponsive-client",
annotationAttributes.get("removeUnresponsiveClient"), DEFAULT_REMOVE_UNRESPONSIVE_CLIENT);
gemfireProperties.setPropertyIfNotDefault("socket-buffer-size",
annotationAttributes.get("socketBufferSize"), DEFAULT_SOCKET_BUFFER_SIZE);
gemfireProperties.setPropertyIfNotDefault("socket-lease-time",
annotationAttributes.get("socketLeaseTime"), DEFAULT_SOCKET_LEASE_TIME);
gemfireProperties.setPropertyIfNotDefault("tcp-port", annotationAttributes.get("tcpPort"), DEFAULT_TCP_PORT);
gemfireProperties.setPropertyIfNotDefault("tombstone-gc-threshold",
annotationAttributes.get("tombstoneGcThreshold"), DEFAULT_TOMBSTONE_THRESHOLD);
gemfireProperties.setPropertyIfNotDefault("udp-fragment-size",
annotationAttributes.get("udpFragmentSize"), DEFAULT_UDP_FRAGMENT_SIZE);
gemfireProperties.setPropertyIfNotDefault("udp-recv-buffer-size",
annotationAttributes.get("udpReceiveBufferSize"), DEFAULT_UDP_RECEIVE_BUFFER_SIZE);
gemfireProperties.setPropertyIfNotDefault("udp-send-buffer-size",
annotationAttributes.get("udpSendBufferSize"), DEFAULT_UDP_SEND_BUFFER_SIZE);
gemfireProperties.setProperty("user-command-packages", annotationAttributes.get("userCommandPackages"));
return gemfireProperties.build();
}
}

View File

@@ -25,7 +25,7 @@ import org.springframework.data.gemfire.util.PropertiesBuilder;
/**
* The ManagerConfiguration class is a Spring {@link org.springframework.context.annotation.ImportBeanDefinitionRegistrar}
* that applies additional GemFire configuration by way of GemFire System properties to configure
* that applies additional GemFire configuration using GemFire System {@link Properties} to configure
* an embedded GemFire Manager.
*
* @author John Blum
@@ -46,27 +46,15 @@ public class ManagerConfiguration extends EmbeddedServiceConfigurationSupport {
protected Properties toGemFireProperties(Map<String, Object> annotationAttributes) {
PropertiesBuilder gemfireProperties = new PropertiesBuilder();
boolean jmxManager = Boolean.valueOf(String.valueOf(annotationAttributes.get("jmxManager")));
if (jmxManager) {
gemfireProperties.setProperty("jmx-manager", Boolean.TRUE.toString());
gemfireProperties.setProperty("jmx-manager-access-file", annotationAttributes.get("jmxManagerAccessFile"));
gemfireProperties.setProperty("jmx-manager-bind-address", annotationAttributes.get("jmxManagerBindAddress"));
gemfireProperties.setProperty("jmx-manager-hostname-for-clients", annotationAttributes.get("jmxManagerHostnameForClients"));
gemfireProperties.setProperty("jmx-manager-password-file", annotationAttributes.get("jmxManagerPasswordFile"));
gemfireProperties.setProperty("jmx-manager-port", resolvePort((Integer) annotationAttributes.get("jmxManagerPort"), DEFAULT_JMX_MANAGER_PORT));
gemfireProperties.setProperty("jmx-manager-start", annotationAttributes.get("jmxManagerStart"));
gemfireProperties.setProperty("jmx-manager-update-rate", annotationAttributes.get("jmxManagerUpdateRate"));
boolean jmxManagerSslEnabled = Boolean.valueOf(String.valueOf(annotationAttributes.get("jmxManagerSslEnabled")));
if (jmxManagerSslEnabled) {
gemfireProperties.setProperty("jmx-manager-ssl-enabled", Boolean.TRUE.toString());
gemfireProperties.setProperty("jmx-manager-ssl-ciphers", annotationAttributes.get("jmxManagerSslCiphers"));
gemfireProperties.setProperty("jmx-manager-ssl-protocols", annotationAttributes.get("jmxManagerSslProtocols"));
gemfireProperties.setProperty("jmx-manager-ssl-require-authentication", annotationAttributes.get("jmxManagerSslRequireAuthentication"));
}
}
gemfireProperties.setProperty("jmx-manager", Boolean.TRUE.toString());
gemfireProperties.setProperty("jmx-manager-access-file", annotationAttributes.get("accessFile"));
gemfireProperties.setProperty("jmx-manager-bind-address", annotationAttributes.get("bindAddress"));
gemfireProperties.setProperty("jmx-manager-hostname-for-clients", annotationAttributes.get("hostnameForClients"));
gemfireProperties.setProperty("jmx-manager-password-file", annotationAttributes.get("passwordFile"));
gemfireProperties.setProperty("jmx-manager-port",
resolvePort((Integer) annotationAttributes.get("port"), DEFAULT_JMX_MANAGER_PORT));
gemfireProperties.setProperty("jmx-manager-start", annotationAttributes.get("start"));
gemfireProperties.setProperty("jmx-manager-update-rate", annotationAttributes.get("updateRate"));
return gemfireProperties.build();
}

View File

@@ -0,0 +1,437 @@
/*
* Copyright 2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.springframework.data.gemfire.config.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Import;
/**
* The WithGemFireProperties annotation marks a Spring {@link org.springframework.context.annotation.Configuration @Configuration}
* annotated class to configure GemFire/Geode System properties at runtime during [Spring Boot] application startup.
*
* @author John Blum
* @see org.springframework.data.gemfire.config.annotation.GemFirePropertiesConfiguration
* @see <a href="http://gemfire.docs.pivotal.io/docs-gemfire/reference/topics/gemfire_properties.html">GemFire System Properties</a>
* @see <a href="http://geode.docs.pivotal.io/docs/reference/topics/gemfire_properties.html">Geode System Properties</a>
* @since 1.9.0
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@Import(GemFirePropertiesConfiguration.class)
@SuppressWarnings("unused")
public @interface WithGemFireProperties {
/**
* Number of seconds the distributed system will wait after the {@literal ack-wait-threshold} for a message
* to be acknowledged before it issues an alert at severe level. A value of zero disables this feature.
*
* Defaults to {@literal 0} seconds.
*/
int ackSevereAlertThreshold() default GemFirePropertiesConfiguration.DEFAULT_ACK_SEVERE_ALERT_THRESHOLD;
/**
* Number of seconds a distributed message can wait for acknowledgment before it sends an alert to signal
* that something might be wrong with the system member that is unresponsive.
*
* The waiter continues to wait. The alerts are logged in the system members log as warnings.
* Valid values are in the range 0…2147483647
*
* Defaults to {@literal 15} seconds.
*/
int ackWaitThreshold() default GemFirePropertiesConfiguration.DEFAULT_ACK_WAIT_THRESHOLD;
/**
* The number of milliseconds a process that is publishing to this process should attempt to distribute
* a cache operation before switching over to asynchronous messaging for this process. The switch to
* asynchronous messaging lasts until this process catches up, departs, or some specified limit is reached,
* such as async-queue-timeout or async-max-queue-size.
*
* This setting controls only peer-to-peer communication and does not apply to client/server
* or multi-site communication.
*
* Defaults to {@literal 0} milliseconds.
*/
long asyncDistributionTimeout() default GemFirePropertiesConfiguration.DEFAULT_ASYNC_DISTRIBUTION_TIMEOUT;
/**
* Affects non-conflated asynchronous queues for members that publish to this member. This is the maximum size
* the queue can reach (in megabytes) before the publisher asks this member to leave the distributed system.
*
* Valid values are in the range 0..1024.
*
* This setting controls only peer-to-peer communication and does not apply to client/server
* or multi-site communication.
*
* Defaults to {@literal 8} MB.
*/
int asyncMaxQueueSize() default GemFirePropertiesConfiguration.DEFAULT_ASYNC_MAX_QUEUE_SIZE;
/**
* Affects asynchronous queues for members that publish to this member. This is the maximum milliseconds
* the publisher should wait with no distribution to this member before it asks this member to leave
* the distributed system. Used for handling slow receivers.
*
* This setting controls only peer-to-peer communication and does not apply to client/server
* or multi-site communication.
*
* Defaults to {@literal 60000} milliseconds.
*/
long asyncQueueTimeout() default GemFirePropertiesConfiguration.DEFAULT_ASYNC_QUEUE_TIMEOUT;
/**
* Relevant only for multi-homed hosts - machines with multiple network interface cards (NICs).
* Specifies the adapter card the cache binds to for peer-to-peer (P2P) communication. Also specifies
* the default location for GemFire Servers to listen on, which is used unless overridden by
* the {@literal server-bind-address}. An empty string causes the member to listen on the default card
* for the machine. This is a machine-wide attribute used for system member and client/server communication.
* It has no effect on Locator location, unless the Locator is embedded in a member process.
*
* Specify the IP address, not the hostname, because each network card may not have a unique hostname.
* An empty string (the default) causes the member to listen on the default card for the machine.
*
* Defaults to unset.
*/
String bindAddress() default "";
/**
* Declarative initialization file for the members cache.
*
* Defaults to unset.
*/
String cacheXmlFile() default "";
/**
* This property specifies the directory in which the cluster configuration related disk-store and artifacts
* are stored. This property is only applicable to dedicated Locators that have {@literal enable-cluster-configuration}
* set to {@literal true}.
*
* Defaults to unset.
*/
String clusterConfigurationDirectory() default "";
/**
* Used only by clients in a client/server installation. This is a client-side property
* that is passed to the server. Affects subscription queue conflation in this clients
* servers. Specifies whether to conflate (true setting), not conflate (false), or to
* use the servers conflation setting (server).
*
* Defaults to {@literal server}.
*/
String conflateEvents() default GemFirePropertiesConfiguration.DEFAULT_CONFLATE_EVENTS;
/**
* Specifies whether sockets are shared by the system members threads. If true, threads share,
* and a minimum number of sockets are used to connect to the distributed system. If false,
* every application thread has its own sockets for distribution purposes. You can override
* this setting for individual threads inside your application. Where possible, it is better to
* set {@literal conserve-sockets} to {@literal true} and enable the use of specific extra sockets
* in the application code if needed. WAN deployments increase the messaging demands on a GemFire system.
* To avoid hangs related to WAN messaging, always set {@literal conserve-sockets} to {@literal false}
* for GemFire members that participate in a WAN deployment.
*
* Defaults to {@literal true}.
*/
boolean conserveSockets() default GemFirePropertiesConfiguration.DEFAULT_CONSERVE_SOCKETS;
/**
* Specifies whether to distribute the deltas for entry updates, instead of the full values,
* between clients and servers, and between peers.
*
* Default to {@literal true}.
*/
boolean deltaPropagation() default GemFirePropertiesConfiguration.DEFAULT_DELTA_PROPAGATION;
/**
* Working directory used when deploying JAR application files to distributed system members.
* This directory can be local and unique to the member or a shared resource.
*
* Defaults to current working directory of this GemFire JVM process.
*/
String deployWorkingDirectory() default GemFirePropertiesConfiguration.DEFAULT_DEPLOY_WORKING_DIRECTORY;
/**
* Boolean indicating whether to disable the use of TCP/IP sockets for inter-cache point-to-point messaging.
* If disabled, the cache uses datagram (UDP) sockets.
*
* Defaults to {@literal false}.
*/
boolean disableTcp() default GemFirePropertiesConfiguration.DEFAULT_DISABLE_TCP;
/**
* Identifier used to distinguish messages from different distributed systems.
*
* Set this to different values for different systems in a multi-site (WAN) configuration. This is required
* for Portable Data eXchange (PDX) data serialization. This setting must be the same for every member
* in the same distributed system and unique to the distributed system within the WAN installation.
*
* -1 means no setting. Valid values are integers in the range -1…255.
*
* Defaults to {@literal -1}.
*/
int distributedSystemId() default GemFirePropertiesConfiguration.DEFAULT_DISTRIBUTED_SYSTEM_ID;
/**
* Boolean instructing the system to detect and handle splits in the distributed system, typically caused by
* a partitioning of the network (split brain) where the distributed system is running. We recommend setting
* this property to {@literal true}. You must set this property to the same value across all your
* distributed system members. In addition, you must set this property to {@literal true} if you are using
* persistent Regions and configure your Regions to use {@literal DISTRIBUTED_ACK} or {@literal GLOBAL} scope
* to avoid potential data conflicts.
*
* Defaults to {@literal false}.
*/
boolean enableNetworkPartitionDetection() default GemFirePropertiesConfiguration.DEFAULT_ENABLE_NETWORK_PARTITION_DETECTION;
/**
* Whether partitioned regions will put redundant copies of the same data in different members
* running on the same physical machine. By default, GemFire tries to put redundant copies on different machines,
* but it will put them on the same machine if no other machines are available. Setting this property
* to {@literal true} prevents this and requires different machines for redundant copies.
*
* Defaults to {@literal false}.
*/
boolean enforceUniqueHost() default GemFirePropertiesConfiguration.DEFAULT_ENFORCE_UNIQUE_HOST;
/**
* Defines the list of groups that this member belongs to. Use commas to separate group names.
* Note that anything defined by the {@literal roles} gemfire property will also be considered
* a group.
*
* Default to unset.
*/
String[] groups() default {};
/**
* Setting this property to {@literal true} causes loading of cluster configuration from the
* {@literal cluster_config} directory in the Locator. This property is only applicable to dedicated Locators
* that have {@literal enable-cluster-configuration} set to {@literal true}.
*
* Defaults to {@literal false}.
*/
boolean loadClusterConfigurationFromDirectory() default GemFirePropertiesConfiguration.DEFAULT_LOAD_CLUSTER_CONFIGURATION_FROM_DIRECTORY;
/**
* The number of seconds that a member should wait for a Locator to start if a Locator is not available
* when attempting to join the distributed system. Use this setting when you are starting Locators
* and peers all at once. This timeout allows peers to wait for the Locators to finish starting up
* before attempting to join the distributed system.
*
* Defaults to {@literal 0} seconds.
*/
long locatorWaitTimeout() default GemFirePropertiesConfiguration.DEFAULT_LOCATOR_WAIT_TIME;
/**
* When true, locks heap and off-heap memory into RAM to prevent the operating system from paging the memory
* out to disk.
*
* Defaults to {@literal false}.
*/
boolean lockMemory() default GemFirePropertiesConfiguration.DEFAULT_LOCK_MEMORY;
/**
* Maximum number of milliseconds to wait for the distributed system to reconnect on each reconnect attempt.
*
* {@link PeerCacheApplication#enableAutoReconnect()} or {@link CacheServerApplication#enableAutoReconnect()}
* must be set to {@literal true} for this property to have any effect.
*
* Defaults to {@literal 60000} milliseconds.
*/
long maxWaitTimeReconnect() default GemFirePropertiesConfiguration.DEFAULT_MAX_WAIT_TIME_RECONNECT;
/**
* GemFire uses the {@literal member-timeout} server configuration, specified in milliseconds, to detect
* the abnormal termination of members. The configuration setting is used in two ways:
*
* 1) First, it is used during the UDP heartbeat detection process. When a member detects that
* a heartbeat datagram is missing from the member that it is monitoring after the time interval
* of 2 * the value of member-timeout, the detecting member attempts to form a TCP/IP stream-socket connection
* with the monitored member as described in the next case.
*
* 2) The property is then used again during the TCP/IP stream-socket connection. If the suspected process
* does not respond to the are you alive datagram within the time period specified in member-timeout,
* the membership coordinator sends out a new membership view that notes the members failure.
*
* Valid values are in the range 1000..600000.
*
* Defaults to {@literal 5000} milliseconds.
*/
long memberTimeout() default GemFirePropertiesConfiguration.DEFAULT_MEMBER_TIMEOUT;
/**
* The range of ports available for unicast UDP messaging and for TCP failure detection. This is specified
* as two integers separated by a hyphen. Different members can use different ranges.
*
* GemFire randomly chooses at least two unique integers from this range for the member, one for
* UDP unicast messaging and the other for TCP failure detection messaging. If {@literal tcp-port}
* is configured to {@literal 0}, it will also randomly select a port from this range for TCP sockets
* used for peer-to-peer communication only.
*
* Therefore, the specified range must include at least three available port numbers (UDP, FD_SOCK,
* and TCP DirectChannel).
*
* The system uniquely identifies the member using the combined host IP address and UDP port number.
*
* You may want to restrict the range of ports that GemFire uses so the product can run in an environment
* where routers only allow traffic on certain ports.
*
* Defaults to {@literal 1024-65535}.
*/
String membershipPortRange() default GemFirePropertiesConfiguration.DEFAULT_MEMBERSHIP_PORT_RANGE;
/**
* Defines this members redundancy zone. Used to separate members into different groups for satisfying
* Partitioned Region redundancy. If this property is set, GemFire will not put redundant copies of data
* in members with the same redundancy zone setting.
*
* Defaults to unset.
*
* @see <a href="http://geode.docs.pivotal.io/docs/developing/partitioned_regions/configuring_ha_for_pr.html">Configure High Availability for a Partitioned Region</a>
*/
String redundancyZone() default "";
/**
* Used to configure the Locators that a cluster will use in order to connect to a remote site in a multi-site
* (WAN) configuration.
*
* To use Locators in a WAN configuration, you must specify a unique distributed system ID
* ({@literal distributed-system-id}) for the local cluster and remote Locator(s) for the remote clusters
* to which you will connect.
*
* For each remote Locator, provide a host name and/or address (separated by @, if you use both),
* followed by a port number in brackets.
*
* Examples:
*
* <pre>
* <code>
* remote-locators=address1[port1],address2[port2]
*
* remote-locators=hostName1@address1[port1],hostName2@address2[port2]
*
* remote-locators=hostName1[port1],hostName2[port2]
* </code>
* </pre>
*
* Defaults to unset.
*/
String remoteLocators() default "";
/**
* When this property is set to {@literal true}, the primary server drops unresponsive clients
* from all secondaries and itself. Clients are deemed unresponsive when their messaging queues
* become full on the server. While a clients queue is full, puts that would add to the queue
* block on the server.
*
* Defaults to {@literal false}.
*/
boolean removeUnresponsiveClient() default GemFirePropertiesConfiguration.DEFAULT_REMOVE_UNRESPONSIVE_CLIENT;
/**
* Receive buffer sizes in bytes of the TCP/IP connections used for data transmission. To minimize the buffer size
* allocation needed for distributing large, serializable messages, the messages are sent in chunks. This setting
* determines the size of the chunks. Larger buffers can handle large messages more quickly, but take up
* more memory.
*
* Defaults to {@literal 32768} bytes.
*/
int socketBufferSize() default GemFirePropertiesConfiguration.DEFAULT_SOCKET_BUFFER_SIZE;
/**
* Time, in milliseconds, a thread can have exclusive access to a socket it is not actively using.
* A value of zero causes socket leases to never expire. This property is ignored
* if {@literal conserve-sockets} is {@literal true}.
*
* Valid values are in the range 0..600000.
*
* Defaults to {@literal 60000} milliseconds.
*/
long socketLeaseTime() default GemFirePropertiesConfiguration.DEFAULT_SOCKET_LEASE_TIME;
/**
* The TCP port to listen on for cache communications. If set to zero, the operating system selects
* an available port. Each process on a machine must have its own TCP port. Note that some operating systems
* restrict the range of ports usable by non-privileged users, and using restricted port numbers can cause
* runtime errors in GemFire startup.
*
* Valid values are in the range 0..65535.
*
* Defaults to {@literal 0}.
*/
int tcpPort() default GemFirePropertiesConfiguration.DEFAULT_TCP_PORT;
/**
* The number of tombstones that can accumulate before the GemFire member triggers garbage collection
* for tombstones.
*
* Defaults to {@literal 100000} tombstones.
*
* @see <a href="http://geode.docs.pivotal.io/docs/developing/distributed_regions/how_region_versioning_works.html#topic_321B05044B6641FCAEFABBF5066BD399">How Destroy and Clear Operations Are Resolved</a>
*/
int tombstoneGcThreshold() default GemFirePropertiesConfiguration.DEFAULT_TOMBSTONE_THRESHOLD;
/**
* Maximum fragment size, in bytes, for transmission over UDP unicast or multicast sockets. Smaller messages
* are combined, if possible, for transmission up to the fragment size setting.
*
* Valid values are in the range 1000..60000.
*
* Defaults to {@literal 60000} bytes.
*/
int udpFragmentSize() default GemFirePropertiesConfiguration.DEFAULT_UDP_FRAGMENT_SIZE;
/**
* The size of the socket buffer used for incoming UDP point-to-point transmissions. If {@literal disable-tcp}
* is {@literal false}, a reduced buffer size of 65535 is used by default.
*
* The default setting of 1048576 is higher than the default OS maximum buffer size on Unix, which should be
* increased to at least 1 megabyte to provide high-volume messaging on Unix systems.
*
* Valid values are in the range 2048.. OS_maximum.
*
* Defaults to {@literal 1048576} bytes.
*/
int udpReceiveBufferSize() default GemFirePropertiesConfiguration.DEFAULT_UDP_RECEIVE_BUFFER_SIZE;
/**
* The size of the socket buffer used for outgoing UDP point-to-point transmissions.
*
* Valid values are in the range 2048..OS_maximum.
*
* Defaults to {@literal 65535} bytes.
*/
int udpSendBufferSize() default GemFirePropertiesConfiguration.DEFAULT_UDP_SEND_BUFFER_SIZE;
/**
* A comma separated list of Java packages that contain classes implementing the Spring Shell
* CommandMarker interface.
*
* Matching classes will be loaded when the VM starts and will be available in the GFSH command-line utility.
*
* Defaults to unset.
*/
String userCommandPackages() default "";
}

View File

@@ -73,9 +73,8 @@ public abstract class EmbeddedServiceConfigurationSupport implements ImportBeanD
/* (non-Javadoc) */
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
if (importingClassMetadata.hasAnnotation(getAnnotationTypeName())) {
Map<String, Object> annotationAttributes =
importingClassMetadata.getAnnotationAttributes(getAnnotationTypeName());
if (isAnnotationPresent(importingClassMetadata)) {
Map<String, Object> annotationAttributes = getAnnotationAttributes(importingClassMetadata);
Properties customGemFireProperties = toGemFireProperties(annotationAttributes);
@@ -93,11 +92,21 @@ public abstract class EmbeddedServiceConfigurationSupport implements ImportBeanD
/* (non-Javadoc) */
protected abstract Properties toGemFireProperties(Map<String, Object> annotationAttributes);
/* (non-Javadoc) */
protected boolean isAnnotationPresent(AnnotationMetadata importingClassMetadata) {
return importingClassMetadata.hasAnnotation(getAnnotationTypeName());
}
/* (non-Javadoc) */
protected boolean hasProperties(Properties properties) {
return !CollectionUtils.isEmpty(properties);
}
/* (non-Javadoc) */
protected Map<String, Object> getAnnotationAttributes(AnnotationMetadata importingClassMetadata) {
return importingClassMetadata.getAnnotationAttributes(getAnnotationTypeName());
}
/* (non-Javadoc) */
protected void registerGemFirePropertiesBeanPostProcessor(BeanDefinitionRegistry registry,
Properties customGemFireProperties) {

View File

@@ -24,6 +24,7 @@ import java.util.Properties;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
/**
@@ -49,6 +50,70 @@ public class PropertiesBuilder implements FactoryBean<Properties> {
return new PropertiesBuilder();
}
/**
* Constructs and initializes a {@link PropertiesBuilder} containing all properties
* from the given {@link InputStream}.
*
* @param in {@link InputStream} source containing properties to use as the defaults for the constructed builder.
* @return a {@link PropertiesBuilder} initialized with properties from the given {@link InputStream}.
* @throws IllegalArgumentException if the {@link InputStream} cannot be read.
* @see java.io.InputStream
* @see java.util.Properties#load(InputStream)
*/
public static PropertiesBuilder from(InputStream in) {
try {
Properties defaults = new Properties();
defaults.load(in);
return new PropertiesBuilder(defaults);
}
catch (IOException e) {
throw new IllegalArgumentException("Failed to read properties from InputStream", e);
}
}
/**
* Constructs and initializes a {@link PropertiesBuilder} containing all properties
* from the given {@link Reader}.
*
* @param reader {@link Reader} source containing properties to use as the defaults for the constructed builder.
* @return a {@link PropertiesBuilder} initialized with properties from the given {@link Reader}.
* @throws IllegalArgumentException if the {@link Reader} cannot be read.
* @see java.io.Reader
* @see java.util.Properties#load(Reader)
*/
public static PropertiesBuilder from(Reader reader) {
try {
Properties defaults = new Properties();
defaults.load(reader);
return new PropertiesBuilder(defaults);
}
catch (IOException e) {
throw new IllegalArgumentException("Failed to read properties from Reader", e);
}
}
/**
* Constructs and initializes a {@link PropertiesBuilder} containing all properties
* from the given {@link InputStream} in XML format.
*
* @param xml {@link InputStream} source containing properties in XML format to use as defaults
* for the constructed builder.
* @return a {@link PropertiesBuilder} initialized with properties from the given XML {@link InputStream}.
* @throws IllegalArgumentException if the XML {@link InputStream} cannot be read.
* @see java.io.InputStream
* @see java.util.Properties#loadFromXML(InputStream)
*/
public static PropertiesBuilder fromXml(InputStream xml) {
try {
Properties defaults = new Properties();
defaults.loadFromXML(xml);
return new PropertiesBuilder(defaults);
}
catch (IOException e) {
throw new IllegalArgumentException("Failed to read properties from XML", e);
}
}
/**
* Constructs an instance of the {@link PropertiesBuilder} class.
*/
@@ -60,6 +125,7 @@ public class PropertiesBuilder implements FactoryBean<Properties> {
* Constructs an instance of the {@link PropertiesBuilder} class initialized with the default {@link Properties}.
*
* @param defaults {@link Properties} used as the defaults.
* @throws NullPointerException if the {@link Properties} reference is {@literal null}.
* @see java.util.Properties
*/
public PropertiesBuilder(Properties defaults) {
@@ -72,10 +138,11 @@ public class PropertiesBuilder implements FactoryBean<Properties> {
* {@link PropertiesBuilder} providing the default {@link Properties} for this builder.
*
* @param builder {@link PropertiesBuilder} providing the default {@link Properties} for this builder.
* @throws NullPointerException if the {@link PropertiesBuilder} reference is {@literal null}.
* @see #PropertiesBuilder(Properties)
*/
public PropertiesBuilder(PropertiesBuilder builder) {
this(builder != null ? builder.build() : null);
this(builder.build());
}
/*
@@ -130,68 +197,7 @@ public class PropertiesBuilder implements FactoryBean<Properties> {
* @see org.springframework.data.gemfire.util.PropertiesBuilder
*/
public PropertiesBuilder add(PropertiesBuilder builder) {
if (builder != null) {
add(builder.build());
}
return this;
}
/**
* Adds all properties from the given {@link InputStream} to this builder.
*
* @param in {@link InputStream} source containing properties to add to this builder.
* @return a reference to this {@link PropertiesBuilder}.
* @throws IllegalArgumentException if the {@link InputStream} cannot be read.
* @see java.util.Properties#load(InputStream)
* @see java.io.InputStream
*/
public PropertiesBuilder from(InputStream in) {
try {
this.properties.load(in);
return this;
}
catch (IOException e) {
throw new IllegalArgumentException("Failed to read properties from InputStream", e);
}
}
/**
* Adds all properties from the given {@link Reader} to this builder.
*
* @param reader {@link Reader} source containing properties to add to this builder.
* @return a reference to this {@link PropertiesBuilder}.
* @throws IllegalArgumentException if the {@link Reader} cannot be read.
* @see java.util.Properties#load(Reader)
* @see java.io.Reader
*/
public PropertiesBuilder from(Reader reader) {
try {
this.properties.load(reader);
return this;
}
catch (IOException e) {
throw new IllegalArgumentException("Failed to read properties from Reader", e);
}
}
/**
* Adds all properties from the given {@link InputStream} in XML format to this builder.
*
* @param xml {@link InputStream} source containing properties in XML format to add to this builder.
* @return a reference to this {@link PropertiesBuilder}.
* @throws IllegalArgumentException if the XML {@link InputStream} cannot be read.
* @see java.util.Properties#loadFromXML(InputStream)
* @see java.io.InputStream
*/
public PropertiesBuilder fromXml(InputStream xml) {
try {
this.properties.loadFromXML(xml);
return this;
}
catch (IOException e) {
throw new IllegalArgumentException("Failed to read properties from XML", e);
}
return (builder != null ? add(builder.build()) : this);
}
/**
@@ -206,6 +212,11 @@ public class PropertiesBuilder implements FactoryBean<Properties> {
return (value != null ? setProperty(name, value.toString()) : this);
}
public PropertiesBuilder setProperty(String name, Object[] values) {
return (!ObjectUtils.isEmpty(values) ? setProperty(name, StringUtils.arrayToCommaDelimitedString(values))
: this);
}
/**
* Sets a property with the given name to the specified {@link String} value. The property is only set
* if the value is not {@literal null}, an empty {@link String} or not equal to the {@link String} literal
@@ -218,7 +229,7 @@ public class PropertiesBuilder implements FactoryBean<Properties> {
* @see java.util.Properties#setProperty(String, String)
*/
public PropertiesBuilder setProperty(String name, String value) {
Assert.hasText(name, String.format("Name [%s] must not be specified", name));
Assert.hasText(name, String.format("Name [%s] must be specified", name));
if (isValuable(value)) {
this.properties.setProperty(name, value);
@@ -227,6 +238,10 @@ public class PropertiesBuilder implements FactoryBean<Properties> {
return this;
}
public <T> PropertiesBuilder setPropertyIfNotDefault(String name, Object value, T defaultValue) {
return (defaultValue == null || !defaultValue.equals(value) ? setProperty(name, value) : this);
}
/**
* Determine whether the given {@link String} value is a valid {@link Properties} value. A property value is
* considered valid if it is not null, not empty and not equal to (case-insensitive) {@link String} literal

View File

@@ -39,7 +39,7 @@ import org.junit.Test;
* @author John Blum
* @see java.util.Properties
* @see org.springframework.data.gemfire.util.PropertiesBuilder
* @since 1.0.0
* @since 1.9.0
*/
public class PropertiesBuilderTests {
@@ -84,59 +84,10 @@ public class PropertiesBuilderTests {
assertThat(properties, is(notNullValue(Properties.class)));
assertThat(properties.size(), is(equalTo(1)));
assertThat(properties.containsKey("one"), is(true));
assertThat(properties.getProperty("one"), is(equalTo("1")));
}
@Test
public void propertiesBuilderIsSingleton() {
assertThat(new PropertiesBuilder().isSingleton(), is(true));
}
@Test
public void addPropertiesFromPropertiesIsSuccessful() {
PropertiesBuilder builder = PropertiesBuilder.create()
.setProperty("one", "1")
.setProperty("two", "@");
Properties sink = builder.build();
assertThat(sink, is(notNullValue(Properties.class)));
assertThat(sink.size(), is(2));
assertThat(sink.getProperty("one"), is(equalTo("1")));
assertThat(sink.getProperty("two"), is(equalTo("@")));
assertThat(sink.containsKey("three"), is(false));
Properties source = new Properties();
source.setProperty("two", "2");
source.setProperty("three", "3");
builder.add(source);
sink = builder.build();
assertThat(sink, is(notNullValue(Properties.class)));
assertThat(sink.size(), is(equalTo(3)));
assertThat(sink, is(not(sameInstance(source))));
assertThat(sink.getProperty("one"), is(equalTo("1")));
assertThat(sink.getProperty("two"), is(equalTo("2")));
assertThat(sink.getProperty("three"), is(equalTo("3")));
}
@Test
public void addPropertiesFromPropertiesBuilderIsSuccessful() {
PropertiesBuilder source = PropertiesBuilder.create()
.setProperty("one", "1")
.setProperty("two", "2");
Properties properties = new PropertiesBuilder().add(source).build();
assertThat(properties, is(notNullValue(Properties.class)));
assertThat(properties.size(), is(equalTo(2)));
assertThat(properties.getProperty("one"), is(equalTo("1")));
assertThat(properties.getProperty("two"), is(equalTo("2")));
}
@Test
public void fromInputStreamIsSuccessful() throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
@@ -145,7 +96,7 @@ public class PropertiesBuilderTests {
source.store(out, "fromInputStreamIsSuccessfulTest");
Properties sink = PropertiesBuilder.create().from(new ByteArrayInputStream(out.toByteArray())).build();
Properties sink = PropertiesBuilder.from(new ByteArrayInputStream(out.toByteArray())).build();
assertThat(sink, is(notNullValue(Properties.class)));
assertThat(sink, is(not(sameInstance(source))));
@@ -160,7 +111,7 @@ public class PropertiesBuilderTests {
source.store(writer, "fromReaderIsSuccessfulTest");
Properties sink = PropertiesBuilder.create().from(new StringReader(writer.toString())).build();
Properties sink = PropertiesBuilder.from(new StringReader(writer.toString())).build();
assertThat(sink, is(notNullValue(Properties.class)));
assertThat(sink, is(not(sameInstance(source))));
@@ -175,13 +126,76 @@ public class PropertiesBuilderTests {
source.storeToXML(out, "fromXmlInputStreamIsSuccessfulTest");
Properties sink = PropertiesBuilder.create().fromXml(new ByteArrayInputStream(out.toByteArray())).build();
Properties sink = PropertiesBuilder.fromXml(new ByteArrayInputStream(out.toByteArray())).build();
assertThat(sink, is(notNullValue(Properties.class)));
assertThat(sink, is(not(sameInstance(source))));
assertThat(sink, is(equalTo(source)));
}
@Test
@SuppressWarnings("unchecked")
public void propertiesBuilderObjectTypeIsPropertiesClass() {
assertThat((Class<Properties>) PropertiesBuilder.create().getObjectType(), is(equalTo(Properties.class)));
}
@Test
public void propertiesBuilderIsSingletonIsTrue() {
assertThat(new PropertiesBuilder().isSingleton(), is(true));
}
@Test
public void addPropertiesFromPropertiesIsSuccessful() {
PropertiesBuilder builder = PropertiesBuilder.create()
.setProperty("one", "1")
.setProperty("two", "@");
Properties sink = builder.build();
assertThat(sink, is(notNullValue(Properties.class)));
assertThat(sink.size(), is(2));
assertThat(sink.containsKey("one"), is(true));
assertThat(sink.containsKey("two"), is(true));
assertThat(sink.containsKey("three"), is(false));
assertThat(sink.getProperty("one"), is(equalTo("1")));
assertThat(sink.getProperty("two"), is(equalTo("@")));
Properties source = new Properties();
source.setProperty("two", "2");
source.setProperty("three", "3");
builder.add(source);
sink = builder.build();
assertThat(sink, is(notNullValue(Properties.class)));
assertThat(sink.size(), is(equalTo(3)));
assertThat(sink, is(not(sameInstance(source))));
assertThat(sink.containsKey("one"), is(true));
assertThat(sink.containsKey("two"), is(true));
assertThat(sink.containsKey("three"), is(true));
assertThat(sink.getProperty("one"), is(equalTo("1")));
assertThat(sink.getProperty("two"), is(equalTo("2")));
assertThat(sink.getProperty("three"), is(equalTo("3")));
}
@Test
public void addPropertiesFromPropertiesBuilderIsSuccessful() {
PropertiesBuilder source = PropertiesBuilder.create()
.setProperty("one", "1")
.setProperty("two", "2");
Properties properties = PropertiesBuilder.create().add(source).build();
assertThat(properties, is(notNullValue(Properties.class)));
assertThat(properties.size(), is(equalTo(2)));
assertThat(properties.containsKey("one"), is(true));
assertThat(properties.containsKey("two"), is(true));
assertThat(properties.getProperty("one"), is(equalTo("1")));
assertThat(properties.getProperty("two"), is(equalTo("2")));
}
@Test
public void setObjectPropertyValuesIsSuccessful() {
Properties properties = PropertiesBuilder.create()
@@ -201,9 +215,21 @@ public class PropertiesBuilderTests {
assertThat(properties.getProperty("string"), is(equalTo("test")));
}
@Test
public void setObjectArrayPropertyValueIsSuccessful() {
Properties properties = PropertiesBuilder.create()
.setProperty("numbers", new Object[] { "one", "two", "three" })
.build();
assertThat(properties, is(notNullValue(Properties.class)));
assertThat(properties.size(), is(equalTo(1)));
assertThat(properties.containsKey("numbers"), is(true));
assertThat(properties.getProperty("numbers"), is(equalTo("one,two,three")));
}
@Test
public void setPropertyIgnoresNullObjectValue() {
Properties properties = PropertiesBuilder.create().setProperty("object", null).build();
Properties properties = PropertiesBuilder.create().setProperty("object", (Object) null).build();
assertThat(properties, is(notNullValue(Properties.class)));
assertThat(properties.isEmpty(), is(true));
@@ -215,12 +241,29 @@ public class PropertiesBuilderTests {
.setProperty("blank", " ")
.setProperty("empty", "")
.setProperty("null", "null")
.setProperty("nullWithWhiteSpace", " null ")
.build();
assertThat(properties, is(notNullValue(Properties.class)));
assertThat(properties.isEmpty(), is(true));
}
@Test
public void setPropertyIgnoresEmptyObjectArray() {
Properties properties = PropertiesBuilder.create().setProperty("emptyArray", new Object[0]).build();
assertThat(properties, is(notNullValue(Properties.class)));
assertThat(properties.isEmpty(), is(true));
}
@Test
public void setPropertyIgnoresNullObjectArray() {
Properties properties = PropertiesBuilder.create().setProperty("nullArray", (Object[]) null).build();
assertThat(properties, is(notNullValue(Properties.class)));
assertThat(properties.isEmpty(), is(true));
}
@Test
public void setStringPropertyValuesIsSuccessful() {
Properties properties = PropertiesBuilder.create()
@@ -230,6 +273,8 @@ public class PropertiesBuilderTests {
assertThat(properties, is(notNullValue(Properties.class)));
assertThat(properties.size(), is(equalTo(2)));
assertThat(properties.containsKey("one"), is(true));
assertThat(properties.containsKey("two"), is(true));
assertThat(properties.getProperty("one"), is(equalTo("1")));
assertThat(properties.getProperty("two"), is(equalTo("2")));
}